各位朋友好久不见~
因为刚结束考研复试,还没怎么写代码,前段时间写了点儿题目,也还没来得及来做笔记。今天我来了,带来了近期的第一题——逆波兰表达式
。
如果是没见过这个名字的人,会比较陌生哈,我们常用的专业术语就是前缀表达式、中缀表达式、后缀表达式。而逆波兰表达式就是后缀表达式。我现在给出以下两个例子让大家熟悉以下中缀和后缀表达式的区别:
中缀表达式:6 / (9 + 3)
-- 对应的
后缀表达式:6, 9, 3, +, /
有人会疑惑,为什么会这样,其实画出表达式树,你就懂了。
表达式树怎么画这里就不赘述了,后缀表达式其实就是表达式树的 后序遍历(LRD) 的一个序列。
那么,这道题目其实就是根据输入的逆波兰表达式,然后通过计算返回对应的结果值,下面给出两个例子:
示例 1:
输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:
输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
上面这两个例子来自与LeetCode官网,tokens的值其实就是依次输入的顺序,首先我们观察逆波兰表达式,当出现符号的时候,我们要进行及时的运算,或者存下符号,当下一个符号来的时候进行优先级判断。
但是这里tokens里面并没有左右小括号的输入,而且从解释的阐述可以看出,符号到来的前后顺序其实就是他的运算次序,也就是不需要留着等下一个符号来了然后判断优先级。
而且该符号出现之前的两个数字其实就是需要用于该符号运算的两个数字,你也可以通过表达式树模拟后序遍历来进行观察。
有了以上的简单分析,我们可以理出一个大概的思路:当出现一个符号的时候,我们需要把紧挨着的两个数字也输出,进行符号运算,并且是前一个数字运算后一个数字,运算结果还应放回去,进行下一次的运算,当整个字符串数组的逆波兰表达式运算完毕后,剩下的元素就是我们要的结果。
你可以再用一个数组进行模拟,我这里就直接用的栈实现了该操作,当出现一个符号的时候,进行两次弹栈操作,依次弹出栈顶的两个元素,运算完毕后,再将运算结果放回栈顶。
代码如下:
/**
* 6ms
* 38.3M O(N)
* @param tokens 字符串数组
* @return 返回逆波兰表达式计算值
* 栈实现。
*/
public int evalRPN(String[] tokens) {
if (tokens.length == 0) {
return 0;
}
int temp;
Stack<Integer> stack = new Stack<>();
for (String ch : tokens) {
switch (ch) {
case "-" -> {
int a = stack.pop();
int b = stack.pop();
temp = b - a;
stack.push(temp);
}
case "+" -> {
temp = stack.pop() + stack.pop();
stack.push(temp);
}
case "/" -> {
int a = stack.pop();
int b = stack.pop();
temp = b / a;
stack.push(temp);
}
case "*" -> {
temp = stack.pop() * stack.pop();
stack.push(temp);
}
default -> stack.push(Integer.valueOf(ch));
}
}
return stack.pop();
}
/**
* 3ms
* 38.3M O(N)
* @param tokens 字符串数组
* @return 返回逆波兰表达式计算值
* 数组实现。
* 如果将改代码改为 foreach 和 switch, 速度3ms,内存38M
*/
public int evalRPN2(String[] tokens) {
if (tokens.length == 0) {
return 0;
}
int[] res = new int[tokens.length];
int index = -1;
for (int i = 0; i < tokens.length; i++) {
if (tokens[i].equals("+")) {
res[index - 1] = res[index - 1] + res[index];
index--;
} else if (tokens[i].equals("-")) {
res[index - 1] = res[index - 1] - res[index];
index--;
} else if (tokens[i].equals("*")) {
res[index - 1] = res[index - 1] * res[index];
index--;
} else if (tokens[i].equals("/")) {
res[index - 1] = res[index - 1] / res[index];
index--;
} else {
res[++index] = Integer.parseInt(tokens[i]);
}
}
return res[index];
}
以上是代码,两个函数的实现方式是一样的,我当时在写这个代码的时候,突然对switch、if else、for、for-each这几个功能差不多的结构产生了好奇,并且两个函数,我第一个用的栈,第二个用的数组,因为栈的底层就是数组实现的,所以进行了多次尝试,然后在上面对运算时间和内存消耗做了比较,数据来自于LeetCode。(可供大家参考,我这里就不赘述了)
今天的题目就到这里哈,感谢大家的阅读,如果大家有新的方法,欢迎评论区一起讨论。~ .~