当前位置: 首页 > 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.xdnf.cn/news/1290061.html

相关文章:

  • ZKmall开源商城的容灾之道:多地域部署与故障切换如何守护电商系统
  • 21.Linux HTTPS服务
  • 【GESP】C++一级知识点之【集成开发环境】
  • 备战国赛算法讲解——马尔科夫链,2025国赛数学建模B题详细思路模型更新
  • UE5.3 C++ 动态多播实战总结
  • SQL 生成日期与产品的所有组合:CROSS JOIN(笛卡尔积)
  • JVM宝典
  • 每日五个pyecharts可视化图表-line:从入门到精通 (4)
  • 什么时候用WS(WebSocket),什么使用用SSE(Server-Sent Events)?
  • Pytest项目_day13(usefixture方法、params、ids)
  • 机器学习处理文本数据
  • linux 开机进入initramfs无法开机
  • 串口通信学习
  • 数据分析专栏记录之 -基础数学与统计知识
  • Spring-Cache 缓存数据
  • windows git安装步骤
  • XGBoost 的适用场景以及与 CNN、LSTM 的区别
  • 网络协议——HTTP协议
  • Linux服务:Apache 虚拟主机配置指南:多站点部署三种方式详解
  • 【超详细!题解|两种做法】洛谷P3196 [HNOI2008] 神奇的国度[MCS算法]
  • 深入剖析 React 合成事件:透过 onClick 看本质
  • 过程设计工具深度解析-软件工程之详细设计(补充篇)
  • Nginx 高级配置
  • 【后端】Spring @Resource和@Autowired的用法和区别
  • 通用同步/异步收发器USART串口
  • excel-随笔记
  • [ 数据结构 ] 时间和空间复杂度
  • Python初学者笔记第二十二期 -- (JSON数据解析)
  • VGG改进(2):基于Local Attention的模型优化
  • 【图像算法 - 13】基于 YOLO12 与 OpenCV 的实时目标点击跟踪系统(系统介绍 + 源码详细)