《数据结构》:中缀表达式转后缀表达式 - 机算原理分析

本文详细阐述了中缀表达式转化为后缀表达式的转换规则,包括栈的作用、运算数处理、操作符优先级判断和括号处理,强调了理解原理的重要性,而非死记硬背转换方法。
摘要由CSDN通过智能技术生成
【前情提要】

        之前写了一篇中缀转后缀表达式的文章👉中缀表达式转后缀表达式 + 后缀表达式的计算,最近回顾了一下,发现内容还比较浅显,只是给出了转换的方法,按照固定的流程进行就好,但是对于背后的原理并没有具体解释。刚开始学习时我也只是一股脑将转换的规则背下来,虽然应付考试没什么问题,但考完后很快就忘记了,复习时又是一片茫然。所以决定花点时间理清转换的原理以及自己的一些疑问,希望能更好地理解转换的过程,而不是纯靠死记硬背。

一、中缀表达式和后缀表达式的区别

中缀表达式:操作符位于两个运算数中间(如 3 + 2)。计算时要综合考虑操作符的优先级和括号。

如 5*(2+1) ,虽然 * 的优先级高于 + ,但括号的存在表示应优先执行括号内的 + 运算。

后缀表达式:操作符位于两个运算数后面(如 3 2 +)。计算时无需考虑操作符的优先级,也无需考虑括号,只需根据操作符在表达式中从左到右出现的顺序依次进行运算。

如 2 3 + 5 4 - *,虽然 * 的优先级高于 +- ,但按照后缀表达式的计算规则,从左往右看 + 最先出现,所以 + 先进行运算,然后是 - ,最后是 *

总结:后缀表达式严格按照从左到右、每个操作符出现的先后进行运算,所以后缀表达式最重要的就是确定操作符在表达式中的顺序。

        

二、中缀转后缀 - 转换规则分析

1、从左至右依次遍历中缀表达式,并准备一个栈存储操作符

Q:这个栈到底用来干什么?

A:此栈用于存放暂时无法确定运算顺序的操作符——入栈的操作符说明还不能确定运算顺序,出栈的操作符就说明已经确定运算顺序,可直接输出到后缀表达式。

        因为中缀表达式的计算要综合考虑操作符的优先级以及括号,故从左至右遍历中缀表达式,先出现的操作符不一定能够先执行运算,而是要根据后面出现的操作符进行判断。

【举个栗子】

        现有一个中缀表达式 2 + 3 * 5 ,从左向右遍历,虽然 + 最先出现,但明显 + 不能先运算,因为后面出现的 * 优先级高于 + ,所以 * 应该先运算,然后才是 +

2、运算数 :直接输出

Q:为什么运算数可以直接输出,中缀和后缀表达式中运算数的相对顺序相同吗?

A:中缀和后缀表达式中所有运算数的相对顺序是相同的。

        因为中缀表达式和后缀表达式的本质区别在于操作符究竟位于两个运算数中间还是后面,而两个运算数的相对位置并没有变化,故可以直接输出到后缀表达式。

【举个栗子】

        现有一个中缀表达式  2*(3+5)+7/1-4 ,转换为后缀表达式 2 3 5 + * 7 1 / + 4,其中六个运算数 2 3 5 7 1 4 在两个表达式中的相对顺序的是相同的。

3、操作符 :入栈(入栈前需判断是否要先出栈)

① 栈空 or 栈顶操作符 < 该操作符(优先级):直接入栈

② 栈顶操作符 ≥ 该操作符(优先级):持续出栈,并将出栈操作符依次输出到后缀表达式,直到栈空 or 栈顶操作符 < 该操作符,该操作符再入栈。

Q:为什么操作符必须入栈?且入栈前还要根据优先级进行出栈操作?

A:入栈是因为暂时无法确定运算顺序,入栈前出栈则是确定了前面的操作符的运算顺序。

        已知这个栈是用于存放暂时还无法确定运算顺序的操作符,且中缀表达式的运算顺序并不是按照操作符从左到右出现的顺序执行的。一个操作符能否直接运算,需要和后面的操作符进行优先级比较才能确定,所以每扫描到一个操作符,都要入栈一次,同时入栈的操作符也为前面还未能确定运算顺序的操作符提供了参考。

