中綴表達式轉后綴表達式


1、中綴表達式和后綴表達式

中綴表達式就是我們正常使用的那種,例如:a+b*c

后綴表達式就是abc*+;

為什么要有中綴表達式和后綴表達式呢?

因為中綴表達式便於人們的理解與計算,但是后綴表達式更方便計算機的運算(如二叉樹、堆棧的方法計算),因此在讀取一個中綴表達式后,將其轉化為后綴表達式更有利於計算

 

2、中綴表達式轉后綴表達式

首先假設我們需要轉化的中綴表達式為:

a + b * c + ( d * e + f ) * g

其轉換成后綴表達式則為 a b c * + d e * f  + g * +

2.1 基於堆棧的方法

轉換過程需要用到棧,具體過程如下:

1、從左到右掃描每一個字符,如果遇到操作數,我們就直接將其輸出。

2、如果遇到操作符,有如下幾種情況

1)如果堆棧是空的,直接將操作符存儲到堆棧中 (push)

2)如果該操作符的優先級大於棧頂的操作符,就直接將操作符存儲到堆棧中(push)

3)如果該操作符的優先級低於堆棧出口的操作符,就將堆棧出口的操作符導出(pop), 直到該操作符的優先級大於堆棧頂端的操作符。將掃描到的操作符導入到堆棧中(push)

    • 優先級  "()" >  "*/"  > "+-"
    • 只有在遇到" ) "的情況下我們才彈出" ( ",其他情況我們都不會彈出" ( "。這一點根據優先級也很好理解,括號的優先級最高

4)如果遇到一個右括號,則將棧元素彈出,將彈出的操作符輸出直到遇到左括號為止。注意,左括號只彈出並不輸出

3、如果我們讀到了輸入的末尾,則將棧中所有元素依次彈出。

 

圖解過程如下:

1、從左到右掃描每一個字符,如果遇到操作數,我們就直接將其輸出。

2、如果遇到操作符,有如下幾種情況

1)如果堆棧是空的,直接將操作符存儲到堆棧中 (push)

2)如果該操作符的優先級大於棧頂的操作符,就直接將操作符存儲到堆棧中(push)

3)如果該操作符的優先級低於堆棧出口的操作符,就將堆棧出口的操作符導出(pop), 直到該操作符的優先級大於堆棧頂端的操作符。將掃描到的操作符導入到堆棧中(push)

遇到『+』號,當前棧中的元素為『*, +』,優先級都不低於當前操作符『+』,故先彈出,『+』再入棧

 遇到『(』號,優先級最高,『(』直接入棧,操作數直接輸出

 

遇到『*』號,優先級高,『(』是棧頂元素,因此直接入棧,操作數直接輸出

遇到『+』號,優先級低,『*』是棧頂元素,先出棧,『+』再入棧,操作數直接輸出 

 4)如果遇到一個右括號,則將棧元素彈出,將彈出的操作符輸出直到遇到左括號為止。注意,左括號只彈出並不輸出 

 

『*』入棧,操作數輸出

3、如果我們讀到了輸入的末尾,則將棧中所有元素依次彈出。 

 

代碼

 1 # 使用堆棧
 2 def getPostExpByStack(inExp):
 3     re = []
 4     op = []
 5     while inExp:
 6         tem = inExp.pop(0)
 7         if not isOperator(tem):
 8             re.append(tem)
 9         else:
10             if tem == '(':
11                 op.append(tem)
12             elif tem == ')':
13                 while op[-1] != "(":
14                     re.append(op.pop())
15                 op.pop()
16             elif tem in ['+', '-', '*', '/']:
17                 while op and op[-1] != "(" and getOperOrder(op[-1]) >= getOperOrder(tem):
18                     re.append(op.pop())
19                 op.append(tem)
20 
21         if op:
22             re = re + op[::-1]
23 
24         return re

 

 

2.2 括號法

好記又簡單,轉換過程如下

1、根據運算符的優先級對中綴表達式加括號(有幾個運算符就有幾對括號)(原本有的括號不用加)

      式子變成:( ( a + ( b * c) ) + ( ( ( d * e ) + f ) * g ) )

2、轉換前綴與后綴表達式

  1)前綴:把運算符號移動到對應的括號前面

        則變成:+ ( + ( a * ( b c) ) * ( + ( * ( d e ) f ) g ) )

        把括號去掉:+ + a * b c * + * d e f g 即為前綴表達式

      2)后綴:把運算符號移動到對應的括號后面

        則變成拉:((a(bc)*)+(((de)*f)+g)*)+

        把括號去掉:abc*+de*f+g *+  即為后綴表達式

 

代碼:

待補充

 

2.3 生成二叉樹以后遍歷

重點是如何生成二叉樹

代碼

 1 import re
 2 
 3 
 4 # 定義二叉樹節點
 5 class TNode:
 6     def __init__(self, x):
 7         self.val = x
 8         self.left = None
 9         self.right = None
