|
| 1 | + |
| 2 | +<p align="center"> |
| 3 | + <a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a> |
| 4 | + <a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a> |
| 5 | + <a href="https://img-blog.csdnimg.cn/20201210231711160.png"><img src="https://img.shields.io/badge/公众号-代码随想录-brightgreen" alt=""></a> |
| 6 | + <a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a> |
| 7 | +</p> |
| 8 | + |
| 9 | + |
| 10 | + |
| 11 | +> 这不仅仅是一道好题,也展现出计算机的思考方式 |
| 12 | +
|
| 13 | +# 150. 逆波兰表达式求值 |
| 14 | + |
| 15 | +https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/ |
| 16 | + |
| 17 | +根据 逆波兰表示法,求表达式的值。 |
| 18 | + |
| 19 | +有效的运算符包括 + , - , * , / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。 |
| 20 | + |
| 21 | +说明: |
| 22 | + |
| 23 | +整数除法只保留整数部分。 |
| 24 | +给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。 |
| 25 | + |
| 26 | + |
| 27 | +示例 1: |
| 28 | +输入: ["2", "1", "+", "3", " * "] |
| 29 | +输出: 9 |
| 30 | +解释: 该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9 |
| 31 | + |
| 32 | +示例 2: |
| 33 | +输入: ["4", "13", "5", "/", "+"] |
| 34 | +输出: 6 |
| 35 | +解释: 该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6 |
| 36 | + |
| 37 | +示例 3: |
| 38 | +输入: ["10", "6", "9", "3", "+", "-11", " * ", "/", " * ", "17", "+", "5", "+"] |
| 39 | +输出: 22 |
| 40 | +解释: |
| 41 | +该算式转化为常见的中缀算术表达式为: |
| 42 | + ((10 * (6 / ((9 + 3) * -11))) + 17) + 5 |
| 43 | += ((10 * (6 / (12 * -11))) + 17) + 5 |
| 44 | += ((10 * (6 / -132)) + 17) + 5 |
| 45 | += ((10 * 0) + 17) + 5 |
| 46 | += (0 + 17) + 5 |
| 47 | += 17 + 5 |
| 48 | += 22 |
| 49 | + |
| 50 | + |
| 51 | +逆波兰表达式:是一种后缀表达式,所谓后缀就是指算符写在后面。 |
| 52 | + |
| 53 | +平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ) 。 |
| 54 | + |
| 55 | +该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) 。 |
| 56 | + |
| 57 | +逆波兰表达式主要有以下两个优点: |
| 58 | + |
| 59 | +* 去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。 |
| 60 | + |
| 61 | +* 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中。 |
| 62 | + |
| 63 | +# 思路 |
| 64 | + |
| 65 | +在上一篇文章中[栈与队列:匹配问题都是栈的强项](https://mp.weixin.qq.com/s/eynAEbUbZoAWrk0ZlEugqg)提到了 递归就是用栈来实现的。 |
| 66 | + |
| 67 | +所以**栈与递归之间在某种程度上是可以转换的!**这一点我们在后续讲解二叉树的时候,会更详细的讲解到。 |
| 68 | + |
| 69 | +那么来看一下本题,**其实逆波兰表达式相当于是二叉树中的后序遍历**。 大家可以把运算符作为中间节点,按照后序遍历的规则画出一个二叉树。 |
| 70 | + |
| 71 | +但我们没有必要从二叉树的角度去解决这个问题,只要知道逆波兰表达式是用后续遍历的方式把二叉树序列化了,就可以了。 |
| 72 | + |
| 73 | +在进一步看,本题中每一个子表达式要得出一个结果,然后拿这个结果再进行运算,那么**这岂不就是一个相邻字符串消除的过程,和[栈与队列:匹配问题都是栈的强项](https://mp.weixin.qq.com/s/eynAEbUbZoAWrk0ZlEugqg)中的对对碰游戏是不是就非常像了。** |
| 74 | + |
| 75 | +如动画所示: |
| 76 | + |
| 77 | + |
| 78 | +相信看完动画大家应该知道,这和[1047. 删除字符串中的所有相邻重复项](https://mp.weixin.qq.com/s/eynAEbUbZoAWrk0ZlEugqg)是差不错的,只不过本题不要相邻元素做消除了,而是做运算! |
| 79 | + |
| 80 | +C++代码如下: |
| 81 | + |
| 82 | + |
| 83 | +```C++ |
| 84 | +class Solution { |
| 85 | +public: |
| 86 | + int evalRPN(vector<string>& tokens) { |
| 87 | + stack<int> st; |
| 88 | + for (int i = 0; i < tokens.size(); i++) { |
| 89 | + if (tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/") { |
| 90 | + int num1 = st.top(); |
| 91 | + st.pop(); |
| 92 | + int num2 = st.top(); |
| 93 | + st.pop(); |
| 94 | + if (tokens[i] == "+") st.push(num2 + num1); |
| 95 | + if (tokens[i] == "-") st.push(num2 - num1); |
| 96 | + if (tokens[i] == "*") st.push(num2 * num1); |
| 97 | + if (tokens[i] == "/") st.push(num2 / num1); |
| 98 | + } else { |
| 99 | + st.push(stoi(tokens[i])); |
| 100 | + } |
| 101 | + } |
| 102 | + int result = st.top(); |
| 103 | + st.pop(); // 把栈里最后一个元素弹出(其实不弹出也没事) |
| 104 | + return result; |
| 105 | + } |
| 106 | +}; |
| 107 | +``` |
| 108 | +
|
| 109 | +# 题外话 |
| 110 | +
|
| 111 | +我们习惯看到的表达式都是中缀表达式,因为符合我们的习惯,但是中缀表达式对于计算机来说就不是很友好了。 |
| 112 | +
|
| 113 | +例如:4 + 13 / 5,这就是中缀表达式,计算机从左到右去扫描的话,扫到13,还要判断13后面是什么运算法,还要比较一下优先级,然后13还和后面的5做运算,做完运算之后,还要向前回退到 4 的位置,继续做加法,你说麻不麻烦! |
| 114 | +
|
| 115 | +那么将中缀表达式,转化为后缀表达式之后:["4", "13", "5", "/", "+"] ,就不一样了,计算机可以利用栈里顺序处理,不需要考虑优先级了。也不用回退了, **所以后缀表达式对计算机来说是非常友好的。** |
| 116 | +
|
| 117 | +可以说本题不仅仅是一道好题,也展现出计算机的思考方式。 |
| 118 | +
|
| 119 | +在1970年代和1980年代,惠普在其所有台式和手持式计算器中都使用了RPN(后缀表达式),直到2020年代仍在某些模型中使用了RPN。 |
| 120 | +
|
| 121 | +参考维基百科如下: |
| 122 | +
|
| 123 | +> During the 1970s and 1980s, Hewlett-Packard used RPN in all of their desktop and hand-held calculators, and continued to use it in some models into the 2020s. |
| 124 | +
|
| 125 | +
|
| 126 | +
|
| 127 | +
|
| 128 | +## 其他语言版本 |
| 129 | +
|
| 130 | +java: |
| 131 | +
|
| 132 | +```Java |
| 133 | +public class EvalRPN { |
| 134 | +
|
| 135 | + public int evalRPN(String[] tokens) { |
| 136 | + Deque<Integer> stack = new LinkedList(); |
| 137 | + for (String token : tokens) { |
| 138 | + char c = token.charAt(0); |
| 139 | + if (!isOpe(token)) { |
| 140 | + stack.addFirst(stoi(token)); |
| 141 | + } else if (c == '+') { |
| 142 | + stack.push(stack.pop() + stack.pop()); |
| 143 | + } else if (c == '-') { |
| 144 | + stack.push(- stack.pop() + stack.pop()); |
| 145 | + } else if (c == '*') { |
| 146 | + stack.push( stack.pop() * stack.pop()); |
| 147 | + } else { |
| 148 | + int num1 = stack.pop(); |
| 149 | + int num2 = stack.pop(); |
| 150 | + stack.push( num2/num1); |
| 151 | + } |
| 152 | + } |
| 153 | + return stack.pop(); |
| 154 | + } |
| 155 | +
|
| 156 | +
|
| 157 | + private boolean isOpe(String s) { |
| 158 | + return s.length() == 1 && s.charAt(0) <'0' || s.charAt(0) >'9'; |
| 159 | + } |
| 160 | +
|
| 161 | + private int stoi(String s) { |
| 162 | + return Integer.valueOf(s); |
| 163 | + } |
| 164 | +
|
| 165 | +
|
| 166 | + public static void main(String[] args) { |
| 167 | + new EvalRPN().evalRPN(new String[] {"10","6","9","3","+","-11","*","/","*","17","+","5","+"}); |
| 168 | + } |
| 169 | +
|
| 170 | +} |
| 171 | +``` |
| 172 | + |
| 173 | +------------------------ |
| 174 | + |
| 175 | +* 微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw) |
| 176 | +* B站:[代码随想录](https://space.bilibili.com/525438321) |
| 177 | +* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ) |
| 178 | + |
| 179 | + |
| 180 | + |
0 commit comments