一句话总结:
红黑树就像一个有严格家规的二叉搜索树——通过颜色标记和旋转操作保持「相对平衡」,防止退化成瘸腿链表,让查找、插入、删除都能在 O(log n) 时间内搞定!
一、红黑树的「家规」
红黑树必须遵守以下五条规则:
- 颜色规则:每个节点非红即黑
- 根节点规则:根必须是黑
- 叶子规则:所有叶子(NIL节点)都是黑
- 红节点规则:红节点的子节点必须全黑(不能有连续红)
- 黑高规则:从任一节点到其叶子的路径,黑节点数量相同
违反家规的后果:通过旋转和变色重新平衡(后文代码演示)
二、为什么用红黑树?
普通二叉搜索树(BST)在插入有序数据时会退化成链表(查询变 O(n)),而红黑树通过自平衡机制:
- 保证平衡:最坏情况下也能保持
O(log n)的查询效率 - 插入/删除高效:比 AVL 树宽松的平衡条件,减少旋转次数
- 广泛实用:适合频繁插入删除的场景(如 Linux 内核调度、Java 的 TreeMap)
对比其他结构:
| 数据结构 | 插入/删除 | 查找 | 平衡性要求 |
|---|---|---|---|
| 普通 BST | 可能退化成 O(n) | 可能退化成 O(n) | 无 |
| 红黑树 | O(log n) | O(log n) | 相对平衡 |
| AVL 树 | O(log n) | O(log n) | 严格平衡 |
三、Kotlin 代码实现关键部分
1. 红黑树节点定义
private enum class Color { RED, BLACK }
class RBNode<T : Comparable<T>>(
var data: T,
var color: Color = Color.RED, // 新节点默认红色(便于调整)
var parent: RBNode<T>? = null,
var left: RBNode<T>? = null,
var right: RBNode<T>? = null
)
2. 插入后修复平衡(核心!)
private fun fixAfterInsertion(node: RBNode<T>) {
var current = node
while (current.parent?.color == Color.RED) { // 父节点是红,需要调整
val parent = current.parent!!
val grandParent = parent.parent!!
if (parent == grandParent.left) { // 父节点是祖父的左孩子
val uncle = grandParent.right
if (uncle?.color == Color.RED) { // 情况1:叔叔是红
// 父和叔变黑,祖父变红,current上移到祖父
parent.color = Color.BLACK
uncle.color = Color.BLACK
grandParent.color = Color.RED
current = grandParent
} else {
if (current == parent.right) { // 情况2:current是右孩子
// 左旋父节点,转化为情况3
current = parent
rotateLeft(current)
}
// 情况3:current是左孩子
parent.color = Color.BLACK
grandParent.color = Color.RED
rotateRight(grandParent)
}
} else { // 对称处理(父节点是祖父的右孩子)
// 类似上述代码,左右旋转方向相反
}
}
root?.color = Color.BLACK // 确保根节点为黑
}
3. 左旋操作(右旋对称)
private fun rotateLeft(node: RBNode<T>) {
val rightChild = node.right!!
// 处理node的右子树的左孩子
node.right = rightChild.left
rightChild.left?.parent = node
// 处理node的父节点
rightChild.parent = node.parent
if (node.parent == null) {
root = rightChild
} else if (node == node.parent!!.left) {
node.parent!!.left = rightChild
} else {
node.parent!!.right = rightChild
}
// 更新node和右孩子的父子关系
rightChild.left = node
node.parent = rightChild
}
四、红黑树的实际应用场景
- 数据库索引:B+树的变种常用于数据库,但红黑树适合内存索引
- 语言标准库:Java的
TreeMap、C++的std::map - 实时系统:保证最坏情况下的操作时间
- 文件系统:如 Ext3 的目录索引
五、红黑树 vs 哈希表
| 特性 | 红黑树 | 哈希表 |
|---|---|---|
| 有序性 | 支持范围查询(中序遍历) | 无序 |
| 最坏时间复杂度 | O(log n) | O(n)(哈希冲突时) |
| 内存开销 | 较高(存储颜色/指针) | 较低 |
六、总结口诀
红黑树,真强悍,
五条家规保平安。
红不连,黑同高,
旋转变色调平衡。
查删插,对数阶,
不怕数据顺序来。
若问哪里用它好?
有序操作快稳牢!