当前位置: 首页 > 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.dtcms.com/a/328545.html

相关文章:

  • Linux软件编程(三)文件操作-文件 I/O
  • 笔试——Day36
  • Linux应用软件编程---文件操作3(文件IO及其指令、文件定位函数lseek、文件IO与标准IO的比较、缓冲区)
  • archlinux中VLC无法播放视频的解决办法
  • 【Datawhale夏令营】多模态RAG学习
  • 关于Linux编程3:fread/fwrite/流的定位/文件IO
  • 存储过程作为系统逻辑核心的架构思考 —— 以 SaaS 系统为例
  • 电商双 11 美妆数据分析:从数据清洗到市场洞察
  • 生产环境中Kubernetes Pod 安全上下文与策略的实战经验分享
  • nt!MmCreatePeb函数分析之peb中OSMajorVersion的由来
  • Flutter ExpansionPanel组件(可收缩的列表)
  • 【入门系列】图像算法工程师如何入门计算机图形学?
  • 数据分析基本内容(第二十节课内容总结)
  • MCU外设初始化:为什么参数配置必须优先于使能
  • redis的过期策略和定时器
  • 支持任意 MCP 协议的客户端
  • SQL180 每类试卷得分前3名
  • Mybatis源码解读-Plugin插件源码
  • (C++)继承全解析及运用
  • Labelme从安装到标注:零基础完整指南
  • MySQL基础面试
  • Springboot整合Thrift
  • 移动端网页调试实战,键盘弹出与视口错位问题的定位与优化
  • 汉高携手SAP推出AI驱动的退换货与争议管理解决方案
  • 赛灵思ZYNQ官方文档UG585自学翻译笔记:UART Controller,通用异步收发传输器控制器
  • Vue接口平台十三——测试记录
  • Ubuntu 全盘备份
  • 九尾狐未来机械晶核技术
  • k3s部署
  • 电脑硬件详解