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

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

目录

一切的起点:我们习惯的数学写法(中缀表达式)

中缀表达式的问题:计算机的“困惑”

后缀表达式 (Postfix Notation)

前缀表达式 (Prefix Notation)

三种表达式的对比与用途

手动转换(Manual Conversion)

1. 遇到操作数(operand)(a, b, c, d, e)

2. 遇到操作符(operator)(+, *, -, /)

3. 遇到括号

4. 遇到字符串末尾

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


一切的起点:我们习惯的数学写法(中缀表达式)

 当我们要计算“3加4”,我们会怎么写?

我们自然而然地会写成 3 + 4

 在这个表达式里,操作符 + 被放在了两个操作数 34中间。这种将操作符放在中间的表示方法,就叫做中缀表达式 (Infix Notation)

这是人类最习惯、最直观的读写方式。我们从小学的数学课本到大学的教科书,几乎所有的数学公式都是中缀表达式,例如: a + b - c , (a + b) * c

中缀表达式的问题:计算机的“困惑”

 我们来看一个稍微复杂一点的中缀表达式:3 + 4 * 5

作为人类,我们受过训练,知道要“先算乘除,后算加减”。我们会先计算 4 * 5 = 20,然后再计算 3 + 20 = 23

但如果一个计算机程序傻傻地从左到右读取这个表达式,它会怎么做?

  1. 读到 3

  2. 读到 +

  3. 读到 4,它可能会立刻计算 3 + 4 = 7

  4. 读到 *

  5. 读到 5,它接着计算 7 * 5 = 35

结果是35,这显然是错误的。

 中缀表达式对人类友好,但对计算机来说,存在两个巨大的问题:

  1. 运算顺序不确定:单纯从左到右的顺序,不一定是真正的运算顺序。

  2. 依赖额外规则:为了解决顺序问题,我们引入了两个额外的、对计算机来说很麻烦的规则:

    • 优先级 (Precedence):规定了 */ 的优先级高于 +-

    • 括号 (Parentheses):我们可以用 ( ) 强制改变运算顺序,例如 (3 + 4) * 5

这意味着,计算机在处理中缀表达式时,不能简单地进行线性读写,它需要反复扫描、需要内置一张优先级表、还需要处理复杂的括号嵌套逻辑。这套流程非常低效和复杂。

核心问题引出: 有没有一种表示方法,它本身就包含了运算顺序,完全不需要依赖优先级或括号,能让计算机从头到尾扫一遍就直接算出结果?

问题的解决方案:后缀和前缀表达式

为了解决计算机的“困惑”,数学家们(特别是波兰逻辑学家扬·武卡谢维奇)发明了两种新的表示法。


后缀表达式 (Postfix Notation)

核心思想: 将操作符放到两个操作数的后面

  • 3 + 4 变成 3 4 +

  • a * b 变成 a b *

我们再来看 3 + 4 * 5 如何用后缀表示:

  1. 根据优先级,我们必须先算 4 * 5,它变成 4 5 *

  2. 现在,原表达式变成了计算 3 加上 (4 5 *) 这个结果。

  3. 将加号 + 放到后面,得到最终结果:3 4 5 * +

后缀表达式的魔力(计算机如何处理它): 计算机拿到 3 4 5 * + 后,会这样做:

  1. 准备一个空的地方(其实就是栈)用来放数字。

  2. 从左到右扫描表达式:

    • 遇到 3:是数字,放进栈。栈:[3]

    • 遇到 4:是数字,放进栈。栈:[3, 4]

    • 遇到 5:是数字,放进栈。栈:[3, 4, 5]

    • 遇到 *:是操作符!从栈里弹出两个数字(5和4),计算 4 * 5 = 20,然后把结果 20 再放回栈。栈:[3, 20]

    • 遇到 +:是操作符!从栈里弹出两个数字(20和3),计算 3 + 20 = 23,然后把结果 23 再放回栈。栈:[23]

  3. 表达式扫描完毕。栈里剩下的唯一一个数字,就是最终结果。

 后缀表达式的结构完全消除了括号和优先级规则。它的运算顺序是唯一的、内嵌的。计算机只需要一个简单的从左到右的扫描和一个栈,就能高效地完成计算。因此,它也被称为逆波兰表示法 (Reverse Polish Notation, RPN)


前缀表达式 (Prefix Notation)

核心思想: 将操作符放到两个操作数的前面

  • 3 + 4 变成 + 3 4

  • a * b 变成 * a b

  • 3 + 4 * 5 变成 + 3 * 4 5

它的计算过程与后缀类似,但通常是从右向左扫描。它同样消除了括号和优先级。它也被称为波兰表示法 (Polish Notation, PN)


三种表达式的对比与用途

特性中缀表达式 (Infix)后缀表达式 (Postfix/RPN)前缀表达式 (Prefix/PN)
位置操作符在操作数中间操作符在操作数后面操作符在操作数前面
人类可读性
计算机处理复杂,需处理优先级和括号简单高效,通常用栈实现简单高效,通常用栈实现
明确性有歧义(需额外规则)无歧义无歧义
主要用途人类日常书写和阅读编译器和解释器(如JVM)、计算器、栈式编程语言(如Forth)LISP等函数式编程语言

