• 栈是什么呢?简单来说,就是一个没有盖子的水杯,装水的时候,先进去的水在最底下,而最后放的水在最上面。倒水的时候,在最上面的水先被倒出去,在最下面的水最后呗倒出去。总结来说,就是先入后出或者后入先出
    在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:十进制转换成二进制

我们在手动计算十进制转换成二进制的时候,用的方法是长除法,具体如下图:

Python数据进栈并输出栈顶元素 python中栈的用法_栈


可以看出,我们是从上向下进行计算的,但是最后从左向右写成而二进制的时候,却是在长除法从下向上的顺序写的。这样的顺序反转,需要用**栈*解决问题。

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,所以应当先计算5
6=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