引言
在上一篇二叉搜索树(BST)的文章中,我们留下了一个关键问题:当数据以有序顺序插入时,BST 会退化成一个链表,查找效率从 O(log n) 跌落到 O(n)。
AVL 树 是历史上第一种自平衡二叉搜索树,由 Adelson-Velsky 和 Landis 于 1962 年提出(AVL 取自两人名字首字母)。它通过监控每个节点的平衡因子 ,在插入和删除后通过四种旋转操作 自动恢复平衡,确保树的高度始终保持在 O(log n),从而保证所有操作的时间复杂度稳定在 O(log n)。

第一部分:AVL 树的核心概念
一、平衡因子
平衡因子(Balance Factor,BF) 是 AVL 树最核心的概念:
平衡因子 = 左子树高度 - 右子树高度
BF ∈ {-1, 0, 1} → 平衡
BF < -1 或 BF > 1 → 失衡,需要旋转调整

二、高度计算
cpp
// 节点的高度 = max(左子树高度, 右子树高度) + 1
// 空节点高度为 0
int height(AVLNode* node) {
if (node == NULL) return 0;
return node->height;
}
int balanceFactor(AVLNode* node) {
if (node == NULL) return 0;
return height(node->left) - height(node->right);
}
void updateHeight(AVLNode* node) {
if (node == NULL) return;
int hl = height(node->left);
int hr = height(node->right);
node->height = (hl > hr ? hl : hr) + 1;
}
三、节点结构
cpp
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int ElemType;
typedef struct AVLNode {
ElemType data; // 数据域
struct AVLNode* left; // 左子节点
struct AVLNode* right; // 右子节点
int height; // 节点高度(关键!)
} AVLNode;
第二部分:四种旋转操作
当插入或删除导致某节点的平衡因子绝对值 > 1 时,需要旋转恢复平衡。根据失衡的形态,分为四种情况:
一、四种失衡形态总览

二、LL 型 → 右旋
形态 :在节点 A 的左子树的左子树插入导致失衡。

右旋代码实现:
cpp
AVLNode* rotateRight(AVLNode* A) {
AVLNode* B = A->left; // ① B = A 的左子
AVLNode* B_right = B->right; // 保存 B 的右子树
B->right = A; // ② B 的右子指向 A
A->left = B_right; // ③ A 的左子指向原来的 B_right
// ④ 更新高度(必须先更新 A,再更新 B)
updateHeight(A);
updateHeight(B);
return B; // ⑤ B 成为新的根
}
三、RR 型 → 左旋
形态 :在节点 A 的右子树的右子树插入导致失衡。与 LL 型镜像对称。

左旋代码实现:
cpp
AVLNode* rotateLeft(AVLNode* A) {
AVLNode* C = A->right;
AVLNode* C_left = C->left;
C->left = A;
A->right = C_left;
updateHeight(A);
updateHeight(C);
return C;
}
四、LR 型 → 先左旋再右旋
形态 :在节点 A 的左子树的右子树插入导致失衡。需要先对左子左旋,再对根右旋。

LR 型旋转代码:
cpp
AVLNode* rotateLR(AVLNode* A) {
A->left = rotateLeft(A->left); // 先对左子节点左旋
return rotateRight(A); // 再对 A 右旋
}
五、RL 型 → 先右旋再左旋
形态 :在节点 A 的右子树的左子树插入导致失衡。与 LR 型镜像对称。
cpp
AVLNode* rotateRL(AVLNode* A) {
A->right = rotateRight(A->right); // 先对右子节点右旋
return rotateLeft(A); // 再对 A 左旋
}
第三部分:插入操作
一、插入流程

二、完整插入代码
cpp
AVLNode* createNode(ElemType val) {
AVLNode* node = (AVLNode*)malloc(sizeof(AVLNode));
if (node == NULL) return NULL;
node->data = val;
node->left = NULL;
node->right = NULL;
node->height = 1; // 新节点高度为 1
return node;
}
AVLNode* avlInsert(AVLNode* root, ElemType key) {
// ===== 第一步:普通 BST 插入 =====
if (root == NULL) {
return createNode(key);
}
if (key < root->data) {
root->left = avlInsert(root->left, key);
} else if (key > root->data) {
root->right = avlInsert(root->right, key);
} else {
return root; // 不允许重复值,直接返回
}
// ===== 第二步:更新高度 =====
updateHeight(root);
// ===== 第三步:检查平衡并旋转 =====
int bf = balanceFactor(root);
// LL 型:左高 2,且新节点在左子树的左边
if (bf > 1 && key < root->left->data) {
return rotateRight(root);
}
// RR 型:右高 2,且新节点在右子树的右边
if (bf < -1 && key > root->right->data) {
return rotateLeft(root);
}
// LR 型:左高 2,且新节点在左子树的右边
if (bf > 1 && key > root->left->data) {
return rotateLR(root);
}
// RL 型:右高 2,且新节点在右子树的左边
if (bf < -1 && key < root->right->data) {
return rotateRL(root);
}
return root; // 没有失衡,直接返回
}
三、插入过程图解

