1、后缀表达式,以及中缀转后缀算法的思考

来源:互联网 发布:许嵩和方文山歌词知乎 编辑:程序博客网 时间:2024/05/23 15:21

所谓运算中的中缀表达式,就是形如a+b这样,运算符号在参与运算的两个量的中间。按照这个逻辑那么后缀表达式也就很好理解了:ab+。

后缀表达式又称逆波兰表达式,这个表达式有特别的用途,可以配合栈(stack)来完成运算,这种运算只需要按顺序进行而不需要考虑运算符的优先级,并且速度很快。

比如a*b+c+d*e,改写为后缀表达式变成:ab*c+de*+。对于这个式子,依次读取每个符号,并执行如下运算:

1、遇到运算量,则将此运算量入栈。

2、遇到运算符,则连续出栈两个运算量,结合此运算符进行运算后,如果当前运算符不是最后一个符号,则将结果作为一个运算量入栈,否则将结果输出为最终运算结果。


比如对于ab*c+de*+,首先a、b入栈,读取到乘号“*”,a、b出栈相乘,再将a*b入栈,c入栈,读取到加号“+”,则a*b、c出栈相加得到a*b+c……

画个图会比较形象,但是太麻烦还是算了。。。。总之这样就能以O(N)完成一个四则运算。加上除法和减法也一样。对于后缀表达式来说,式子本身已经带有了运算顺序的信息,而不需要考虑括号和运算符优先级。但是作为一个学过信息论的人肯定知道,信息是不会自己冒出来的,所以如何将普通中缀表达式中必须依靠已知的运算符优先级和括号才能表达的运算顺序信息转移到运算量排列中形成后缀表达式呢?这是一个问题

一、首先考虑没有括号的简单情况:

这里做一点小变化,我们把运算符用数字来表示,参与运算的量用字母表示,比如a 1 b 2 c,1、2代表第一个和第二个运算符。对于后缀表达式,开头肯定是“ab”无疑,b后面只能有两种情况,要么是运算符1,要么是c。如果b后面是运算符1,则意味着1的优先级不小于2(平级的情况下从右向左结合)。如果2的优先级比较高,那么应该写做abc21,至于为什么不写做bc2a1,呢?我实在是不想去证明这样写有什么不对,姑且定一条规则认为运算量的顺序不能变。。。

从这个简单的3个运算量的运算中可以看出一点门道来:首先ab开头;1、2两个运算符按优先级出现,优先级相同则按顺序出现。

于是我们可以用栈来实现这个过程,依次读取中缀表达式,规则如下:

1、遇到运算量,则输出到后缀表达式中。

2、遇到运算符,如果栈为空,则入栈。

3、遇到运算符,如果栈中有运算符,则将栈中的运算符依次出栈输出到后缀式,直到位于top的运算符的优先级小于当前运算符。

4、读取完整个中缀式后,将栈中的运算符依次出栈输出到后缀式直到栈空。


对于a*b+c+d*e,这个过程如下:

1、读取a,输出a,栈空;

2、读取*,*入栈,栈:*;

3、读取b,输出ab,栈:*;

4、读取+,出栈*,输出ab*,+入栈,栈:+;

5、读取c,输出ab*c,栈:+;

6、读取+,出栈+,输出ab*c+,+入栈,栈:+;

7、读取d,输出ab*c+d,栈:+;

8、读取*,*入栈,栈:* +;

9、读取e,输出ab*c+de,出栈*,输出ab*c+de*,出栈+,输出ab*c+de*+。

要充分证明这个算法实在是有点困难,可以想到的是,当检查一个运算符n与前一个运算符n-1的运算顺序时,如果n需要先被执行,则n需要先被写入后缀式,这跟栈的后入先出规则是符合的,n入栈等待它的第二个运算量写入后缀式,然后检查运算符n+1是否比n有更高的优先级,是则继续将n+1的后面那个运算量写入后缀式,否则开始出栈,将优先于运算符n+1的运算进行完。理论上来说这样的算法支持无限多的运算优先级分级。

二、如果有括号

毫无疑问括号拥有最高的优先级,而且括号的特殊在于它是接在运算符之后(左括号)而不是一个运算量之后的,括号本身又不参与运算。显然的一点是,在后缀式中,括号之前的那个运算符一定是把括号中的运算都进行完毕了再添到末尾的。所以可以把左括号入栈,并且作为一种“底”,将栈分隔成两部分,括号以上用来按之前的规则计算括号内的式子,此时而遇到右括号则将左括号出栈,表示当前括号部分运算完毕。

所以只需要把规则改动一下:

1、遇到运算量,则输出到后缀表达式中。

2、遇到运算符,如果栈为空,则入栈。

3、遇到运算符,如果是右括号,则按照第4条执行,否则,如果栈中有运算符,则将栈中的运算符依次出栈输出到后缀式,直到位于top的运算符的优先级小于当前运算符,或top是一个左括号

4、遇到运算符,如果是右括号,则出栈top处的符号,直到出栈一个左括号(不输出到后缀式)。

5、读取完整个中缀式后,将栈中的运算符依次出栈输出到后缀式直到栈空。


显然这个方法是正确无疑的,当然我肯定想不出来这种东西,仅仅看了算法之后回过头来想想而已。

书上有一个问题:取幂运算不是从右向左结合而是从左向右结合的,比如2^2^10 = 2^1024,而不是4^10;这个嘛,应该很好解决,对于待入栈的“^”运算符,规则第3条中要求位于top的运算符优先级小于等于当前运算符,那么出栈的时候即是从左向右结合的


0 0