【举个栗子】

现有一个中缀表达式 2 * 3 + 5 ……,将式子遮住,从左边开始一点点露出,模拟从左往右遍历的过程。

① 从左向右遍历,第一个扫描到的操作符为 *

但现在还无法确定 * 的运算顺序,因为表达式后面的情况未知 2 *   ???,比如:

如果接下来是 2 * 3 + 5 ,那么 * 就应该先于 + 运算;

如果接下来是 2 * (3 + 5) ,那么 就应该先于 * 运算;

所以 * 的运算顺序还无法确定,只能先入栈。

② 继续往后遍历,第二个扫描到的操作符为 +

同样的,现在也无法确定 + 的运算顺序,因为后面的情况未知 2 * 3 +   ???,比如:

如果接下来是 2 * 3 + 5 - 2 ,那么 + 就应该先于 运算(左优先原则);

如果接下来是 2 * 3 + 5 / 2 ,那么 就应该先于 + 运算;

所以 + 的运算顺序也无法确定,也要入栈。但是,+ 却为前面还未确定顺序的 * 提供了参考:优先级 * > ,说明前面的 * 先于 + 运算,故 * 的运算顺序确定,即:* 出栈并输出到后缀表达式,+ 再入栈。

总结:从左往右遍历中缀表达式,扫描到的每个操作符在其后的操作符还未知时,都无法确定运算顺序,故需要入栈。同时,每个入栈的操作符也为前面的操作符的运算顺序提供了参考。

4、左括号 :直接入栈(入栈后优先级降至最低)

5、右括号 :直接出栈,并依次输出到后缀表达式,直到左括号出栈

6、重复以上步骤直至中缀表达式遍历完成,且操作符全部出栈

这三条规则的原理和前面也大同小异,出栈和入栈的目的都是为了确定操作符的运算顺序。

中缀表达式化为后缀表达式的步骤如下: 1. 初始化一个空栈和一个空后缀表达式。 2. 从左到右扫描中缀表达式的每个元素。 3. 如果当前元素是操作数,则将其添加到后缀表达式的末尾。 4. 如果当前元素是左括号,则将其压入栈中。 5. 如果当前元素是右括号,则将栈中的元素弹出并添加到后缀表达式中,直到遇到左括号。左括号不会被添加到后缀表达式中,也不会被弹出。 6. 如果当前元素是运算符,则比较其与栈顶运算符的优先级。如果当前运算符的优先级小于或等于栈顶运算符的优先级,则将栈顶运算符弹出并添加到后缀表达式中,直到当前运算符的优先级大于栈顶运算符的优先级或栈为空,然后将当前运算符压入栈中。 7. 重复步骤2至6,直到扫描完整个中缀表达式。 8. 如果栈中还有元素,则将它们依次弹出并添加到后缀表达式中。 例如,将中缀表达式 - + 6 * 3 - 7 4 / 8 2 化为后缀表达式过程如下: - 首先,初始化一个空栈和一个空后缀表达式。 - 从左到右扫描中缀表达式的每个元素: - 第一个元素是减号,是运算符,将其压入栈中。 - 第二个元素是加号,是运算符,由于栈为空,将其压入栈中。 - 第三个元素是数字6,是操作数,将其添加到后缀表达式的末尾。 - 第四个元素是乘号,是运算符,将其压入栈中。 - 第五个元素是数字3,是操作数,将其添加到后缀表达式的末尾。 - 第六个元素是减号,是运算符,将其压入栈中。 - 第七个元素是数字7,是操作数,将其添加到后缀表达式的末尾。 - 第八个元素是数字4,是操作数,将其添加到后缀表达式的末尾。 - 第九个元素是除号,是运算符,将其压入栈中。 - 第十个元素是数字8,是操作数,将其添加到后缀表达式的末尾。 - 第十一个元素是数字2,是操作数,将其添加到后缀表达式的末尾。 - 扫描完整个中缀表达式后,栈中还有运算符,将其依次弹出并添加到后缀表达式中,得到后缀表达式 6 3 * 7 4 - + 8 2 / -。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值