逆波兰表示法求四则运算

1,998 阅读4分钟

逆波兰表示法Reverse Polish notationRPN,或逆波兰记法),是一种是由波兰数学家扬·武卡谢维奇1920年引入的数学表达式方式,在逆波兰记法中,所有操作符置于操作数的后面,因此也被称为后缀表示法。逆波兰记法不需要括号来标识操作符的优先级。

逆波兰表示法可用来解决表达式的优先运算问题。比如对中缀表达式12 +( 7 - 3 )* 2 + 9 / 3,用逆波兰表示法应该是:12 7 3 - 2 * + 9 3 / +。逆波兰表示法虽然看起来奇怪,不利于人阅读,但利于计算机处理。

我们从小学习的数学使用的是中缀表达式

转化规则

逆波兰表示法规则如下: 初始化两个栈,一个用来存储临时操作符的栈S1, 一个用来存储逆波兰表达式的栈S2,然后从左到右遍历表达式的每个元素:

  1. 若取出的字符是数字就直接压入S2栈;
  2. 若取出的字符是“(”,则直接压入S1栈。
  3. 若取出的字符是运算符(比如 + - * /),则将该运算符与S1栈栈顶元素比较:
    • 如果栈顶元素是“(”,则将该运算符入栈S1。
    • 如果栈顶元素是加减乘除之类的运算符, 而且该运算符优先级大于S1栈栈顶运算符优先级,则将该运算符进S1栈,否则,将S1栈的栈顶运算符弹出,压入S2栈  中,直至S1栈栈顶运算符低于(不包括等于)该运算符优先级。然后再将该运算符送入S1栈。
  4. 若取出的字符是“)”,则将S1栈顶元素依次弹出并压入到S2中,直到遇到“(”,注意“(”出栈但不要压入S2栈。
  5. 重复步骤1-4,直到遍历完成,如果S1栈中还有运算符,则将S1栈中的运算符逐个出栈,压入S2栈中。

转化示例

逆波兰表示法可用来解决表达式的优先运算问题。比如对中缀表达式12 +( 7 - 3 )* 2 + 9 / 3,用逆波兰表示法应该是:12 7 3 - 2 * + 9 3 / +。下面就以这个表达式,逐步讲解如何将我们常见的运算表达式(中缀表达式)转为逆波兰记法并求值。

输入操作S1S2
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 = 2323

因此。12 +(7-3)* 2 + 9 / 3最终结果为23。

LeetCode 真题

逆波兰表达式求值

150.逆波兰表达式求值

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];
};