📚 故事背景:图书馆的烦恼与智能书架
小白是图书管理员,管理着按书号排序的书架(普通二叉搜索树)。
问题:新书《C++ Primer》(书号3)、《算法导论》(书号5)、《Java核心》(书号1)依次上架后,书架退化成“单链表”:
Copy
1 (《Java核心》)
\
3 (《C++ Primer》)
\
5 (《算法导论》)
找《算法导论》要翻3次!效率从O(log n)退化成O(n)
馆长说:“我们需要自动调整平衡的智能书架!”于是诞生了两种方案:
⚖️ 一、AVL树:强迫症的完美平衡
想象一个严格的书架:每层左右高度差必须≤1,否则立即旋转调整
核心原理:
-
平衡因子:每个节点记录
左子树高 - 右子树高,值只能为-1, 0, 1 -
旋转调整:插入/删除后检查平衡因子,若绝对值>1则通过旋转恢复平衡
旋转的四种情况(以插入导致失衡为例) :
| 失衡类型 | 触发条件 | 旋转方式 |
|---|---|---|
| LL型 | 左子树的左子树插入导致失衡 | 单次右旋 |
| RR型 | 右子树的右子树插入导致失衡 | 单次左旋 |
| LR型 | 左子树的右子树插入导致失衡 | 先左旋再右旋 |
| RL型 | 右子树的左子树插入导致失衡 | 先右旋再左旋 |
Java代码实现旋转(以LL型右旋为例):
java
Copy
class AVLNode {
int key;
AVLNode left, right;
int height; // 节点高度
AVLNode(int key) {
this.key = key;
this.height = 1;
}
}
private AVLNode rightRotate(AVLNode y) {
AVLNode x = y.left; // 1. 获取左孩子x
AVLNode T2 = x.right; // 2. 保存x的右子树T2
x.right = y; // 3. y成为x的右孩子
y.left = T2; // 4. T2成为y的左孩子
// 更新高度
y.height = Math.max(height(y.left), height(y.right)) + 1;
x.height = Math.max(height(x.left), height(x.right)) + 1;
return x; // 5. 返回新的根节点x
}
AVL树缺点:维护成本高,频繁插入/删除时旋转次数多
🔴 二、红黑树:实用主义的近似平衡
想象一个更聪明的书架:允许左右稍有不均(最长路径≤2倍最短路径),用颜色规则约束调整
五大核心规则:
- 节点非红即黑
- 根节点必须黑
- 叶子节点(NIL空节点)视为黑
- 红节点的子节点必须黑(不能有连续红节点)
- 从任意节点到叶子路径的黑节点数相同(黑高一致)
插入调整策略(新节点默认红色):
Java代码实现插入调整:
java
Copy
class RedBlackNode {
int key;
RedBlackNode left, right, parent;
boolean isRed; // 颜色标记
RedBlackNode(int key) {
this.key = key;
this.isRed = true; // 新节点默认红色
}
}
private void fixInsert(RedBlackNode node) {
while (node.parent != null && node.parent.isRed) {
if (parent == grandparent.left) {
RedBlackNode uncle = grandparent.right;
if (uncle != null && uncle.isRed) { // 情况1:叔叔为红
parent.isRed = false;
uncle.isRed = false;
grandparent.isRed = true;
node = grandparent;
} else { // 情况2:叔叔为黑
if (node == parent.right) { // LR型
leftRotate(parent);
node = parent;
parent = node.parent;
}
parent.isRed = false; // 父变黑
grandparent.isRed = true; // 祖父变红
rightRotate(grandparent); // 右旋祖父
}
}
// 对称情况(省略)
}
root.isRed = false; // 根始终为黑
}
⚔️ 三、AVL vs 红黑树:如何选择?
| 特性 | AVL树 | 红黑树 |
|---|---|---|
| 平衡要求 | 严格(高度差≤1) | 宽松(最长路径≤2倍最短) |
| 旋转频率 | 高(维护成本大) | 低(O(1)~O(log n))1 |
| 查询速度 | 更快(树更矮) | 稍慢(树更高) |
| 适用场景 | 读多写少(如数据库索引) | 写频繁(如Map、进程调度) |
| 典型应用 | Windows NT内核 | Java HashMap、Linux进程管理15 |
📌 核心结论:
要极致查询速度 → AVL树
要高并发插入删除 → 红黑树
💡 四、为什么工程更爱红黑树?
红黑树的优势本质是用颜色规则模拟2-3-4树:
-
红色节点 = 与父节点组成2-3-4树中的3节点或4节点
-
黑色节点 = 普通2节点
这种设计让红黑树在插入时最多2次旋转,删除时最多3次旋转,而AVL树可能需O(log n)次旋转
🌟 终极总结
-
普通二叉树:无序插入会退化成链表,查找效率O(n)
-
AVL树:旋转严格维护平衡,适合静态数据集
-
红黑树:用红黑规则和少量旋转实现近似平衡,适合动态数据
面试口诀:
查询多用AVL,增删多用红黑;
红黑五大律,三旋定乾坤!
试着运行代码,感受旋转如何让“书架”自动保持平衡。需要深入任何细节(如删除调整),欢迎继续追问!