栈(stack)定义与实例 栈(stack)定义: 距离栈底越近的数据项, 留在栈中的时间就越长而最新加入栈的数据项会被最先移除,这种次序通常称为“后进先出LIFO”:Last in First out这是一种基于数据项保存时间的次序,时间越短的离栈顶越近,而时间越长的离栈底越近.
抽象数据类型Stack:操作样例
Stack Operation
Stack Contents
Return Value
s= Stack()
[]
Stack object
s.isEmpty()
[]
True
s.push(4)
[4]
s.push(‘dog’)
[4,’dog’]
s.peek()
[4,’dog’]
‘dog’
s.push(True)
[4,’dog’,True]
s.size()
[4,’dog’,True]
3
s.isEmpty()
[4,’dog’,True]
False
s.push(8.4)
[4,’dog’,True,8.4]
s.pop()
[4,’dog’,True]
8.4
s.pop()
[4,’dog’]
True
s.size()
[4,’dog’]
2
Python的面向对象机制, 可以用来实现用户自定义类型 将ADT Stack实现为Python的一个Class,将ADT Stack的操作实现为Class的方法 由于Stack是一个数据集,所以可以采用Python的原生数据集来实现,我们选用最常用的数据集List来实现
可以将List的任意一端(index=0或者-1)设置为栈顶,我们选用List的末端(index=-1)作为栈顶,这样栈的操作就可以通过对list的append和pop来实现:
不同的实现方案保持了ADT接口的稳定性但性能有所不同,栈顶首端的版本(左),其push/pop的复杂度为O(n),而栈顶尾端的实现(右),其push/pop的复杂度为O(1) .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Stack : def __init__ (self ): self.items = [] def isEmpty (self ): return self.items == [] def push (self, item ): self.items.append(item) def pop (self ): return self.items.pop() def peek (self ): return self.items[len(self.items)-1 ] def size (self ): return len(self.items)
下面看看如何构造括号匹配识别算法从左到右扫描括号串,最新打开的左括号,应该匹配最先遇到的右括号这样,第一个左括号(最早打开),就应该匹配最后一个右括号(最后遇到)这种次序反转的识别,正好符合栈的特性 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 def parChecker (symbolString ): s = Stack() balanced = True index = 0 while index < len(symbolString) and balanced: symbol = symbolString[index] if symbol == "(" : s.push(symbol) else : if s.isEmpty(): balanced = False else : s.pop() index = index + 1 if balanced and s.isEmpty(): return True else : return False print(parChecker('((()))' )) print(parChecker('(()' )) def parChecker (symbolString ): s = Stack() balanced = True index = 0 while index < len(symbolString) and balanced: symbol = symbolString[index] if symbol in "([{" : s.push(symbol) else : if s.isEmpty(): balanced = False else : top = s.pop() if not matches(top,symbol): balanced = False index = index + 1 if balanced and s.isEmpty(): return True else : return False def matches (open,close ): opens = "([{" closers = ")]}" return opens.index(open) == closers.index(close) print(parChecker('{{([][])}()}' )) print(parChecker('[{()]' ))
在实际的应用里, 我们会碰到更多种括号,如python中列表所用的方括号“[]”,字典所用的花括号“{}” 元组和表达式所用的圆括号“()” ,下面这些是匹配的{ { ( [ ] [ ] ) } ( ) },[ [ { { ( ( ) ) } } ] ],[ ] [ ] [ ] ( ) { } 下面这些是不匹配的,( [ ) ] ( ( ( ) ] ) ),[ { ( ) ] .
中缀转后缀算法 首先我们来看中缀表达式A+B*C , 其对应的后缀表达式是ABC*+ 操作数ABC 的顺序没有改变。操作符的出现顺序,在后缀表达式中反转了由于*的优先级比+高,所以后缀表达式中操作符的出现顺序与运算次序一致
从左到右扫描中缀表达式单词列表:
如果单词是操作数,则直接添加到后缀表达式列表的 末尾
如果单词是左括号“(”,则压入opstack栈顶
如果单词是右括号“)”,则反复弹出opstack栈顶操作符,加入到输出列表末尾,直到碰到左括号 如果单词是操作符“*/+-”,则压入opstack栈顶
但在压入之前,要比较其与栈顶操作符的优先级
如果栈顶的高于或等于它,就要反复弹出栈顶操作符,加入到输出列表末尾
直到栈顶的操作符优先级低于它
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 def infixToPostfix (infixexpr ): prec = {} prec["*" ] = 3 prec["/" ] = 3 prec["+" ] = 2 prec["-" ] = 2 prec["(" ] = 1 opStack = Stack() postfixList = [] tokenList = infixexpr.split() for token in tokenList: if token in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" or token in "0123456789" : postfixList.append(token) elif token == '(' : opStack.push(token) elif token == ')' : topToken = opStack.pop() while topToken != '(' : postfixList.append(topToken) topToken = opStack.pop() else : while (not opStack.isEmpty()) and \ (prec[opStack.peek()] >= prec[token]): postfixList.append(opStack.pop()) opStack.push(token) while not opStack.isEmpty(): postfixList.append(opStack.pop()) return " " .join(postfixList) print(infixToPostfix("A * B + C * D" )) print(infixToPostfix("( A + B ) * C - ( D - E ) * ( F + G )" ))
后缀表达式求值 如“4 5 6 +”, 我们先扫描到4、 5两个操作数,但还不知道对这两个操作数能做什么计算, 需要继续扫描后面的符号才能知道,继续扫描, 又碰到操作数6,还是不能知道如何计算, 继续暂存入栈,直到“ ”, 现在知道是栈顶两个操作数5、 6做乘法 ,弹出两个操作数, 计算得到结果30。需要注意:
先弹出的是右操作数,后弹出的是左操作数,这个对于-/ 很重要!
为了继续后续的计算, 需要把这个中间结果30压入栈顶
继续扫描后面的符号
当所有操作符都处理完毕, 栈中只留下1个操作数, 就是表达式的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ef postfixEval(postfixExpr): operandStack = Stack() tokenList = postfixExpr.split() for token in tokenList: if token in "0123456789" : operandStack.push(int(token)) else : operand2 = operandStack.pop() operand1 = operandStack.pop() result = doMath(token,operand1,operand2) operandStack.push(result) return operandStack.pop() def doMath (op, op1, op2 ): if op == "*" : return op1 * op2 elif op == "/" : return op1 / op2 elif op == "+" : return op1 + op2 else : return op1 - op2 print(postfixEval('7 8 + 3 2 + /' ))