手动转换(Manual Conversion)

我们把用户输入的中缀表达式,转换成计算机擅长处理的后缀(或前缀)表达式,这个过程叫做编译或解析。它是一个“翻译”工作,目的是消除歧义,提升计算机的处理效率。

现在我们来推导手动转换的规则。我们需要一个最终的后缀表达式结果,和一个临时的“操作符中转站”(这个中转站其实就是个栈)。

我们以 a + b * c - (d + e) 为例。

1. 遇到操作数(operand)(a, b, c, d, e

  •  在后缀表达式中,操作数的相对顺序和中缀表达式中完全一样。

  • 规则: 遇到操作数,直接输出到结果中。


2. 遇到操作符(operator)(+, *, -, /

  •  这是最复杂的部分,我们需要处理优先级。我们用“中转站”(栈)来暂存操作符。

  • 情景A:中转站是空的,或栈顶是 (

    • 没有比它更早的操作符,或它是一个新子表达式的开始。直接把它放进中转站。

  • 情景B:中转站里已经有操作符了

    •  假设中转站栈顶是 op1,新来的操作符是 op2

    • 如果 op2 的优先级 > op1 的优先级 (例如,* > +)

      •  op2 对应的运算应该先于 op1 的运算。就像 a+b*c* 应该先算。所以,我们让 op2 “插队”,把它也压入中转站,等待它的第二个操作数。

      • 规则: 新操作符优先级高于栈顶操作符,则入栈。

    • 如果 op2 的优先级 <= op1 的优先级 (例如,+ <= *,或 + <= +)

      •  op1 对应的运算应该先算或至少不比 op2 晚。就像 a*b+c* 必须先算。所以,我们不能把 op2 直接压进去。我们必须把中转站里优先级更高或相等的 op1 先“请”出来,输出到结果中,表示 op1 的运算先完成。

      • 规则: 新操作符优先级低于或等于栈顶操作符,则将栈顶操作符弹出并输出,然后重复此过程,直到新操作符可以入栈或栈为空。


3. 遇到括号

  • 遇到左括号 (

    • 左括号标志着一个高优先级的子区域的开始。它会无视任何已有的操作符优先级。

    • 规则: 遇到 (,直接压入中转站。它将成为这个子区域的一个“边界墙”。

  • 遇到右括号 )

    • 右括号意味着一个子区域的结束。中转站里,从最近的那个 ( 开始的所有操作符,都必须在这个子区域内完成计算。

    • 规则: 遇到 ),将中转站里的操作符依次弹出并输出,直到遇到 ( 为止。最后,将这个 ( 也从栈中弹出并丢弃(因为它只起标记作用,不是运算的一部分)。


4. 遇到字符串末尾

  •  整个表达式都看完了,但中转站里可能还剩下一些操作符。

  • 规则: 将中转站(栈)里剩余的所有操作符,依次弹出并输出。


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

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

最终结果:a b c * + d e + -

未完待续…………

http://www.dtcms.com/a/328271.html

相关文章:

  • 【速通版!语义通信基础与前沿学习计划】
  • C++中类之间的关系详解
  • AR巡检:三大核心技术保障数据准确性
  • Langchain入门:构建一个PDF摄取和问答系统
  • 51 单片机分层架构的模块依赖关系图
  • 解决ROS编译顺序不对,需要内部依赖,因此要多次编译的问题
  • Python初学者笔记第二十二期 -- (JSON数据解析)
  • MySQL 数据库表操作与查询实战案例
  • 双十一美妆数据分析:洞察消费趋势与行业秘密
  • 机械臂的智能升维:当传统机械臂遇见Deepoc具身智能大模型从自动化工具到具身智能体的范式革命
  • Element用法---Loading 加载
  • C++的异常的使用和规范
  • 【盘古100Pro+开发板实验例程】FPGA学习 | 均值滤波 | 图像实验指导手册
  • 【代码随想录day 18】 力扣 501.二叉搜索树中的众数
  • 免费播客翻译与转录:用中文收听全球播客
  • Langchain入门:文本摘要
  • C++学习之数据结构:AVL树
  • java八股文-MySql面试题-参考回答
  • GPFS api
  • 在 C语言 中构建安全泛型容器:使用 maybe 实现安全除法
  • 【PCB设计经验】去耦电容如何布局?
  • 力扣top100(day01-04)
  • 企业级的即时通讯平台怎么保护敏感行业通讯安全?
  • 电竞 体育数据 API 应用场景全解析
  • Day50--图论--98. 所有可达路径(卡码网),797. 所有可能的路径
  • Quartz
  • Mybatis源码解读-SqlSession 会话源码和Executor SQL操作执行器源码
  • 谷歌云代理商:用 AI 启航,Gemini 重塑旅游酒店行业新体验
  • 【SpringBoot】07 容器功能 - SpringBoot底层注解的应用与实战 - @ConfigurationProperties配置绑定
  • 从0入门LangGraph,手搓高质量Agent