📖 故事背景
森林里的猴王要选接班人,他设计了一个特别的选拔规则:
- 每只猴子按实力排名(数字代表实力大小)
- 排名树 必须满足:任意猴子的左子树和右子树高度差 ≤ 1
- 违反规则时,需要立即通过「旋转」调整
🌳 为什么需要平衡?
普通二叉搜索树可能退化成链表(比如按顺序插入1,2,3,4),查找效率从O(log n)降到O(n)。AVL树通过自平衡确保高效操作!
🧠 AVL核心原理
java
objectivec
class AVLNode {
int value; // 猴子的实力值
AVLNode left; // 左子树(实力小的猴子)
AVLNode right; // 右子树(实力强的猴子)
int height; // 关键!记录子树高度
AVLNode(int val) {
this.value = val;
this.height = 1; // 新节点初始高度为1
}
}
⚖️ 平衡因子(Balance Factor)
java
scss
int getBalance(AVLNode node) {
if (node == null) return 0;
// 左子树高度 - 右子树高度
return height(node.left) - height(node.right);
}
平衡规则 :|BF| ≤ 1
当BF > 1
→ 左子树太高
当BF < -1
→ 右子树太高
🔄 四大旋转绝招(图解+代码)
场景1:左左失衡(LL)
🐒 问题 :新猴子插在左子树的左侧
🛠️ 解法 :右旋
java
ini
AVLNode rightRotate(AVLNode y) {
AVLNode x = y.left;
AVLNode T2 = x.right;
// 旋转核心操作
x.right = y;
y.left = T2;
// 更新高度(先更新下层节点y)
y.height = 1 + Math.max(height(y.left), height(y.right));
x.height = 1 + Math.max(height(x.left), height(x.right));
return x; // 返回新的根节点
}
upload.wikimedia.org/wikipedia/c...
场景2:右右失衡(RR)
🐒 问题 :新猴子插在右子树的右侧
🛠️ 解法 :左旋
java
ini
AVLNode leftRotate(AVLNode x) {
AVLNode y = x.right;
AVLNode T2 = y.left;
// 旋转核心操作
y.left = x;
x.right = T2;
// 更新高度
x.height = 1 + Math.max(height(x.left), height(x.right));
y.height = 1 + Math.max(height(y.left), height(y.right));
return y; // 返回新的根节点
}
upload.wikimedia.org/wikipedia/c...
场景3:左右失衡(LR)
🐒 问题 :新猴子插在左子树的右侧
🛠️ 解法 :先左旋再右旋
java
scss
// 1. 对左子树左旋
node.left = leftRotate(node.left);
// 2. 对当前节点右旋
return rightRotate(node);
upload.wikimedia.org/wikipedia/c...
场景4:右左失衡(RL)
🐒 问题 :新猴子插在右子树的左侧
🛠️ 解法 :先右旋再左旋
java
scss
// 1. 对右子树右旋
node.right = rightRotate(node.right);
// 2. 对当前节点左旋
return leftRotate(node);
upload.wikimedia.org/wikipedia/c...
🚀 完整插入操作(Java实现)
java
scss
public class AVLTree {
AVLNode root;
// 插入入口
void insert(int value) {
root = insertNode(root, value);
}
private AVLNode insertNode(AVLNode node, int value) {
// 1. 标准BST插入
if (node == null) return new AVLNode(value);
if (value < node.value)
node.left = insertNode(node.left, value);
else if (value > node.value)
node.right = insertNode(node.right, value);
else
return node; // 值相等不插入
// 2. 更新当前节点高度
node.height = 1 + Math.max(height(node.left), height(node.right));
// 3. 检查平衡因子
int balance = getBalance(node);
// 4. 处理四种失衡情况
// 左左失衡
if (balance > 1 && value < node.left.value)
return rightRotate(node);
// 右右失衡
if (balance < -1 && value > node.right.value)
return leftRotate(node);
// 左右失衡
if (balance > 1 && value > node.left.value) {
node.left = leftRotate(node.left);
return rightRotate(node);
}
// 右左失衡
if (balance < -1 && value < node.right.value) {
node.right = rightRotate(node.right);
return leftRotate(node);
}
return node; // 平衡则直接返回
}
// 辅助方法:获取节点高度
int height(AVLNode node) {
return node == null ? 0 : node.height;
}
}
💡 关键总结
特性 | 说明 |
---|---|
平衡定义 | 任意节点左右子树高度差 ≤ 1 |
核心优势 | 保证最坏情况下操作复杂度为O(log n) |
旋转操作 | LL/RR(单旋), LR/RL(双旋) |
时间复杂度 | 插入/删除/查找均为O(log n) |
应用场景 | 数据库索引、实时系统等需要稳定性能的场景 |
🌟 猴王的智慧:通过动态平衡,AVL树确保所有猴子(节点)都能被快速找到,避免出现"一支独大"的不公平现象!
通过这个有趣的故事和清晰的代码示例,相信你已经掌握了AVL树的精髓!试着画一画不同旋转场景的树形图,理解会更深刻哦~