NOTE本页面由ChatGPT生成。
第十六章:树
根据老师的《第16章 树》课件整理。
本章可以按照一条主线理解:普通无向树 → 一般连通图中的生成树 → 带方向层次结构的根树 → Huffman 编码与二叉树周游。
一、本章知识框架
| 模块 | 核心问题 | 需要掌握的内容 |
|---|---|---|
| §16.1 无向树及其性质 | 什么样的图叫树? | 树、森林、树叶、分支点;树的等价判定;树叶数量 |
| §16.2 生成树 | 如何从一般连通图中抽取一棵树? | 生成树、树枝、弦、余树;破圈法;基本回路;基本割集;最小生成树 |
| §16.3 根树及其应用 | 如何表示具有层次关系的结构? | 根树术语; 叉树;根子树;Huffman 树;前缀码;二叉树周游 |
§16.1 无向树及其性质
1. 无向树的基本定义
1.1 树
连通且不含回路的无向图称为无向树,通常简称为树,记作 。
判断一个图是否为树时,需要同时检查两个条件:
- 图是连通的;
- 图中不存在简单回路。
只有“无回路”还不够。例如,若一个无向图由两条互不相连的链组成,它没有回路,但它不是树,而是森林。
1.2 森林
每个连通分支都是树的非连通无向图,称为森林。
可以将森林理解为“若干棵互不相连的树的集合”。
1.3 平凡树
只含一个顶点的树称为平凡树。
1.4 树叶与分支点
在无向树中:
- 度数为 的顶点称为树叶;
- 度数大于等于 的顶点称为分支点。
注意:这里讨论的是无向树。后面学习根树时,“树叶”的定义会改为“出度为 的顶点”。
2. 树的六种等价刻画
设无向图 有 个顶点、 条边,则下列命题互相等价:
- 是树,即 连通且无回路;
- 中任意两个顶点之间存在唯一的路径;
- 无回路,并且 ;
- 连通,并且 ;
- 连通,并且每一条边都是桥;
- 无回路,但在任意两个不同顶点之间添加一条新边后,所得图中出现唯一一个包含新边的圈。
2.1 为什么“任意两点之间有唯一通路”等价于树?
- 因为图连通,所以任意两点之间至少存在一条路径;
- 如果两点之间存在两条不同路径,则两条路径合起来会形成一个回路;
- 因此,在树中两点之间的路径只能有一条。
反过来,如果任意两点之间都有唯一的路径,那么图显然连通;同时,图中不可能存在回路,否则回路上的两个顶点之间会有两条不同路径。
2.2 为什么树一定有 条边?
一棵树从一个顶点开始,每增加一个新顶点,为保持连通且不形成回路,必须恰好增加一条边。因此,当顶点数为 时,边数恰好为 。
2.3 为什么树中的每条边都是桥?
桥是删除后会使连通分支数增加的边。
树中任意两点之间的路径唯一。如果删除一条边,那么这条边两端之间原本唯一的路径被切断,图一定不再连通。因此,树中的每一条边都是桥。
2.4 两个常见误区
- 只有 ,不能直接推出图是树。还需要补充“连通”或“无回路”。
- 只有“连通”,也不能推出图是树,因为图中可能存在回路。
3. 非平凡树至少有两片树叶
定理
设 是一棵 阶非平凡无向树,则 中至少有两片树叶。
证明思路一:使用握手定理
设树叶数量为 。树中每个树叶的度数为 ,其余 个顶点的度数至少为 。
由握手定理和树的边数公式,有:
。
另一方面:
。
因此:
。
整理得到:
。
证明思路二:寻找最长路径
在树中任选一条最长路径。它的两个端点一定都是树叶。
如果某个端点的度数大于 ,还可以从该端点继续向外延伸路径,这与“路径已经最长”矛盾。
4. 树叶数量的常用计算公式
对于一棵非平凡树,设树叶数量为 ,则有:
。
这个公式说明:
- 度数为 的顶点不会改变树叶数量;
- 每出现一个 度顶点,会比基础情况多出 片树叶;
- 每出现一个 度顶点,会比基础情况多出 片树叶。
例题
某棵无向树中有:
- 个 度顶点;
- 个 度顶点;
- 个 度顶点。
则树叶数为:
。
§16.2 生成树
1. 生成树、树枝、弦与余树
设 是一个无向图。
1.1 生成树
如果 是 的生成子图,并且 本身是一棵树,则称 是 的一棵生成树。
“生成子图”表示:
- 保留 的全部顶点;
- 只保留 的一部分边。
因此,生成树的作用是:在保留所有顶点连通关系的前提下,尽可能删去多余边。
1.2 树枝
属于生成树 的边称为 的树枝。
若 有 个顶点,则任意生成树一定有 条树枝。
1.3 弦
属于原图 但不属于生成树 的边称为 的弦。
若 有 个顶点、 条边,则弦的数量为:
。
1.4 余树
由所有弦导出的子图称为生成树 的余树,通常记作 。
注意:余树不一定连通,也不一定无回路。它的名字中虽然含有“树”,但它不一定真的是树。
2. 图存在生成树的充要条件
定理
无向图 存在生成树,当且仅当 是连通图。
证明与构造方法:破圈法
若连通图 中没有回路,则 本身就是一棵生成树。
若 中存在回路,则从任意一个回路中删除一条边。删除回路中的边不会破坏连通性,因为回路中仍有另一条路径连接这条边的两个端点。
不断重复“找到回路并删除其中一条边”的过程,直到图中不再存在回路。最终得到的连通无回路生成子图就是一棵生成树。
这套方法称为破圈法。
3. 基本回路与基本回路系统
设 是连通图 的一棵生成树。
3.1 基本回路
向生成树 中加入一条弦 ,一定会产生唯一一个回路。这个回路称为弦 对应的基本回路。
原因是:
- 在树 中, 到 原本存在唯一一条路径;
- 再加入弦 后,这条弦与原路径合起来构成一个回路;
- 因为树中的路径唯一,所以产生的回路也唯一。
3.2 求基本回路的算法
对于每一条弦 :
- 在生成树 中找到 到 的唯一通路;
- 将这条通路与弦 合并;
- 得到弦 对应的基本回路。
3.3 基本回路系统
若图 有 个顶点、 条边,则生成树中有 条树枝,弦有 条。
每条弦对应一个基本回路,因此基本回路系统中共有:
个基本回路。
4. 基本割集与基本割集系统
4.1 基本割集
设 是生成树 中的一条树枝。
删除 后,树 会分裂成两个连通分支,记为 和 。
在原图 中,所有连接 与 的边构成的集合,称为树枝 对应的基本割集。
4.2 求基本割集的算法
对于每一条树枝 :
- 从生成树 中删除 ;
- 得到两个连通分支 和 ;
- 在原图 中找出所有一个端点属于 、另一个端点属于 的边;
- 这些边组成 对应的基本割集。
4.3 基本割集系统
一棵 阶生成树有 条树枝,每条树枝对应一个基本割集。因此,基本割集系统中共有:
个基本割集。
4.4 基本回路与基本割集的对照
| 概念 | 操作对象 | 操作方式 | 结果 |
|---|---|---|---|
| 基本回路 | 弦 | 向生成树中加入一条弦 | 得到唯一回路 |
| 基本割集 | 树枝 | 从生成树中删除一条树枝 | 得到两个分支,再找所有跨越两部分的边 |
可以用一句话记忆:
弦加进去找圈,树枝删掉找割集。
5. 最小生成树
5.1 定义
设 是无向连通带权图, 是 的一棵生成树。
生成树中所有边权之和称为 的权,记作 。
在 的所有生成树中,权值最小的生成树称为最小生成树,简称 MST。
5.2 Kruskal 算法:避圈法
Kruskal 算法的核心思想是:
按照边权从小到大尝试加入边,但始终避免形成回路。
具体步骤:
- 将所有边按照权值从小到大排序;
- 从最小边开始依次检查;
- 如果加入当前边不会形成回路,则保留该边;
- 如果加入当前边会形成回路,则舍弃该边;
- 当选中的边达到 条时停止。
5.3 算法竞赛中的实现方式
在程序中,通常使用并查集判断两个顶点是否已经连通:
- 如果当前边的两个端点已经属于同一个集合,则加入后会形成回路,应舍弃;
- 如果属于不同集合,则加入该边,并合并两个集合。
5.4 注意事项
- 原图必须连通,才能得到最小生成树;
- 若原图不连通,Kruskal 算法得到的是最小生成森林;
- 若存在相同权值的边,最小生成树可能不唯一,但最小权值相同。
§16.3 根树及其应用
1. 根树的基本概念
1.1 有向树
如果一个有向图的基图是一棵无向树,则称该有向图为有向树。
1.2 根树
有一个顶点入度为 ,其余顶点入度均为 的非平凡有向树,称为根树。
其中:
- 入度为 的顶点称为树根;
- 每个非根顶点都有唯一的父亲。
实际画根树时,通常把树根画在最上方,并省略边上的箭头。默认所有边均由上指向下。
1.3 根树中的常用术语
| 术语 | 定义 |
|---|---|
| 树根 | 入度为 的顶点 |
| 树叶 | 入度为 且出度为 的顶点 |
| 内点 | 入度为 且出度大于 的顶点 |
| 分支点 | 树根与内点的统称 |
| 顶点 的层数 | 从树根到 的通路长度 |
| 树高 | 所有顶点层数的最大值 |
树根的层数规定为 。
2. 家族树术语
根树经常被看成家族关系:
- 若顶点 邻接到顶点 ,则称 是 的儿子, 是 的父亲;
- 若 和 是同一个顶点的儿子,则称 与 是兄弟;
- 若 且 可达 ,则称 是 的祖先, 是 的后代。
3. 根树的分类
3.1 有序树
若规定同一层顶点之间的先后次序,则称为有序树。
3.2 叉树
若根树中每个分支点至多有 个儿子,则称为 叉树。
3.3 叉正则树
若每个分支点恰好有 个儿子,则称为 叉正则树。
3.4 叉完全正则树
若一棵 叉正则树的所有树叶层数都相同,则称为 叉完全正则树。
3.5 二叉树
当 时,得到二叉树。
对于二叉正则有序树,每个分支点的两个儿子有明确顺序,分别称为左儿子和右儿子;对应的根子树称为左子树和右子树。
4. 根子树
设 为一棵根树, 是其中任意一个顶点。
由 及其所有后代导出的子图,称为以 为根的根子树,记作 。
在递归理解二叉树时,根子树非常重要:一棵树可以被拆成“树根 + 若干棵根子树”。
5. 最优二叉树:Huffman 树
5.1 带权路径长度
设二叉树 有 片树叶 ,各树叶权值分别为 。
树的权定义为:
。
其中, 表示树叶 的层数。
在所有具有相同树叶权值的二叉树中,使 最小的二叉树称为最优二叉树,也称为 Huffman 树。
5.2 Huffman 算法
给定权值 :
- 先建立 个只有树叶的单点树;
- 每次选择权值最小的两个根结点;
- 新建一个分支点,将这两个结点作为儿子;
- 新分支点的权值等于两个儿子权值之和;
- 重复上述过程,直到只剩下一棵树。
最终得到 Huffman 树。
5.3 快速求树的总权
Huffman 树的带权路径长度 等于所有新建分支点权值之和。
5.4 例题
给定权值:
。
一种合并过程为:
- ;
- ;
- ;
- ;
- 。
因此:
。
5.5 注意事项
当存在相同权值时,Huffman 树的具体形状可能不唯一,但最小总权 相同。
6. 最佳前缀码
6.1 前缀
设字符串 。
其前缀是:
,其中 。
6.2 前缀码
若一个编码集合中,任意一个码字都不是另一个码字的前缀,则称该编码集合为前缀码。
例如:
- 是前缀码;
- 是前缀码;
- 不是前缀码,因为 是 的前缀。
6.3 为什么二叉树可以产生前缀码?
对二叉树中的每个分支点:
- 左边标记为 ;
- 右边标记为 。
从树根走到每片树叶,将路径上的数字依次拼接起来,得到该树叶对应的编码。
由于任何一片树叶都不可能是另一片树叶的祖先,因此任何一个码字都不会成为另一个码字的前缀。
6.4 最佳前缀码
若用字符出现频率作为树叶权值,并构造 Huffman 树,则由这棵 Huffman 树产生的前缀码所需二进制位数最少。
因此:
设计最佳前缀码,本质上就是构造最优二叉树。
6.5 课件中的八进制编码例子
设八进制数字出现频率为:
| 数字 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|---|
| 频率 | 25% | 20% | 15% | 10% | 10% | 10% | 5% | 5% |
使用 Huffman 算法后,传输 个按上述比例出现的数字,需要 个二进制位。
平均每个数字需要:
个二进制位。
若使用定长编码,因为共有 个符号,每个符号需要 个二进制位,因此 个数字需要 位。
Huffman 编码比定长编码节省了 位。
7. 二叉树的周游
二叉树周游是指:按照某种规定的次序,对每个顶点访问一次且仅访问一次。
7.1 三种周游方式
| 周游方式 | 访问顺序 | 记忆口诀 |
|---|---|---|
| 前序周游 | 根、左子树、右子树 | 根在前 |
| 中序周游 | 左子树、根、右子树 | 根在中 |
| 后序周游 | 左子树、右子树、根 | 根在后 |
7.2 递归理解
假设一棵二叉树由“根、左子树、右子树”构成,则:
- 前序:先访问根,再递归访问左子树和右子树;
- 中序:先递归访问左子树,再访问根,再递归访问右子树;
- 后序:先递归访问左右子树,最后访问根。
8. 用二叉树表示算式
8.1 表示规则
使用二叉有序正则树表示算式时:
- 运算符放在分支点;
- 数字或变量放在树叶;
- 最高层次的运算放在树根;
- 对于减法和除法,被减数、被除数放在左子树中。
8.2 前缀表达式:波兰式
按照前序周游访问表达式树,得到前缀表达式,也称波兰式。
例如:
写成前缀表达式为:
8.3 后缀表达式:逆波兰式
按照后序周游访问表达式树,得到后缀表达式,也称逆波兰式。
例如:
写成后缀表达式为:
8.4 与程序设计的联系
- 前缀表达式和后缀表达式都不需要额外括号;
- 后缀表达式可以使用栈直接求值;
- 编译器中的表达式分析、计算器实现、栈结构题目都可能使用逆波兰式。
四、三组容易混淆的概念
1. 树与生成树
| 概念 | 含义 |
|---|---|
| 树 | 本身就是连通无回路无向图 |
| 生成树 | 从一个连通图中选出部分边,使全部顶点仍然连通且无回路 |
2. 基本回路与基本割集
| 概念 | 核心操作 |
|---|---|
| 基本回路 | 加入一条弦 |
| 基本割集 | 删除一条树枝 |
3. 最小生成树与 Huffman 树
| 概念 | 解决的问题 | 贪心操作 |
|---|---|---|
| 最小生成树 | 在图中连接全部顶点,使边权总和最小 | 每次尽量选最小边,并避免形成回路 |
| Huffman 树 | 让带权路径长度最小 | 每次合并权值最小的两个结点 |
五、考试前需要掌握的题型
1. 判断一个无向图是否为树
检查:
- 是否连通;
- 是否无回路;
- 或者能否利用“连通且 ”;
- 或者能否利用“无回路且 ”。
2. 根据各类度数求树叶数量
优先使用:
。
也可以设树叶数量为 ,结合:
列方程。
3. 求一个连通图的生成树
使用破圈法:不断删除回路中的任意一条边,直到没有回路。
4. 求基本回路系统
对每条弦:在生成树中找弦两端之间的唯一通路,再加上这条弦。
5. 求基本割集系统
对每条树枝:先删树枝,将树分成两部分,再找原图中所有跨越两部分的边。
6. 求最小生成树
使用 Kruskal 算法:边权排序,依次取边,遇到会成圈的边就跳过。
7. 构造 Huffman 树
不断选择当前最小的两个权值相加,并将每次得到的新权值重新放回集合。
Huffman 树总权等于所有合并结果之和。
8. 写出二叉树的三种周游序列
- 前序:根左右;
- 中序:左根右;
- 后序:左右根。
9. 表达式与波兰式、逆波兰式互相转换
先构造表达式树,再进行前序或后序周游。
六、速记总结
- 树:连通无回路。
- 阶树:恰有 条边。
- 非平凡树:至少有两片树叶。
- 连通图一定存在生成树。
- 破圈法:删掉回路边,连通性不变。
- 基本回路:弦加进去找圈。
- 基本割集:树枝删掉找跨边。
- Kruskal:从小到大选边,避免成圈。
- 根树:根的入度为 ,其他顶点入度为 。
- Huffman:反复合并最小的两个权值。
- 前序:根左右;中序:左根右;后序:左右根。
- 波兰式对应前序周游,逆波兰式对应后序周游。
七、自测问题
- 为什么“有 条边”不能单独推出一个图是树?
- 为什么向一棵树中添加任意一条新边后,只会产生一个回路?
- 为什么从树中删除任意一条边后,图一定不连通?
- 基本回路与基本割集分别对应生成树中的哪一类边?
- 一个 阶 条边的连通图有多少条弦?有多少个基本回路?
- Kruskal 算法为什么需要避免加入会形成回路的边?
- Huffman 算法中,为什么频率高的字符通常会被安排在较浅层?
- 二叉树的前序、中序、后序周游分别对应什么访问顺序?
- 波兰式与逆波兰式分别由哪一种周游方式得到?