10 
11 
12 # 判斷是否是運算符
13 def isOperator(ch):
14     if ch in ['+', '-', '*', '/', '^', '(', ')']:
15         return True
16     return False
17 
18 
19 # 判斷優先級
20 def getOperOrder(self, ch):
21     if ch == '(':
22         return 1
23     if ch in ['+', '-']:
24         return 2
25     if ch in ['*', '/']:
26         return 3
27     if ch == '^':
28         return 4
29     return 0
30 
31 
32 # 二叉樹操作類
33 class Solution:
34 
35     # 后綴表達式生成二叉樹
36     def postExpToTree(self, exp):
37         if not exp:
38             return
39         re = []
40         while exp:
41             tem = exp.pop(0)
42             if not isOperator(tem):
43                 re.append(TNode(tem))
44             else:
45                 p = TNode(tem)
46                 p.right = re.pop()
47                 p.left = re.pop()
48                 re.append(p)
49         return re.pop()
50 
51     # 前綴表達式生成二叉樹
52     def PreExpTree(self, exp):
53         re = []
54         while exp:
55             tmp = exp.pop()
56             if not isOperator(tmp):
57                 re.append(TNode(tmp))
58             else:
59                 p = TNode(tmp)
60                 p.left = re.pop()
61                 p.right = re.pop()
62                 re.append(p)
63         return re.pop()
64 
65     # 中綴表達式生成二叉樹
66     # 1、可以先將中綴表達式轉換成后綴表達式,在用后綴表達式的方式生成二叉樹 見方法 堆棧生成中綴表達式
67     # 2、直接生成二叉樹
68     def InexpTree(self, exp):
69         pro = dict(zip('(+-*/', [0, 1, 1, 2, 2]))
70         stack, ops = [], []
71         for left, num, op, right in re.findall(r'(\()|(\d+)|([-+*/])|(\))', s + '+'):
72             if left:
73                 ops.append(left)
74             elif num:
75                 stack.append(TNode(num))
76             elif op:
77                 while ops and pro[ops[-1]] >= pro[op]:
78                     r, l = stack.pop(), stack.pop()
79                     stack.append(TNode(ops.pop(), l, r))
80                 ops.append(op)
81             else:
82                 while ops[-1] != '(':
83                     r, l = stack.pop(), stack.pop()
84                     stack.append(TNode(ops.pop(), l, r))
85                 ops.pop()
86         return stack[0]
View Code

 

3、中綴表達式和后綴表達式求值

3.1 后綴表達式求值

逆波蘭表達式求值

 1 class Solution:
 2     def evalRPN(self, tokens: List[str]) -> int:
 3         data_stack = []
 4         for i in tokens:
 5             if i not in ['+', '-', '*', '/']:
 6                 data_stack.append(int(i))
 7             else:
 8                 b = data_stack.pop()
 9                 a = data_stack.pop()
10                 if i == '+':
11                     res = a + b
12                 elif i == '-':
13                     res = a - b
14                 elif i == '*':
15                     res = a * b
16                 else:
17                     res = int(a / b)
18                 data_stack.append(res)
19 
20         return data_stack[-1]

 

3.2 中綴表達式求值

基本計算器

給你一個字符串表達式 s ,請你實現一個基本計算器來計算並返回它的值。

示例 1:

輸入:s = "1 + 1"
輸出:2
示例 2:

輸入:s = " 2-1 + 2 "
輸出:3
示例 3:

輸入:s = "(1+(4+5+2)-3)+(6+8)"
輸出:23

方法一:括號展開 + 棧

由於字符串除了數字與括號外,只有加號和減號兩種運算符。因此,如果展開表達式中所有的括號,則得到的新表達式中,數字本身不會發生變化,只是每個數字前面的符號會發生變化。

因此,我們考慮使用一個取值為 {−1,+1} 的整數 sign 代表「當前」的符號。根據括號表達式的性質,它的取值:

與字符串中當前位置的運算符有關;
如果當前位置處於一系列括號之內,則也與這些括號前面的運算符有關:每當遇到一個以 − 號開頭的括號,則意味着此后的符號都要被「翻轉」。

考慮到第二點,我們需要維護一個棧 ops,其中棧頂元素記錄了當前位置所處的每個括號所「共同形成」的符號。例如,對於字符串 1+2+(3-(4+5)):

掃描到 1+2 時,由於當前位置沒有被任何括號所包含,則棧頂元素為初始值 +1;
掃描到 1+2+(3 時,當前位置被一個括號所包含,該括號前面的符號為 + 號,因此棧頂元素依然 +1;
掃描到 1+2+(3-(4 時,當前位置被兩個括號所包含,分別對應着 + 號和 − 號,由於 + 號和 − 號合並的結果為 − 號,因此棧頂元素變為 −1。

在得到棧 ops 之后, sign 的取值就能夠確定了:如果當前遇到了 + 號,則更新 sign←ops.top();如果遇到了遇到了 − 號,則更新 sign←−ops.top()。

然后,每當遇到 ( 時,都要將當前的 sign 取值壓入棧中;每當遇到 ) 時,都從棧中彈出一個元素。這樣,我們能夠在掃描字符串的時候,即時地更新 ops 中的元素。

 1 class Solution:
 2     def calculate(self, s: str) -> int:
 3         ops = [1]
 4         sign = 1
 5 
 6         ret = 0
 7         n = len(s)
 8         i = 0
 9         while i < n:
10             if s[i] == ' ':
11                 i += 1
12             elif s[i] == '+':
13                 sign = ops[-1]
14                 i += 1
15             elif s[i] == '-':
16                 sign = -ops[-1]
17                 i += 1
18             elif s[i] == '(':
19                 ops.append(sign)
20                 i += 1
21             elif s[i] == ')':
22                 ops.pop()
23                 i += 1
24             else:
25                 num = 0
26                 while i < n and s[i].isdigit():
27                     num = num * 10 + ord(s[i]) - ord('0')
28                     i += 1
29                 ret += num * sign
30         return ret

 

方法二:轉逆波蘭表達式

見上

 

方法三:eval()

過不了全部測試樣例

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM