一、AVL树的定义
1.1 平衡因子
平衡因子 = 左子树高度 - 右子树高度
AVL树要求所有节点的平衡因子只能是 -1、0、1。
text
节点高度:从该节点到最远叶子节点的边数
空树高度:-1 或 0(不同定义,本文用-1)
1.2 为什么需要平衡
普通BST插入有序序列时会退化:
text
插入:1,2,3,4,5
退化后:
1
\
2
\
3
\
4
\
5
查找效率 O(n)
AVL树通过旋转保持平衡,查找效率始终 O(log n)。
二、节点结构
c
typedef struct AVLNode {
int data;
struct AVLNode *left;
struct AVLNode *right;
int height; // 节点高度
} AVLNode, *AVLTree;
2.1 辅助函数
c
// 获取节点高度
int getHeight(AVLNode *node) {
return node == NULL ? -1 : node->height;
}
// 计算平衡因子
int getBalanceFactor(AVLNode *node) {
if (node == NULL) return 0;
return getHeight(node->left) - getHeight(node->right);
}
// 更新节点高度
void updateHeight(AVLNode *node) {
if (node == NULL) return;
int leftH = getHeight(node->left);
int rightH = getHeight(node->right);
node->height = (leftH > rightH ? leftH : rightH) + 1;
}
三、四种旋转调整
3.1 右旋(LL型)
触发条件:平衡因子 > 1 且 左子树的平衡因子 ≥ 0
text
不平衡节点: A
左孩子: B
A B
/ \ / \
B C 右旋→ D A
/ \ / \
D E E C
c
AVLNode* rightRotate(AVLNode *y) {
AVLNode *x = y->left;
AVLNode *T2 = x->right;
// 旋转
x->right = y;
y->left = T2;
// 更新高度
updateHeight(y);
updateHeight(x);
return x;
}
3.2 左旋(RR型)
触发条件:平衡因子 < -1 且 右子树的平衡因子 ≤ 0
text
不平衡节点: A
右孩子: B
A B
/ \ / \
C B 左旋→ A E
/ \ / \
D E C D
c
AVLNode* leftRotate(AVLNode *y) {
AVLNode *x = y->right;
AVLNode *T2 = x->left;
// 旋转
x->left = y;
y->right = T2;
// 更新高度
updateHeight(y);
updateHeight(x);
return x;
}
3.3 左右旋(LR型)
触发条件:平衡因子 > 1 且 左子树的平衡因子 < 0
text
步骤1:对左孩子左旋
步骤2:对当前节点右旋
A A C
/ \ / \ / \
B C 左旋→ C C 右旋→ B A
/ \ / / \ \
D E B D E C
/ \
D E
c
AVLNode* leftRightRotate(AVLNode *node) {
node->left = leftRotate(node->left);
return rightRotate(node);
}
3.4 右左旋(RL型)
触发条件:平衡因子 < -1 且 右子树的平衡因子 > 0
text
步骤1:对右孩子右旋
步骤2:对当前节点左旋
c
AVLNode* rightLeftRotate(AVLNode *node) {
node->right = rightRotate(node->right);
return leftRotate(node);
}
四、插入操作
c
AVLNode* insert(AVLNode *root, int value) {
// 1. 普通BST插入
if (root == NULL) {
AVLNode *newNode = (AVLNode*)malloc(sizeof(AVLNode));
newNode->data = value;
newNode->left = NULL;
newNode->right = NULL;
newNode->height = 0;
return newNode;
}
if (value < root->data) {
root->left = insert(root->left, value);
} else if (value > root->data) {
root->right = insert(root->right, value);
} else {
return root; // 重复值不插入
}
// 2. 更新高度
updateHeight(root);
// 3. 计算平衡因子,判断是否需要旋转
int balance = getBalanceFactor(root);
// 左子树过高
if (balance > 1) {
if (value < root->left->data) {
// LL型
return rightRotate(root);
} else {
// LR型
return leftRightRotate(root);
}
}
// 右子树过高
if (balance < -1) {
if (value > root->right->data) {
// RR型
return leftRotate(root);
} else {
// RL型
return rightLeftRotate(root);
}
}
return root;
}
五、完整代码演示
c
#include <stdio.h>
#include <stdlib.h>
typedef struct AVLNode {
int data;
struct AVLNode *left;
struct AVLNode *right;
int height;
} AVLNode, *AVLTree;
int getHeight(AVLNode *node) {
return node == NULL ? -1 : node->height;
}
int getBalanceFactor(AVLNode *node) {
if (node == NULL) return 0;
return getHeight(node->left) - getHeight(node->right);
}
void updateHeight(AVLNode *node) {
if (node == NULL) return;
int leftH = getHeight(node->left);
int rightH = getHeight(node->right);
node->height = (leftH > rightH ? leftH : rightH) + 1;
}
AVLNode* rightRotate(AVLNode *y) {
AVLNode *x = y->left;
AVLNode *T2 = x->right;
x->right = y;
y->left = T2;
updateHeight(y);
updateHeight(x);
return x;
}
AVLNode* leftRotate(AVLNode *y) {
AVLNode *x = y->right;
AVLNode *T2 = x->left;
x->left = y;
y->right = T2;
updateHeight(y);
updateHeight(x);
return x;
}
AVLNode* leftRightRotate(AVLNode *node) {
node->left = leftRotate(node->left);
return rightRotate(node);
}
AVLNode* rightLeftRotate(AVLNode *node) {
node->right = rightRotate(node->right);
return leftRotate(node);
}
AVLNode* insert(AVLNode *root, int value) {
if (root == NULL) {
AVLNode *newNode = (AVLNode*)malloc(sizeof(AVLNode));
newNode->data = value;
newNode->left = NULL;
newNode->right = NULL;
newNode->height = 0;
return newNode;
}
if (value < root->data) {
root->left = insert(root->left, value);
} else if (value > root->data) {
root->right = insert(root->right, value);
} else {
return root;
}
updateHeight(root);
int balance = getBalanceFactor(root);
// LL
if (balance > 1 && value < root->left->data) {
return rightRotate(root);
}
// RR
if (balance < -1 && value > root->right->data) {
return leftRotate(root);
}
// LR
if (balance > 1 && value > root->left->data) {
return leftRightRotate(root);
}
// RL
if (balance < -1 && value < root->right->data) {
return rightLeftRotate(root);
}
return root;
}
void inorder(AVLNode *root) {
if (root == NULL) return;
inorder(root->left);
printf("%d ", root->data);
inorder(root->right);
}
void preorder(AVLNode *root) {
if (root == NULL) return;
printf("%d ", root->data);
preorder(root->left);
preorder(root->right);
}
void printTree(AVLNode *root, int level) {
if (root == NULL) return;
printTree(root->right, level + 1);
for (int i = 0; i < level; i++) printf(" ");
printf("%d(h=%d,bf=%d)\n", root->data, root->height, getBalanceFactor(root));
printTree(root->left, level + 1);
}
int main() {
AVLNode *root = NULL;
printf("=== 插入序列: 10, 20, 30, 40, 50, 25 ===\n");
int values[] = {10, 20, 30, 40, 50, 25};
for (int i = 0; i < 6; i++) {
root = insert(root, values[i]);
printf("\n插入 %d 后:\n", values[i]);
printf("中序遍历: ");
inorder(root);
printf("\n");
printf("树结构:\n");
printTree(root, 0);
}
return 0;
}
运行结果:
text
=== 插入序列: 10, 20, 30, 40, 50, 25 ===
插入 10 后:
中序遍历: 10
树结构:
10(h=0,bf=0)
插入 20 后:
中序遍历: 10 20
树结构:
20(h=1,bf=-1)
10(h=0,bf=0)
插入 30 后:
中序遍历: 10 20 30
树结构:
30(h=1,bf=-1)
20(h=0,bf=0)
10(h=0,bf=0)
插入 40 后:
中序遍历: 10 20 30 40
树结构:
40(h=2,bf=-2)
30(h=1,bf=-1)
20(h=0,bf=0)
10(h=0,bf=0)
插入 50 后:
中序遍历: 10 20 30 40 50
树结构:
50(h=2,bf=-2)
40(h=1,bf=-1)
30(h=0,bf=0)
20(h=0,bf=0)
10(h=0,bf=0)
插入 25 后:
中序遍历: 10 20 25 30 40 50
树结构:
50(h=2,bf=-1)
40(h=1,bf=1)
30(h=0,bf=0)
25(h=0,bf=0)
20(h=1,bf=0)
10(h=0,bf=0)
六、旋转判断总结
| 平衡因子 | 子树平衡因子 | 类型 | 操作 |
|---|---|---|---|
| >1 | ≥0 | LL | 右旋 |
| >1 | <0 | LR | 左右旋 |
| <-1 | ≤0 | RR | 左旋 |
| <-1 | >0 | RL | 右左旋 |
记忆口诀:
-
LL:左边太重,右旋
-
RR:右边太重,左旋
-
LR:左子树的右边太重,先左旋左子,再右旋
-
RL:右子树的左边太重,先右旋右子,再左旋
七、复杂度分析
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
| 查找 | O(log n) | 树高始终 log n |
| 插入 | O(log n) | 查找位置 + 旋转(常数次) |
| 删除 | O(log n) | 类似插入 |
AVL树的树高严格控制在 log n 级别,性能稳定。
八、AVL树 vs 普通BST
| 对比项 | 普通BST | AVL树 |
|---|---|---|
| 最坏查找 | O(n) | O(log n) |
| 插入复杂度 | O(log n)平均 | O(log n) |
| 实现复杂度 | 简单 | 复杂(需要旋转) |
| 额外开销 | 无 | 每个节点存储高度 |
| 适用场景 | 数据随机 | 数据有序或对性能要求高 |
九、小结
这一篇我们学习了AVL树:
| 要点 | 说明 |
|---|---|
| 平衡因子 | 左高-右高,取值-1,0,1 |
| LL | 右旋 |
| RR | 左旋 |
| LR | 左旋左子 + 右旋 |
| RL | 右旋右子 + 左旋 |
| 插入 | BST插入 + 更新高度 + 旋转平衡 |
核心思想:在BST插入后,从插入点向上检查平衡因子,发现不平衡就旋转调整。
下一篇我们讲红黑树(原理与C语言模拟)。
十、思考题
-
为什么AVL树的高度平衡能保证查找效率O(log n)?
-
在LR旋转中,为什么需要先左旋再右旋?直接右旋行不行?
-
如果插入序列是
30,20,10,会触发哪种旋转?画图说明。 -
尝试实现AVL树的删除操作。
欢迎在评论区讨论你的答案。