考研数据结构之树与森林
树与森林是数据结构中的重要非线性结构,其与二叉树的相互转换是考研高频考点。本文系统梳理树的存储结构、树/森林与二叉树的转换规则,以及遍历方法,结合真题解析帮助读者掌握核心逻辑。
一、树的存储结构
树的存储需同时记录节点数据及其逻辑关系,常用方法有以下三种:
1. 双亲表示法
- 原理:用数组顺序存储节点,并为每个节点附加一个
parent域,记录其父节点的数组下标。 - 特点:
- 查找父节点时间复杂度为
O(1),但查找子节点需遍历整个数组。 - 适合需要频繁访问父节点的场景(如并查集)。
- 查找父节点时间复杂度为
- 示例:
struct Node { int data; int parent; // 父节点索引 };
2. 孩子表示法
- 实现方式:
- 方法一:为每个节点固定分配多个指针域指向子节点(定长结点法)。
- 方法二:使用链表动态存储子节点(孩子链表法)。
- 特点:
- 灵活支持多子节点,但空间开销较大。
- 适合需要频繁访问子树的场景。
3. 孩子兄弟表示法(链式存储)
- 结构定义:每个节点包含两个指针域,分别指向第一个孩子和下一个兄弟。
typedef struct CSNode { int data; struct CSNode *firstChild; // 指向第一个孩子 struct CSNode *nextSibling; // 指向下一个兄弟 } CSNode; - 核心作用:为树与二叉树的转换提供直接支持(详见下文)。
二、树、森林与二叉树的转换
1. 树 → 二叉树
- 转换规则:
- 左孩子右兄弟:将每个节点的左指针指向其第一个孩子,右指针指向其下一个兄弟。
- 根节点无右子树:转换后的二叉树根节点右子树必为空。
- 示例:
原树: A 转换后二叉树:A / | \ / B C D B \ C \ D
2. 森林 → 二叉树
- 步骤:
- 将森林中每棵树转换为二叉树。
- 将每棵二叉树的根节点依次连接为右子树。
- 关键点:森林中各树的根节点互为兄弟,构成二叉树的右子树链。
3. 二叉树 → 树/森林
- 判断依据:
- 若二叉树根节点有右子树,则可转换为森林;
- 若根节点无右子树,则转换为单棵树。
- 逆转换规则:
- 将二叉树按左孩子右兄弟规则分解。
- 断开右子树链,恢复为多棵树。
三、树与森林的遍历
1. 树的遍历
- 先根遍历:
- 访问根节点 → 依次先根遍历每棵子树。
- 等价于转换后二叉树的先序遍历。
- 后根遍历:
- 依次后根遍历每棵子树 → 访问根节点。
- 等价于转换后二叉树的中序遍历。
2. 森林的遍历
- 先序遍历:
- 访问森林中第一棵树的根节点。
- 先序遍历第一棵树的子树森林。
- 先序遍历剩余树构成的森林。
- 中序遍历:
- 中序遍历第一棵树的子树森林。
- 访问第一棵树的根节点。
- 中序遍历剩余树构成的森林。
四、真题解析
1. 树的存储结构应用
题目(改编自):
已知树的双亲表示法存储结构如下:
data = [A, B, C, D], parent = [-1, 0, 0, 1]
请画出该树的逻辑结构。
解析:
- 索引0为根节点A(
parent=-1)。 - 索引1的B和索引2的C的父节点为A(
parent=0)。 - 索引3的D的父节点为B(
parent=1)。
答案:
A
/ \
B C
/
D
2. 树与二叉树的转换
题目(经典真题,):
将以下树转换为二叉树,并写出其先序和中序遍历序列。
A / \ B C / \ D E
解析:
- 转换结果:
A / B / \ D C \ E - 先序序列:
A B D E C(等同原树的先根遍历)。 - 中序序列:
D E B C A(等同原树的后根遍历)。
3. 森林的遍历序列
题目(2023年真题,):
森林F由两棵树构成:
第一棵树: A 第二棵树: B / \ \ C D E写出森林F的先序和中序遍历序列。
解析:
- 先序遍历:
- 访问A → 先序遍历A的子树(C, D)。
- 访问B → 先序遍历B的子树(E)。
结果:A C D B E。
- 中序遍历:
- 中序遍历A的子树(C, D)→ 访问A。
- 中序遍历B的子树(E)→ 访问B。
结果:C D A E B。
五、总结
- 存储结构:双亲表示法适合查找父节点,孩子兄弟表示法是转换二叉树的基础。
- 转换规则:左孩子右兄弟是树与二叉树互转的核心逻辑。
- 遍历等价性:树的先根遍历等同二叉树的先序,后根遍历等同二叉树的中序。
掌握这些知识点,可高效解决树与森林相关的遍历、转换及存储问题。后续文章将探讨平衡二叉树与哈夫曼树等进阶内容。

2873

被折叠的 条评论
为什么被折叠?



