常见数据结构的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实现相关推荐

  1. python处理时间序列非平稳_手把手教你用Python处理非平稳时间序列

    简介 预测一个家庭未来三个月的用电量,估计特定时期道路上的交通流量,预测一只股票在纽约证券交易所交易的价格--这些问题都有什么共同点? 它们都属于时间序列数据的范畴!如果没有"时间" ...

  2. java栈和队列验证回文串_栈和队列的基本操作及其应用(回文判断)

    实验二栈和队列的基本操作及其应用 一.实验目的 1.掌握栈和队列的顺序存储结构和链式存储结构,以便在实际中灵活应用. 2.掌握栈和队列的特点,即后进先出和先进先出的原则. 3.掌握栈和队列的基本运算, ...

  3. 最小栈设计并实现一个minstack类_栈相关

    20. 有效的括号 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效. 有效字符串需满足: 左括号必须用相同类型的右括号闭合. 左括号必须以正确的顺序闭合. ...

  4. 关于递归函数转换为非递归函数的一些方式

    关于递归函数转换非递归函数的一些方式 前言 目的 可行性 转换的几种途径 转换示例 第一个例子:阶乘n! 第二个例子:菲波那契数列 效率的比较 1.阶乘三种方式函数的执行效率比较 2.Fibonacc ...

  5. 百融榕树使用非Java语言栈,百融榕树具有明显优势

    百融榕树Service Mesh架构出现之前,由于相同的语言栈有明显的协同优势,这显然会导致研发团队在选择语言栈时会有所顾虑,甚至不是按照适用的场景选择语言,比如百融榕树S初创团队一开始选择使用了Ja ...

  6. 栈和队列之仅用递归函数和栈操作逆序一个栈

    import java.util.Stack;/*** recursion 递归的意思* @author chenyu* 题目:仅用递归函数和栈操作逆序一个栈,列如一次压入1.2.3.4.5 栈顶到栈 ...

  7. 递归算法转化为非递归算法

    (1)直接用循环结构的算法替代递归算法(直接转化法,不需要使用栈) (2)用栈模拟系统的运行过程,通过分析只保存必须保存的信息,从而用非递归方法替代递归算法.(间接转化法,需要使用栈) 用循环结构替代 ...

  8. php递归函数实用吗,php递归函数怎么用才有效

    本篇讲解php递归函数的的有效操作. 有关php递归函数的用法,分享几个php递归函数的例子,在php编程中,使用递归进行函数调用很常见,递归函数用的好,可以提高代码效率,通过例子学习php递归函数的 ...

  9. [Effective JavaScript 笔记]第29条:避免使用非标准的栈检查属性

    许多js环境都提供检查调用栈的功能.调用栈是指当前正在执行的活动函数链.在某些旧的宿主环境中,每个arguments对象含有两个额外的属性:arguments.callee和arguments.cal ...

最新文章

  1. 2022-2028年中国防水橡胶布行业市场发展模式及投资前景分析报告
  2. cad画流程图的插件_CAD制图太慢?62款辅助插件汇总,款款精品,效率提升80%
  3. ubuntu安装openssh-server 报依赖错误的解决过程
  4. java高并发下的数据安全
  5. 开启 Truffle Ganache  MetaMask交互
  6. 11gR2 RAC手动添加节点数据库实例
  7. Page.ClientScript.RegisterStartupScript() 方法与Page.ClientScript.RegisterClientScriptBlock() 方法...
  8. java 异常对象_在java中的异常处理中的异常对象是什么
  9. 虚拟机网卡无法启动获取ip地址
  10. 促销惊喜活动优惠海报设计,可临摹PSD分层格式
  11. 实时时钟DS1302
  12. 考研程序设计30题系列(21-30题)
  13. 14行Python代码,让AI和AI无限聊天,他们会聊出什么
  14. Flutter获取Android/iOS设备信息
  15. IOS手机长按图片无法弹出识别二维码
  16. install - graph-tool
  17. mysql个人办公使用_Access数据库是给办公人员用的~闲杂人等不要来凑热闹
  18. 杰理强制升级工具4.0使用和原理解析
  19. 为什么工程师出身的 CEO 越来越“香”?
  20. web前端培训开发,你必须知道的CSS盒模型

热门文章

  1. matlab uigetfile的用法,matlab中uigetfile的用法
  2. Go gRPC 调试工具 grpcui
  3. 如何查看电脑CPU实时功耗
  4. java量_Java 2. 量与常量
  5. linux卸载splunk,linux安装splunk-enterprise
  6. 高教杯历年真题_喜报 | 2019“高教社”杯全国大学生数学建模竞赛获奖名单!...
  7. centos7 dns配置_Linux Sever简单笔记(第十三堂课)之linux下的网络管理及DHCP配置的相关操作 - 我杨晓东太难了...
  8. C语言程序怎么保存文件,急求如何将下列C语言程序数据存储到文件中?
  9. java 蓝桥杯 求先序排列
  10. 使用邻接矩阵实现有向图最短路径Dijkstra算法