四则运算的 中缀表达式 转 后缀表达式 思路整理

中缀表达式与后缀表达式特点

中缀表达式定义及其相关内容
后缀表达式定义及其相关内容

首先,四则运算中只有4个运算符和两个括号,分别为 +-*/()。这4个运算符都是双目运算符,也就是说每个运算符至少有两个运算目。

其次,中缀表达式的特点是两个运算目分别在运算符的两边,而后缀表达式的运算目按出现次序先后放在运算符的前面。

也就是说,在输出后缀表达式的时候,如果两个运算目尚未输出,则不得输出操作这两个运算目的运算符。

假设中缀表达式为:a+b-c
则后缀表达式应为:ab+c-
从左到右输出后缀表达式时,如果b尚未输出,则不得输出+

最后是四则运算符的优先级问题。众所周知, */ 的优先级高于 +-,当优先级相同时,按照中缀表达式由左向右逐个符号进行计算。

为什么用栈结构?

当我们操作后缀表达式的时候,如何按照正确的顺序输出运算符?

举例:

中缀表达式:
a+b*c-d

我们假设用一个数据结构存储运算符,用一个队列来存储输出顺序,然后按照从左往右的顺序逐个扫描中缀表达式。

  • 首先扫描到运算目 a。运算目 a 是整个表达式的第一个,所以可以直接放入输出队列。

输出队列:a
数据结构:

  • 然后我们扫描到运算符 + ,但是此时输出队列里没有 + 的第二个运算目,所以我们只能先把它放入数据结构里存放起来。

输出队列:a
数据结构:+

  • 扫描到运算目 b 。运算目不能影响运算符的顺序,因此直接将它放入输出队列。

输出队列:ab
数据结构:+

  • 此时我们还不能将 + 放入输出队列,因为我们还不能确定 b 是不是 + 的第二个运算目。(事实上它也确实不是,+ 的第二个运算目是 b*c 的值)
  • 扫描到运算符 * ,现在我们可以确定了,实际上 b 是 * 的第一个运算目。出于和 + 同样考虑,我们把乘号放入数据结构。

输出队列:ab
数据结构:+*

  • 扫描到运算目 c ,放入输出队列。
  • 虽然四则运算中,乘除已经是优先级的天花板了,但是拓展到更广阔的领域,我们依然不能确定,乘除是最高的优先级,所以和加号一样,暂时不予输出。

输出队列:abc
数据结构:+*

  • 扫描到运算目 - ,我们现在可以确定 c 是 * 的第二个运算目,所以我们将数据结构里的 * 放入输出队列。

输出队列:abc*
数据结构:+

  • 此时,剩余在数据结构里的 + 和 扫描中的 - 是同级关系,应该先计算 a+(b*c的值) 。所以 + 优先输出

输出队列:abc*+
数据结构:

  • 此时我们仍未扫描到 - 的第二个运算目,所以我们把 - 放入数据结构中。

输出队列:abc*+
数据结构:-

  • 扫描到运算目 d,放入到输出队列中

输出队列:abc*+d
数据结构:-

  • 中缀表达式结束,我们可以确定 d 为运算符 - 的第二个运算目,所以将数据结构中的 - 放入到输出队列中。此时后缀表达式全部输出序列完成。

输出队列:abc*+d-
数据结构:

观察可以发现:+ 先于 * 进入数据结构,但却是 * 先被数据结构弹出,由此可见,按照该算法,该数据结构具有后进先出的特点,故采用栈结构。

优先级表格设计

计算机没有我们人这么聪明,所以我们需要用一些更直观的手段来描述优先级,于是有了优先级表 P

运算符+ / -* / /
优先级01

根据上面的情况总结归纳:

  • 如果扫描到运算目,则直接放入输出队列
  • 如果扫描到运算符,则根据情况不同有三种情况:
  1. 栈为空栈,则直接压入
  2. 栈顶元素优先级高于或等于被扫描的运算符,则弹出栈顶至输出队列。被扫描运算符与新栈顶继续比较,直到被扫描运算符被压入栈内。(优先级高先运算规则和从左到右规则)
  3. 栈顶元素优先级低于被扫描的运算符,则将被扫描的运算符压入栈内。

