当前位置: 首页 > news >正文

数据结构:后缀表达式:结合性 (Associativity) 与一元运算符 (Unary Operators)

目录

结合性 (Associativity):解决同级操作符的“站位”问题

问题的引出

结论:对原有规则的精炼

一元运算符 (Unary Operators):解决符号的“身份”问题

问题的引出

如何区分一元和二元操作符?

如何将一元运算符整合进我们的转换算法?

结论:对原有算法的扩展

手动转换示例:a * (-b + c)


我们接着上一次的内容,深入探讨在处理表达式时必须面对的两个更精细的问题:结合性 (Associativity) 和 一元运算符 (Unary Operators)。

数据结构:中缀到后缀的转换(Infix to Postfix Conversion)-CSDN博客

结合性 (Associativity):解决同级操作符的“站位”问题

问题的引出

我们上次得出的规则是:

当新操作符的优先级低于或等于栈顶操作符时,就将栈顶操作符弹出。

这个规则在处理 a + b * c 时工作得很好。

但我们来思考一个新情景:8 - 5 + 3。 这里的 -+ 优先级完全相同。按照我们已有的规则来手动转换:

  1. 读到 8:输出 8

  2. 读到 -:操作符栈为空,压入 -。栈:[-]

  3. 读到 5:输出 8 5

  4. 读到 +:新操作符 + 的优先级与栈顶的 - 相等。根据“低于或等于就弹出”的规则,我们将 - 弹出。

    • 输出变为 8 5 -

    • 操作符栈变为空,然后压入 +。栈:[+]

  5. 读到 3:输出 8 5 - 3

  6. 字符串结束:弹出栈中剩余的 +。最终结果:8 5 - 3 +

我们来验算一下这个后缀表达式:8 5 - 得到 3,然后再计算 3 3 + 得到 6。 这个结果,其实等价于我们先计算了 (8 - 5) 再加上 3

 这个 (8 - 5) + 3 的计算顺序不是凭空来的。这是我们在数学中一个根深蒂固但可能没注意到的隐性规则:

对于 +-*/ 这些操作符,当它们优先级相同时,我们总是从左到右依次计算。

这个“从左到右”的组合规则,就叫做左结合性 (Left-to-Right Associativity)

我们的转换算法中“等于就弹出”这一条,恰好就完美地、自动地实现了左结合性。

因为当遇到同级操作符时,它总是让先出现(在左边)的操作符先被弹出并输出,从而保证了它在后缀表达式中也更靠前,也就被先计算。


现在,引出新的问题:是不是所有操作符都是左结合的?

思考一下幂运算 ^。表达式 2 ^ 3 ^ 2 应该如何计算?

  • 方案A(左结合)(2 ^ 3) ^ 2 = 8 ^ 2 = 64

  • 方案B(右结合)2 ^ (3 ^ 2) = 2 ^ 9 = 512

在绝大多数编程语言和数学约定中,幂运算是右结合的,也就是方案B。赋值运算符 = 也是一个典型的右结合操作符(例如 a = b = 5 的意思是 a = (b = 5))。

 如果我们对 ^ 仍然使用“等于就弹出”的规则,当第二个 ^ 遇到栈顶的第一个 ^ 时,它会把第一个 ^ 弹出去,这就导致了左结合的计算顺序,这对于幂运算是错误的。

为了正确处理右结合操作符,我们必须修改规则:

  • 对于右结合操作符,当新来的操作符与栈顶的操作符优先级相等时,我们不应该弹出栈顶的那个,而是应该让新来的也压入栈中,因为它应该“等待”更右边的运算先完成。

  • 换句话说,只有当新来的操作符优先级严格地低于栈顶的右结合操作符时,才把它弹出来。


结论:对原有规则的精炼

我们的操作符优先级比较规则,必须同时考虑结合性

  1. 当新操作符 op2 和栈顶操作符 op1 比较时:

    • 如果 op1 的优先级 > op2 的优先级,则弹出 op1

    • 如果 op1 的优先级 < op2 的优先级,则压入 op2

    • 如果 op1 的优先级 == op2 的优先级:

      • 并且 op1 是左结合的,则弹出 op1

      • 并且 op1 是右结合的,则压入 op2

结合性 (Associativity) 的本质,是为同优先级操作符提供了一个解决歧义的“方向”规则。


一元运算符 (Unary Operators):解决符号的“身份”问题

问题的引出

 到目前为止,我们处理的所有操作符 +, -, *, / 都是二元运算符 (Binary Operators),因为它们都需要两个操作数。

现在看这个表达式:3 * -5。 这里的 - 是什么意思?

它不是“减法”,因为它的右边是 5,但左边是 *,而不是一个数字。这个 - 的作用是“取负”,它只作用于一个操作数 5

这种只作用于一个操作数的操作符,就叫做一元运算符 (Unary Operator)

 这立刻带来了一个巨大的问题:符号 - 现在有了双重身份。它可以是“二元减”,也可以是“一元负”。我们的算法在读到一个 - 时,必须先搞清楚它的身份,才能决定如何处理。


