1 + 2 - (4 - 2 * 3 /2) * 5
正常的中缀表达式(运算符左右是参与计算的两个值),符合两条运算规则
1.运算符优先级高的先计算。
2.运算符优先级相同则左到右依次计算。
第一条运算规则是高于第二条的。
如果严格按照这个定律的话,中缀表达式在正常处理上,是比较跳脱的,步骤如下:
1.需要先找到最高优先级的运算符,参与计算。
2.重复1,直到剩下的运算符优先级相同。
3.从左到右依次计算。
如果表达式中只有 '+' , '-', '*' , '/' ,那还比较方便,因为只有两个优先级,所以只需要两轮计算,第一轮计算 '*' ,'/' ,第二轮计算'+','-'。
但是如果有括号的话,因为括号的优先级不是固定的,括号内的括号的优先级比括号外的括号的优先级高。
这样的情况下,程序实现的话,需要一轮一轮的计算去消除括号,十分繁琐。
仔细想想,繁琐的原因是因为优先级的不确定性,需要自己去找当前优先级最高的运算符。如果能把表达式中的运算符转换成按实际计算顺序的形式,相当于消除掉了第一条运算定理(运算符优先级高的先计算)那么计算起来就十分便捷,只需要按序读取计算即可。
本文第一行的运算表达式如下:
1 + 2 - (4 - 2 * 3 /2) * 5;
按实际计算顺序转换成新的表达式:
到这里发现,行不通,因为中缀表达式的特性,运算符左右是参数计算的值,运算符放中间的话,如果右值是一个表达式需要优先计算,并不知道右值需要计算到第几个计算符,换言之,还得加括号。。
逆波兰表达式能解决这个问题,了解一下。它是一种后缀表达式。
逆波兰表达式如何构建?
本文第一行的运算表达式转化为逆波兰表达式:
1 2 4 2 3 * 2 / - - + 5 *
转化的过程及其简单:
1.把所有的数值抄下来
1 2 4 2 3 2 5
2.根据运算符的优先顺序,将运算符添加到相应的两个数值后
2.1 先计算1 + 2 - (4 - 2 * 3 /2) * 5中的'*'
添加至待计算值的后方,变为:1 2 4 2 3 * 2 5
2.2 再计算1 + 2 - (4 - 2 * 3 /2) * 5中的 '/'
添加至待计算值的后方,变为:1 2 4 2 3 * 2 / 5
2.3 再计算1 + 2 - (4 - 2 * 3 /2) * 5中的 '-'
添加至待计算值的后方,变为:1 2 4 2 3 * 2 / - 5
3. 根据2依次类推,得到逆波兰表达式:
1 2 4 2 3 * 2 / - - + 5 *
逆波兰表达式如何计算?
以下面的逆波兰表达式为例:
1 2 4 2 3 * 2 / - - + 5 *
1. 读取第一个运算符,向前取运算符所需的数值个数参与计算。
1 2 4 2 3 * 2 / - - + 5 *
2.将计算结果填入当前位置,得到新的逆波兰表达式
1 2 4 6 2 / - - + 5 *
3.重复步骤1,步骤2,直到没有运算符为止,得到了最终结果。
逆波兰表达式有啥用?
根据计算过程,它是顺序计算的。在程序中,只需要一个stack,一次遍历表达式,就可以得到计算结果。
程序计算流程如下:
1.从左到右,数值依次入栈,若碰见运算符,执行步骤2
2.从栈中取运算符所需的数值个数参与计算,将计算结果入栈.
3.重复步骤1,步骤2 直到逆波兰表达式读取完。
4.此时栈中还有一个数值,这个数值就是最终结果。
为什么逆波兰表达式(后缀表达式)好使?中缀表达式为什么不行?前缀表达式行么?
从左到右顺序读取时,只有后缀表达式是先拿到参与计算的数值,再拿到计算符,此时,值一定是个确定的值。前缀,中缀表达式都是先拿到了计算符,后拿到参与计算的值,而此时,这个值可能还只是一个子表达式,需要计算才能得到确定的值。后缀表达式能保证参与计算的一定是值,而不是表达式。
以1 + 2 - (4 - 2 * 3 /2) * 5为例子。第一步计算2*3和第一步计算1+2都是没问题的。所以同一个中序表达式有多个逆波兰表达式形态
中序表达式转逆波兰表达式,就是确定运算符相对优先的,然后填入对应的位置即可。为什么是相对有限的?
当然我们关心的是怎么转换。
算法实现思路 中序表达式转逆波兰表达式
举几个例子:
有表达式 1 a 2 b 3 c 4 d 5
其中a,b,c,d分别是运算符。
若优先级是a>=b>=c>=d,计算顺序显而易见,(((1 a 2) b 3) c 4) d 5
即:从左向右计算,如果当前运算符优先级大于等于下一个运算符,则直接计算。
若优先级是a<b<c<d ,计算顺序显而易见 ,最优先计算的是d,其次是c,再次b,最后a
即:从左向右计算,如果当前运算符优先级小于下一个运算符,则先将此运算符入栈,如果运算符读取完,则依次出栈计算。
若优先级是a<b<c=d,
即:从左向右计算,如果当前运算符优先级小于下一个运算符,则先将此运算符入栈,如果运算符读取完