[LeetCode] 由 “中缀表达式 --> 后缀表达式" 所想
如何利用栈解决问题。
本文的引申:如何手写语法分析器
实现调度场算法
“9+(3-1)*3+10/2” --> “9 3 1-3*+ 10 2/+”
规则:
从左到右遍历中缀表达式的每个数字和符号,
若是数字就输出,即成为后缀表达式的一部分;
若是符号,则判断其与栈顶符号的优先级,
是右括号或优先级低于栈顶符号(乘除优先加减)
-
- 则栈顶元素依次出栈并输出,
- 并将当前符号进栈,
一直到最终输出后缀表达式为止。
举个栗子:
1. 初始化一空栈,用来对符号进出栈使用。
2. 第一个字符是数字9,输出9,后面是符号“+”,进栈。
3. 第三个字符是“(”,依然是符号,因其只是左括号,还未配对,故进栈。
4. 第四个字符是数字3,输出,总表达式为9 3,接着是“-”进栈。
5. 接下来是数字1,输出,总表达式为9 3 1,后面是符号“)”,此时,我们需要去匹配此前的“(”,所以栈顶依次出栈,并输出,直到“(”出栈为止。此时左括号上方只有“-”,因此输出“-”,总的输出表达式为9 3 1 -
6. 接着是数字3,输出,总的表达式为9 3 1 - 3 。紧接着是符号“*”,因为此时的栈顶符号为“+”号,优先级低于“*”,因此不输出,进栈。
7. 之后是符号“+”,此时当前栈顶元素比这个“+”的优先级高,因此栈中元素出栈并输出(没有比“+”号更低的优先级,所以全部出栈),总输出表达式为 9 3 1 - 3 * +;然后将当前这个符号“+”进栈。也就是说,前6张图的栈底的“+”是指中缀表达式中开头的9后面那个“+”,而下图中的栈底(也是栈顶)的“+”是指“9+(3-1)*3+”中的最后一个“+”。
8. 紧接着数字10,输出,总表达式变为9 3 1-3 * + 10。
9. 最后一个数字2,输出,总的表达式为 9 3 1-3*+ 10 2
10. 因已经到最后,所以将栈中符号全部出栈并输出。最终输出的后缀表达式结果为 9 3 1-3*+ 10 2/+
- 将中缀表达式转化为后缀表达式(栈用来进出运算的符号)。
- 将后缀表达式进行运算得出结果(栈用来进出运算的数字)。
整个过程,都充分利用了找的后进先出特性来处理,理解好它其实也就理解好了栈这个数据结构。
Python 代码实践
import logging class Stack: def __init__(self): self.items = [] def isEmpty(self): return self.items == [] def push(self, item): logging.info("Info: push {}".format(item)) self.items.append(item) def pop(self): if True == self.isEmpty(): logging.info("Info: it's empty.") return None return self.items.pop() def peek(self): if True == self.isEmpty(): logging.info("Info: it's empty.") return None return self.items[-1] def size(self): return len(self.items) def __str__(self): return "{}".format(self.items) logging.basicConfig(level=logging.INFO) # “9+(3-1)*3+10/2” --> “9 3 1-3*+ 10 2/+” 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 == ')': # do...while, 把括号区间的符号都输出 while True: topToken = opStack.pop() if topToken == '(': break postfixList.append(topToken) 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 )"))
End.