一、题目描述
给你一个字符串数组 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^4
tokens[i]
是一个算符("+"
、"-"
、"*"
或"/"
),或是在范围[-200, 200]
内的一个整数
逆波兰表达式:
逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
- 平常使用的算式则是一种中缀表达式,如
( 1 + 2 ) * ( 3 + 4 )
。 - 该算式的逆波兰表达式写法为
( ( 1 2 + ) ( 3 4 + ) * )
。
逆波兰表达式主要有以下两个优点:
- 去掉括号后表达式无歧义,上式即便写成
1 2 + 3 4 + *
也可以依据次序计算出正确结果。 - 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中
二、解题思路
逆波兰表达式(后缀表达式)是一种把运算符写在操作数后面的表达式形式,它天然适合用栈来实现计算。具体算法如下:
1. 初始化一个栈用于存放数字。
2. 遍历给定的字符串数组 tokens
。
3. 对于每个元素:
- 如果它是一个数字,直接压入栈中。
- 如果它是一个运算符,从栈中弹出两个数字(先弹出的是第二个操作数,后弹出的是第一个操作数),然后根据运算符进行计算,将计算结果压入栈中。
4. 遍历完成后,栈顶元素就是最终结果。
三、具体代码
import java.util.Stack;
public class Solution {
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for (String token : tokens) {
if (token.equals("+") || token.equals("-") || token.equals("*") || token.equals("/")) {
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;
}
} else {
stack.push(Integer.parseInt(token));
}
}
return stack.pop();
}
}
四、时间复杂度和空间复杂度
1. 时间复杂度
- 遍历一次给定的字符串数组
tokens
,数组长度为n
。 - 对于每个元素,如果是数字,直接压入栈中,这个操作是
O(1)
。 - 如果是运算符,从栈中弹出两个数字并执行运算,再压入栈中,这个操作也是
O(1)
。 - 因此,总的时间复杂度是
O(n)
,其中n
是数组tokens
的长度。
2. 空间复杂度
- 使用了一个栈来存储数字,最坏情况下,所有的数字都未被运算符操作,而是直接压入栈中,这时候栈的大小为
n/2
(因为每个运算符会消耗两个数字)。 - 因此,总的空间复杂度是
O(n)
,其中n
是数组tokens
的长度。
综上所述,该算法的时间复杂度是 O(n)
,空间复杂度也是 O(n)
。
五、总结知识点
1. 栈(Stack)的使用:
- 栈是一种后进先出(Last In First Out, LIFO)的数据结构。
- 在 Java 中,
Stack
类是java.util
包的一部分,用于实现栈的所有功能,如push
(压栈)、pop
(出栈)等。
2. 字符串比较:
- 使用
equals
方法来比较字符串是否相等,而不是使用==
,因为==
比较的是两个字符串对象的引用是否相同,而equals
比较的是字符串内容是否相同。
3. 类型转换:
- 使用
Integer.parseInt
方法将字符串转换为整数类型。这个方法可能会抛出NumberFormatException
,如果字符串不是有效的整数表示。
4. switch 语句:
- 使用
switch
语句来根据不同的运算符执行不同的代码块。
5. 异常处理:
- 虽然
Integer.parseInt
可能会抛出异常,但在这个代码中没有显式地处理这个异常。在实际应用中,应该添加异常处理逻辑,比如使用try-catch
语句来捕获并处理可能出现的异常。
6. 基本算术运算:
- 代码中实现了基本的算术运算,包括加法(
+
)、减法(-
)、乘法(*
)和除法(/
)。
7. 数据结构操作:
- 代码中频繁使用了栈的操作,包括向栈中压入元素(
push
)和从栈中弹出元素(pop
)。
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。