栈
- 栈是什么呢?简单来说,就是一个没有盖子的水杯,装水的时候,先进去的水在最底下,而最后放的水在最上面。倒水的时候,在最上面的水先被倒出去,在最下面的水最后呗倒出去。总结来说,就是先入后出或者后入先出。
在python中,栈的实现如下:
# 将列表上端或者右端作为顶端
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:简单括号匹配
说明:出现括号的时候,都是左括号和右括号成对出现。有一个左括号“(”,必定有一个右括号“)”与之对应。而且,第一个左括号应该匹配最后一个右括号。
当出现次序反转的问题,就应当用“栈”来解决问题。
算法思路:将输入中的字符一个一个地检查,如果是左括号“(”,就压入栈;如果是右括号“)”,栈不空的时候就弹出栈(因为栈里只有左括号),栈空的时候说明这个右括号是多余的,不能匹配。
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 += 1
if balanced and s.isEmpty():
return True
else:
return False
测试代码:
print(parChecker('((()))'))
True
print(parChecker('(()'))
False
- 应用2:十进制转换成二进制
我们在手动计算十进制转换成二进制的时候,用的方法是长除法,具体如下图:
可以看出,我们是从上向下进行计算的,但是最后从左向右写成而二进制的时候,却是在长除法从下向上的顺序写的。这样的顺序反转,需要用**栈*解决问题。
def divideBy2(decNumber):
remstack = Stack()
while decNumber > 0:
rem = decNumber % 2
remstack.push(rem)
decNumber = decNumber // 2
binString = ""
while not remstack.isEmpty():
binString = binString + str(remstack.pop())
return binString
- 应用3:中缀转后缀
首先简单介绍一下前缀、中缀和后缀的概念。
中缀:运算符在两个操作对象之间。
前缀:运算符在两个操作对象之前。
后缀:运算符在两个操作对象之后。
这么解释的话,估计大家也不明白,直接举例说明。for example:
中缀:2 + 5 * 6 = 32,这是大家最熟悉的运算方式。
**
前缀:+ 2 * 5 6。从右往左看,遇到的第一个运算符是“ > ”,紧挨着它的操作数是5和 6,优先计算5 * 6,剩下一个运算符和一个操作数就很好计算了。
**
后缀:2 5 6 * +。从左往右看,遇到的第一个运算符是“ * ”,紧挨着它的操作数是5和6,所以应当先计算56=30,然后表达式变成了2 30 +,进行30+2=32。
这里我们需要解决的问题是:中缀转后缀。
注意事项:
说明:
初始化时,空栈用来暂存操作符,空列表用于保存后缀表达式。
1、一开始的时候,表达式从左到右,遇到操作数就压入列表,遇到第一个操作符就压入空栈;
2、新遇到的操作符要与在栈顶的元素比较优先级,优先级高的送入列表,优先级低的留在栈中;
3、特别需要注意的是,带括号的转换。因为括号的优先级最高,遇到左括号就压入栈中,当遇到右括号时,开始反复弹栈,将出来的元素放入列表末端,直到遇到左括号,将其也送入列表末端,再将右括号本身送入列表末端。
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)
- 应用4:后缀表达式求值
原理很简单,从左到右,一次找运算符运算就行了。但是有需要注意的地方:
设计到“-”和“/”运算,前后顺序不能呼唤,因此弹栈的时候,第一次弹出来的要放在后面operand2,第二次弹出来的要放在前面operand1。
def 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