一些思考…
上一篇文章中,我们在遇到与符号栈栈顶优先级相同的符号相同或不变的符号时,会进行数字栈和符号栈分别弹栈→运算→压栈的操作,这是为什么呢?为什么不可以不弹栈运算,等到计算式扫描完成之后再统一进行弹栈运算呢?
—这种算法看似简化了时间复杂度,但是会遇到运算顺序的问题。
考虑下面两种情景:
- 1-2-3=?: 扫描完成后,如果按照先压栈,到最后统一弹栈的方法,我们的数字栈为[1,2,3], 符号栈为[-,-]。此时运算结果为: 2-3=-1, 1-(-1)=2. 显然错误
- 1/2/3=?:扫描完成后,运算顺序实则为 1/(2/3)=3/2,同样错误
为什么存在这种问题呢?很简单,最后弹栈的操作改变了运算的顺序。在接下来的后缀表达式转换中同样存在一个问题
中缀→后缀
思路
将思想“遇到优先级相同或更低的元素直接计算,遇到优先级更高的不进行运算”的思想转化成一个表达式,根据运算该表达式得到最终值
方法
换汤不换药,但是后缀表达式的突出优点是,没有括号!!,我们这次考虑中缀表达式中存在符号的情景,来体会后缀表达式的优越性╰(°▽°)╯
- 建立一个栈缓存结构
- 遇到数字直接输出
- 遇到符号,如果栈为空则先压栈,如果不为空,则比较栈顶元素和该符号的优先级:
3.1如果该符号优先级较大,则直接将该符号压入栈中
3.2 如果该符号优先级和栈顶符号优先级相同或更小,则先弹出栈顶元素,输出,再将该符号压栈
3.3 如果栈顶为括号,则不弹栈 - 遇到左括号 ( 则直接压栈
- 遇到右括号 **)**则输出所有栈内符号,直到遇到左括号( 注意左右括号都不输出到表达式中)
一个栗子
中缀表达式 (3-5) * (6+17 * 8)/ 3
按照上面的方法:
- 左括号压栈
- 3输出
- 负号“-”压栈
- 5输出
- 遇到右括号—弹栈 表达式为“35-”
- 遇到乘号“*”,压栈
- 遇到左括号,压栈
- 6输出
- 加号“+” ,此时栈顶为括号,按兵不动
- 17输出
- 遇到乘号”*“,此时栈顶为加号,直接压栈
- 8输出
- 右括号,弹栈!此时表达式为:3 5 - 6 17 8 * +, 栈中只有一个乘号
- 遇到除号“/”,弹出乘号,压入除号
- 输出3
- 输出除号,最终表达式为: 3 5 - 6 17 8 * + * 3/
后缀表达式的运算
后缀表达式的计算就很简单了—
建立一个栈缓存结构,遇到数字压栈, 遇到符号则弹出栈顶两个元素进行运算,再存入栈中。
第二个栗子
上一个栗子中的表达式: 3 5 - 6 17 8 * + * 3/
- 存入3,5
- 遇到“-”,弹出3,5,3-5=-2. 压入-2
- 压入6,17,8
- 遇到“”,弹出 17,8,178=136, 压入136
- 遇到“+”,弹出6,136, 6+136=142,压入142
- 遇到“*”,弹出-2,142,(-2)*142=-284
- 压入3
- 遇到“/”,弹出-284,3,-284/3=-94.67
与中缀表达式运算结果相同。
代码
懒得写了,时间复杂度和空间复杂度均为O(n).
后记
很经典的题目,但是很有意思,思维要严谨,寻找其中的规律。