在现实生活和计算机系统中,多叉树(如文件系统的目录树、企业组织架构图、XML/JSON 语法树)与森林(多个独立树的集合)更为普遍。多叉树的节点度数各不相同,若为每个节点预留最大可能的孩子指针数组,会浪费大量内存,而且操作逻辑也会变得繁杂。
二叉树由于每个节点固定只有两个有序槽位(左、右),具有更紧凑、更高效的存储与算法实现。
那么,能否将任意形态的多叉树,甚至是由多棵树构成的森林,完全等价地转换为一棵二叉树?
答案是孩子兄弟表示法(Left-Child Right-Sibling, LCRS)。通过这一表示法,许多本来只针对标准二叉树的高效算法就可以应用到多叉树与森林中,替代那些繁杂的操作。
学习目标
完成本节后,你应该能够:
- 掌握树转换为二叉树的“加线、抹线、旋转”几何法则与本质原理;
- 解释为什么由单棵树转换得到的二叉树,其根节点的右孩子必定为空;
- 熟练完成森林与二叉树之间的相互可逆转换(双向映射);
- 区分树的先根遍历、后根遍历与森林的先序、后序遍历规则,理解树为什么没有“中根遍历”;
- 严格推导并牢记树/森林遍历与二叉树遍历的等价映射定理(尤其是“后根遍历
二叉树中序遍历”的深层原因)。
4.5.1 树与二叉树的转换
1. 转换的底层语义:左孩子,右兄弟
我们在 4.1 节 介绍过孩子兄弟表示法:
firstChild(左指针):指向该节点的第一个孩子(长子);nextSibling(右指针):指向该节点的下一个相邻右侧兄弟。
当我们将 firstChild 当作二叉树的 left,将 nextSibling 当作二叉树的 right 时,一般多叉树就自然转换为了一棵二叉树。
struct CSNode {
int val;
CSNode* firstChild = nullptr; // 左指针:第一个孩子
CSNode* nextSibling = nullptr; // 右指针:下一个兄弟
};lcrs-node.hpp2
3
4
5
2. 树转二叉树的直观几何规则(三步法)
- 加线:在所有属于同一个父节点的兄弟节点之间,画一条水平连线;
- 抹线:对树中的每一个节点,只保留它与**第一个孩子节点(最左孩子)**的连线,抹去它与其他所有孩子节点的连线;
- 旋转平移:以根节点为轴心,顺时针旋转
。原来的长子成为左孩子,原来的水平兄弟链成为右斜向下的右孩子链。
PROP性质 · 单树转换二叉树的根右链为空
由任意一棵单棵树转换得到的二叉树,其根节点的右子树一定为空(root->right == nullptr)。 原因:树的根节点是全局唯一的,它在逻辑上没有任何兄弟节点,因此其 nextSibling 必定为空。
4.5.2 森林与二叉树的转换
森林(forest)是
当
1. 森林转换为二叉树
既然单棵树的根节点没有兄弟,那么如果有多棵树,我们就可以将第二棵树
森林转二叉树转换算法:
- 将森林中的每一棵树
分别按照 4.5.1 节规则转换为对应的二叉树 ; - 第一棵二叉树
的根节点直接作为整棵二叉树的根; - 从
开始,依次将后一棵二叉树的根节点,作为前一棵二叉树根节点的 右孩子(right) 连接起来。
2. 二叉树还原为森林
二叉树转换为森林是上述过程的完全逆过程:
- 切断右链:从二叉树的根节点开始,沿着根的右孩子指针一路向下,将所有右指针连线全部断开,得到
棵独立的二叉树 ; - 独立还原:对每一棵二叉树
,按逆向规则还原为单棵多叉树 ; - 组合森林:将还原出的所有树集合起来,即得到原始森林
。
THM定理 1 · 森林与二叉树的双射同构
森林与二叉树之间存在 一一对应(Bijective Mapping) 关系。任意给定的森林都可以唯一转换得到一棵二叉树,任意二叉树也可以唯一还原为原始森林。
4.5.3 树与森林的遍历及对应关系
1. 树的遍历规则
对于一棵非空的一般多叉树,有两种自然的深度优先遍历策略:
- 先根遍历(Pre-order Tree Traversal):
- 先访问根节点;
- 从左到右,依次先根遍历根节点的每一棵子树。
- 后根遍历(Post-order Tree Traversal):
- 从左到右,依次后根遍历根节点的每一棵子树;
- 最后访问根节点。
WARN易错点 · 为什么一般树没有“中根遍历”?
二叉树有严格的“左子树”与“右子树”,根节点正好位于中间(
2. 森林的遍历规则
设森林
- 先序遍历森林(Pre-order Forest Traversal):
- 访问第一棵树的根节点
; - 先序遍历第一棵树中根节点的子树森林
; - 先序遍历除了第一棵树之后剩余的树构成的森林
。
- 访问第一棵树的根节点
- 后序遍历森林(Post-order Forest Traversal):
- 后序遍历第一棵树中根节点的子树森林
; - 访问第一棵树的根节点
; - 后序遍历除了第一棵树之后剩余的树构成的森林
。
- 后序遍历第一棵树中根节点的子树森林
3. 核心定理:树/森林遍历与二叉树遍历的等价对应
这是树形结构理论与算法题中常用的对应关系:
THM定理 2 · 树/森林遍历与二叉树遍历的等价映射
关键在于把“树的孩子”翻译成“二叉树的左子树”,把“树的兄弟链”翻译成“二叉树的右子树”。因此,原始结构中的访问顺序会保持不变,并映射到二叉树的对应遍历序列:
| 原始结构 | 原始遍历方式 | 对应转换后二叉树的遍历方式 |
|---|---|---|
| 树(Tree) | 先根遍历 | 二叉树的前序遍历( |
| 树(Tree) | 后根遍历 | 二叉树的中序遍历( |
| 森林(Forest) | 先序遍历 | 二叉树的前序遍历( |
| 森林(Forest) | 后序遍历 | 二叉树的中序遍历( |
交互式演示:转换与同步遍历
左侧保留原树,右侧依次演示“加兄弟线、只留首孩子、改为二叉树布局”。转换完成后,每前进一步,原树的后根遍历与二叉树的中序遍历就会同时输出同一个节点。点击任意一侧的节点,可在另一侧找到相同字母,并检查它的 left、right 分别对应哪个孩子和兄弟。
做两个对照
课本示例两侧都应输出 B E C D A。切换三棵树的森林,观察各根如何通过右指针串联;再切换先根/先序模式,比较对应的二叉树前序。这里以“输出一个节点”为同步单位,两种算法的递归调用步骤不必完全相同。
PROOF为什么“树的后根遍历”对应“二叉树的中序遍历”而非后序遍历?(严格归纳证明)
1、基础情况(高度为 1 的树)
若树仅有根节点
2、归纳假设
假设对于所有高度小于
3、归纳步骤(高度为
设原树根节点为
根据转换规则构造二叉树:
的左指针指向 ;- 对于任意
,节点 的右指针指向 (即通过右指针串起兄弟链表); - 由于
是整棵树的根,它没有右兄弟,因此二叉树中 的右指针为空。
现在对转换后的二叉树进行中序遍历(左子树
第一步:遍历
- 先完整遍历
的左子树(即 除 本身); - 访问
本身; - 进入
的右子树——而 的右子树恰好是以 为根的二叉树; - 继续完整遍历
的左子树( 除 本身),访问 ,进入 的右子树( )…… - 依此类推,直到处理完
的整棵子树。
因此,遍历完
第二步:访问根节点
中序遍历访问
第三步:遍历
因
将三步合并,得到二叉树中序遍历的完整展开式:
由归纳假设,对于每个
这正是原树以
补充说明:为什么二叉树的“后序遍历”不行?
若对转换后的二叉树做后序遍历(左子树
后序遍历在处理以
原树后根遍历要求的是“先完整输出
综上,只有中序遍历能完美承载树的后根遍历顺序。
配套理论题
本节对应的理论题库如下:
| 顺序 | 题目与入口 |
|---|---|
| 08 | 树与森林理论题精练 |
小结与自测
树与森林通过 孩子兄弟二叉链表(左孩子、右兄弟) 与二叉树建立了双射同构关系。森林多根串联于右链,树的先根映射二叉树前序,树的后根映射为二叉树的中序。
请尝试回答以下自测问题:
- 一棵含有
个节点的一般树转换成二叉树后,这棵二叉树的根节点的左指针与右指针分别有什么特点? - 已知某森林包含 3 棵树,节点总数分别为
。转换为二叉树后,二叉树根节点的右子树包含多少个节点? - 为什么一般多叉树不能定义中根遍历?
- 给定一棵多叉树,其先根遍历序列为
A B C D E,后根遍历序列为B D E C A。请写出该多叉树转换为二叉树后的中序遍历序列。 - 简述在文件系统或组织架构等多叉树系统中,采用孩子兄弟二叉链表代替指针数组的主要工程优势。
参考答案 · 题 4
由定理 2 可知:树的后根遍历等价于转换后二叉树的中序遍历。因此,题目中所给的后根遍历序列 B D E C A,就是该多叉树对应的二叉树中序遍历序列。
也就是说,答案为:
B D E C A
这是一个典型的“陷阱题”:先根 + 后根并不能唯一确定一棵多叉树,但在孩子兄弟表示中,它们恰好分别对应二叉树的前序与中序,因此无需还原整棵树即可直接得到答案。
下一节进入4.6 二叉树的经典问题:我们将综合运用递归框架、DFS/BFS 状态转移与树形动态规划,系统讨论二叉树形态统计、路径回溯、对称变换与最近公共祖先等经典问题。