当中缀表达式被扫描完以后,后缀表达式就算是完成了。

但上述方法还可以简化,单独运行一个函数去判断栈空过于费劲,我们可以放入一个优先级最低的作为栈底。

新优先级表:

运算符#+ / -* / /
优先级012

这样比较情况就只有两种,相对省事了一点。

带括号的四则运算

括号的引入改变了原有的计算顺序,所以需要进行一定修改。
括号内的算术内容要优先计算,所以从这个角度来讲,括号内的优先级高于括号外的优先级。

举例:

a+b*(c+d/e)-f

按照之前的算法逻辑,我们可以直接跳转到下面状态

当前扫描:(
输出队列:ab
栈:# + *

  • 由于括号优先级要高于 * /,所以括号得以入栈。
  • 扫描 c ,放入输出队列

当前扫描:c
输出队列:abc
栈:# + * (

  • 扫描 + ,此时栈顶为 ( 优先级高于 +,如果按照之前优先级设计,加号将无法入栈。所以我们要对优先级设计进行修改。 为此我们引入栈内优先级和栈外优先级。
  • 栈内优先级(in stack priority,简写为:isp):当运算符在栈内时所拥有的优先级。( 应持有最低的优先级才能让后续其他运算符入栈。#除外。
  • 栈外优先级(in coming priority,简写为:icp):当运算符在栈外时所拥有的优先级。( 应持有最高的优先级才能让自己得以入栈
运算符#(+ / -* / /
栈内优先级(isp)0123
栈外优先级(icp)0423
  • 因为 ( 的栈内优先级低于 + 的栈外优先级,所以 + 得以入栈
  • 扫描 d

当前扫描:d
输出队列:abcd
栈:# + * ( +

  • 扫描 /

当前扫描:/
输出队列:abcd
栈:# + * ( + /

  • 扫描 e

当前扫描:e
输出队列:abcde
栈:# + * ( + /

  • 扫描 ),此时按照后缀表达式的规则,应该将 / 弹出。所以 ) 的栈外优先级应该低于或等于 / 的栈内优先级。
  • 同理,) 的栈外优先级应该低于或等于 + 的栈内优先级。

当前扫描:)
输出队列:abcde/+
栈:# + * (

  • 此时栈顶为 ( ,后缀表达式中没有括号一说,所以 ( 被弹出,但不被放入输出队列。
  • 至此,本括号内的所有算式结束,如果放任 ) 继续弹出其他运算符,则有可能使后缀表达式发生错误(双层括号的情况,自己推演)。因此,必须将 ( )比较的结果与 ) 和其他运算符比较的结果区分开来。

最终的运算符优先级表

运算符#(+ / -* / /)
栈内优先级(isp)0135
栈外优先级(icp)06241

至此,只有括号匹配时,被扫描的栈外优先级栈顶栈内优先级相等。除此之外,当被扫描的栈外优先级高于栈顶栈内优先级时,栈外元素入栈,反之,栈顶元素弹出并继续比对。

最终后缀表达式:abcde/+*+ -

总结

算法详述:

  • 从左往右扫描中缀表达式
  • 如果扫描到数字,加入后缀表达式
  • 如果扫描到运算符
  1. icp(‘被扫描运算符’) > isp(‘栈顶运算符’),被扫描运算符入栈;继续扫描中缀表达式。
  2. icp(‘被扫描运算符’) < isp(‘栈顶运算符’),栈顶运算符弹出,加入后缀表达式;被扫描运算符继续比较新栈顶。
  3. icp(‘被扫描运算符’) == isp(‘栈顶运算符’),栈顶元素弹出,不加入后缀表达式;继续扫描中缀表达式。

运算符优先级表

运算符#(+ / -* / /)
栈内优先级(isp)0135
栈外优先级(icp)06241
  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值