目录
通用的中缀转换后缀算法
先来说一下“通用”是什么意思。
上一节说到,我们在把中缀表达式转化为前缀后缀表达式的时候需要先把中缀表达式转化为全括号的形式,有的小伙伴可能会觉得有点麻烦,那么是不是可以跳过这一步呢?
当然是可以的,这就是“通用”的转化算法。
A+B*C
首先我们看中缀表达式A+B*C,它的后缀表达式是什么呢?很简单,是ABC*+,在这里,大家观察:
-
操作数ABC的顺序没有改变。
-
操作符出现的顺序在后缀表达式中反转了。
-
由于*的优先级比+高,所以后缀表达式中操作符的出现顺序与运算次序一致。
在中缀表达式转化为后缀表达式的过程中,操作符要比操作数晚输出,所以在扫描到对应的第二个操作数之前,要先把操作符存起来。
这个暂存的操作符,由于优先级的规则,还有可能要反转次序输出。在A+B*C中,虽然“+”先出现,但是优先级比“*”低,所以它要等“*”处理完之后,才能再处理。
因此,这种反转的特性没,可以考虑用栈来处理。
(A+B)*C
(A+B)*C对应的后缀表达式是AB+C*,这里“+”比"*"出现的早,是因为括号使得“+”的优先级提升。
遇到左括号时,要标记下来,其后出现的操作符优先级会提升,一旦扫描到对应的右括号,则马上输出这个操作符。
总结下来就是:在对中缀表达式从左到右扫描的过程中,采用栈来暂存未处理的操作符。这样的话,栈顶的操作符就是最急存进去的,当遇到新的操作符时,需要跟栈顶的操作符比较优先级,再进行处理。
算法流程
在本节算法中,约定中缀表达式是由一系列空格隔开的单词(token)组成,操作符包括*/+-()
首先创建空栈opstack来暂存操作符,空表postfixList来保存后缀表达式。
A + B * C =split=> ['A', '+', 'B', '*', 'C']
具体的:
- 如果单词是操作数,则直接添加到后缀表达式的末尾
- 如果单词是左括号,则压入opstack栈顶。
- 如果单词是右括号,则反复弹出opstack的栈顶操作符,加入到输出列表末尾,直到碰到左括号
- 如果单词是操作符,压入栈顶
注意:对于步骤4,在压入之前,要比较其与栈顶操作符的优先级,如果栈顶的优先级高于或者等于它,就要反弹出栈顶操作符,加入到输出列表末尾,直到栈顶操作符的优先级低于它。
扫描结束后,把opstack栈中剩余的操作符依次弹出,添加到输出列表末尾。
实例
以上图为例,逐步说明。
- 取出操作数A放入列表,此时postfixList---> A,opstack = 空
- 取出“*”压入opstack,此时postfixList---> A,opstack = *
- 取出操作数B放入列表,此时postfixList---> AB,opstack = *
- 取出“+”,然后将‘+’与栈顶的*比较优先级,发现‘*’的优先级高于‘+’,把‘*’放入列表,‘+’也入栈顶,此时postfixList---> AB*,opstack = +
- 取出C加入列表,此时postfixList---> AB*C,opstack = +
- 取出*,然后将‘*’与栈顶的+比较优先级,*的优先级高,压入栈顶此时postfixList---> AB*C,opstack = *+
- 取出D加入列表,然后将剩余将栈例剩余的操作符依次弹出放入列表。
- 完成,输出AB*CD*+
代码实现
class Stack():
"""定义一个栈"""
def __init__(self):
self.items = []
def isEmpty(self):
return self.items() == []
def pop(self):
return self.items.pop()
def push(self, item):
return self.items.append(item)
def peek(self):
return self.items[len(self.items)-1]
def size(self):
return len(self.items)
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 'ABCDEFHIJKLMNOPQRSTUVWXYZ' 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[toptoken]):
postfixlist.append(opstack.pop())
opstack.push(toptoken)
while not opstack.isEmpty():
postfixlist.append(opstack.pop())
return ' '.join(postfixlist)