数据结构与算法学习笔记 —— 栈(stack)
栈 Stack
1.简介
# 本片学习笔记为观看北京大学陈斌教授的熟虑结构与算法课程(python版)记录
栈是一种有次序的数据项集合,该集合的两端分别为栈底(base)和 栈顶(top)
*数据项的添加和移除仅发生在栈顶
进出栈原则:后进先出(Last in First Out)
距离栈底过越近的数据项,留在栈中的时间就越长。而最新加入栈的数据项会被最先移除。
用List实现 ADT Stack:
此处选用List的末端(index = -1)作为栈顶:
1 class Stack: 2 def __init__(self): #构造一个stack对象 3 self.items = [] 4 5 def push(self,item): #将item添加至栈顶,无返回值 6 self.items.append(item) 7 8 def pop(self): #移除栈顶元素,并返回修改后的栈 9 return self.items.pop() 10 11 def peek(self): #返回栈顶元素 12 return self.items[len(self.items)-1] 13 14 def isEmpty(self): #返回栈是否为空 15 return self.items == [] 16 17 def size(self): #返回栈中数据项的数量 18 return len(self.items)
注: 选用list左端为栈顶也可,但性能有所不同。栈顶首端为左的情况下,push和pop的复杂度为O(n), 而栈顶尾端右的情况下,push和pop的复杂度为O(1)
2.栈的应用
2.1 括号匹配
括号的使用必须遵循平衡原则:
1. 每个开口括号,必须对应一个闭口括号
2. 每对括号必须要正确的嵌套
Eg.
正确的: (()()()()),(((()))),(()((())()))
错误的: ((((((()),())),(()()(()
方法:
从左到右扫面括号串,最新遇到的左括号,应该匹配到最先遇到的右括号。因此,第一个左括号应该匹配到最后一个右括号。这种次序反转的识别模式,刚好符合栈的特性。
所以我们可以从左扫面括号串,若为左边开口括号push到栈顶,若为右括号则先判断栈是否为空。如果空了,那么说明没有左括号与其匹配,则这个括号串是错误的。若栈不为空,则将栈顶的左括号pop(与刚刚扫描到的右括号匹配成功)
扫描完成后,再检查一下栈中的左括号有没有全部被移除(匹配)出去。若仍有剩余,则括号串不合法。
具体流程可见下图:
python代码实现:
s1 = "(()((())()))" s2 = "((((((())" def parChecker(s): stack = Stack() for item in s: if item == '(': stack.push(item) else: if stack.isEmpty(): return False else: stack.pop() return stack.isEmpty() print(parChecker(s1,s2))
* 如果涉及多种不同的括号(如[] {} ()), 这要留意括号的匹配问题
2.2 进制转换
将十进制整数N转换为base进制:
def convert(num,base): stack = Stack() output = '' while num > 0: remainder = num % base stack.push(str(remainder)) num = num // base while not stack.isEmpty(): output = output + stack.pop() return output
2.3 表达式转换求值
对输入的表达式进行求值:例如 A + B*C
step1.
首先将表达式转换为后缀表达式,
即 首先将中缀表达式转换为全括号形式。然后将所有的操作符移动到子表达式所在的右括号处,替代之,再删除所有括号
中缀转后缀的流程为:
如果扫描对象token是操作数,则直接添加到后缀表达式列表的末尾。
如果token是左括号,则押入opstack栈顶
如果是右括号,则反复pop出栈顶并添加到后缀表达式列表的末尾
如果操作符,则push到栈顶:
- 但是在push之前, 要比较其与栈顶操作符的优先级
- 如果栈顶的高于或等于它,就要反复弹出栈顶操作符,直到栈顶操作符优先级低于它
def infixtopostfix(a): priority = { '*':3, '/':3, '+':2, '-':2, '(':1 } opstack = Stack() output = [] a = a.split() for token in a: if token.isdigit(): output.append(token) elif token == '(': opstack.push(token) elif token == ')': topToken = opstack.pop() while topToken != '(': output.append(topToken) topToken = opstack.pop() else: while (not opstack.isEmpty()) and priority[token] <= priority[opstack.peek()]: output.append(opstack.pop()) opstack.push(token) while not opstack.isEmpty(): output.append(opstack.pop()) return output
求值过程:
从左到右扫描token列表
如果扫描为一个操作数,则将其转化为整数并且push到operandstack栈中;
如果扫描为一个操作符,则pop出栈顶两个数(第一个为有操作数,第二个为做操作数,顺序很重要)。
计算出结果并且push回栈中:
def postfixeval(l): operands = Stack() for token in l: if token.isdigit(): operands.push(int(token)) else: operand_r = operands.pop() operand_l = operands.pop() if token == '-': operands.push(operand_l - operand_r) elif token == '+': operands.push(operand_l + operand_r) elif token == '*': operands.push(operand_l * operand_r) else: operands.push(operand_l/operand_r) return operands.pop()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架