题目
标题和出处
标题:逆波兰表达式求值
难度
4 级
题目描述
要求
给定一个字符串数组 tokens \texttt{tokens} tokens,表示一个逆波兰表达式。
计算表达式的值。返回表示表达式结果的一个整数。
逆波兰表达式的规则如下:
- 有效的运算符包括 ‘+’ \texttt{`+'} ‘+’、 ‘-’ \texttt{`-'} ‘-’、 ‘*’ \texttt{`*'} ‘*’、 ‘/’ \texttt{`/'} ‘/’。
- 每个运算对象可以是整数,也可以是另一个逆波兰表达式。
- 两个整数之间的除法总是向零取整。
- 不会出现除数为零的情况。
- 输入表示有效的逆波兰表达式。
- 答案和所有中间结果在 32 \texttt{32} 32 位有符号整数范围内。
示例
示例 1:
输入:
tokens
=
["2","1","+","3","*"]
\texttt{tokens = ["2","1","+","3","*"]}
tokens = ["2","1","+","3","*"]
输出:
9
\texttt{9}
9
解释:
((2
+
1)
*
3)
=
9
\texttt{((2 + 1) * 3) = 9}
((2 + 1) * 3) = 9
示例 2:
输入:
tokens
=
["4","13","5","/","+"]
\texttt{tokens = ["4","13","5","/","+"]}
tokens = ["4","13","5","/","+"]
输出:
6
\texttt{6}
6
解释:
(4
+
(13
/
5))
=
6
\texttt{(4 + (13 / 5)) = 6}
(4 + (13 / 5)) = 6
示例 3:
输入:
tokens
=
["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
\texttt{tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]}
tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出:
22
\texttt{22}
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
\begin{aligned} & \quad \texttt{((10 * (6 / ((9 + 3) * -11))) + 17) + 5} \\ & \texttt{= ((10 * (6 / (12 * -11))) + 17) + 5} \\ & \texttt{= ((10 * (6 / -132)) + 17) + 5} \\ & \texttt{= ((10 * 0) + 17) + 5} \\ & \texttt{= (0 + 17) + 5} \\ & \texttt{= 17 + 5} \\ & \texttt{= 22} \end{aligned}
((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 \texttt{1} \le \texttt{tokens.length} \le \texttt{10}^\texttt{4} 1≤tokens.length≤104
- tokens[i] \texttt{tokens[i]} tokens[i] 或者是一个运算符( "+" \texttt{"+"} "+"、 "-" \texttt{"-"} "-"、 "*" \texttt{"*"} "*" 或 "/" \texttt{"/"} "/"),或者是一个在范围 [-200, 200] \texttt{[-200, 200]} [-200, 200] 内的整数
解法一
思路和算法
逆波兰表达式又称后缀表达式,由波兰的逻辑学家 J・卢卡西维兹于 1929 年提出。逆波兰表达式的特点是:没有括号,运算符总是放在和它相关的操作数之后,严格遵循从左到右的运算。
计算逆波兰表达式的值时需要使用栈存储操作数,从左到右遍历并计算。具体操作如下:
-
如果遇到操作数,则将操作数入栈;
-
如果遇到运算符,则将两个操作数出栈,其中先出栈的是第二个操作数,后出栈的是第一个操作数,使用运算符对两个操作数运算,得到新操作数,将新操作数入栈。
遍历结束之后,栈内只有一个元素,该元素即为逆波兰表达式的值。
下图为示例 1 的计算逆波兰表达式的过程。
代码
class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> stack = new ArrayDeque<Integer>();
int length = tokens.length;
for (int i = 0; i < length; i++) {
String token = tokens[i];
if (isNumber(token)) {
stack.push(Integer.parseInt(token));
} else {
int num2 = stack.pop();
int num1 = stack.pop();
switch (token) {
case "+":
stack.push(num1 + num2);
break;
case "-":
stack.push(num1 - num2);
break;
case "*":
stack.push(num1 * num2);
break;
case "/":
stack.push(num1 / num2);
break;
default:
}
}
}
return stack.pop();
}
public boolean isNumber(String token) {
return Character.isDigit(token.charAt(token.length() - 1));
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 tokens \textit{tokens} tokens 的长度。需要遍历数组 tokens \textit{tokens} tokens 一次,计算逆波兰表达式的值。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 tokens \textit{tokens} tokens 的长度。空间复杂度主要取决于栈空间,栈内元素个数不会超过逆波兰表达式的长度。
解法二
思路和算法
解法一使用栈存储操作数。也可以使用数组模拟栈操作。
对于长度为 n n n 的逆波兰表达式,其中有 n + 1 2 \dfrac{n + 1}{2} 2n+1 个操作数和 n − 1 2 \dfrac{n - 1}{2} 2n−1 个运算符。当遇到操作数时,将操作数入栈,栈内元素个数加 1 1 1;当遇到运算符时,将 2 2 2 个操作数出栈,使用运算符运算后得到 1 1 1 个新的操作数并入栈,因此栈内元素个数减 1 1 1。根据上述分析可知,栈内元素个数不会超过原始逆波兰表达式中的操作数个数,即任何时候栈内元素个数不会超过 n + 1 2 \dfrac{n + 1}{2} 2n+1。因此,使用数组模拟栈操作时,将数组的长度定义为 n + 1 2 \dfrac{n + 1}{2} 2n+1 即可。
数组的左端即下标 0 0 0 的位置为栈底。使用 top \textit{top} top 表示栈顶元素所在下标,初始时 top = − 1 \textit{top} = -1 top=−1,表示栈为空。
当元素入栈时,首先将 top \textit{top} top 的值加 1 1 1,然后将入栈元素赋值到下标 top \textit{top} top 处。
当元素出栈时,首先获得下标 top \textit{top} top 处的元素,然后将 top \textit{top} top 的值减 1 1 1。
遍历结束之后,栈内只有一个元素,此时 top = 0 \textit{top} = 0 top=0,位于下标 0 0 0 处的元素即为逆波兰表达式的值。
代码
class Solution {
public int evalRPN(String[] tokens) {
int length = tokens.length;
int[] stack = new int[(length + 1) / 2];
int top = -1;
for (int i = 0; i < length; i++) {
String token = tokens[i];
if (isNumber(token)) {
stack[++top] = Integer.parseInt(token);
} else {
int num2 = stack[top--];
int num1 = stack[top--];
switch (token) {
case "+":
stack[++top] = num1 + num2;
break;
case "-":
stack[++top] = num1 - num2;
break;
case "*":
stack[++top] = num1 * num2;
break;
case "/":
stack[++top] = num1 / num2;
break;
default:
}
}
}
return stack[top];
}
public boolean isNumber(String token) {
return Character.isDigit(token.charAt(token.length() - 1));
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 tokens \textit{tokens} tokens 的长度。需要遍历数组 tokens \textit{tokens} tokens 一次,计算逆波兰表达式的值。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 tokens \textit{tokens} tokens 的长度。空间复杂度主要取决于模拟栈操作的数组,其长度为 n + 1 2 \dfrac{n + 1}{2} 2n+1。