32.栈的应用补充-表达式求值

目录

一. 前缀表达式与后缀表达式

二. 中缀表达式转后缀表达式的手算方法

三. 后缀表达式的手算方法

四. 后缀表达式的机算方法

五. 中缀表达式转前缀表达式的手算方法

六. 前缀表达式的机算方法

七. 中缀表达式转后缀表达式的机算方法

八. 中缀表达式的机算


一. 前缀表达式与后缀表达式

让我们从大家熟悉的表达式说起:((15÷(7 -(1 +1))) × 3)-(2 +(1 +1))。表达式由三个部分组成:操作数、运算符、界限符。其中界限符不可省略,它反映了运算的先后顺序。

某波兰数学家就想:我们能否不用界限符也可以无歧义的表达运算顺序?答案显然是可以的,这就是“波兰表达式”(前缀表达式)和“逆波兰表达式”(后缀表达式)。

前缀表达式:二目运算符+操作数1+操作数2;后缀表达式:操作数1+操作数2+二目运算符

例如:

中缀表达式前缀表达式后缀表达式
a+b+abab+
a+b-c-+abcab+c-
a+b-c*d-+ab*cdab+cd*-

需要说明的是,由于确定中缀表达式的各个运算符的运算顺序不同(例如第3个既可以先算a+b也可以先算c*d),同一中缀表达式转化的前缀表达式和后缀表达式也可能不同。

二. 中缀表达式转后缀表达式的手算方法

  1. 确定中缀表达式中各个运算符的运算顺序;
  2. 选择下一个运算符,按照[左操作数  右操作数  运算符]的方式组合成一个新的操作数;
  3. 如果还有运算符没被处理,就继续2;

例如:前面的中缀表达式各运算符顺序确定如下:

在后缀表达式中,1 1 +先作为一个操作数,然后用7减去,就是7 1 1 + -,再用15除,就是15 7 1 1 + - /,以此类推。

再如,对同一中缀表达式确定不同的运算符顺序:

客观来看两种都正确。但是如果用计算机实现转换,算法需要有确定性,显然前面一种转化运算符的顺序就是运算顺序。更优。

那么如何保证对中缀表达式计算得到后缀表达式的时候,运算符的顺序就是运算顺序呢?显然我们要在确定中缀表达式的运算符运算顺序时不要FreeStyle。这里应当采用所谓“左优先”原则,即:要左边的运算符能先计算,就优先算左边的。

例如:对下面的中缀表达式,确定运算符顺序如下:

三. 后缀表达式的手算方法

方法:从左往右扫描,每遇到一个运算符,就让运算符前面最近的两个操作数执行对应运算,合体为一个操作数。应当注意:两个操作数的左右顺序问题。
 

后缀表达式的特点是:最后出现的操作数被最先计算。

四. 后缀表达式的机算方法

前面说到后缀表达式的特点是:最后出现的操作数被最先计算。这就很完美符合栈的后进先出的特点。接下来讲如何利用栈实现机算。这里栈是操作数栈

  1. 从左往右扫描下一个元素,直到处理完所有元素;
  2. 若扫描到操作数则压入栈,并回到1,否则执行3;
  3. 若扫描到运算符,则弹出两个栈顶元素,执行相应运算,运算结果压回栈顶,回到1;
     

以(三)中的第2张图为例:A入栈-B入栈-扫描到+,弹出A和B,执行完A+B后把(A+B)压入栈内-C入栈-D入栈-扫描到*,弹出C和D,执行C*D中把(C*D)压入栈内-E入栈-扫描到/,弹出E和(C*D)【注意先出栈的是右操作数】,执行(C*D)/E后把结果压入栈内-扫描到—,把(C*D)/E和(A+B)弹出,计算(A+B)-(C*D)/E后压入栈内-F入栈-扫描到+,弹出F和(A+B)-(C*D)/E,执行完(A+B)-(C*D)/E+F后压入栈内-扫描结束,这个时候栈内只剩一个元素,它就是最终结果(A+B)-(C*D)/E+F。

