124 lines
5.0 KiB
Markdown
124 lines
5.0 KiB
Markdown
好的!先从“平衡树”的共同目标说起,再分别讲 AVL、红黑树、B-Tree,最后给一张对照表和选型建议。
|
||
|
||
# 什么是“平衡树”
|
||
|
||
“平衡树”都是在**二叉搜索树(BST)**或其多叉推广的框架上,通过**限制高度**来保证查找/插入/删除的时间复杂度稳定在 `O(log n)`。不同平衡树的“平衡标准”不同,越严格,高度越低(查找更快),但维护成本(旋转/分裂/合并)会更高。
|
||
|
||
---
|
||
|
||
# AVL 树(Adelson-Velsky and Landis)
|
||
|
||
**核心不变量**
|
||
|
||
* 对任一节点,`|height(left) - height(right)| ≤ 1`(平衡因子 ∈ {-1,0,+1})。
|
||
* 通常在节点里存高度或平衡因子,插删后自底向上更新。
|
||
|
||
**失衡与旋转**
|
||
|
||
* LL 型 → 右旋
|
||
* RR 型 → 左旋
|
||
* LR 型 → 先左旋子节点再右旋
|
||
* RL 型 → 先右旋子节点再左旋
|
||
|
||
> 插入至多 1 次“单/双旋”(最多 2 次旋转);删除可能沿路径多处修复(最坏 `O(log n)` 次)。
|
||
|
||
**复杂度与性质**
|
||
|
||
* 查找/插入/删除:`O(log n)`
|
||
* 高度上界:约 `≈ 1.44 * log2(n)`(非常低)
|
||
* 优点:查找性能最稳定、树更矮
|
||
* 缺点:更新更“敏感”,旋转相对多;实现需维护高度
|
||
|
||
**适用**
|
||
|
||
* **查多改少**或对**最坏时延敏感**的内存场景(如需要更低树高的索引、路由表等)
|
||
|
||
---
|
||
|
||
# 红黑树(Red-Black Tree)
|
||
|
||
**核心不变量(颜色与黑高)**
|
||
|
||
1. 根为黑;2) 叶子(NIL)为黑;
|
||
2. 红节点不能有红孩子(红红不相邻);
|
||
3. 任一节点到其后代 NIL 的所有路径具有**相同黑高**。
|
||
|
||
**插删修复**
|
||
|
||
* 通过**着色 + 旋转**修复;
|
||
* 插入:至多 2 次旋转;删除:至多 3 次旋转(但可能多次“上溯”重着色)。
|
||
|
||
**复杂度与性质**
|
||
|
||
* 查找/插入/删除:`O(log n)`
|
||
* 高度上界:`≤ 2 * log2(n+1)`(比 AVL 略高)
|
||
* 优点:更新旋转次数少、实现成熟、工业广泛使用
|
||
* 缺点:查找平均高度略高于 AVL
|
||
|
||
**适用**
|
||
|
||
* **查改均衡**的通用有序容器场景。C++ 的 `std::map`/`std::set` 通常用红黑树实现;大多数语言标准库的有序映射/集合也选择红黑树。
|
||
|
||
---
|
||
|
||
# B-Tree(多叉平衡搜索树)
|
||
|
||
(数据库/文件系统的主角,常用的是 **B+ 树**变体)
|
||
|
||
**结构**
|
||
|
||
* 每个节点可有最多 `m` 个孩子(多叉),节点里存**有序键数组**与孩子指针;
|
||
* **所有叶子在同一层**;
|
||
* 除根外:每个节点的键数在 `[ceil(m/2)-1, m-1]` 之间(保证“半满”)。
|
||
|
||
**为何快(外存友好)**
|
||
|
||
* 把一个节点的键打包成**一页(如 4KB)**,一次 I/O 读入很多键;
|
||
* 分支因子大(几百级),**高度极低**,因此磁盘/SSD 的随机 I/O 次数很少。
|
||
* 复杂度常写作 `O(log_m n)`(以分支因子为底),I/O 次数非常小。
|
||
|
||
**插入/删除**
|
||
|
||
* **插入**:若目标节点满则**分裂**(把中间键上推给父节点),可能层层上推到根,根也满则新建根,高度+1;
|
||
* **删除**:若节点“欠满”,先向兄弟**借键**,不行再**合并**,可能向上连锁。
|
||
|
||
**B+ 与 B\***
|
||
|
||
* **B+ 树**:所有**数据都在叶子**,内节点只存键;叶子链表天然支持**范围扫描**与顺序遍历(数据库最常见)。
|
||
* **B\***:更激进的“借/并”策略,节点利用率更高(≥ 2/3 满),进一步减少高度与 I/O。
|
||
|
||
**适用**
|
||
|
||
* **磁盘/SSD** 上的大规模索引:数据库(如 InnoDB 使用 B+ 树)、文件系统、KV 存储等。
|
||
* 内存里也可用,但优势主要在外存局部性与范围扫描。
|
||
|
||
---
|
||
|
||
# 对照速览
|
||
|
||
| 维度 | AVL | 红黑树 | B-Tree / B+ |
|
||
| ----- | ----------------- | -------------- | -------------- |
|
||
| 结构 | 二叉 | 二叉(带颜色) | 多叉(分支因子大) |
|
||
| 平衡标准 | 每节点左右高差 ≤ 1 | 黑高相等、红红不相邻 | 节点半满,叶子同层 |
|
||
| 高度 | 最矮(≈ 1.44 log₂ n) | 略高(≤ 2 log₂ n) | **极矮**(logₘ n) |
|
||
| 查找 | 最稳定 | 稍逊于 AVL | 节点内二分后 I/O 极少 |
|
||
| 插入/删除 | 旋转较多 | 旋转较少 + 着色 | 分裂/合并,页级操作 |
|
||
| 范围扫描 | 好 | 好 | **最好**(B+ 叶子链) |
|
||
| 典型场景 | 内存、查多改少 | 通用有序容器 | 外存索引/数据库 |
|
||
|
||
---
|
||
|
||
# 什么时候选谁?
|
||
|
||
* **内存、查多改少、追求极低高度** → 选 **AVL**。
|
||
* **内存、查改都多、工程通用与易用性** → 选 **红黑树**(标准库同款)。
|
||
* **磁盘/SSD、大数据、范围查询** → 选 **B+ 树 / B-Tree**(页友好,极少 I/O)。
|
||
|
||
---
|
||
|
||
如果你愿意,我可以再给你:
|
||
|
||
1. **AVL 和红黑树的旋转小抄**(带 4 种失衡示意);
|
||
2. **B+ 树节点尺寸推算**(给定页大小、键宽/指针宽,估算分支因子与高度);
|
||
3. 一段**C++ 伪代码/最小实现骨架**帮助你在题目里自写平衡树。
|