前缀、中缀、后缀表达式是对表达式的不同记法,其区别在于运算符相对于操作数的位置不同,前缀表达式的运算符位于操作数之前.
中缀和后缀同理虽然人的大脑很容易理解与分析中缀表达式,但对计算机来说中缀表达式却是很复杂的,因此计算表达式的值时,通常需要先将中缀表达式转换为前缀或后缀表达式,然后再进行求值。对计算机来说,计算前缀或后缀表达式的值非常简单。
举例:
中缀表达式:1 + (2 + 3) × 4 - 5
前缀表达式:- + 1 × + 2 3 4 5
后缀表达式:1 2 3 + 4 × + 5 -
波兰式
什么是波兰式?
将操作符号写在操作数之前,也就是前缀表达式
中缀转前缀转换过程同样需要用到栈,具体过程如下:
将中缀表达式转换为前缀表达式:
(1) 初始化两个栈:运算符栈S1和储存中间结果的栈S2;
(2) 从右至左扫描中缀表达式;
(3) 遇到操作数时,将其压入S2;
(4) 遇到运算符时,比较其与S1栈顶运算符的优先级:
- (4-1) 如果S1为空,或栈顶运算符为右括号“)”,则直接将此运算符入栈
- (4-2) 否则,若优先级比栈顶运算符的较高或相等,也将运算符压入S1
- (4-3) 否则,将S1栈顶的运算符弹出并压入到S2中,再次转到(4-1)与S1中新的栈顶运算符相比较;
前缀表达式的计算机求值
- 从右至左扫描表达式
- 遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素 op 次顶元素),并将结果入栈
- 重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果
计算前缀表达式的值:- + 1 × + 2 3 4 5
- 从右至左扫描,将5,4,3,2压入堆栈;
- 遇到+运算符,弹出2和3(2为栈顶元素,3为次顶元素),计算2+3的值,得到5,将5压入栈;
- 遇到×运算符,弹出5和4,计算5×4的值,得到20,将20压入栈;
- 遇到1,将1压入栈;
- 遇到+运算符,弹出1和20,计算1+20的值,得到21,将21压入栈;
- 遇到-运算符,弹出21和5,计算21-5的值,得到16为最终结果
逆波兰表达式
什么是逆波兰表达式?
转换规则
1)我们使用一个stack栈结构存储操作符,用一个List结构存储后缀表达式结果
2)首先读取到数字,直接存入list中
3)当读取到左括号"("时,直接压栈,当读取到运算符时,分两种情况讨论
a.当运算符栈为空或者栈顶操作符的优先级小于当前运算符优先级时(如+和-的优先级低于 * 和 /),直接入栈
b.当运算符不为空时且栈顶操作符的优先级大于或等于当前运算符优先级时,循环执行出栈操作并加入list中,直到遇到优先级小于当前运算符的元素为止。循环执行完后再将当前运算符压栈。另外需要注意的是,只有遇到右括号时,左括号才出栈
4) 当遇到右括号")"时,循环执行出栈操作并加入到list中,直到遇到左括号为止。并将左括号弹出,但不加入list中
5) 表达式的值读取完后,将操作符栈中的所有元素弹出并加入到list中
执行完上面步骤后,list中存储的顺序即为我们转换后的后缀表达式的结果
转换实例
运算符在操作数的后面 3 * 4
===> 3 4 *
, 所以也被称为后缀表达式。
使用逆波兰表达式时,是不需要使用括号的。在中缀表达式中,例如:(3 - 4) * 5
,我们需要使用括号提高减号的优先级,而在逆波兰表达式中是不需要使用括号的,3 4 - 5 *
,直接在3 4
操作数后面跟着一个-
号, 就可以表示3 - 4
是首先计算的。
再举几个例子:
3 - 4 * 5
===>3 4 5 * -
(4 + (13 / 5))
===>4 13 5 / +
((2 + 1) * 3) = 9
===>2 1 + 3 *
手动计算逆波兰表达式
面对一个复杂逆波兰表达式,我们改如何计算呢?
例如:10 6 9 3 + -11 * / * 17 + 5 +
我们首先从头开始遍历逆波兰表达式字符串
- 遇到第一个
+
号9 + 3 = 12
,现在的表达式是,10 6 12 -11 * / * 17 + 5 +
- 遇到第二个
*
号12 * -11 = -132
,现在的表达式是10 6 -132 / * 17 + 5 +
- 遇到第三个
/
号6 / -132 = -0.04
,现在的表达式10 -0.04 * 17 + 5 +
- 遇到第四个
*
号10 * -0.04 = -0.4
,现在的表达式-0.4 17 + 5 +
- 遇到第五个
+
号-0.4 + 17 = 16.6
,现在的表达式16.6 5 +
- 遇到第六个
+
号16.6 + 5 = 21.6