五. 中缀表达式转前缀表达式的手算方法

  1. 确定中缀表达式中各个运算符的运算顺序;
  2. 选择下一个运算符,按照「运算符  左操作数  右操作数」的方式组合成一个新的操作数;
  3. 如果还有运算符没被处理,就继续2;

相似的,为了得到唯一表达式,采用“右优先”原则:只要右边的运算符能先计算,就优先算右边的。例如下面的表达式,选择右面的计算顺序可以得到倒序的运算顺序。

六. 前缀表达式的机算方法

此部分和(四)类似,只不过是从右往左扫描:

  1. 从右往左扫描下一个元素,直到处理完所有元素;
  2. 若扫描到操作数则压入栈,并回到1;否则执行3;
  3. 若扫描到运算符,则弹出两个栈顶元素,执行相应运算【注意:先出栈的是左操作数】,运算结果压回栈顶,回到1;

七. 中缀表达式转后缀表达式的机算方法

初始化一个栈,用于保存暂时还不能确定运算顺序的运算符。从左到右处理各个元素,直到末尾。可能遇到三种情况:

  • 遇到操作数。直接加入后缀表达式。
  • 遇到界限符。遇到“(”直接入栈;遇到“)”则依次弹出栈内运算符并加入后缀表达式,直到弹出“(”为止。注意:“(”和“)”不加入后缀表达式。
  • 遇到运算符。依次弹出栈中优先级高于或等于当前运算符的所有运算符,并加入后缀表达式,若碰到“(”或栈空则停止。之后再把当前运算符入栈。

按上述方法处理完所有字符后,将栈中剩余运算符依次弹出,并加入后缀表达式。

仍然以(三)中的第2张图为例:初始化一个用来存放运算符的栈。

从前到后扫描中缀表达式:A直接加入后缀表达式—+号入栈—B直接加入后缀表达式—扫描到-,+从栈弹出加入后缀表达式,然后-入栈—C直接加入后缀表达式—*入栈—D直接加入后缀表达式—扫描到/,*从栈弹出加入后缀表达式,然后/入栈—E直接加入后缀表达式—扫描到+,/和-依次从栈弹出并加入后缀表达式,然后+入栈—F直接加入后缀表达式—扫描结束,此时栈内只有一个+,把+输出加入到后缀表达式中。

我们再看一个带括号的中缀表达式:A直接加入后缀表达式—+号入栈—B直接加入后缀表达式—*入栈—(直接入栈—C直接加入后缀表达式—扫描到-,此时运算符栈顶元素是(,所以直接把-入栈—D直接加入后缀表达式—扫描到),此时栈底到栈顶有+,*,(,-,依次从栈顶全部弹出【(不进入后缀表达式】—“-”号入栈—E直接加入后缀表达式—扫描到/,入栈—F直接加入后缀表达式—扫描结束,此时栈内有-,/,依次输出/和-,结束。

八. 中缀表达式的机算

其实就是(七)和(四)的结合。

  1. 初始化两个栈,操作数栈和运算符栈;
  2. 若扫描到操作数,压入操作数栈;
  3. 若扫描到运算符或界限符,则按照“中缀转后缀”相同的逻辑压入运算符栈(期间也会弹出运算符,每当弹出一个运算符时,就需要再弹出两个操作数栈的栈顶元素并执行相应运算,运算结果再压回操作数栈);
     

仍然以(三)中的第2张图为例:A入操作数栈—+入运算符栈—B入操作数栈—扫描到-,弹出运算符等级相同的+,并从操作数栈弹出A和B,计算A+B后压回操作数栈,最后-号入运算符栈—C入操作数栈—*入运算符栈—D入操作数栈—扫描到/,弹出运算符*,同时弹出C和D,计算C*D后压回操作数栈,最后把/压入运算符栈—E入操作数栈—扫描到+,/和-依次弹出。弹出/时计算C*D/E,压回操作数栈中,然后弹出-时计算A+B-C*D/E,压回操作数栈中,最后把+压入运算符栈—F入操作数栈—扫描结束,把运算符栈清空,每弹出一个运算符弹出两个操作数,这样就是A+B-C*D/E+F。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北京地铁1号线

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值