第四部分:删除操作
一、删除流程

二、完整删除代码
cpp
// 找最小值节点
AVLNode* findMinNode(AVLNode* node) {
while (node->left != NULL) node = node->left;
return node;
}
AVLNode* avlDelete(AVLNode* root, ElemType key) {
// ===== 第一步:普通 BST 删除 =====
if (root == NULL) return NULL;
if (key < root->data) {
root->left = avlDelete(root->left, key);
} else if (key > root->data) {
root->right = avlDelete(root->right, key);
} else {
// 找到了要删除的节点
if (root->left == NULL || root->right == NULL) {
// 情况1&2:叶子或单子
AVLNode* temp = (root->left != NULL) ? root->left : root->right;
if (temp == NULL) {
temp = root;
root = NULL;
} else {
*root = *temp; // 拷贝内容
}
free(temp);
} else {
// 情况3:双子 → 找后继替换
AVLNode* successor = findMinNode(root->right);
root->data = successor->data;
root->right = avlDelete(root->right, successor->data);
}
}
// 如果树被删空了
if (root == NULL) return NULL;
// ===== 第二步:更新高度 =====
updateHeight(root);
// ===== 第三步:检查平衡并旋转 =====
int bf = balanceFactor(root);
int bfLeft = balanceFactor(root->left);
int bfRight = balanceFactor(root->right);
// LL 型:左高2,且左子平衡或左倾
if (bf > 1 && bfLeft >= 0) {
return rotateRight(root);
}
// LR 型:左高2,且左子右倾
if (bf > 1 && bfLeft < 0) {
return rotateLR(root);
}
// RR 型:右高2,且右子平衡或右倾
if (bf < -1 && bfRight <= 0) {
return rotateLeft(root);
}
// RL 型:右高2,且右子左倾
if (bf < -1 && bfRight > 0) {
return rotateRL(root);
}
return root;
}
删除 vs 插入的失衡判断差异:
| 操作 | LL 判断 | LR 判断 |
|---|---|---|
| 插入 | key < root->left->data |
key > root->left->data |
| 删除 | bfLeft >= 0 |
bfLeft < 0 |
插入时新节点位置已知,可以直接用 key 判断。删除时,需要根据子节点的平衡因子来判断具体的失衡类型。
第五部分:完整代码与测试
一、完整 AVL 树实现
cpp
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
typedef int ElemType;
typedef struct AVLNode {
ElemType data;
struct AVLNode* left;
struct AVLNode* right;
int height;
} AVLNode;
// ==================== 高度与平衡因子 ====================
int height(AVLNode* node) {
return (node == NULL) ? 0 : node->height;
}
int balanceFactor(AVLNode* node) {
if (node == NULL) return 0;
return height(node->left) - height(node->right);
}
void updateHeight(AVLNode* node) {
if (node == NULL) return;
int hl = height(node->left);
int hr = height(node->right);
node->height = (hl > hr ? hl : hr) + 1;
}
// ==================== 创建节点 ====================
AVLNode* createNode(ElemType val) {
AVLNode* node = (AVLNode*)malloc(sizeof(AVLNode));
if (node == NULL) return NULL;
node->data = val;
node->left = node->right = NULL;
node->height = 1;
return node;
}
// ==================== 四种旋转 ====================
AVLNode* rotateRight(AVLNode* A) {
AVLNode* B = A->left;
AVLNode* B_right = B->right;
B->right = A;
A->left = B_right;
updateHeight(A);
updateHeight(B);
return B;
}
AVLNode* rotateLeft(AVLNode* A) {
AVLNode* C = A->right;
AVLNode* C_left = C->left;
C->left = A;
A->right = C_left;
updateHeight(A);
updateHeight(C);
return C;
}
AVLNode* rotateLR(AVLNode* A) {
A->left = rotateLeft(A->left);
return rotateRight(A);
}
AVLNode* rotateRL(AVLNode* A) {
A->right = rotateRight(A->right);
return rotateLeft(A);
}
// ==================== 插入 ====================
AVLNode* avlInsert(AVLNode* root, ElemType key) {
if (root == NULL) return createNode(key);
if (key < root->data)
root->left = avlInsert(root->left, key);
else if (key > root->data)
root->right = avlInsert(root->right, key);
else
return root;
updateHeight(root);
int bf = balanceFactor(root);
if (bf > 1 && key < root->left->data) return rotateRight(root); // LL
if (bf < -1 && key > root->right->data) return rotateLeft(root); // RR
if (bf > 1 && key > root->left->data) return rotateLR(root); // LR
if (bf < -1 && key < root->right->data) return rotateRL(root); // RL
return root;
}
// ==================== 删除 ====================
AVLNode* findMinNode(AVLNode* node) {
while (node->left != NULL) node = node->left;
return node;
}
AVLNode* avlDelete(AVLNode* root, ElemType key) {
if (root == NULL) return NULL;
if (key < root->data)
root->left = avlDelete(root->left, key);
else if (key > root->data)
root->right = avlDelete(root->right, key);
else {
if (root->left == NULL || root->right == NULL) {
AVLNode* temp = root->left ? root->left : root->right;
if (temp == NULL) { temp = root; root = NULL; }
else { *root = *temp; }
free(temp);
} else {
AVLNode* successor = findMinNode(root->right);
root->data = successor->data;
root->right = avlDelete(root->right, successor->data);
}
}
if (root == NULL) return NULL;
updateHeight(root);
int bf = balanceFactor(root);
int bfLeft = balanceFactor(root->left);
int bfRight = balanceFactor(root->right);
if (bf > 1 && bfLeft >= 0) return rotateRight(root); // LL
if (bf > 1 && bfLeft < 0) return rotateLR(root); // LR
if (bf < -1 && bfRight <= 0) return rotateLeft(root); // RR
if (bf < -1 && bfRight > 0) return rotateRL(root); // RL
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 freeTree(AVLNode* root) {
if (root == NULL) return;
freeTree(root->left);
freeTree(root->right);
free(root);
}
// ==================== 测试 ====================
int main() {
AVLNode* root = NULL;
// 有序插入(最考验平衡能力)
printf("有序插入 1~7:\n");
for (int i = 1; i <= 7; i++) {
root = avlInsert(root, i);
printf("插入 %d → 前序:", i);
preorder(root);
printf(" (高度=%d)\n", height(root));
}
printf("\n中序遍历:");
inorder(root);
printf("\n");
// 删除测试
printf("\n删除 4:\n");
root = avlDelete(root, 4);
printf("前序:"); preorder(root);
printf(" (高度=%d)\n", height(root));
printf("中序:"); inorder(root);
printf("\n");
// 重复插入测试
printf("\n尝试插入重复值 3:\n");
root = avlInsert(root, 3);
printf("前序:"); preorder(root);
printf(" (高度=%d,未变化)\n", height(root));
freeTree(root);
return 0;
}
运行结果:

第六部分:性能分析
一、时间复杂度
| 操作 | BST(最坏) | AVL 树 |
|---|---|---|
| 查找 | O(n) | O(log n) |
| 插入 | O(n) | O(log n) |
| 删除 | O(n) | O(log n) |
二、AVL vs 红黑树
| 对比项 | AVL 树 | 红黑树 |
|---|---|---|
| 平衡条件 | 严格(|BF| ≤ 1) | 宽松(颜色约束) |
| 高度 | 更矮(查找更快) | 稍高 |
| 插入旋转次数 | 最多 2 次 | 最多 2 次 |
| 删除旋转次数 | 可能 O(log n) | 最多 3 次 |
| 适用场景 | 查询多的场景 | 增删多的场景 |
| STL 容器 | --- | std::map, std::set |
总结
一、AVL 树核心要点
| 要点 | 内容 |
|---|---|
| 平衡因子 | BF = 左子树高 - 右子树高,BF ∈ {-1, 0, 1} |
| LL 型 | 左左失衡 → 右旋 |
| RR 型 | 右右失衡 → 左旋 |
| LR 型 | 左右失衡 → 先左旋再右旋 |
| RL 型 | 右左失衡 → 先右旋再左旋 |
| 插入 | BST 插入 + 回溯更新高度 + 最多 2 次旋转 |
| 删除 | BST 删除 + 回溯更新高度 + 可能多次旋转 |
二、四种旋转记忆口诀
LL 单右旋,RR 单左旋
LR 左下后右上,RL 右下后左上
失衡看两边,哪边低往哪边转
三、一句话记忆
AVL 树通过平衡因子(左高-右高)监控平衡,一旦 BF 超出 [-1,1] 就通过四种旋转恢复:LL 右旋、RR 左旋、LR 先左后右、RL 先右后左,保证树高始终为 O(log n),从而查找、插入、删除均为 O(log n)。