前缀、中缀、后缀表达式求值以及相互转换
一、 表达式求值问题
1. 前缀表达式求值问题
从右向左扫描,遇到数字时,将数字压入栈,遇到运算符时候,把栈内前两个数字弹出,先弹出的数字放在运算符前面,然后进行相应运算,并将计算结果入栈。重复直到表达式最右端。
例:前缀表达式是:- 1 + 2 3(从右向左扫描)
a) 将数字3,2依次入栈;
b) 遇见运算符+,依次弹出2,3,计算2+3=5,将5入栈继续扫描;
c) 将1入栈;
d) 遇见运算符-,依次弹出1,5,计算1-5=-4,将-4入栈继续扫描;
e) 此时入栈到最左端,停止,所得结果-4
2. 中缀表达式求值问题
正常求值
3. 后缀表达式求值问题
从左向右扫描,遇到数字时,将数字压入栈,遇到运算符时候,把栈内前两个数字弹出,先弹出的数字放在运算符后面,然后进行相应运算,并将计算结果入栈。重复直到表达式最右端。
例:后缀表达式是34+5*6- (从左向右扫描)
a) 将数字3,4依次入栈;
b) 遇见运算符+,依次弹出4,3,计算3+4=7,将7入栈继续扫描;
c) 将5入栈;
d) 遇见运算符*,依次弹出5,7,计算7*5=35,将35入栈继续扫描;
e) 将6入栈;
f) 遇见运算符-,依次弹出两个6,35,计算35-6=29,将29入栈继续扫描;
g) 此时入栈到最右端,停止,所得结果29
二、 表达式相互转换问题
1. 前缀转化为其他类型表达式(构造树)
示例:* + 4 2 + 3 6
构造树: 1. 从左往右扫描字符串
2. 遇到操作符则递归构造树节点,当前操作符是根节点,并递归构造左右子节点
1) 前缀转化为中缀
中序遍历当前结果,中缀表达式为:(4+2)*(3+6)
2) 前缀转化为后缀
后序遍历当前结果,后续表达式为:4 2 + 3 6 + *
2. 中缀转化为其他类型表达式(栈或构造树)
方法一:栈
1)中缀转化为前缀
a) 初始化两个栈:运算符栈S1和储存中间结果的栈S2;
b) 从右至左扫描中缀表达式;
c) 遇到操作数时,将其压入S2;
d) 遇到运算符时,比较其与S1栈顶运算符的优先级:
i. 如果S1为空,或栈顶运算符为右括号“)”,则直接将此运算符入栈;
ii. 如果S1栈非空且当前运算符优先级比栈顶运算符的较高或相等,也将运算符压入S1;
iii. 如果S1栈非空但且当前运算符优先级比栈顶运算符的较低,将S1栈顶的运算符弹出并压入到S2中(当前运算符不放入S2栈),再次与S1中新的栈顶运算符相比较;
e) 遇到括号时:
i. 如果是右括号“)”,则直接压入S1;
ii. 如果是左括号“(”,则依次弹出S1栈顶的运算符,并压入S2,直到遇到一次右括号为止,此时将这一对括号丢弃;
f) 重复步骤(b)至(e),直到表达式的最左边;
g) 将S1中剩余的运算符依次弹出并压入S2;
h) 依次弹出S2中的元素并输出,结果即为中缀表达式对应的前缀表达式。
2)中缀转化为后缀
1、初始化两个栈,运算符栈S1和数据栈S2
2、从左向右扫描中缀表达式,遇见数字直接入栈S2
3、遇见运算符,将比较运算符与S1栈顶运算符优先级
a) 如果S1栈顶是NULL或者是“(”则直接将遇见的运算符入栈S1
b) 如果S1栈顶非空也不是“(”,且遇见的运算符比S1内优先级要高,那么将遇见的运算符直接入栈S1(注意转换为前缀表达式时是优先级较高或相同,而这里则不包括相同的情况)
c) 如果S1栈顶是“)”或者遇到的运算符优先级更小。则将S1内运算符出栈到S2里,再转回a)
4、遇见括号
a) 如果是左括号“(”,则直接压入S1;
b) 如果是右括号“)”,则依次弹出S1栈顶的运算符,并压入S2,直到遇到左括号为止,此时将这一对括号丢弃;
5、重复2-4,直至到表达式最右边
6、将S1中剩余运算符弹出到S2
7、依次弹出S2中的元素并输出,结果的逆序即为所求后缀表达式(转换为前缀表达式时不用逆序)。
将中缀表达式exp转换为后缀表达式,采用了一个op运算符栈,其他的用数组存储。
方法二:构造树
首先将中缀表达式转换为表达式树,然后后序遍历表达式树,所得结果就是后缀表达式。
将中缀表达式转化为表达式树方法:表达式树的树叶是操作数,而其他的节点为操作符,根节点为优先级最低且靠右的操作符(如上述表达式优先级最低的是- 和+,但 + 更靠右,所以根为+),圆括号不包括。如上述中缀表达式转换后的表达式树如下:
经过前序遍历表达式树后得到的前缀表达式为:-+1*+2345
经过后序遍历表达式树后得到的后缀表达式为:123+4*+5–
- 后缀转化为其他类型表达式(构造树)
从左到右扫描后缀表达式,一次一个符号读入表达式。如果符号是操作数,那么就建立一个单节点树并将它推入栈中。如果符号是操作符,那么就从栈中弹出两个树T1和T2(T1先弹出)并形成一颗新的树,该树的根就是操作符,它的左、右儿子分别是T2和T1。然后将指向这棵新树的指针压入栈中。
前三个符号是操作数,因此创建三颗单节点树并将指向它们的指针压入栈中。
“+”被读入,因此指向最后两颗树的指针被弹出,形成一颗新树,并将指向新树的指针压入栈中。以下的流程图以相同原理执行。
1) 后缀转化为前缀
先序遍历所得的表达式树即得到我们所需的前缀表达式:-+1*+2345
2) 后缀转化为中缀
中序遍历所得的表达式树即得到我们所需的中缀表达式:1+((2+3)*4)-5