Files
Data-Structure/Algorithm/Graph/Tree/表达式树/readme.md
2025-09-16 23:10:48 +08:00

86 lines
3.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

好的 👍 我来总结一下 **生成表达式树** 的常见方法(只讲思路,不上代码):
---
## 1. **基于后缀表达式(逆波兰式)**
这是最常见也最简单的方法。
* **步骤**
1. 先把中缀表达式(如 `3 + 5 * 2`)转为后缀表达式(`3 5 2 * +`),通常用 **栈** 实现。
2. 遍历后缀表达式:
* 遇到操作数(数字/变量),创建一个叶子节点,压入栈。
* 遇到运算符,弹出两个节点(右操作数在栈顶),生成一个新运算符节点,把两个节点作为它的左右子树,再把这个新节点压入栈。
3. 最后栈中唯一的节点就是整棵表达式树的根。
* **优点**:逻辑清晰,栈结构天然适合。
* **应用**:编译器、计算器。
---
## 2. **基于递归下降 / 文法解析**
* 将表达式看作一棵语法树,利用递归来解析。
* **例子**
* 一个表达式 `E` 可以分为:`E = T (+/- T)*`
* 一个项 `T` 可以分为:`T = F (*// F)*`
* 一个因子 `F` 可以是:数字 / 变量 / 括号里的子表达式
* 递归解析时,每一层返回一个子树节点,最后拼成完整的树。
* **优点**结构清晰和正规语法BNF一一对应。
* **应用**:编译器前端、解释器。
---
## 3. **运算符优先级栈法Shunting-yard**
* 由 Dijkstra 提出的著名算法,本质是把中缀转后缀(再转树)。
* **步骤**
1. 准备两个栈:运算符栈、操作数栈。
2. 依次读入符号:
* 数字 → 压入操作数栈。
* 运算符 → 根据优先级处理,出栈已有运算符并生成树节点。
* 括号 → 特殊处理。
3. 最终把所有运算符出栈,并拼接成树。
* **优点**:不必先生成后缀表达式,直接就能建树。
* **应用**:表达式求值、解释器。
---
## 4. **自顶向下(分治法)**
* 如果你能快速找到当前表达式中 **优先级最低的主运算符**(比如在 `(3+5)*2` 中的 `*`),就可以:
1. 把它作为根节点。
2. 左右部分递归生成子树。
* **例子**
* 表达式 `3 + 5 * 2 - 4`
* 最外层的最低优先级是 `+``-`,最右的 `-` 成为根
* 左边 `3+5*2` 递归成一棵子树,右边 `4` 变叶子
* **优点**:直观,完全递归化。
* **缺点**:实现时需要小心括号和优先级。
---
## 5. **抽象语法树 (AST) 生成**
* 在编译原理里语法分析器Parser生成的就是 **AST**,本质上就是表达式树。
* 一般通过 **词法分析 → 语法分析**,然后根据语法规则建树。
* **特点**相比一般表达式树AST 会忽略括号、某些中间节点,只保留运算逻辑。
---
📌 **总结**
* 如果是实际写程序 → 推荐 **方法 1后缀表达式 + 栈)****方法 3Shunting-yard**
* 如果是研究语法 / 编译原理 → 用 **方法 2递归下降****方法 5AST 生成)**
* 如果只是想理解表达式结构 → **方法 4分治法** 最直观。
---
要不要我帮你画几个 **同一个表达式在不同方法下生成的树的对比图**