红黑树
红黑树由 鲁道夫·贝尔 (Rudolf Bayer)在1972年提出,最初叫"对称二叉B树 "(Symmetric Binary B-Trees)。
后来在1978年,莱昂纳德·吉布斯 (Leonard Guibas)和 罗伯特·塞奇威克 (Robert Sedgewick)在研究中引入了"颜色"的概念来描述节点状态,并正式命名为"Red-Black Tree"。
名字的由来与"奇葩"之处
红黑树的命名听起来很酷,甚至有点像某种神秘组织或武侠门派,比如"红莲教""黑风寨"......但实际上,它的名字毫无浪漫色彩,也和红与黑的哲学无关。
他们选择"红"和"黑",并不是因为:
- 红色代表危险、黑色代表安全,
- 或者红是革命、黑是镇压,
- 也不是像围棋那样象征阴阳...... 它的名字来源其实非常"工程"、非常"现实":
红黑只是用来标记节点的两种颜色,选"红"和"黑"仅仅是因为------当年所用的打印设备能很好地区分这两种颜色。 但正是这种"不讲究"的命名,反而体现了计算机科学中一种典型的风格:功能优先,名字随意,能用就行。
所以,"红黑"本质上就像"A状态"和"B状态",换成"绿黄树""上下树""01树"也未尝不可,只是"红黑"这个叫法被历史保留了下来。
这也算是技术史上的一个有趣花絮,让红黑树不仅是一棵平衡树,还带点"冷幽默"的色彩。
定义
红黑树(Red-Black Tree)是一种自平衡的二叉搜索树 (Self-balancing Binary Search Tree),它在普通二叉搜索树的基础上,通过为每个节点添加一个"颜色"属性(红色或黑色),并遵守一组特定的规则,来确保树的整体高度始终保持在对数级别,从而保证查找、插入、删除操作的时间复杂度为 O(log n)。
五大性质(规则)
每个节点必须满足以下五条性质:
- 每个节点是红色或黑色。
- 根节点是黑色。
- 每个叶子节点(NIL 节点,即空指针)是黑色 。
(这些是逻辑上的叶子,实际实现中常以null或哨兵节点表示) - 红色节点的子节点必须是黑色(即不能有两个连续的红色节点,父子不能同红)。
- 从任一节点到其每个叶子的所有路径上,包含相同数目的黑色节点(称为"黑高"一致)。
这五条规则共同作用,使得红黑树在最坏情况下也能保持近似平衡。
核心原理
-
平衡机制 :
不像 AVL 树那样严格保持左右子树高度差不超过 1,红黑树是弱平衡 的。
它通过颜色标记和旋转操作,在插入和删除后进行局部调整,防止树退化成链表。
-
插入与删除的调整策略:
- 插入新节点默认为红色 :
因为插入黑色会破坏"黑高"规则(第5条),而插入红色只可能违反"不能连续红"(第4条),更容易修复。 - 修复操作包括 :
- 变色(recolor)
- 左旋(left rotate)
- 右旋(right rotate)
- 删除后也可能需要调整 :
删除黑色节点会破坏黑高,需要通过兄弟节点的调整(如借节点、合并)来修复。
- 插入新节点默认为红色 :
-
旋转操作:
- 左旋:将右子树"提上来",原节点成为其左子节点。
- 右旋 :将左子树"提上来",原节点成为其右子节点。
旋转不改变中序遍历结果(仍保持二叉搜索树性质),但能改变结构以恢复平衡。
红黑树的特点
| 特点 | 说明 |
|---|---|
| 时间复杂度 | 查找、插入、删除均为 O(log n),最坏情况性能稳定。 |
| 平衡性 | 弱平衡,最长路径不超过最短路径的两倍。 |
| 效率 | 插入和删除的调整次数通常较少,比 AVL 树更"宽松",适合频繁修改的场景。 |
| 实现复杂度 | 比普通二叉搜索树复杂,但比 AVL 树稍简单(调整次数少)。 |
实际实现中的关键设计
-
节点结构通常包含三个指针:
left:左孩子right:右孩子parent:父节点
-
parent 指针的作用:
- 可以快速访问父节点、兄弟节点、叔节点、祖父节点。
- 在插入/删除修复过程中,无需从根节点重新搜索路径。
- 实现旋转、变色等操作更高效。
-
为什么加 parent 指针?
- 虽然理论上可以不用,但会极大增加查找父节点的成本。
- 实际工程中(如 C++ STL 的
map/set、Linux 内核的进程调度、Java 的TreeMap)都使用了 parent 指针。 - 是典型的 空间换时间 设计。
红黑树 vs 其他平衡树
| 对比项 | 红黑 树 | AVL 树 | 普通 BST |
|---|---|---|---|
| 平衡严格性 | 弱平衡 | 严格平衡 | 不平衡 |
| 插入/删除性能 | 更快(调整少) | 较慢(可能多次旋转) | 快(不调整) |
| 查找性能 | 稍慢(树稍高) | 更快(树更矮) | 最坏 O(n) |
| 适用场景 | 频繁插入删除 | 频繁查找 | 简单场景 |
| 实现复杂度 | 中等 | 较高 | 简单 |
应用场景
- C++ STL 中的
std::map、std::set - Java 中的
TreeMap、TreeSet - Linux 内核中的
CFS(完全公平调度器)使用红黑树管理进程 - 数据库索引结构(某些实现)
- 网络路由表、事件调度器等需要高效动态维护有序数据的系统
总结一句话:
红黑树是一种高效、实用的自平衡二叉搜索树,通过颜色规则和旋转操作维持近似平衡,实际实现中通常包含 parent 指针以支持快速访问父节点和兄弟节点,是"空间换时间"的经典体现,广泛应用于现代系统和库中。