二叉树¶
概述¶
二叉树(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。
度数限制
graph TB
subgraph "节点A(度=2)"
A1[A] --> B1[○]
A1 --> C1[○]
end
subgraph "节点B(度=1)"
D[B] --> E[○]
end
subgraph "节点C(度=0,叶子)"
F[C]
end
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层 | \(2^{1-1} = 1\) | 1 ✓ |
| 第2层 | \(2^{2-1} = 2\) | 2 ✓ |
| 第3层 | \(2^{3-1} = 4\) | 4 ✓ |
| 第4层 | \(2^{4-1} = 8\) | 8 ✓ |
性质2:深度为k的节点总数¶
深度为 k 的二叉树最多有 \(2^k - 1\) 个节点。
验证
| 深度 k | 公式 | 节点数 |
|---|---|---|
| k=1 | \(2^1 - 1 = 1\) | 1 |
| k=2 | \(2^2 - 1 = 3\) | 3 |
| k=3 | \(2^3 - 1 = 7\) | 7 |
性质3:叶子节点与度数为2节点的关系¶
对任何非空二叉树,若 \(n_0\) 为叶子节点数,\(n_2\) 为度数为2的节点数,则:
验证示例
graph TB
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_0 = 3\) (D, E, C)
- 度数为2节点 \(n_2 = 2\) (A, B)
\(n_0 = n_2 + 1 \Rightarrow 3 = 2 + 1\) ✓
性质4:完全二叉树的深度¶
n 个节点的完全二叉树深度为 \(\lfloor \log_2 n \rfloor + 1\)
验证
| 节点数 n | 公式 | 深度 |
|---|---|---|
| n=1 | \(\lfloor \log_2 1 \rfloor + 1 = 0 + 1\) | 1 ✓ |
| n=3 | \(\lfloor \log_2 3 \rfloor + 1 = 1 + 1\) | 2 ✓ |
| n=7 | \(\lfloor \log_2 7 \rfloor + 1 = 2 + 1\) | 3 ✓ |
性质5:完全二叉树的节点编号¶
对完全二叉树按层序编号,对任一编号为 i 的节点:
- 父节点编号:\(\lfloor i/2 \rfloor\)
- 左子节点编号:\(2i\)
- 右子节点编号:\(2i + 1\)
编号关系验证
graph TB
1((1)) --> 2((2))
1 --> 3((3))
2 --> 4((4))
2 --> 5((5))
3 --> 6((6))
3 --> 7((7))
- 父节点(1)的子节点: 左 \(= 2 \times 1 = 2\), 右 \(= 2 \times 1 + 1 = 3\)
- 节点(2)的父节点: \(\lfloor 2/2 \rfloor = 1\)
- 节点(5)的父节点: \(\lfloor 5/2 \rfloor = 2\)
特殊二叉树详解¶
1. 满二叉树(Full Binary Tree)¶
所有叶子节点在同一层,且所有分支节点都有两个子节点。
满二叉树(深度为3)
graph TB
1((1)) --> 2((2))
1 --> 3((3))
2 --> 4((4))
2 --> 5((5))
3 --> 6((6))
3 --> 7((7))
特点: - ✓ 所有叶子在同一层 - ✓ 每个分支节点都有2个子节点 - • 叶子节点数 = 2^(k-1),k为深度 - • 总节点数 = 2^k - 1
2. 完全二叉树(Complete Binary Tree)¶
除最后一层外,每层节点数达到最大,且最后一层节点从左到右连续排列。
完全二叉树
graph TB
1((1)) --> 2((2))
1 --> 3((3))
2 --> 4((4))
2 --> 5((5))
3 --> 6((6))
叶子节点4,5,6在最后一层连续排列
非完全二叉树
graph TB
1((1)) --> 2((2))
1 --> 3((3))
2 --> 4((4))
2 --> 5((5))
5 --> 7((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。
平衡 vs 不平衡
graph TB
subgraph "平衡二叉树"
B1((4)) --> B2((2))
B1 --> B3((6))
B2 --> B4((1))
B2 --> B5((3))
B3 --> B6((5))
B3 --> B7((7))
end
subgraph "不平衡二叉树"
U1((1)) --> U2((2))
U2 --> U3((3))
end
- 平衡树: |左高-右高| = 0 ≤ 1 ✓
- 不平衡树: |左高-右高| = 2 > 1 ✗
5. 斜树(Skewed Tree)¶
所有节点都只有左子节点或只有右子节点,退化为链表。
斜树
graph TB
subgraph "左斜树"
L1((4)) --> L2((3))
L2 --> L3((2))
L3 --> L4((1))
end
subgraph "右斜树"
R1((1)) --> R2((2))
R2 --> R3((3))
R3 --> R4((4))
end
查找效率退化为 \(O(n)\)
二叉树的存储方式¶
1. 链式存储¶
| C | |
|---|---|
链式存储结构
graph LR
N1[("Node 1: data=1, left→, right→]
N2[("Node 2: data=2, left→, right→]
N3[("Node 4: data=4, left=NULL, right=NULL")]
N1 --> N2
N2 --> N3
每个节点包含数据域和两个指针域
2. 顺序存储(数组)¶
适用于完全二叉树:
完全二叉树的数组存储
graph TB
1((1, idx=0)) --> 2((2, idx=1))
1 --> 3((3, idx=2))
2 --> 4((4, idx=3))
2 --> 5((5, idx=4))
3 --> 6((6, idx=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: [4,2,5] 1 [3]
- 根据长度分割前序: [1] [2,4,5] [3]
- 递归构建...
从数组构建完全二叉树¶
| 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