中缀表达式 转换为 前缀 后缀表达式

来源:互联网 发布:高干子弟知乎 编辑:程序博客网 时间:2024/04/30 09:41

中缀转后缀表达式

考虑表达式 A + B * CA B C * +是等价的后缀表达式。 我们已经注意到,操作数 A,B 和 C 保持在它们的相对位置。只有操作符改变位置。再看中缀表达式中的运算符。从左到右出现的第一个运算符为 +。 然而,在后缀表达式中,+ 在结束位置,因为下一个运算符 * 的优先级高于加法。 原始表达式中的运算符的顺序在生成的后缀表达式中相反。

当我们处理表达式时,操作符必须保存在某处,因为它们相应的右操作数还没有看到。 此外,这些保存的操作符的顺序可能由于它们的优先级而需要反转。这是在该示例中的加法和乘法的情况,由于加法运算符在乘法运算符之前,并且具有较低的优先级,因此需要在使用乘法运算符之后出现。 由于这种顺序的反转,考虑使用栈来保存运算符直到用到它们是有意义的。

(A + B)* C的情况会是什么样呢? 回想一下,A B + C *是等价的后缀表达式。从左到右处理此中缀表达式,我们先看到 +。 在这种情况下,当我们看到 *+已经放置在结果表达式中,由于括号它的优先级高于*。 我们现在可以开始看看转换算法如何工作。当我们看到左括号时,我们保存它,表示高优先级的另一个运算符将出现。该操作符需要等到相应的右括号出现以表示其位置(回忆完全括号的算法)。 当右括号出现时,可以从栈中弹出操作符。

当我们从左到右扫描中缀表达式时,我们将使用栈来保留运算符。这将提供我们在第一个例子中注意到的反转。 堆栈的顶部将始终是最近保存的运算符。每当我们读取一个新的运算符时,我们需要考虑该运算符如何与已经在栈上的运算符(如果有的话)比较优先级。

假设中缀表达式是一个由空格分隔的标记字符串。 操作符标记是*,/,+- ,以及左右括号。操作数是单字符 A,B,C 等。 以下步骤将后缀顺序生成一个字符串。

  1. 创建一个名为 opstack 的空栈以保存运算符。给输出创建一个空列表。
  2. 通过使用字符串方法拆分将输入的中缀字符串转换为标记列表。
  3. 从左到右扫描标记列表。
    • 如果标记是操作数,将其附加到输出列表的末尾。
    • 如果标记是左括号,将其压到 opstack 上。
    • 如果标记是右括号,则弹出 opstack,直到删除相应的左括号。将每个运算符附加到输出列表的末尾。
    • 如果标记是运算符,*,/,+- ,将其压入 opstack。但是,首先删除已经在 opstack 中具有更高或相等优先级的任何运算符,并将它们加到输出列表中。
  4. 当输入表达式被完全处理时,检查 opstack。仍然在栈上的任何运算符都可以删除并加到输出列表的末尾。

Figure 展示了对表达式 A * B + C * D 的转换算法。注意,第一个 * 在看到 + 运算符时被删除。另外,当第二个 * 出现时, + 保留在栈中,因为乘法优先级高于加法。在中缀表达式的末尾,栈被弹出两次,删除两个运算符,并将 + 作为后缀表达式中的最后一个运算符。

3.9.中缀后缀和后缀表达式.figure9]![

Figure

为了在 Python 中编写算法,我们使用一个名为 prec 的字典来保存操作符的优先级。这个字典将每个运算符映射到一个整数,可以与其他运算符的优先级(我们使用整数3,2和1)进行比较。左括号将赋予最低的值。这样,与其进行比较的任何运算符将具有更高的优先级,将被放置在它的顶部。第15行将操作数定义为任何大写字符或数字。完整的转换函数见 ActiveCode 1。

from pythonds.basic.stack import Stackdef 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 )"))

执行结果如下

>>> infixtopostfix("( A + B ) * ( C + D )")'A B + C D + *'>>> infixtopostfix("( A + B ) * C")'A B + C *'>>> infixtopostfix("A + B * C")'A B C * +'>>>

中缀转前缀表达式

首先构造一个运算符栈,然后从右至左扫描中缀表达式。如果是操作数,则直接输出,作为前缀表达式的一个直接转换表达式Temp(最后,前缀表达式由该表达式翻转得到);如果是运算符,则比较优先级:若该运算符优先级大于等于栈顶元素,则将该运算符入栈,否则栈内元素出栈并加到Temp表达式尾端,直到该运算符大于等于栈顶元素的优先级时,再将该运算符压入栈中。遇到右括号直接压入栈中,如果遇到一个左括号,那么就将栈元素弹出并加到Temp表达式尾端,但左右括号并不输出。最后,若运算符栈中还有元素,则将元素一次弹出并加到Temp表达式尾端,最后一步是将Temp表达式翻转。

假定有中缀表达式1 + (( 2 + 3)* 4 ) – 5

其过程如下图所示:

和转为后缀不同之处在于:

  • 从右向左读,第一个reverse
  • 最后还要再reverse一下
  • 栈中的元素的优先级必须大于表达式中的元素才能出栈,后缀是大于等于
  • 加入栈中的是),因为是从右向左扫
def midTopost(expr):    prec={}    prec["*"] = 3    prec["/"] = 3    prec["+"] = 2    prec["-"] = 2    prec["("] = 1    opstack = Stack() # 用于保存符号    postfixList = [] # 保存最终的表达式    tokenList = expr.split()    tokenList.reverse()    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())    postfixList.reverse()    return " ".join(postfixList)