数据结构:树(Tree)详细解析
一、基本概念
1.1 定义
树是一种非线性 数据结构,由n(n≥0)个节点组成的有限集合:
- 若n=0,称为空树
- 若n>0,则:
- 有且仅有一个根节点(Root)
- 其余节点可分为m(m≥0)个互不相交的有限集合 ,每个集合本身又是一棵树,称为根的子树
1.2 重要术语
| 术语 | 定义 |
|---|---|
| 节点(Node) | 树的基本单元,包含数据和指向其他节点的指针 |
| 根节点(Root) | 树的最顶层节点,没有父节点 |
| 父节点(Parent) | 当前节点的直接上层节点 |
| 子节点(Child) | 当前节点的直接下层节点 |
| 兄弟节点(Sibling) | 具有相同父节点的节点 |
| 叶节点(Leaf) | 没有子节点的节点(度为0) |
| 内部节点 | 至少有一个子节点的节点 |
| 度(Degree) | 节点拥有的子树数量(子节点数) |
| 树的度 | 树中所有节点度的最大值 |
| 层次(Level) | 根为第1层,向下递增 |
| 深度/高度 | 树中节点的最大层次 |
| 路径 | 从根到某节点所经过的节点序列 |
| 祖先/子孙 | 从根到某节点的路径上的所有节点是该节点的祖先;反之为其子孙 |
| 森林 | 多棵互不相交的树的集合 |
二、树的分类
2.1 二叉树(Binary Tree)
定义 :每个节点最多有两个子节点(左子节点和右子节点),是最基础且应用最广泛的树结构。二叉树在计算机科学中常用于实现搜索、排序等算法。
特殊类型:
-
满二叉树
- 所有非叶节点都有两个子节点,形成完美对称结构
- 所有叶节点都在同一层,具有最大节点数
- 深度为k的满二叉树有 2^k - 1 个节点(如深度3的满二叉树有7个节点)
- 典型应用:完全平衡的决策树
-
完全二叉树
- 除最后一层外,每层节点数达到最大(即2^{i-1}个节点)
- 最后一层节点从左向右连续排列,允许缺失右侧节点
- 适用于数组存储(父子节点可通过下标计算定位)
- 示例:二叉堆的实现基础
-
二叉搜索树(BST)
- 严格遵循:左子树所有节点值 < 根节点值 < 右子树所有节点值
- 中序遍历结果为升序序列(重要性质)
- 查找/插入/删除时间复杂度:平均O(log n),最坏O(n)
- 变种:支持重复值的BST可定义左子树≤根节点
-
平衡二叉树(AVL树)
- 通过平衡因子(左右子树高度差)严格保证不超过1
- 四种旋转操作(LL/RR/LR/RL)保持平衡
- 示例:数据库索引需要保证稳定查询效率
- 优势:最坏情况下仍保持O(log n)操作复杂度
-
红黑树
- 通过颜色标记和旋转实现自平衡
- 五大特性:
- 节点是红或黑
- 根节点是黑
- 所有叶节点(NIL)是黑
- 红节点的子节点必须是黑
- 从任一节点到其叶节点的路径包含相同数量的黑节点
- 应用:Java的TreeMap、Linux进程调度
2.2 多叉树
-
B树
- 每个节点最多包含m个子节点(m阶B树)
- 键值按序排列,实现高效区间查询
- 典型应用:文件系统(如NTFS)、MySQL的InnoDB引擎
- 与B+树对比:B树节点存储数据,可能导致非叶节点访问
-
B+树
- 改进特点:
- 数据仅存储在叶节点,非叶节点作为索引
- 叶节点通过指针连接形成有序链表
- 更高扇出(更多子节点),降低树高
- 数据库首选:支持范围查询(如SQL的BETWEEN语句)
- 改进特点:
-
Trie树(字典树)
- 典型结构:
- 根节点表示空字符串
- 边表示字符
- 节点标记是否为单词结尾
- 优势:O(L)时间完成字符串查找(L为字符串长度)
- 应用案例:搜索引擎自动补全、拼写检查
- 典型结构:
2.3 特殊树结构
-
堆(Heap)
- 存储方式:通常用数组实现,下标关系:
- 父节点i的左子节点:2i+1
- 父节点i的右子节点:2i+2
- 操作示例:
- 插入:上浮(sift up)
- 删除:下沉(sift down)
- 应用场景:Dijkstra算法中的优先队列
- 存储方式:通常用数组实现,下标关系:
-
线段树
- 构建方法:
- 叶子节点存储原始数据
- 非叶节点存储子节点区间合并值(如求和/最大值)
- 查询示例:求数组[2,5]区间和
- 优势:支持动态数据的高效区间操作
- 构建方法:
-
树状数组(Fenwick Tree)
- 核心思想:利用二进制低位技术
- 操作复杂度:
- 更新/查询:O(log n)
- 建树:O(n log n)
- 适用场景:频繁计算前缀和(如股票交易分析)
三、存储结构
3.1 链式存储(最常用)
c
struct TreeNode {
int data;
struct TreeNode* left;
struct TreeNode* right;
};
3.2 数组存储(完全二叉树)
- 父节点索引:i
- 左子节点:2*i + 1
- 右子节点:2*i + 2
- 父节点:(i-1)/2
3.3 孩子表示法
c
struct ChildNode {
int index;
struct ChildNode* next;
};
struct TreeNode {
int data;
struct ChildNode* firstChild;
};
3.4 孩子兄弟表示法
将多叉树转换为二叉树:
- 左指针:指向第一个孩子
- 右指针:指向下一个兄弟
四、遍历算法
4.1 深度优先遍历(DFS)
python
# 递归实现
def preorder(root): # 前序:根->左->右
if root:
visit(root)
preorder(root.left)
preorder(root.right)
def inorder(root): # 中序:左->根->右
if root:
inorder(root.left)
visit(root)
inorder(root.right)
def postorder(root): # 后序:左->右->根
if root:
postorder(root.left)
postorder(root.right)
visit(root)
4.2 广度优先遍历(BFS/层次遍历)
python
from collections import deque
def level_order(root):
if not root:
return
queue = deque([root])
while queue:
node = queue.popleft()
visit(node)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
五、重要操作
5.1 查找
- 二叉搜索树查找:O(log n) ~ O(n)
- 平衡二叉树查找:O(log n)
- B树/B+树查找:O(log_m n),m为阶数
5.2 插入
- 二叉搜索树:找到适当位置插入
- 平衡二叉树:插入后可能需要旋转
- B树:可能分裂节点
5.3 删除
- 叶节点:直接删除
- 有一个子节点:用子节点替代
- 有两个子节点:用前驱或后继替代
5.4 旋转操作(平衡树)
- 左旋
- 右旋
- 左右旋
- 右左旋
六、复杂度分析
| 操作 | 普通二叉树 | 平衡二叉树 | B树/B+树 |
|---|---|---|---|
| 查找 | O(n) | O(log n) | O(log_m n) |
| 插入 | O(n) | O(log n) | O(log_m n) |
| 删除 | O(n) | O(log n) | O(log_m n) |
| 空间 | O(n) | O(n) | O(n) |
七、总结与对比
| 树类型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 二叉搜索树 | 简单直观,中序遍历有序 | 可能退化为链表 | 教学、简单应用 |
| AVL树 | 严格平衡,查找效率高 | 旋转开销大,插入删除慢 | 查询密集型 |
| 红黑树 | 近似平衡,插入删除快 | 实现复杂 | 综合性能要求高 |
| B树 | 磁盘友好,适合大数据 | 内存中不如二叉树 | 文件系统、数据库 |
| B+树 | 范围查询高效 | 内部节点不存数据 | 数据库索引 |
| Trie树 | 前缀匹配高效 | 空间消耗大 | 字典、自动补全 |