先说一说中缀表达式。
中缀表达式也就是我们常用常说的算术表达式,像5*3, 3+4*6, (4+2)/6,(4-1)*(21+4) 这些都是中缀表达式。中缀表达式的特点是运算符在被运算的两个数中间。
中缀表达式的运算对我们来说并不难,简单的中缀表达式我们小学就会了。但对于电脑来说,运算中缀表达式却并不简单。电脑不懂加减乘除的优先级,如果再加几个括号套过去套过来,只会把电脑搞的不要不要的~
这时候,我们就需要一种优秀的方法来解决这个问题了。于是就有了后缀表达式。
顾名思义,后缀表达式就是运算符在被运算的两个数后面。像5*3写成后缀表达式就是53*,4*2+3就是42*3+。后缀表达式没有括号,运算符也没有优先级,所有运算都是从左往右的。
后缀表达式的运算较为简单,从左往右依次读取,如果读到运算符,就把运算符前两个数字执行相应运算,将结果保存到这个位置。比如4 2 * 3 +,先从左往右读取,第三个遇到*,就把*前面的4和2拿出来相乘,得到8,把8放回去,后缀表达式就变成了8 3 +,再次读取到+,8加上3等于11,就得到了4 2 * 3 +这个后缀表达式的值为11.
后缀表达式没有优先级,括号优先级这些都体现在它的存放顺序中,它存放的顺序直接决定了运算的顺序。中缀表达式3+4*6可以表示成3 4 6 * +,也可以更直观地表示成4 6 * 3 +。像中缀表达式(4+2)/6表示成后缀表达式就是4 2 + 6 /
电脑运算后缀表达式就很方便了,只需要从左往右跑一遍就可以了,而不需要像我们运算中缀表达式那样跳过去跳过来的。如果我们要用电脑处理中缀表达式,可以先将中缀表达式转化为后缀表达式。
接下来就是重点难点了,中缀转后缀。
中缀转后缀要用到栈。
1.先写简单的中缀转后缀,一位数的不包含括号的中缀表达式转换:
将中缀表达式先保存到一个字符串a,再创一个字符串b用于储存产生的后缀表达式,同时还需要一个用于存运算符的栈c。
这里的运算符有个优先级的概念,有必要先说一下。每个运算符的优先级不同,我简单归纳了一下:
+ | - | * | / | ( | ) | # |
2 | 2 | 3 | 3 | 1 | 4 | 0 |
这里有个#号不是运算符,后面用的时候再说。
初始b和栈c都为空,a里面有一个中缀表达式。然后从左往右依次读取中缀表达式a的每一位,a[i]如果是数字就直接存进字符串b(这里的数字都是一位数),如果是运算符就和栈顶元素比较,如果这个运算符的优先级大于或等于栈顶运算符的优先级,就把栈顶运算符取出来放进b字符串里面,然后再用该运算符a[i]继续和栈顶运算符进行比较,重复上面的操作,一直到找到优先级低于它的运算符或者栈为空。从左往右一直把a字符串跑完一遍后,b里面已经有一部分后缀表达式了,栈c里面可能还有一部分运算符。那么,我们接下来只需要把栈c里面的运算符倒进b里面就行了,也就是把栈c的元素依次出栈并存到字符串b里面。这样一个后缀表达式b就完成了。
这里有个小技巧,可以在开始前先朝栈c里面放一个字符#,字符#的优先级是最低的,没有运算符能把它弹出来。这样判断的时候就不用判断是否为空栈了。
实战转换一下3*6+3。注意这里的数字都是以字符的形式存的,所以运算的时候要减去个0字符。
首先创字符串a,b和栈c:
字符串a | a[i](操作的字符) | 操作 | 字符串b | 栈c |
3*6+3 | 3 | 3进b | 3 | NULL |
3*6+3 | * | *进c | 3 | * |
3*6+3 | 6 | 6进b | 3 6 | * |
3*6+3 | + | *的优先级大于+,弹出*到b,c为空,+进c | 3 6 * | + |
3*6+3 | 3 | 3进b | 3 6 * 3 | + |
a中处理完了 | 将c中元素倒入b | 3 6 * 3 + | NULL |
得到了它的后缀表达式为3 6 * 3 +
这是最简单的一种中缀转后缀了,基础理解好了后面就好说了。
2.然后就是有括号的中缀表达式,例如6*(4+2)/2
依然是从左往右读,其他处理方式也一样,只是两个括号处理方式不同。读到左括号(不用判断直接存进栈c,不把其他运算符弹出栈。如果读到右括号) 不用进栈,直接一个个把栈c的运算符出栈到b里面,直到遇到到左括号( ,然后把左括号出栈扔了。继续往后读a[i]。
以6*(4+2)/2算一遍:
字符串a | a[i](操作的字符) | 操作 | 字符串b | 栈c |
6*(4+2)/2 | 6 | 6进b | 6 | NULL |
6*(4+2)/2 | * | *进c | 6 | * |
6*(4+2)/2 | ( | (直接进c | 6 | *( |
6*(4+2)/2 | 4 | 4进b | 6 4 | *( |
6*(4+2)/2 | + | +进c | 6 4 | *(+ |
6*(4+2)/2 | 2 | 2进b | 6 4 2 | *(+ |
6*(4+2)/2 | ) | 取栈c的栈顶+,+进b,再取栈顶(,直接删除 | 6 4 2 + | * |
6*(4+2)/2 | / | *的优先级等于/,*进b,c栈空,/进c | 6 4 2 + | / |
6*(4+2)/2 | 2 | 2进b | 6 4 2 + * 2 | */ |
a处理完毕 | c倒入b中 | 6 4 2 + * 2 / | NULL | |
所以,6*(4+2)/2的后缀表达式为6 4 2 + * 2 /
3.然后再说多位数的。比如32+45*12这种。
关键是题目中的数字是以字符的形式储存的,并不是他们真正的值。
这里有两种方法解决,一种是读取的时候如果为连续数字,则直接乘十往后加,也就是加出它真实的数字。比如32,先读取3,然后读取2,因为是连续数字,所以3*10+2=32。这种方法比较方便,但应用实在有限,因为是字符类型保存的,所以保存的值的大小不能超过256。并且这个数字还可能和运算符的ascll码相同,导致最后当成了运算符处理。所以这方法其实没什么卵用~
第二种是普遍方法,更利于中缀转后缀后输出。简单说就是在后缀表达式的每个数字完了后加上一个#,这样会让b字符串变成类似这样的样子:3 2 # 4 5 # 1 2 # * / 。这样输出的时候还需要稍作处理再输出。
还是以32+45*12为例写一下吧:
字符串a | a[i](操作的字符) | 操作 | 字符串b | 栈c |
32+45*12 | 3 | 3进b | 3 | NULL |
32+45*12 | 2 | 2进b | 3 2 | NULL |
32+45*12 | + | +不为数字,#进b,+进c | 3 2 # | + |
32+45*12 | 4 | 4进b | 3 2 # 4 | + |
32+45*12 | 5 | 5进b | 3 2 # 4 5 | + |
32+45*12 | * | *不为数字,#进b,*进c | 3 2 # 4 5 # | +* |
32+45*12 | 1 | 1进b | 3 2 # 4 5 # 1 | +* |
32+45*12 | 2 | 2进b | 3 2 # 4 5 # 1 2 | +* |
a处理完了 | 最后一个为数字,#进b,c倒入b | 3 2 # 4 5 # 1 2 # * + | NULL |
写了几个小时,终于写完了~~
累~
字符串a | a[i](操作的字符) | 操作 | 字符串b | 栈c |
6*(4+2)/2 | 6 | 6进b | 6 | NULL |
6*(4+2)/2 | * | *进c | 6 | * |
6*(4+2)/2 | ( | (直接进c | 6 | *( |
6*(4+2)/2 | 4 | 4进b | 6 4 | *( |
6*(4+2)/2 | + | +进c | 6 4 | *(+ |
6*(4+2)/2 | 2 | 2进b | 6 4 2 | *(+ |
6*(4+2)/2 | ) | 取栈c的栈顶元素+,+进b,再取栈顶元素( ,直接删除 | 6 4 2 + | * |
字符串a | a[i](操作的字符) | 操作 | 字符串b | 栈c |
6*(4+2)/2 | 6 | 6进b | 6 | NULL |
6*(4+2)/2 | * | *进c | 6 | * |
6*(4+2)/2 | ( | (直接进c | 6 | *( |
6*(4+2)/2 | 4 | 4进b | 6 4 | *( |
6*(4+2)/2 | + | +进c | 6 4 | *(+ |
6*(4+2)/2 | 2 | 2进b | 6 4 2 | *(+ |
6*(4+2)/2 | ) | 取栈c的栈顶元素+,+进b,再取栈顶元素( ,直接删除 | 6 4 2 + | * |
字符串a | a[i](操作的字符) | 操作 | 字符串b | 栈c |
6*(4+2)/2 | 6 | 6进b | 6 | NULL |
6*(4+2)/2 | * | *进c | 6 | * |
6*(4+2)/2 | ( | (直接进c | 6 | *( |
6*(4+2)/2 | 4 | 4进b | 6 4 | *( |
6*(4+2)/2 | + | +进c | 6 4 | *(+ |
6*(4+2)/2 | 2 | 2进b | 6 4 2 | *(+ |
6*(4+2)/2 | ) | 取栈c的栈顶元素+,+进b,再取栈顶元素( ,直接删除 | 6 4 2 + | * |