逆波兰表示法俗称后缀表达式,是算术表达式的一种形式。
举个例子,我们常见的算术表达式3*4+5+6
叫做中缀表达式,如果将其写成后缀表达试,就会变成34*5+6+
,显然这样的运算符跑到操作数后面去了。
和中缀表达式想比,后缀表达式显得让人难以理解,但是对计算机而言却是非常友好的,因为它的计算模式和 栈 的数据结构非常吻合。例如,如果我们在计算器中输入一段表达式例如3*4+5+6
,计算器就会把表达式转换成34*5+6+
,然后再进行计算得出我们的值。
计算原理
逆波兰表达式的计算原理就是:把表达式拆成数组结构,从左到右进行扫描,遇到运算符就对其前面的操作数进行运算,直到没有任何操作符为止。
下面演示计算过程:
- [3, 4, *, 5, +, 6, +]
- [12, 5, +, 6, +]
- [12, 5, +, 6, +]
- [17, 6, +]
- [23]
最后的结果就是数组的第一个字段:23。
我们先不忙着用代码实现上面的扫描计算,因为后缀表达式写起来太不习惯了,所以先得把一个中缀表达式转换成后缀表达式。
实现转换
常用的转换算法为调度场算法,这里就不详细介绍算法的原理了,下面的代码也是基于此算法。
首先,我们定义了一系列操作符,并提供一些通用的方法。
1 | // RNP.js |
在具体算法开始前定义2个栈,分别是 operatorStack
- 临时运算符栈,rnpStack
- 逆波兰表达式栈
1 | function rnp(expression){ |
然后从左到右一次遍历表达式字符串。
如果是操作数,直接入栈rnpStack
,如果是运算符就有几种情况需要处理。
如果不是括号,则检查运算符优先级大于栈顶优先级,直接入栈。否则将栈顶元素依次出栈放入rnpStack
,直到优先级低于当前操作数为止。这里需要注意的是,如果是-
可能是取负值,所以要额外判断和处理。
1 | let l2 = operatorStack.length; |
如果是左括号,也直接入栈rnpStack
。
如果是右括号,需要从operatorStack
中依次出栈,找到最近的(
为止,然后将(
以外的其他预算符全部入栈rnpStack
。
1 | const temp = []; |
遍历完成后,将operatorStack
中剩余的预算符依次入栈到rnpStack
。
1 | // 将剩余的操作符入栈 |
最后得到的rnpStack
就是逆波兰表达式数组。
计算
现在用上面得到的rnpStack
来完成扫描计算算法。
原理就是定义一个栈,如果遇到操作数就入栈,如果遇到操作符就根据类型选择数据出栈,并将计算的结果入栈。
1 | function calRNP(rnpArr) { |
最后得到的数组,如果长度为1,则第一个元素就是计算的最终值。
最后,让我们试一下吧。
1 | const rnpArr = RNP('3*(32*(2+10)+10)'); |
参考资料
此处有本文的完整代码。