系列文章目录
1. java使用栈翻转字符串
2. java使用栈解析分隔符匹配
文章目录
前言
前面两篇文章分析了栈的基本使用,最后我们使用栈来解析算术表达式。如 4+6,或者5*(2+3),或者((2+4)7)+3*(8-4))等。
计算机的算法直接求算术表达式的值,还是比较困难的。因此分两步实现算法:
1. 将算术表达式转化成另一种形式:后缀表达式。
2. 计算后缀表达式的值。
一、后缀表达法
日常算术表达式的将操作符(+,-,*,/)放在两个操作数之间。所以把这种写法叫做中缀表达法。例如日常我们写的形如3+3,5/7或者使用字母A+B,A/B等。
后缀表达式,由一位波兰的数学家发明,操作符跟在两个操作数的后面。这样,A+B就称为AB+,A/B成为AB/
中缀表达式和后缀表达式相互转换
中缀表达式 | 后缀表达式 |
---|---|
A+B-C | AB+C- |
A*B/C | AB*C/ |
A+B*C | ABC*+ |
A*B+C | AB*C+ |
A*(B+C) | ABC+* |
AB+CD | ABCD+ |
(A+B)*(C-D) | AB+CD-* |
((A+B)*C)-D | AB+C*D- |
A+B*(C-D/(E+F)) | ABCDEF+/-*+ |
二、把中缀表达式转换成后缀表达式
下面内容解释中缀表达式的算术表达式是如何转换成后缀表达式。为了理解怎样创建后缀表达式,先看怎么计算后缀表达式的值。如2*(3+4)的后缀表达式234+*是怎样求出结果14的。(为了简单起见,本次讨论的操作数都是一位数字)
1. 人类是如何计算中缀表达式的值
虽然对计算机来说求值很难,对于人类来说计算出3+5+4或者3*(4+5)的值不难。通过分析人类是如何计算这些表达式的值,可以得到一些把表达式转换成后缀表达式的启发。
总的来说,人类解算术表达式的时候,应该遵循以下几条规则:
- 从左到右读取算式。
- 已经读到了可以计算值的两个操作数和一个操作符时,就可以计算,并用计算结果代替那两个操作数和那个操作符。
- 继续这个过程。从左到右,能算就算,直到表达式的结尾。
下面来看三个非常简单的中缀表达式求值的例子。
求值3+4-5
读取元素 | 解析后的表达式 | 说明 |
---|---|---|
3 | 3 | |
+ | 3+ | |
4 | 3+4 | |
- | 7 | 看到-后,可计算3+4 |
7- | ||
5 | 7-5 | |
END | 2 | 表达式末端后,可计算7-5 |
直到看到4后面的操作符才可以计算3+4,如果后面操作符时*或/,就要在乘除运算完成之后,才能做加法操作。
但是在这个例子中4后面的操作符是‘-’,它和加法运算的优先级相同,所以看到‘-’后,既可以计算3+4了,得到7,代替3+4.到表达式末端既可以计算7-5了。
求值3+4*5
读取元素 | 解析后的表达式 | 说明 |
---|---|---|
3 | 3 | |
+ | 3+ | |
4 | 3+4 | |
* | 3+4* | 不能计算3+4,因为*比+优先级更高 |
看到5时,就可以计算4*5 | ||
5 | 3+4*5 | |
3+20 | ||
END | 23 | 到达表达式末端时,可计算3+20 |
因为乘,除有比加,减更高的优先级所以要等计算出4*5的结果后才能做加3的运算。 |
有一点需要注意,在执行乘法运算之后还是不能直接执行加法运算,当发现5后面什么也没有了,他就是表达式的结尾了。才可以执行加法运算。
括号用于覆盖操作符原有的优先级。下面看3*(4+5)的解析过程
读取元素 | 解析后的表达式 | 说明 |
---|---|---|
3 | 3 | |
* | 3* | |
( | 3*( | |
4 | 3*(4 | 由于括号,所以不能计算3*4 |
+ | 3*(4+ | |
5 | 3*(4+5 | 仍不能计算4+5 |
) | 3*(4+5) | 看到)后,就可以计算4+5了 |
3*9 | 看完4+5后,可计算3*9 | |
27 | ||
END | 完毕 |
通过上面三个例子,计算中缀表达式时,既要向前,又要向后读表达式。向前(从左到右)读操作数和操作符。等到读到足够的信息来执行一个运算时,向后去找出两个操作数和操作符,执行运算。
有时如果后面是更高级别的操作符或者括号时,就必须推迟此运算。这种情况下,必须先执行后面的级别更高的运算;然后再回头(向左)来执行前面的运算。
虽然我们可以直接编写这样的求值算法,但是,先把表达式转换成后缀表达法计算会更容易。
2.如何将中缀表达式转换成后缀表达式
将中缀表达式转换成后缀表达式的规则和计算中缀表达式的规则类似。但是,将中缀表达式转换成后缀表达式不用做算术运算。只是把操作数和操作符重新排列成另一种形式:后缀表达法。
中缀表达法转后缀表达法规则如下
1. 如果中缀字符串中的字符是操作数,则立即把它复制到后缀字符串中。也就是说,如果看到中缀字符串中的A时,就立即把A写到后缀字符串里。
2. 一旦可以利用操作符求中缀表达式的某部分的值,就把该运算符复制到后缀字符串中。
将A+B-C转换成后缀表达式
从中缀比爱哦大师中读取字符串 | 分解中缀表达式过程 | 求后缀表达式过程 | 说明 |
---|---|---|---|
A | A | A | |
+ | A+ | A | |
B | A+B | AB | |
- | A+B- | AB+ | 读到-,可以把+复制到后缀字符串中 |
C | A+B-C | AB+C | |
END | A+B-C | AB+C- | 当读到表达式结尾处,可以复制- |
每当计算求值的时候,只需要把操作符写到后缀表达式的输出中。
将A+B*C转换成后缀表达式
从中缀表达式中读取字符串 | 分解中缀表达式过程 | 求后缀表达式过程 | 说明 |
---|---|---|---|
A | A | A | |
+ | A+ | A | |
B | A+B | AB | |
* | A+B* | AB | |
C | A+B*C | ABC | 看到C后可以复制* |
A+B*C | ABC* | ||
END | A+B*C | ABC*+ | 看到表达式末端时,可以复制+ |
将A*(B+C)转换成后缀表达式
从中缀表达式中读取字符串 | 分解中缀表达式过程 | 求后缀表达式过程 | 说明 |
---|---|---|---|
A | A | A | |
* | A* | A | |
( | A*( | A | |
B | A*(B | AB | 因为有括号,所以不能复制* |
A*(B+ | AB | ||
+ | A*(B+C | ABC | 不能复制+ |
C | A*(B+C) | ABC+ | 看到)时,可以复制+ |
) | A*(B+C) | ABC+* | 复制完+后,可以复制* |
END | A*(B+C) | ABC+* | 完毕 |
在转换过程中,需要向前和向后两个防线来扫描中缀表达式,以完成后缀表达式的转换。当某个操作符后面的操作符优先级更高或者为括号时,必须要比底优先级的操作符更早的写到后缀表达式字符串中。
A+BC和A(B+C)转换到后缀表达式的过程中,操作符的顺序是颠倒的。因为第一个操作符必须等第二个操作符输出后才能输出,所以操作符在后缀字符串中的顺序和中缀字符串中的顺序是相反的。下面再举一个较长的表达式可以更清楚的说明这一点。
这里先加入栈的内容,稍后再解释。
将A+B*(C-D)转换成后缀表达式
从中缀表达式中读取字符串 | 分解中缀表达式过程 | 求后缀表达式过程 | 栈中的内容 |
---|---|---|---|
A | A | A | |
+ | A+ | A | + |
B | A+B | AB | + |
* | A+B* | AB | +* |
( | A+B*( | AB | +*( |
C | A+B*(C | ABC | +*( |
- | A+B*(C- | ABC | +*(- |
D | A+B*(C-D | ABCD | +*(- |
) | A+B*(C-D) | ABCD- | +*( |
A+B*(C-D) | ABCD- | +*( | |
A+B*(C-D) | ABCD- | +* | |
A+B*(C-D) | ABCD-* | + | |
A+B*(C-D) | ABCD-*+ |