Article / 文章
LeetCode 150 逆波兰表达式求值:栈的经典应用
给你一个字符串数组 tokens,表示一个根据逆波兰表示法表示的算术表达式。 请你计算该表达式。返回一个表示表达式值的整数。 注意: - 有效的算符为 '+'、'-'、'' 和 '/' - 每个操作数(运算对象)都可以是一个整数或者另一个表达式 - 两个整数之间的除法总是向零截断 - 表达式中不含除零运算 - 输入是一个根据逆波兰表示法表示的算术表达式 -
题目描述
给你一个字符串数组 tokens,表示一个根据逆波兰表示法表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
注意:
- 有效的算符为
'+'、'-'、'*'和'/' - 每个操作数(运算对象)都可以是一个整数或者另一个表达式
- 两个整数之间的除法总是向零截断
- 表达式中不含除零运算
- 输入是一个根据逆波兰表示法表示的算术表达式
- 答案及所有中间计算结果可以用 32 位整数表示
难度
中等
题目链接
示例
示例 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^4tokens[i]是一个算符("+"、"-"、"*"或"/"),或是在范围[-200, 200]内的一个整数
解题思路
方法:栈
逆波兰表达式的特点是操作符在操作数之后,适合用栈来处理。
关键点:
- 遇到数字时入栈
- 遇到运算符时,从栈中弹出两个操作数进行计算
- 计算结果重新入栈
- 处理负数的情况
具体步骤:
- 遍历tokens数组
- 对于每个token:
- 如果是数字,转换为整数并入栈
- 如果是运算符,从栈中弹出两个操作数,计算结果并入栈
- 最后栈顶元素即为表达式结果
时间复杂度:O(n),其中n是tokens数组的长度。 空间复杂度:O(n),栈中最多存储n/2个数字。
图解思路
以示例1为例,演示计算过程:
- 初始状态:
栈:空
- 处理“2”:
栈:[2]
- 处理“1”:
栈:[2, 1]
- 处理”+“:
栈:[3] // 2 + 1 = 3
- 处理“3”:
栈:[3, 3]
- 处理”*“:
栈:[9] // 3 * 3 = 9
代码实现
C# 实现
public class Solution {
public int EvalRPN(string[] tokens) {
Stack<int> stack = new Stack<int>();
foreach (string token in tokens) {
if (IsOperator(token)) {
int b = stack.Pop();
int a = stack.Pop();
stack.Push(Calculate(a, b, token));
} else {
stack.Push(int.Parse(token));
}
}
return stack.Pop();
}
private bool IsOperator(string token) {
return token == "+" || token == "-" || token == "*" || token == "/";
}
private int Calculate(int a, int b, string op) {
switch (op) {
case "+": return a + b;
case "-": return a - b;
case "*": return a * b;
case "/": return a / b;
default: throw new ArgumentException("Invalid operator");
}
}
}
Python 实现
class Solution:
def evalRPN(self, tokens: List[str]) -> int:
stack = []
for token in tokens:
if token in "+-*/":
b = stack.pop()
a = stack.pop()
stack.append(self.calculate(a, b, token))
else:
stack.append(int(token))
return stack.pop()
def calculate(self, a: int, b: int, op: str) -> int:
if op == "+": return a + b
if op == "-": return a - b
if op == "*": return a * b
if op == "/": return int(a / b) # 向零截断
raise ValueError("Invalid operator")
C++ 实现
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> st;
for (const string& token : tokens) {
if (isOperator(token)) {
int b = st.top(); st.pop();
int a = st.top(); st.pop();
st.push(calculate(a, b, token));
} else {
st.push(stoi(token));
}
}
return st.top();
}
private:
bool isOperator(const string& token) {
return token == "+" || token == "-" || token == "*" || token == "/";
}
int calculate(int a, int b, const string& op) {
if (op == "+") return a + b;
if (op == "-") return a - b;
if (op == "*") return a * b;
if (op == "/") return a / b; // 向零截断
throw invalid_argument("Invalid operator");
}
};
性能分析
各语言实现的性能对比:
| 实现语言 | 执行用时 | 内存消耗 | 特点 |
|---|---|---|---|
| C# | 92 ms | 38.2 MB | 实现简洁,性能适中 |
| Python | 156 ms | 16.8 MB | 代码最简洁 |
| C++ | 24 ms | 9.6 MB | 性能最优 |
补充说明
代码亮点
- 使用栈实现逆波兰表达式的计算
- 处理了除法的向零截断
- 代码结构清晰,易于维护
常见错误
- 没有处理负数的情况
- 除法没有向零截断
- 栈操作顺序错误(先弹出的是第二个操作数)
相关题目
讨论
有几个问题可以思考一下:
- 逆波兰表达式(后缀表达式)和我们常见的中缀表达式比,有什么优势?
- 如果要把中缀表达式转换成逆波兰表达式,应该怎么做?
- 除了计算器,栈还能用来解决哪些问题?
欢迎在评论区讨论。
如果你都看到这里了,说明还是有点收获的吧?给个赞鼓励一下呗。