红黑树(Red-Black Tree)详解
自平衡二叉查找树的五条核心性质、插入与删除的旋转/变色逻辑,以及与普通 BST 的复杂度对比;算法课程笔记整理版
红黑树 (Red-Black Tree) 详解
1. 背景与概念 (Background & Concept)
红黑树(Red-Black Tree)是一种自平衡二叉查找树(Self-balancing Binary Search Tree)。它在计算机科学中被广泛应用,能在最坏情况下保证基本动态集合操作(如插入、删除、查找)的时间复杂度为 。
相比于普通的二叉查找树(BST),红黑树通过在每个节点上增加一个存储位来表示节点的颜色(红色或黑色),并通过对任何一条从根到叶子的简单路径上各个节点的颜色进行约束,确保没有一条路径会比其他路径长出两倍,因而是近似平衡的。
2. 核心性质 (Core Properties)
红黑树必须满足以下 5条严格的性质(这是红黑树保持平衡的基石):
- 节点颜色:每个节点要么是红色,要么是黑色。
- 根节点:根节点必须是黑色。
- 叶子节点(NIL):所有叶子节点(即空节点 NIL)都是黑色。(注意:红黑树中的叶子节点指的是那些不包含实际数据的虚拟空节点)。
- 红色节点限制:如果一个节点是红色的,则它的两个子节点都必须是黑色。(即:不能有两个连续的红色节点相连)。
- 黑高(Black-Height)一致性:从任一节点到其每个叶子节点的所有简单路径都包含相同数目的黑色节点。
直观理解:性质4和性质5共同决定了红黑树的平衡性。因为路径上不能有连续的红节点,且每条路径的黑节点数量必须相同,所以最短的可能路径(全黑)和最长的可能路径(红黑交替)之间的长度比最多是 1:2。
3. 为什么需要红黑树?(对比 AVL 树)
虽然 AVL 树也是自平衡二叉查找树,且比红黑树更加严格平衡(左右子树高度差不超过1),但红黑树在工程实践中更为常用。
| 特性 | 红黑树 (Red-Black Tree) | AVL 树 |
|---|---|---|
| 平衡程度 | 近似平衡(最长路径不超过最短路径的2倍) | 严格平衡(高度差 ) |
| 查找效率 | ,略慢于 AVL | ,极快 |
| 插入效率 | ,最多只需 2次 旋转 | ,最多只需 2次 旋转 |
| 删除效率 | ,最多只需 3次 旋转 | ,可能需要 次旋转 |
| 适用场景 | 频繁插入、删除操作的动态数据集合 | 查找极其频繁,插入删除较少的静态数据集合 |
结论:红黑树在插入和删除时的旋转次数有严格的常数上限(最多3次),维护平衡的开销较小,因此在综合性能(查找、插入、删除)上表现更加稳定。
4. 核心操作 (Core Operations)
为了在插入和删除后维持红黑树的5条性质,主要依赖两种操作:变色(Recoloring) 和 旋转(Rotation)。
4.1 旋转 (Rotation)
旋转是一种局部操作,用于在不破坏二叉查找树性质(左小右大)的前提下,改变树的拓扑结构。
- 左旋(Left Rotate):将节点向左下方移动,其右子节点变为它的父节点。
- 右旋(Right Rotate):将节点向右下方移动,其左子节点变为它的父节点。
4.2 插入 (Insertion)
- 初始插入:将新节点按照普通二叉查找树的规则插入。
- 初始颜色:新插入的节点一律染成红色。(原因:如果染成黑色,必然会破坏性质5(黑高一致性),处理起来极其麻烦;染成红色只可能破坏性质4(不能有连续红节点),可以通过较少的操作修复)。
- 修复失衡(Fixup):如果插入节点的父节点也是红色(破坏了性质4),则需要根据**叔叔节点(父节点的兄弟节点)**的颜色分为三种情况处理:
- 情况1:叔叔节点是红色。
- 策略:父节点和叔叔节点变黑,祖父节点变红。然后将当前节点指针指向祖父节点,继续向上递归处理。
- 情况2:叔叔节点是黑色,且当前节点是右孩子(内侧)。
- 策略:以父节点为支点进行左旋,转化为情况3。
- 情况3:叔叔节点是黑色,且当前节点是左孩子(外侧)。
- 策略:父节点变黑,祖父节点变红,然后以祖父节点为支点进行右旋。
- 情况1:叔叔节点是红色。
4.3 删除 (Deletion)
删除操作是红黑树中最复杂的部分。
- 按照普通 BST 的规则删除节点。
- 如果被删除的节点是红色,树的红黑性质不受影响,直接结束。
- 如果被删除的节点是黑色,会破坏性质5(导致某条路径少了一个黑节点),此时需要引入“双重黑色(Double Black)”的概念,并根据兄弟节点及其子节点的颜色,分为4种复杂情况进行“变色+旋转”修复,最终将“缺失的黑色”向上推或者通过旋转消化掉。
5. 实际应用场景 (Applications)
由于红黑树优秀的综合性能,它在底层系统和基础库中无处不在:
- C++ STL:
std::map,std::set,std::multimap,std::multiset的底层实现通常都是红黑树。 - Java:
java.util.TreeMap,java.util.TreeSet,以及 JDK 1.8 之后HashMap中的链表长度大于8时会转化为红黑树。 - Linux 内核:
- CFS (Completely Fair Scheduler):完全公平调度器使用红黑树来管理进程的运行时间(虚拟运行时间
vruntime作为 Key)。 - epoll:使用红黑树来管理和维护注册的 socket 文件描述符。
- 内存管理:如 VMA (Virtual Memory Area) 的管理。
- CFS (Completely Fair Scheduler):完全公平调度器使用红黑树来管理进程的运行时间(虚拟运行时间
6. 总结 (Summary)
红黑树通过牺牲严格的平衡性(允许高度差达到2倍),换取了在插入和删除时更少的旋转次数。它是一种在查找、插入、删除三个操作上都达到了极佳平衡的数据结构,时间复杂度稳定在 ,是工程界最受欢迎的动态查找树。
DISCUSS