题目要求:
/**
* @author yangshuo
* @date 2020/3/23 14:11
*
* 逆波兰表达式求值
*
* 根据逆波兰表示法,求表达式的值。
*
* 有效的运算符包括 +, -, *, / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
*
* 说明:
*
* 整数除法只保留整数部分。
* 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
*
* 示例 1:
*
* 输入: ["2", "1", "+", "3", "*"]
* 输出: 9
* 解释: ((2 + 1) * 3) = 9
*
* 示例 2:
*
* 输入: ["4", "13", "5", "/", "+"]
* 输出: 6
* 解释: (4 + (13 / 5)) = 6
*
* 示例 3:
*
* 输入: ["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
*/
初步解法:
刚开始的第一个想法是利用栈来实现,这也是逆波兰表达式的官方解法。遇见数字压栈,遇见运算符出栈两个数字计算即可。
代码:
public int evalRPN(String[] tokens) {
// 设置栈
Stack<Integer> stack = new Stack<>();
// 设置一个集合保存运算符
Set<String> set = new HashSet<>();
set.add("+");
set.add("-");
set.add("*");
set.add("/");
for (int i= 0; i < tokens.length; i++){
// 如果是运算符就出栈两个数字做计算
if (set.contains(tokens[i])){
int right = stack.pop();
int left = stack.pop();
int calculation = calculation(left, right, tokens[i]);
stack.add(calculation);
}else{
// 如果不是就压栈
stack.add(Integer.valueOf(tokens[i]));
}
}
// 最后栈顶的元素肯定是最后的计算结果
return stack.peek();
}
private int calculation(int left, int right, String operator){
switch (operator){
case "+":
return left + right;
case "-":
return left - right;
case "*":
return left * right;
case "/":
return left / right;
default:
return 0;
}
}
执行结果:
速度还算可以击败86.47%,内存占用特别高,还是有优化的空间。不过后面的执行都是41.4MB大概leetcode出错了吧,但是可以肯定的是肯定还有更好的办法。
优化解法:
根据给的示例,观察发现如果能够记住每次括号的位置,这样就计算后括号左移就能计算出最终结果,但是不能计算比较复杂的情况比如(a+b)*(c+d),之后就是一般的优化代码的方式,先套用进去,先是从后往前遍历看有没有规律可寻,可惜我没有看出来有什么不同,但是最优解的大神能看出来,后面大家可以看一下,他是如何击败100%的。
第二点就是寻找从前往后找的规律,把图示画出来就能发现,每次遍历的n-1和n-2的位置可以复用,这样做就可以节省栈的开销,速度和内存都是得到提升。
代码如下:
public int evalRPN(String[] tokens) {
// 将栈的结构用数组代替
int [] stack = new int[tokens.length/ 2 + 1];
int index = 0;
for (String s : tokens){
// 每次做运算,将之前已经参加过运算的index -1 和 index -2 的值覆盖掉,这里计算完毕覆盖index-2的值
switch (s){
case "+":
stack[index - 2] += stack[index-1];
index --;
break;
case "-":
stack[index - 2] -= stack[index-1];
index --;
break;
case "*":
stack[index - 2] *= stack[index-1];
index --;
break;
case "/":
stack[index - 2] /= stack[index-1];
index --;
break;
default:
// 这里由于index++的性质,所以覆盖的实际上是上一步index-- 后的值
stack[index ++] = Integer.parseInt(s);
}
}
// 最后栈顶的元素肯定是最后的计算结果
return stack[0];
}
执行结果:
比之前提升了4ms,但是只击败了99.7%。
可能leetCode出错了,这里显示的内存没有发生变化,实际上这个解答和击败100%内存的解答是一致的。
终极解法:
不知道哪位大神想出来的递归解法,可以观摩一下
public int evalRPN(String[] tokens) {
index = tokens.length - 1;
return getPrefix(tokens);
}
int index;
public int getPrefix(String[] tokens) {
String token = tokens[index--];
if (token.equals("+")) {
int prefix1 = getPrefix(tokens);
int prefix0 = getPrefix(tokens);
return prefix0 + prefix1;
} else if (token.equals("-")) {
int prefix1 = getPrefix(tokens);
int prefix0 = getPrefix(tokens);
return prefix0 - prefix1;
} else if (token.equals("*")) {
int prefix1 = getPrefix(tokens);
int prefix0 = getPrefix(tokens);
return prefix0 * prefix1;
} else if (token.equals("/")) {
int prefix1 = getPrefix(tokens);
int prefix0 = getPrefix(tokens);
return prefix0 / prefix1;
} else {
return Integer.parseInt(token);
}
}
微信公众号:二虎程序