关于中缀表达式和逆波兰表达式(终结篇)
上篇
逆波兰表达式被广泛应用于编译原理中,但是小李近来在研究计算一元一次方程的时候发现通过逆波兰算法计算一元一次方程会更简单,原因是逆波兰表达式有一个其他的算法不能比拟的优点——拆括号(关于一元一次方程的算法程序,我会在以后陆续登载)。
标准的表达式如“A+B”,在数学上学名叫中缀表达式(Infix Notation),原因是运算符号在两个运算对象的中间。相对应的还有前缀表达式(Prefix Notation),如:“+ - A * B C D”,转换成中缀表达式为:“A - B * C + D”;后缀表达式(Postfix Notation),比如前所述的中缀表达式转换为后缀表达式为:“A B C * - D +”。为了纪念波兰数学家鲁卡谢维奇(Jan Lukasiewicz),前缀表达式被称作波兰表达式,后缀表达式称为逆波兰表达式(Reverse Polish Notation)。
后缀表达式的优点是显而易见的,编译器在处理时候按照从左至右的顺序读取逆波兰表达式,遇到运算对象直接压入堆栈,遇到运算符就从堆栈提取后进的两个对象进行计算,这个过程正好符合了计算机计算的原理。
后缀表达式比前缀表达式更加易于转换,并且它的最左面一定为数字,这一点在实际编程的时候就会体会到它的好处了。
逆波兰表达式有一个更大的优点,就是拆括号,根据运算符的级别将中缀表达式转换成逆波兰表达式后,运算顺序就已经替代了运算符的级别,这样也避免了括号提高运算级别的特殊处理。
现在我就先简单介绍一下从中缀表达式转换为逆波兰表达式的标准算法。
a)给出一个中缀表达式1*(2+3)
b)系统先定义两个先进后出的堆栈:运算符号栈(简称入栈in),后缀表达式输出符号栈(简称出栈out)
c)系统按从左至右的顺序读取中缀表达式
d)读入数字直接压入出栈(out)
e)读入第一个运算符直接压入入栈(in)
f)读入“(”直接压入入栈(in),此时两栈的数据为:in 1 ; out *,(
e)将第二次读取的运算符“+”与入栈中的栈顶运算符“(”进行比较,高于栈顶级别的直接进栈,低于或等于栈顶级别的要将入栈in解栈(即出栈),按次压入出栈中。比如现在入栈的运算顺序为(,*,/,此时若系统读取的运算符为+,级别比/要低,此时要按/,*的顺序压入出栈out中,并在入栈中释放/和*符号,最后得到 in ( ; out /,*的结果。
f)最后读取“)”时要找到入栈in中最近的“(”,将其前面所有符号全部按后进先出的顺序压入出栈,并解压,“(”与“)”抵消。此时两栈的数据为:in 1,2,3,+ ; out *
g)系统读取中缀表达式结束后将入栈in中的所有符号按后进先出的顺序全部解压,并依次压入出栈out中,最后出栈的结果就应该为1,2,3,+,*
h)按先进先出的顺序将出栈out解压得到后缀标准表达式1,2,3,+,*
两个堆栈先后数据情况:
In | out |
| 1 |
* | 1 |
*,( | 1 |
*,( | 1,2 |
*,(,+ | 1,2 |
*,(,+ | 1,2,3 |
* | 1,2,3,+ |
| 1,2,3,+,* |
将中缀表达式转换成逆波兰表达式过程中,要注意的是“(”无论入栈中级别为何直接入栈,在遇到“)”时候找到最后进入的“(”,并把“(”前面所有的符号都压入出栈。小李在编程中就犯过错误,仅凭运算符的级别来判断,结果出现错误的结果。