题目描述
求逆波兰(后缀)表达式的值。运算符写在两个操作数之后,如 “2 1 + 3 *” 表示 (2+1)×3。
思路解析
直觉是把后缀转回我们熟悉的中缀 (2+1)×3 再算,但要反推括号该加在哪、谁先算,逻辑绕且容易错。难点是运算顺序藏在排列里,没有显式括号告诉你边界。
转折:后缀表达式里「运算符紧跟在它的两个操作数后面」,所以从左到右扫——是数字就压栈先存着;遇到运算符,它要的两个操作数正好是栈顶最近压入的两个,弹出来算完再把结果压回。最近压入的最先被取走,这正是栈。扫完栈里剩的唯一一个数就是答案。
准备 · 栈空:上面是 token 序列,下面是栈。开始逐个 token 处理,栈一开始是空的。
读 2 · 数字入栈:读到数字 2,先存着——压入栈。栈:[2]。
读 1 · 数字入栈:读到数字 1,继续压栈。栈:[2, 1],栈顶是 1。
读 + · 弹两个操作数:读到 +,它要两个操作数。先弹出的 1 是右操作数 b,后弹出的 2 是左操作数 a。顺序很关键:要算的是 a 运算符 b,也就是 2 + 1,不是 1 + 2。
读 + · 结果入栈:算 a + b = 2 + 1 = 3,把结果 3 压回栈。栈变成 [3]。注意这里加法两个数交换无所谓,但若是减法就必须 a−b(2−1),反了就成 1−2 了。
读 3 · 数字入栈:读到数字 3,压栈。栈:[3, 3]——下面那个 3 是刚才 2+1 的结果,上面是新读到的 3。
读 * · 弹两个操作数:读到 *,同样弹两个:先弹 b=3(右),再弹 a=3(左)。它俩相乘。
读 * · 结果入栈:算 a × b = 3 × 3 = 9,压回栈。栈:[9]。
结束 · 栈顶即答案:所有 token 读完,栈里只剩一个数 9,它就是整个表达式的值。
后缀表达式没有优先级和括号的烦恼:操作数先存栈、运算符来了就地取最近两个结算。凡是「最近出现的最先被消费」的场景,都该想到栈。
参考代码(Python)
1stack = []
2for tk in tokens:
3 if tk in class="cl-str">"+-*/":
4 b = stack.pop() # 先弹的是右操作数
5 a = stack.pop() # 后弹的是左操作数
6 if tk == class="cl-str">"+": stack.append(a + b)
7 elif tk == class="cl-str">"-": stack.append(a - b) # 必须 a - b
8 elif tk == class="cl-str">"*": stack.append(a * b)
9 else: stack.append(int(a / b)) # 向零取整
10 else: stack.append(int(tk)) # 数字入栈
11return stack[-1]复杂度分析
- 时间复杂度:O(n) —— n 个 token 每个只处理一次,进出栈都是 O(1)
- 空间复杂度:O(n) —— 栈最多同时装约一半个数字
套路模板
记住骨架:操作数入栈、运算符弹两个(下面是左 a、上面是右 b)算完入栈。中缀转后缀、基本计算器都靠它。
1stack = []
2for tk in tokens:
3 if 是操作数(tk): stack.append(转换(tk))
4 else: # 是运算符
5 b = stack.pop() # 上面 = 右操作数
6 a = stack.pop() # 下面 = 左操作数
7 stack.append(运算(a, tk, b)) # 永远 a op b
8return stack[-1]易错点
- 错误写法:a、b 顺序搞反,写成 b op a → 正确写法:b=先弹(右), a=后弹(左), 算 a op b(减法、除法不满足交换律:3 1 - 要算 a−b=3−1=2,反了成 1−3=−2 就错)
- 错误写法:除法直接用 Python // → 正确写法:用 int(a / b) 向零取整(题目要求向零取整,而 -7 // 2 = -4,与要求的 -3 不同)
以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握。