在程序设计中,可能碰到须要对字符串数学表达式求值的问题,经常使用的方法是解析表达式,生成二叉树,而后进行计算。编译器就是使用这种方法来解析程序中的表达式的。这种方法实现起来有点难度,须要考虑运算符的优先级,括号的配对,堆栈的使用等等。咱们正常状况下看到的数学表达式若是用二叉树遍历的话,刚好是中序遍历,故叫作中序表达式。除此以外,还有前序表达式,后序表达式。如:a+b+c(中序),++abc(前序),ab+c+(后序),若是表达式含有×,/,()等就更复杂了。php
后缀表达式也称逆波兰表达式 因其使表达式求值变得轻松,因此被广泛使用。java
逆波兰式:算法
在一般的表达式中,二元运算符老是置于与之相关的两个运算对象之间(如:1+1),因此这种表示法也称为中缀表示。波兰逻辑学家J.Lukasiewicz于1929年提出了另外一种表示表达式的方法,称为逆波兰记法,在逆波兰记法中,全部操做符置于操做数的后面,所以也被称为后缀表示法。示例以下:express
中缀表示编程 |
逆波兰式编程语言 |
a+b性能 |
a,b,+lua |
a+(b-c)spa |
a,b,c,-,+.net |
a+(b-c)*d |
a,b,c,-,d,*,+ |
a+d*(b-c) |
a,d,b,c,-,*,+ |
a=1+3 |
a=1,3 + |
逆波兰表达式是一种十分有用的表达式,它将复杂表达式转换为能够依靠简单的操做获得计算结果的表达式。它的优点在于只用两种简单操做,入栈和出栈就能够搞定任何普通表达式的运算。
中缀表达式转换为逆波兰式:
将一个普通的中序表达式转换为逆波兰表达式的通常算法是:
一、首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。
二、读入一个中缀表达式,为了方便起见,可在其最右端追加一个最低优先级运算符(如:#号)。(这样作的目的是,最后读入#号运算符时将运算符栈中全部运算符都输出)。
三、从左至右扫描该中缀表达式,若是当前字符是数字,则分析到该数字串的结束,并将该数字串直接输出。
四、若是不是数字,该字符则是运算符,此时需比较该运算符与运算符栈顶运算符的优先关系:
(1)、若该运算符优先级高于栈顶运算符优先级别(或栈为空),则直接将该运算符压入运算符栈中;
(2)、若该运算符优先级小于或等于此运算符栈顶的运算符,则弹出栈顶运算符并输出,重复比较、输出,直到栈为空或该运算符优先级高于栈顶运算符,而后将该运算符入栈。
五、重复上述操做(3)-(4)直至扫描完整个简单算术表达式,肯定全部字符都获得正确处理,输出结果即是中缀表达式转化为逆波兰表示的简单算术表达式。 中缀表达式(a+b)*c-(a+b)/e的逆波兰式是ab+c*ab+e/-。
逆波兰表达式是一种利用栈来进行运算的数学表达式。
以一个表达式 1 + 2 * 3 为例。
以顺序方式输入这个表达式,计算机首先须要解析这个表达式,而后递归的求值。
好比从左起进行顺序解析,获得一个符号树:
+
/ \
1 *
/ \
2 3
计算机会递归的从叶子开始求值,最后回到根,得出结果。
因此对于一个比较复杂的表达式,树可能会很深,并且递归的过程将会消耗大量的内存,因为有解析和运算两个过程,时间开销也不理想。
若是将上式改成逆波兰表达式: 3 2 * 1 +
那么就能实现 “在线处理”。在线处理必须是这类问题在计算机中最快的算法。
仍是从左侧开始,扫描这个表达式。
扫描到第一项,为一个操做数,那么能够把这个数压栈,而后继续扫面。
第二项仍是一个操做数,一样的压栈继续。
到第三项,获得一个双目运算符,这时候计算机就从操做数栈中弹出两个数做为运算符的两个参数,而后进行运算,并将结果再压回操做数栈。
接着第四项,一个操做数,压栈。第五项,又是一个双目运算符,那么弹出两个操做数进行运算,把结果压栈。
到此,这个表达式就扫描结束了,操做数栈中最后会剩下表达式的运算结果。
不须要两次操做,只要从头扫描到尾就能求出结果,也不须要递归,只须要一个很小的栈就能够。因此逆波兰表达式算法取得了时间复杂度和空间复杂度上的双重优点。
而且:
因此在机器上实现起来很是的方便。早期,处理器性能比较弱的时候,使用这种方式就能够在性能不足的机器上实现任意复杂度的表达式运算。很棒吧。
至于你的问题,主要是在于“为啥个人汽车不能飞”,很简单,由于不支持。
使用一种软件的时候须要按照软件提供的功能来操做,编程语言也是一种软件系统,若是你想在这里面使用逆波兰表达式,那么须要软件提供对逆波兰表达式的支持。对于编程语言,就是语法级的支持。
编译器在编译时怎么实现的我没有研究过。可是对于常数值,在编译时就能够肯定,对于变量,我想仍是和平台相关吧。对于寄存器比较少的平台,有可能会把表达式的运算序列化成一个逆波兰表达式。只是有可能,没有研究过编译器的实现。
应用主要是任何基于栈的程序语言,计算机(至关大一部分工程计算机都是基于栈的)。
Evaluate the value of an arithmetic expression in Reverse Polish Notation.
Valid operators are +, -, *, /. Each operand may be an integer or another expression.
Some examples:
["2", "1", "+", "3", "*"] -> ((2 + 1) * 3) -> 9 ["4", "13", "5", "/", "+"] -> (4 + (13 / 5)) -> 6
计算逆波半表达式的值,有效的运算符是:+、-、*、/,每一个操做数要么是一个整数要么是另外一个表达式
使用栈进行操做
算法实现类
import java.util.Stack; public class Solution { public int evalRPN(String[] tokens) { // 参数校验 if (tokens == null || tokens.length < 1) { throw new IllegalArgumentException(); } int op1; int op2; // 操做数栈 Stack<Integer> stack = new Stack<>(); for (String token: tokens) { // 说明是运算符,要取栈顶两个元素进行运算 if ("+".equals(token) || "-".equals(token) || "*".equals(token) || "/".equals(token)) { // 取栈顶元素 op2 = stack.pop(); op1 = stack.pop(); // 进行运算 switch (token.charAt(0)) { case '+': op1 += op2; break; case '-': op1 -= op2; break; case '*': op1 *= op2; break; case '/': op1 /= op2; break; } // 结果入栈 stack.push(op1); } // 说明是操做数,入栈 else { stack.push(Integer.parseInt(token)); } } return stack.pop(); } }
运算符优先级参考:
优先级分为栈内优先级isp(In stack priority)和栈外优先级icp(In coming priority)。除了括号之外,其余运算符进栈后优先级都升1,这样能够体如今中缀表达式中相同优先级的操做符自左向右计算的要求,让位于栈顶的操做符先退栈并输出。各运算符及符号优先级:
操做符 |
# |
^ |
*,/,% |
+,- |
( |
) |
isp |
0 |
7 |
5 |
3 |
1 |
8 |
icp |
0 |
6 |
4 |
2 |
8 |
1 |