二叉树¶
概述¶
二叉树(Binary Tree)是一种重要的树形数据结构,每个节点最多有两个子节点,分别称为左子节点(Left Child)和右子节点(Right Child)。二叉树是许多高级数据结构(如BST、AVL树、红黑树、堆等)的基础。
二叉树的递归定义
二叉树要么为空集,要么由一个根节点和两个互不相交的、分别称为左子树和右子树的二叉树组成。这个递归定义是理解二叉树所有操作的关键。
二叉树的可视化结构¶
graph TB
A((1)) --> B((2))
A --> C((3))
B --> D((4))
B --> E((5))
C --> F((6))
C --> G((7))
style A fill:#E3F2FD
style B fill:#E8F5E9
style C fill:#E8F5E9
style D fill:#FFF3E0
style E fill:#FFF3E0
style F fill:#FFF3E0
style G fill:#FFF3E0
树的层次结构
| 第1层 |
1
|
← 根节点 (Root) |
| 第2层 |
2
3
|
← 第1层的子节点 |
| 第3层 |
4
5
6
7
|
← 叶子节点 (Leaf) |
树的高度: 3 | 节点总数: 7
二叉树特点详解¶
1. 递归定义¶
二叉树的每个节点都可以看作是一棵子树的根,这使得递归成为处理二叉树最自然的方式。
2. 度数限制¶
每个节点的度数(子节点数量)最多为2,可以是0、1或2。
度数示意
节点A (度=2)
节点B (度=1)
节点C (度=0)
(叶子节点)
3. 有序性¶
左右子树有严格的顺序区分,即使某个节点只有一个子节点,也必须区分是左子节点还是右子节点。
4. 层次结构¶
节点按层次组织,根节点在第1层,其子节点在第2层,以此类推。
基本概念详解¶
| 概念 | 定义 | 示例 |
|---|---|---|
| 节点(Node) | 包含数据元素和指向子树的指针 | 上图中1-7都是节点 |
| 节点度数(Degree) | 节点拥有的子树数量 | 节点1的度数为2 |
| 叶子节点(Leaf) | 度数为0的节点 | 节点4,5,6,7 |
| 分支节点(Branch) | 度数大于0的节点 | 节点1,2,3 |
| 父节点(Parent) | 若有子节点,则为父节点 | 节点1是2,3的父节点 |
| 子节点(Child) | 父节点的下层节点 | 节点2,3是1的子节点 |
| 兄弟节点(Sibling) | 同一父节点的节点 | 节点2,3互为兄弟 |
| 节点层次(Level) | 根为第1层,子节点层次=父节点层次+1 | 节点4在第3层 |
| 树的深度/高度(Depth/Height) | 最大层次数 | 上图高度为3 |
| 路径(Path) | 从一个节点到另一个节点的边序列 | 1→2→4 |
| 路径长度 | 路径上的边数 | 1到4的路径长度为2 |
二叉树重要性质¶
性质1:第i层节点数¶
第i层最多有 2^(i-1) 个节点。
验证:
第1层: 21-1 = 1 个节点 ✓
第2层: 22-1 = 2 个节点 ✓
第3层: 23-1 = 4 个节点 ✓
第4层: 24-1 = 8 个节点 ✓
性质2:深度为k的节点总数¶
深度为k的二叉树最多有 2^k - 1 个节点。
验证:
k=1: 21 - 1 = 1 个节点
k=2: 22 - 1 = 3 个节点
k=3: 23 - 1 = 7 个节点
性质3:叶子节点与度数为2节点的关系¶
对任何非空二叉树,若n₀为叶子节点数,n₂为度数为2的节点数,则:
n₀ = n₂ + 1
graph TB
subgraph "性质验证示例"
A((A)) --> B((B))
A --> C((C))
B --> D((D))
B --> E
end
style D fill:#E8F5E9
style E fill:#E8F5E9
style C fill:#E8F5E9
验证上图:
叶子节点 n₀ = 3 (D, E, C)
度数为2节点 n₂ = 2 (A, B)
n₀ = n₂ + 1 ✓
性质4:完全二叉树的深度¶
n个节点的完全二叉树深度为 ⌊log₂n⌋ + 1
验证:
n=1: ⌊log₂1⌋ + 1 = 0 + 1 = 1 ✓
n=3: ⌊log₂3⌋ + 1 = 1 + 1 = 2 ✓
n=7: ⌊log₂7⌋ + 1 = 2 + 1 = 3 ✓
性质5:完全二叉树的节点编号¶
对完全二叉树按层序编号,对任一编号为i的节点:
- 父节点编号:⌊i/2⌋
- 左子节点编号:2i
- 右子节点编号:2i + 1
完全二叉树的编号
|
1
|
| │ |
|
2
3
|
| │ |
|
4
5
6
7
|
父节点(1)的子节点: 左=2×1=2, 右=2×1+1=3
节点(2)的父节点: ⌊2/2⌋=1
节点(5)的父节点: ⌊5/2⌋=2
特殊二叉树详解¶
1. 满二叉树(Full Binary Tree)¶
所有叶子节点在同一层,且所有分支节点都有两个子节点。
满二叉树(深度为3)
|
1
|
| │ |
|
2
3
|
| │ |
|
4
5
6
7
|
✓ 所有叶子在同一层
✓ 每个分支节点都有2个子节点
• 叶子节点数 = 2k-1,k为深度
• 总节点数 = 2k - 1
2. 完全二叉树(Complete Binary Tree)¶
除最后一层外,每层节点数达到最大,且最后一层节点从左到右连续排列。
完全二叉树
|
1
|
| │ |
|
2
3
|
| │ |
|
4
5
6
|
叶子节点4,5,6在最后一层连续排列
非完全二叉树
|
1
|
| │ |
|
2
3
|
| │ |
|
4
5
|
| │ |
|
7
|
✗ 节点5的位置"空缺",不连续
✗ 应该先有左子节点
完全二叉树的重要性
完全二叉树是堆的基础,其编号性质使得可以用数组高效存储,无需指针。
3. 二叉搜索树(Binary Search Tree, BST)¶
左子树所有节点值小于根节点,右子树所有节点值大于根节点。
graph TB
A((5)) --> B((3))
A --> C((7))
B --> D((2))
B --> E((4))
C --> F((6))
C --> G((8))
style A fill:#E3F2FD
BST特性:
• 左子树所有值 < 根节点值
• 右子树所有值 > 根节点值
• 中序遍历得到有序序列: 2 3 4 5 6 7 8
• 查找、插入、删除平均O(log n)
4. 平衡二叉树(Balanced Binary Tree)¶
左右子树高度差不超过1。
平衡二叉树
|
4
|
| │ |
|
2
6
|
| │ |
|
1
3
5
7
|
|左高-右高| = 0 ≤ 1 ✓
不平衡二叉树
|
1
|
| │ |
|
2
|
| │ |
|
3
|
|左高-右高| = 2 > 1 ✗
5. 斜树(Skewed Tree)¶
所有节点都只有左子节点或只有右子节点,退化为链表。
左斜树
4 |
| │ |
3 |
| │ |
2 |
| │ |
1 |
右斜树
1 |
| │ |
2 |
| │ |
3 |
| │ |
4 |
查找效率退化为O(n)
二叉树的存储方式¶
1. 链式存储¶
| C | |
|---|---|
链式存储结构
data: 1
left: →
right: →
data: 2
left: →
right: →
data: 4
left: NULL
right: NULL
每个节点包含数据域和两个指针域
2. 顺序存储(数组)¶
适用于完全二叉树:
完全二叉树的数组存储
|
1
(索引0)
|
| │ |
|
2
3
(索引1) (索引2)
|
| │ |
|
4
5
6
(3) (4) (5)
|
数组: [1, 2, 3, 4, 5, 6]
索引: 0 1 2 3 4 5
索引关系:
• 父节点: ⌊(i-1)/2⌋
• 左子节点: 2i+1
• 右子节点: 2i+2
| C | |
|---|---|
二叉树遍历详解¶
遍历是二叉树最基本也是最重要的操作,指按某种顺序访问所有节点且每个节点仅访问一次。
四种遍历方式对比¶
graph TB
subgraph "遍历顺序示意"
A((A)) --> B((B))
A --> C((C))
end
style A fill:#E3F2FD
style B fill:#E8F5E9
style C fill:#FFF3E0
| 遍历方式 | 访问顺序 | 上图结果 | 应用场景 |
|---|---|---|---|
| 前序遍历 | 根→左→右 | A B C | 复制树、前缀表达式 |
| 中序遍历 | 左→根→右 | B A C | BST有序输出、中缀表达式 |
| 后序遍历 | 左→右→根 | B C A | 计算树高度、后缀表达式、释放树 |
| 层序遍历 | 按层次 | A B C | 按层处理、序列化 |
1. 前序遍历(Preorder)¶
graph TB
subgraph "前序遍历: 根→左→右"
A((1)) --> B((2))
A --> C((3))
B --> D((4))
B --> E((5))
end
style A fill:#E3F2FD
style B fill:#E8F5E9
style D fill:#FFF3E0
style E fill:#FFF3E0
style C fill:#F3E5F5
遍历顺序: 1 → 2 → 4 → 5 → 3
执行过程:
1. 访问根节点1
2. 递归遍历左子树(以2为根) → 访问2, 4, 5
3. 递归遍历右子树(以3为根) → 访问3
2. 中序遍历(Inorder)¶
graph TB
subgraph "中序遍历: 左→根→右"
A((1)) --> B((2))
A --> C((3))
B --> D((4))
B --> E((5))
end
style D fill:#E3F2FD
style B fill:#E8F5E9
style E fill:#FFF3E0
style A fill:#F3E5F5
style C fill:#FCE4EC
遍历顺序: 4 → 2 → 5 → 1 → 3
对于BST,中序遍历得到有序序列!
3. 后序遍历(Postorder)¶
graph TB
subgraph "后序遍历: 左→右→根"
A((1)) --> B((2))
A --> C((3))
B --> D((4))
B --> E((5))
end
style D fill:#E3F2FD
style E fill:#E8F5E9
style B fill:#FFF3E0
style C fill:#F3E5F5
style A fill:#FCE4EC
遍历顺序: 4 → 5 → 2 → 3 → 1
4. 层序遍历(Level Order)¶
graph TB
subgraph "层序遍历: 按层次从上到下,从左到右"
A((1)) --> B((2))
A --> C((3))
B --> D((4))
B --> E((5))
end
style A fill:#E3F2FD
style B fill:#E8F5E9
style C fill:#E8F5E9
style D fill:#FFF3E0
style E fill:#FFF3E0
遍历顺序: 1 → 2 → 3 → 4 → 5
常用操作实现¶
计算节点数¶
| C | |
|---|---|
计算树高度¶
| C | |
|---|---|
查找节点¶
| C | |
|---|---|
销毁树¶
| C | |
|---|---|
二叉树构建¶
从前序和中序序列构建¶
原理:
• 前序序列第一个元素是根节点
• 在中序序列中找到根节点,左边是左子树,右边是右子树
• 递归构建左右子树
示例:
前序: [1, 2, 4, 5, 3]
中序: [4, 2, 5, 1, 3]
1. 前序第一个元素1是根
2. 在中序中找1: [4,2,5] 1 [3]
3. 根据长度分割前序: [1] [2,4,5] [3]
4. 递归构建...
从数组构建完全二叉树¶
| C | |
|---|---|
C++ 实现¶
时间复杂度分析¶
| 操作 | 平均时间 | 最坏时间 | 说明 |
|---|---|---|---|
| 遍历 | O(n) | O(n) | 访问所有节点 |
| 查找节点 | O(n) | O(n) | 可能需要遍历整棵树 |
| 插入节点 | O(n) | O(n) | 需要找到插入位置 |
| 删除节点 | O(n) | O(n) | 需要找到删除节点 |
| 计算高度 | O(n) | O(n) | 需要遍历所有节点 |
空间复杂度¶
| 情况 | 空间复杂度 | 说明 |
|---|---|---|
| 递归遍历 | O(h) | h为树高度,递归栈深度 |
| 层序遍历 | O(w) | w为树最大宽度 |
| 平衡树 | O(log n) | 高度最小 |
| 斜树(最坏) | O(n) | 退化为链表 |
应用场景¶
- 表达式树:计算表达式求值
- Huffman编码:数据压缩
- 决策树:机器学习分类
- 文件系统:目录结构表示
- 语法树:编译器设计
- 数据库索引:B+树的基础
- 优先队列:堆的实现
参考资料¶
- 《算法导论》第12章 - 二叉搜索树
- 《数据结构与算法分析》第4章 - 树
- Binary Tree - Wikipedia
- Tree Traversal - Wikipedia