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]
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()
過不了全部測試樣例