题目难度: 中等
今天继续更新 Leetcode 的剑指 Offer(专项突击版)系列, 大家在公众号 算法精选 里回复
剑指offer2
就能看到该系列当前连载的所有文章了, 记得关注哦~
题目描述
-
根据 逆波兰表示法,求该后缀表达式的计算结果。
-
有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
说明:
- 整数除法只保留整数部分。
- 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 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 + * 也可以依据次序计算出正确结果。
- 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中。
题目思考
- 如何模拟整个计算过程?
解决方案
思路
- 分析题目, 正如提示中所说的, 我们可以采用栈来解决这个问题:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中。
- 具体实现方面, 我们可以维护一个操作符映射字典, 它的 key 是对应符号, value 是具体的操作, 这里直接使用 Python3 的 lambda 表达式
- 然后遍历每个 token, 判断其是否是操作符:
- 是的话, 就利用映射字典得到具体的 lambda 操作, 然后依次弹出栈顶两个数字进行计算, 并将结果压入栈中
- 否则就将其转成 int 压入栈中
- 由于题目保证给定的表达式一定有效, 那么最终栈中一定只存在一个数字, 就是最终结果, 直接将其返回即可
- 下面的代码就对应了上面的整个过程, 并且有详细的注释, 方便大家理解
复杂度
- 时间复杂度 O(N): 只需要遍历所有 token 一遍
- 空间复杂度 O(N): 栈最多存 N 个元素
代码
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
# 操作符映射字典: {符号:lambda操作}
operators = {
"+": lambda a, b: a + b,
"-": lambda a, b: a - b,
"*": lambda a, b: a * b,
"/": lambda a, b: int(a / b),
}
# 数字栈
numStack = []
for token in tokens:
if token in operators:
# 当前token是操作符, 提取其对应操作
op = operators[token]
# 然后弹出栈顶的两个操作数, 栈顶是b, 下面一个是a
b, a = numStack.pop(), numStack.pop()
# 然后将计算得到的新数字重新压入栈中
numStack.append(op(a, b))
else:
# 当前token是数字, 将其转换成int后压入栈中
numStack.append(int(token))
# 最终栈只存在一个元素, 即为最终结果
return numStack.pop()
大家可以在下面这些地方找到我~😊
我的公众号: 算法精选, 欢迎大家扫码关注~😊