华为 OD 训练营 · 题解精讲
LC150. 逆波兰表达式求值
LC150. 逆波兰表达式求值 预习和复习可以查看我的讲解视频。
题目描述
根据 逆波兰表示法,求表达式的值。 有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。 注意 两个整数之间的除法只保留整数部分,向零截断。 可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。 示例 1: 输入:tokens = ["2","1","+","3","*"] 输出:9 解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9 示例 2: 输入:tokens = ["4","13","5","/","+"] 输出:6 解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6 示例 3: 输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"] 输出:22 解释:该算式转化为常见的中缀算术表达式为: ((10 * (6 / ((9 + 3) * -11))) + 17) + 5 = ((10 * (6 / (12 * -11))) + 17) + 5 = ((10 * (6 / -132)) + 17) + 5 = ((10 * 0) + 17) + 5 = (0 + 17) + 5 = 17 + 5 = 22 提示: 1 <= tokens.length <= 10^4 tokens[i] 是一个算符("+"、"-"、"*" 或 "/"),或是在范围 [-200, 200] 内的一个整数 逆波兰表达式: 逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。 平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ) 。 该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) 。 逆波兰表达式主要有以下两个优点: 去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + *也可以依据次序计算出正确结果。 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中
题目解析
解题思路
逆波兰表达式(后缀表达式)天然适合用栈求值。核心规则:遇到数字入栈,遇到运算符则弹出栈顶两个数字计算,结果再压回栈。遍历结束后栈中唯一元素即为表达式结果。
数据结构:使用 Python 列表模拟栈(result 作为栈)。
关键点:
- 运算符弹出顺序:先弹出的是右操作数(
rightNum),后弹出的是左操作数(leftNum),因为栈是后进先出。 - 除法向零截断:使用
int(leftNum / rightNum)实现(Python 的//对负数会向下取整,不符合题意,因此用int()截断小数部分)。
关键步骤
1. 初始化空栈 result = []。 2. 遍历 tokens:
- 若当前 token 是运算符:
- 弹出
rightNum = result.pop() - 弹出
leftNum = result.pop() - 根据运算符计算
ans - 否则(数字字符串):
ans = int(token) - 将
ans压入栈result.append(ans)
3. 返回栈顶 result[-1]。
以 ["2","1","+","3","*"] 为例的栈变化:
初始: []
读 "2": [2]
读 "1": [2, 1]
读 "+": 弹出 1 和 2 → 2+1=3 → [3]
读 "3": [3, 3]
读 "*": 弹出 3 和 3 → 3*3=9 → [9]
返回 9复杂度分析
- 时间复杂度:O(n),其中 n 为 tokens 长度。每个 token 只处理一次,栈操作 O(1)。
- 空间复杂度:O(n),最坏情况下所有 token 都是数字(如
["1","2","3","4","5"]),栈需存储所有数字。
参考代码
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
# 使用一个列表作为栈,存储操作数,从左到右遍历逆波兰表达式
result = []
# 遍历字符串数组
for token in tokens:
# 如果是运算符
if token in "+-*/":
# 先出栈的是右操作数
rightNum = result.pop()
# 后出栈的是左操作数
leftNum = result.pop()
# 计算结果
if token == "+":
ans = leftNum + rightNum
elif token == "-":
ans = leftNum - rightNum
elif token == "*":
ans = leftNum * rightNum
elif token == "/":
ans = int(leftNum / rightNum)
else:
# 转换为数字
ans = int(token)
# 存储结果
result.append(ans)
# 返回栈顶元素
return result[-1]