如何区分一元和二元操作符?

答案是看它的上下文,也就是它出现的位置。

  • 一个操作符是二元的,如果它的前面是一个操作数(数字)或者一个右括号 )

    • 例如:10 - 5 (- 前面是 10),(a+b) - c (- 前面是 ))

  • 一个操作符是一元的,如果它的前面什么都没有(它是表达式的第一个字符),或者它的前面是另一个操作符,或者是一个左括号 (

    • 例如:-5 (前面什么都没有),3 * -5 (- 前面是 *),10 + (-5) (- 前面是 ()


如何将一元运算符整合进我们的转换算法?

  1. 身份识别:在我们的扫描程序中,当遇到一个有歧义的符号(如 -+)时,我们必须先通过上述的上下文规则来判断它的“身份”。

  2. 赋予独立的属性:为了在算法中清晰地处理,我们可以把“一元负”看作一个全新的、独立的操作符。为了方便,我们可以在内部给它起个别名,比如用 ~ 来代表一元负。

    • 这样 3 * -5 在内部就被预处理成了 3 * ~5

  3. 确定一元操作符的优先级和结合性

    • 优先级:在 3 * -5 中,我们必须先计算 -5(也就是 ~5),然后再进行乘法。这意味着一元运算符的优先级非常高,必须高于 */

    • 结合性:在 - - 5 中,它的意思是 - (-5),结果是 5。计算顺序是从右到左。所以,一元运算符是右结合的。


结论:对原有算法的扩展

为了处理一元运算符,我们的转换过程需要增加一个预处理步骤,或者在主循环中增加判断逻辑:

  1. 在扫描时:当遇到 +-,通过检查它前面的字符,来确定它是二元还是。

  2. 在操作符栈中:如果确定是“一元负”,就把它当作一个具有高优先级和右结合性的特殊操作符(如 ~)来处理。


手动转换示例:a * (-b + c)

  1. 预处理/识别a * ( ~b + c ),我们将括号内的 - 识别为一元负 ~

  2. 开始转换

当前字符操作符中转站(栈)后缀表达式结果解释
a[]a操作数,输出
*[*]a栈空,* 入栈
([*, (]a( 直接入栈
~[*, (, ~]a栈顶是(~直接入栈 (~优先级高)
b[*, (, ~]a b操作数,输出
+[*, (, +]a b ~+优先级低于~,弹出~;然后+入栈
c[*, (, +]a b ~ c操作数,输出
)[*]a b ~ c +遇到),弹出+,丢弃(
末尾[]a b ~ c + *弹出栈中剩余的*

最终结果:a b ~ c + *。这个结果正确地表达了先计算 b 的负值,然后与 c 相加,最后再与 a 相乘的顺序。

http://www.lryc.cn/news/618606.html

相关文章:

  • 现代化水库运行管理矩阵建设的要点
  • AI Agent——基于 LangGraph 的多智能体任务路由与执行系统实战
  • 【实时Linux实战系列】实时能耗监测与优化技术
  • 《吃透 C++ 类和对象(上):封装、实例化与 this 指针详解》
  • Python训练营打卡Day30-文件的规范拆分和写法
  • 543.二叉树的直径
  • 【前端:Html】--2.进阶:表单
  • 数字孪生重构园区管理效率:技术落地与产业升级的三重跃迁
  • JVM学习笔记-----图解方法执行流程
  • Nginx 启用 HTTPS:阿里云免费 SSL 证书详细图文教程(新手0.5小时可完成)
  • openssl中,公钥和私钥的区别和作用?
  • API 接口接入开发全演示:淘宝商品数据实时抓取
  • 代码随想录刷题Day29
  • 基于51单片机220V交流电流检测系统过流阈值报警设计
  • 通信接口与通信约规
  • 【牛客刷题】REAL806 放它一马:怪物经验值最大化策略详解
  • 【基于DesignStart的M3 SoC】
  • 终端安全检测和防御技术
  • UGUI源码剖析(6):遮罩的“魔法”与“算法”——从C#到Shader,彻底揭示Mask与RectMask2D的原理
  • OpenHarmony编译与烧录
  • HTTPS服务
  • MCU外设初始化:为什么参数配置必须优先于使能
  • Ceph的FileStore存储引擎详解
  • 如何提升需求分析能力
  • NLP—词向量转换评论学习项目分析
  • 【SpringBoot】05 容器功能 - SpringBoot底层注解的应用与实战 - @Configuration + @Bean
  • IIS Express中可以同时加载并使用.net4.0和.NET 2.0的 DLL
  • 面试八股之从jvm层面深入解析Java中的synchronized关键字
  • 使用pyqt5实现可勾选的测试用例界面
  • MM DEMO-2025 | 北航新融合LLM与多模态交互的无人机导航系统!AirStar,智能空中助手等你来体验