逆波兰表示法(Reverse Polish notation,RPN,或逆波兰记法),是一种是由波兰数学家扬·武卡谢维奇1920年引入的数学表达式方式,在逆波兰记法中,所有操作符置于操作数的后面,因此也被称为后缀表示法。逆波兰记法不需要括号来标识操作符的优先级。
逆波兰表示法可用来解决表达式的优先运算问题。比如对中缀表达式12 +( 7 - 3 )* 2 + 9 / 3
,用逆波兰表示法应该是:12 7 3 - 2 * + 9 3 / +
。逆波兰表示法虽然看起来奇怪,不利于人阅读,但利于计算机处理。
我们从小学习的数学使用的是中缀表达式。
转化规则
逆波兰表示法规则如下: 初始化两个栈,一个用来存储临时操作符的栈S1, 一个用来存储逆波兰表达式的栈S2,然后从左到右遍历表达式的每个元素:
- 若取出的字符是数字就直接压入S2栈;
- 若取出的字符是“(”,则直接压入S1栈。
- 若取出的字符是运算符(比如 + - * /),则将该运算符与S1栈栈顶元素比较:
- 如果栈顶元素是“(”,则将该运算符入栈S1。
- 如果栈顶元素是加减乘除之类的运算符, 而且该运算符优先级大于S1栈栈顶运算符优先级,则将该运算符进S1栈,否则,将S1栈的栈顶运算符弹出,压入S2栈 中,直至S1栈栈顶运算符低于(不包括等于)该运算符优先级。然后再将该运算符送入S1栈。
- 若取出的字符是“)”,则将S1栈顶元素依次弹出并压入到S2中,直到遇到“(”,注意“(”出栈但不要压入S2栈。
- 重复步骤1-4,直到遍历完成,如果S1栈中还有运算符,则将S1栈中的运算符逐个出栈,压入S2栈中。
转化示例
逆波兰表示法可用来解决表达式的优先运算问题。比如对中缀表达式12 +( 7 - 3 )* 2 + 9 / 3
,用逆波兰表示法应该是:12 7 3 - 2 * + 9 3 / +
。下面就以这个表达式,逐步讲解如何将我们常见的运算表达式(中缀表达式)转为逆波兰记法并求值。
输入 | 操作 | S1 | S2 |
---|---|---|---|
12 | 入栈S2 | [] | [12] |
+ | 入栈S1 | [+] | [12] |
( | 入栈S1 | [+, (] | [12] |
7 | 入栈S2 | [+, (] | [12, 7] |
- | S1栈顶是(,因此直接入栈 | [+, (, -] | [12, 7] |
3 | 入栈S2 | [+, (, -] | [12, 7, 3] |
) | S1栈顶“-”和“(”依次出栈,将“-”入栈S2 | [+] | [12, 7, 3, -] |
* | 乘号优先级大于加号,因此直接入栈S1 | [+, *] | [12, 7, 3, -] |
2 | 入栈S2 | [+, *] | [12, 7, 3, -, 2] |
+ | 加号优先级小于乘号等于加号,依次将S1的乘号和加号入栈S2,再将加号入栈S1 | [+] | [12, 7, 3, -, 2, *, +] |
9 | 入栈S2 | [+] | [12, 7, 3, -, 2, *, +, 9] |
/ | 除号优先级大于加号,直接入栈S1 | [+, /] | [12, 7, 3, -, 2, *, +, 9] |
3 | 入栈S2 | [+, /] | [12, 7, 3, -, 2, *, +, 9, 3] |
S1栈中的剩下的运算符逐个出栈,压入S2栈中 | [] | [12, 7, 3, -, 2, *, +, 9, 3, /, +] |
栈S2最终为[12, 7, 3, -, 2, *, +, 9, 3, /, +],转为字符串即12 7 3 - 2 * + 9 3 / +
求值
将中缀表达式转为逆波兰表示法之后,求值就很简单了。做法是遍历逆波兰表达式,如果遇到运算符,则取出运算符前面的两个数字进行运算,然后再将该符号替换成运算后的结果,重复这一过程,最终会得到一个数值,该数值即为表达式的结果。以12 7 3 - 2 * + 9 3 / +
为例:
运算符 | 操作前 | 操作 | 操作后 |
---|---|---|---|
- | 12 7 3 - 2 * + 9 3 / + | 减号前面为7和3,弹出7和3,7 - 3 = 4,4将减号替换 | 12 4 2 * + 9 3 / + |
* | 12 4 2 * + 9 3 / + | 乘号前面为4和2,弹出4和2,4 * 2 = 8,8将乘号替换 | 12 8 + 9 3 / + |
+ | 12 8 + 9 3 / + | 加号前面为12和8,弹出12和8,12 + 8 = 20,20将加号替换 | 20 9 3 / + |
/ | 20 9 3 / + | 除号前面为9和3,弹出9和3,9 / 3 = 3,3将除号替换 | 20 3 + |
+ | 20 3 + | 加号前面为20和3,弹出20和3,20 + 3 = 23 | 23 |
因此。12 +(7-3)* 2 + 9 / 3
最终结果为23。
LeetCode 真题
逆波兰表达式求值
var evalRPN = function(tokens) {
const stack = [];
const length = tokens.length;
for(let i = 0; i < length; i++) {
const c = tokens[i];
if (c === '+' || c === '-' || c === '*' || c === '/') {
const val1 = parseInt(stack.pop());
const val2 = parseInt(stack.pop());
switch(c) {
case '+':
stack.push(val2 + val1)
break;
case '-':
stack.push(val2 - val1);
break
case '*':
stack.push(val2 * val1);
break;
case '/':
stack.push(parseInt(val2 / val1))
break;
}
} else {
stack.push(c)
}
}
return stack[0];
};