使用栈将递归函数转化为非递归函数_栈(Stack)及其应用-Python实现
常见数据结构的Python实现-栈
目录
1.1 基本概念
1.2 栈的实现
1.3 应用(括号匹配)
1.4 应用(中缀转后缀-整数)
1.5 应用( 中缀转后缀-浮点数)
1) 拆分表达式
2) 中缀转后缀
1.6 应用(计算中缀表达式)
1) 计算后缀表达式
2) 计算中缀表达式
1. 栈
1.1 基本概念
栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。
- 栈顶(top)
- 栈底(bottom)
- 空栈:栈中元素个数为0
- 进栈(push),即插入操作
- 退栈(pop),即删除,出栈,弹栈
复杂度分析:
栈属于常见的一种线性结构,对于进栈和退栈而言,时间复杂度都为 O(1)
本例中所有的代码实现:git仓库地址:01-栈及其应用(计算中缀表达式)
1.2 栈的实现
class Stack(object):def __init__(self):"""创建一个Stack类对栈进行初始化参数设计"""self.stack = [] #存放元素的栈
def push(self, data):"""压入 push :将新元素放在栈顶当新元素入栈时,栈顶上移,新元素放在栈顶。"""self.stack.append(data)
def pop(self):"""弹出 pop :从栈顶移出一个数据- 栈顶元素拷贝出来- 栈顶下移- 拷贝出来的栈顶作为函数返回值"""# 判断是否为空栈if self.stack:return self.stack.pop()else:raise IndexError("从空栈执行弹栈操作")
def peek(self):"""查看栈顶的元素"""# 判断栈是否为空if self.stack:return self.stack[-1]
def is_empty(self):"""判断栈是否为空"""# 栈为非空时,self.stack为True,再取反,为Falsereturn not bool(self.stack)
def size(self):"""返回栈的大小"""return len(self.stack)
1.3 应用(括号匹配)
目标:
使用堆栈作为数据结构,检查括号字符串是否完全匹配
def balanced_parentheses(parentheses):"""由于只检查()是否平衡,比较简单思路是,遇到( 则将( 进栈遇到 )则将当前栈顶的( 出栈如果是这两个符号以外的其他字符,则不执行任何操作如果:在执行 )出栈的时候,栈已经为空,则表示符号不平衡【说明 )更多】在执行完所有的出栈后,栈不为空,则符号也不平衡【说明( 更多】"""
stack = Stack(limit=len(parentheses))
for p in parentheses:if p == '(':stack.push(p)elif p == ')':if stack.is_empty():return Falsestack.pop()
return stack.is_empty()
if __name__ == "__main__":examples = ['(()())', '(()))', '((())', '(()((())())())', '() ( )']print("运行结果:")for example in examples:print(example, ":", str(balanced_parentheses(example)))
运行结果:
1.4 应用(中缀转后缀-整数)
- 中缀表达式
平时所有的标准四则运算表达式,如9 + (3-1) * 3 + 9/2
- 后缀表达式
转化为后缀表达式便于计算机计算9 3 1 - 3 * + 9 2 / +
转化规则
从左到右遍历中缀表达式的每个数字和符号
首先判断字符是否为数字:
- 是数字直接输出
- 不是数字再对字符串进行分析
- 左括号:左括号直接压栈,如果栈顶元素已经为左括号
(
,也不影响左括号进栈 - 右括号:依次弹栈并输出栈里面的符号,直到遇到左括号
(
注意,只有在遇到)
的情况下才弹出(
,其他情况都不弹出(
左括号只弹栈,不输出为后缀表达式的一部分 - 其他符号【
+ - * /
】:- 入栈的条件是:当前符号的优先级高于栈顶元素或者为空栈,否则执行弹栈操作,将依次弹出的符号,作为后缀表达式的一部分,一直弹栈,直到满足入栈要求为止
- 或者称之为,弹栈的条件为:当前字符的优先级小于或等于栈顶元素且栈非空,则执行弹栈,直到栈顶元素比当前字符优先级大,或者当前为空栈,才停止弹栈
- 如果栈顶元素为
(
则当前符号直接压栈
- 左括号:左括号直接压栈,如果栈顶元素已经为左括号
实现代码:
def infix2postfix(strings):# 符号的优先级次序表priority_dict = {'(': 0,'+': 10,'-': 10,'*': 20,'/': 20,')': 30}
# 首先创建空的 栈 数据结构,为后续做好准备stack = Stack()
# 创建空的列表,存放后缀表达式postfix = []
for char in strings:# 先判断当前字符是否为数字,若是,该方法返回True,输出到后缀表达式if char.isdigit():postfix.append(char)
# 若不是数字,再进行其他分析# 若是左括号,直接压栈elif char == "(":stack.push(char)
# 若是右括号,则依次弹栈,直到遇到左括号elif char == ")":while stack.peek() != "(":pop_char = stack.pop()postfix.append(pop_char)# 将符号弹出到后缀表达式后,再将( 弹栈,注意,只弹栈,不输出到后缀表达式stack.pop()
# 如果是加减乘除elif (char == '+' or char == '-' or char == '*' or char == '/'):# 如果当前符号的优先级小于等于栈顶元素,且栈为非空,则弹栈if (stack.is_empty() != True) and (priority_dict[char] <= priority_dict[stack.peek()]):while (stack.is_empty() != True) and (priority_dict[char] <= priority_dict[stack.peek()]):pop_char = stack.pop()postfix.append(pop_char)# 一直弹栈到满足压栈的要求为止,则将当前字符压栈stack.push(char)# 如果当前字符本身优先级就比栈顶元素优先级高,或者当前为空栈,则直接执行压栈操作else:stack.push(char)
# 如果在遍历完表达式后栈不为空,则依次弹栈while stack.is_empty() != True:postfix.append(stack.pop())
print(postfix)return postfix
if __name__ == "__main__":infix2postfix("9+(3-1)*3+9/2")
- 运行结果:
['9', '3', '1', '-', '3', '*', '+', '1', '9', '/', '+']
思考一下,上述程序有问题,
1)只能处理10以内的加减乘除,因为程序是一位一位读取的,因此,会将10拆成 1 和 0
如:
infix2postfix("9+(3-1)*3+10/2")
# 输出结果为:
['9', '3', '1', '-', '3', '*', '+', '1', '0', '2', '/', '+']
2)不能进行带有小数点的浮点数的运算
infix2postfix("9.2+(3-1)*3+9/2")
# 输出结果为:
['9', '2', '3', '1', '-', '3', '*', '+', '9', '2', '/', '+']
后续对这两个存在的问题进行改进
1.5 应用( 中缀转后缀-浮点数)
要解决上面的两个问题,需要将字符串组成的中缀表达式正确地拆分
需要将字符串拆解称为单个字符组成的列表
要实现的效果是:
- 输入表达式字符串
- 拆分成数字和符号组成的字符串列表
1) 拆分表达式
实现代码:
def Strng2ListofString(aString):"""将一个中缀表达式拆分数字(包括整数和浮点数)、符号 拆分成单个字符"""symbol_list = ['+', '-', '*', '/', '(', ')']
# 在最后补全符号,便于后续操作aString = aString + '+'
# 存放拆分后的字符char_list = []single_char = ""
for char in aString:# 如果char不在指定的符号集中,则认为是数字if char not in symbol_list:single_char += charelse:char_list.append(single_char)char_list.append(char)single_char = ""# 删掉空白字符串char_list = [char for char in char_list if char != '']# 删掉多余的补充的加号+char_list = char_list[:-1]
return char_list
2) 中缀转后缀
有了上述的拆分函数,那么就能正确地执行中缀转后缀了
由于中缀转后缀规则中,对数字和符号的处理不同,
这里需要有一个函数,实现判断拆分后的字符串是识别为数字还是符号,该功能实现为:
def is_number(aString):"""判断一个字符串是否是一个数字或者浮点数是,返回True否,返回False"""try:float(aString)return Trueexcept:return False
代码实现:
def infix2postfix(strings):"""输入:未经处理的原始中缀表达式输出:后缀表达式(拆成单个字符组成列表)"""
# 给定符号的优先级次序,后续压栈,弹栈规则使用priority_dict = {'(': 0,'+': 10,'-': 10,'*': 20,'/': 20,')': 30, }
# 将数据字符串表达式处理为 单字符列表strings = Strng2ListofString(strings)
# 为了兼顾第一个符号为负号-的情况# 对于-2*3这种,改写成 0-2*3 的形式if strings[0] == '-':strings.insert(0, '0')
# 首先创建空的 栈 数据结构,为后续做好准备stack = Stack()
# 创建空的列表,存放后缀表达式postfix = []
for char in strings:
# 先判断当前字符是否为数字,若是,该方法返回True,直接附加到后缀表达式中去if is_number(char):postfix.append(char)
# 若不是数字,再进行其他分析# 若是左括号,直接压栈elif char == "(":stack.push(char)
# 若是右括号,则依次弹栈,直到遇到左括号elif char == ")":while stack.peek() != "(":postfix.append(stack.pop())
# 将符号弹出到后缀表达式后,再将( 弹栈,注意,只弹栈,不输出到后缀表达式stack.pop()
# 如果是加减乘除elif (char == '+' or char == '-' or char == '*' or char == '/'):# 如果当前符号的优先级小于等于栈顶元素,且栈为非空,则弹栈if (stack.is_empty() != True) and (priority_dict[char] <= priority_dict[stack.peek()]):while (stack.is_empty() != True) and (priority_dict[char] <= priority_dict[stack.peek()]):postfix.append(stack.pop())# 一直弹栈到满足压栈的要求为止,则将当前字符压栈stack.push(char)# 如果当前字符本身优先级就比栈顶元素优先级高,或者当前为空栈,则直接执行压栈操作else:stack.push(char)
# 如果在遍历完表达式后栈不为空,则依次弹栈while stack.is_empty() != True:postfix.append(stack.pop())
return postfix
运行结果:
能够正确地处理浮点数和大于1位的数字
最后,得到了后缀表达式就可以轻松地利用栈进行运算,计算得出表达式的值
1.6 应用(计算中缀表达式)
将上述的代码组合起来,先由中缀表达式得到后缀表达式,再计算后缀表达式即可
1) 计算后缀表达式
后缀表达式,例如:9 3 1 - 3 * + 9 2 / +
后缀表达式的计算规则为:
- 从左到右遍历表达式的每个数字和符号
- 遇到数字就进栈
- 遇到符号,就将处于栈顶的两个数字进行运算,(top数为操作数,second_top数为被操作数)
如减法,除法,top数为减数、除数;second_top数为被减数、被除数
运算结果进栈 - 一直到遍历结束,将最终得到的计算结果出栈
# 输入后缀表达式进行计算
def cal_postfix(postfix_list):"""输入后缀表达式输出计算结果"""stack = Stack()
for char in postfix_list:if is_number(char):stack.push(char)elif char == '+':top_num = float(stack.pop())sec_num = float(stack.pop())res = sec_num + top_numstack.push(res)elif char == '-':top_num = float(stack.pop())sec_num = float(stack.pop())res = sec_num - top_numstack.push(res)elif char == '*':top_num = float(stack.pop())sec_num = float(stack.pop())res = sec_num * top_numstack.push(res)elif char == '/':top_num = float(stack.pop())sec_num = float(stack.pop())res = sec_num / top_numstack.push(res)
res = stack.pop()return res
2) 计算中缀表达式
将上面函数组合即可
代码实现:
def cal_infix(aString):"""输入:原始的中缀表达式,输出:表达式的计算结果
中间步骤:1.将原始中缀表达式转写为后缀表达式(包含了首位为负号‘-’的处理)infix2postfix2.计算后缀表达式 cal_postfix"""return cal_postfix(infix2postfix(aString))
效果:
使用栈将递归函数转化为非递归函数_栈(Stack)及其应用-Python实现相关推荐
- python处理时间序列非平稳_手把手教你用Python处理非平稳时间序列
简介 预测一个家庭未来三个月的用电量,估计特定时期道路上的交通流量,预测一只股票在纽约证券交易所交易的价格--这些问题都有什么共同点? 它们都属于时间序列数据的范畴!如果没有"时间" ...
- java栈和队列验证回文串_栈和队列的基本操作及其应用(回文判断)
实验二栈和队列的基本操作及其应用 一.实验目的 1.掌握栈和队列的顺序存储结构和链式存储结构,以便在实际中灵活应用. 2.掌握栈和队列的特点,即后进先出和先进先出的原则. 3.掌握栈和队列的基本运算, ...
- 最小栈设计并实现一个minstack类_栈相关
20. 有效的括号 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效. 有效字符串需满足: 左括号必须用相同类型的右括号闭合. 左括号必须以正确的顺序闭合. ...
- 关于递归函数转换为非递归函数的一些方式
关于递归函数转换非递归函数的一些方式 前言 目的 可行性 转换的几种途径 转换示例 第一个例子:阶乘n! 第二个例子:菲波那契数列 效率的比较 1.阶乘三种方式函数的执行效率比较 2.Fibonacc ...
- 百融榕树使用非Java语言栈,百融榕树具有明显优势
百融榕树Service Mesh架构出现之前,由于相同的语言栈有明显的协同优势,这显然会导致研发团队在选择语言栈时会有所顾虑,甚至不是按照适用的场景选择语言,比如百融榕树S初创团队一开始选择使用了Ja ...
- 栈和队列之仅用递归函数和栈操作逆序一个栈
import java.util.Stack;/*** recursion 递归的意思* @author chenyu* 题目:仅用递归函数和栈操作逆序一个栈,列如一次压入1.2.3.4.5 栈顶到栈 ...
- 递归算法转化为非递归算法
(1)直接用循环结构的算法替代递归算法(直接转化法,不需要使用栈) (2)用栈模拟系统的运行过程,通过分析只保存必须保存的信息,从而用非递归方法替代递归算法.(间接转化法,需要使用栈) 用循环结构替代 ...
- php递归函数实用吗,php递归函数怎么用才有效
本篇讲解php递归函数的的有效操作. 有关php递归函数的用法,分享几个php递归函数的例子,在php编程中,使用递归进行函数调用很常见,递归函数用的好,可以提高代码效率,通过例子学习php递归函数的 ...
- [Effective JavaScript 笔记]第29条:避免使用非标准的栈检查属性
许多js环境都提供检查调用栈的功能.调用栈是指当前正在执行的活动函数链.在某些旧的宿主环境中,每个arguments对象含有两个额外的属性:arguments.callee和arguments.cal ...
最新文章
- 2022-2028年中国防水橡胶布行业市场发展模式及投资前景分析报告
- cad画流程图的插件_CAD制图太慢?62款辅助插件汇总,款款精品,效率提升80%
- ubuntu安装openssh-server 报依赖错误的解决过程
- java高并发下的数据安全
- 开启 Truffle Ganache MetaMask交互
- 11gR2 RAC手动添加节点数据库实例
- Page.ClientScript.RegisterStartupScript() 方法与Page.ClientScript.RegisterClientScriptBlock() 方法...
- java 异常对象_在java中的异常处理中的异常对象是什么
- 虚拟机网卡无法启动获取ip地址
- 促销惊喜活动优惠海报设计,可临摹PSD分层格式
- 实时时钟DS1302
- 考研程序设计30题系列(21-30题)
- 14行Python代码,让AI和AI无限聊天,他们会聊出什么
- Flutter获取Android/iOS设备信息
- IOS手机长按图片无法弹出识别二维码
- install - graph-tool
- mysql个人办公使用_Access数据库是给办公人员用的~闲杂人等不要来凑热闹
- 杰理强制升级工具4.0使用和原理解析
- 为什么工程师出身的 CEO 越来越“香”?
- web前端培训开发,你必须知道的CSS盒模型
热门文章
- matlab uigetfile的用法,matlab中uigetfile的用法
- Go gRPC 调试工具 grpcui
- 如何查看电脑CPU实时功耗
- java量_Java 2. 量与常量
- linux卸载splunk,linux安装splunk-enterprise
- 高教杯历年真题_喜报 | 2019“高教社”杯全国大学生数学建模竞赛获奖名单!...
- centos7 dns配置_Linux Sever简单笔记(第十三堂课)之linux下的网络管理及DHCP配置的相关操作 - 我杨晓东太难了...
- C语言程序怎么保存文件,急求如何将下列C语言程序数据存储到文件中?
- java 蓝桥杯 求先序排列
- 使用邻接矩阵实现有向图最短路径Dijkstra算法