一、什么是 AVL 树?
AVL 树是第一个自平衡二叉搜索树 ,1962 年由 Adelson-Velsky 和 Landis 提出。它解决了普通二叉搜索树在插入有序数据时退化成链表的问题,通过自动旋转 保持树的平衡,让查找、插入、删除操作的时间复杂度始终维持在 O(log n)。
AVL 树的核心特点:
- 每个节点维护一个平衡因子(BF):左子树高度 - 右子树高度;
- 平衡因子的绝对值 ≤ 1,否则触发旋转修复;
- 支持四种旋转方式:LL(右旋)、RR(左旋)、LR(先左旋再右旋)、RL(先右旋再左旋)。
二、为什么需要 AVL 树?
普通二叉搜索树在插入有序数据时,会退化成一条链表,此时查找效率从 O(log n) 降到 O(n),严重影响性能。
例如,依次插入 10、20、30、40、50,普通二叉搜索树会变成一条右斜的链表:
10
\
20
\
30
\
40
\
50
此时查找 50 需要遍历所有节点,效率极低。
AVL 树通过自动旋转,始终保持树的平衡,避免了这种情况。
三、AVL 树的核心概念
1. 平衡因子(BF)
平衡因子 = 左子树高度 - 右子树高度,AVL 树要求所有节点的平衡因子满足:
- |BF| ≤ 1:树是平衡的;
- |BF| = 2:树失去平衡,需要旋转修复。
2. 四种旋转方式
根据失衡的类型,AVL 树有四种旋转修复方式:
- LL(左左失衡) :左子树的左子树插入节点,导致失衡,需要右旋;
- RR(右右失衡) :右子树的右子树插入节点,导致失衡,需要左旋;
- LR(左右失衡) :左子树的右子树插入节点,导致失衡,需要先左旋再右旋;
- RL(右左失衡) :右子树的左子树插入节点,导致失衡,需要先右旋再左旋。
四、AVL 树的节点定义
AVL 树的节点需要维护数据、左右子节点指针、高度三个信息,对应的 C 语言代码定义如下:
// AVL树节点结构体
typedef struct AVLNode {
int val; // 数据域
struct AVLNode* left; // 左子节点指针
struct AVLNode* right; // 右子节点指针
int height; // 节点高度
} AVLNode;
五、AVL 树的基本操作
1. 获取节点高度
获取节点的高度,空节点的高度为 0:
// 获取节点高度
int getHeight(AVLNode* node) {
if (node == NULL) return 0;
return node->height;
}
2. 计算平衡因子
计算节点的平衡因子:
// 计算平衡因子
int getBalance(AVLNode* node) {
if (node == NULL) return 0;
return getHeight(node->left) - getHeight(node->right);
}
3. 创建新节点
创建一个新的 AVL 树节点,初始化高度为 1:
// 创建新节点
AVLNode* createAVLNode(int val) {
AVLNode* node = (AVLNode*)malloc(sizeof(AVLNode));
node->val = val;
node->left = NULL;
node->right = NULL;
node->height = 1; // 新节点高度为1
return node;
}
4. 右旋(LL 失衡修复)
当左子树的左子树插入节点导致失衡时,需要右旋:
// 右旋(LL失衡修复)
AVLNode* rightRotate(AVLNode* y) {
AVLNode* x = y->left;
AVLNode* T2 = x->right;
// 旋转
x->right = y;
y->left = T2;
// 更新高度
y->height = 1 + (getHeight(y->left) > getHeight(y->right) ? getHeight(y->left) : getHeight(y->right));
x->height = 1 + (getHeight(x->left) > getHeight(x->right) ? getHeight(x->left) : getHeight(x->right));
// 返回新的根节点
return x;
}
5. 左旋(RR 失衡修复)
当右子树的右子树插入节点导致失衡时,需要左旋:
// 左旋(RR失衡修复)
AVLNode* leftRotate(AVLNode* x) {
AVLNode* y = x->right;
AVLNode* T2 = y->left;
// 旋转
y->left = x;
x->right = T2;
// 更新高度
x->height = 1 + (getHeight(x->left) > getHeight(x->right) ? getHeight(x->left) : getHeight(x->right));
y->height = 1 + (getHeight(y->left) > getHeight(y->right) ? getHeight(y->left) : getHeight(y->right));
// 返回新的根节点
return y;
}
6. 插入节点
插入节点后,自动检查平衡因子,触发旋转修复:
// 插入节点
AVLNode* insertAVLNode(AVLNode* node, int val) {
// 1. 执行普通二叉搜索树的插入
if (node == NULL) return createAVLNode(val);
if (val < node->val) {
node->left = insertAVLNode(node->left, val);
} else if (val > node->val) {
node->right = insertAVLNode(node->right, val);
} else {
return node; // 重复节点不插入
}
// 2. 更新当前节点的高度
node->height = 1 + (getHeight(node->left) > getHeight(node->right) ? getHeight(node->left) : getHeight(node->right));
// 3. 计算平衡因子,判断是否失衡
int balance = getBalance(node);
// 4. 修复失衡
// LL失衡:右旋
if (balance > 1 && val < node->left->val) {
return rightRotate(node);
}
// RR失衡:左旋
if (balance < -1 && val > node->right->val) {
return leftRotate(node);
}
// LR失衡:先左旋再右旋
if (balance > 1 && val > node->left->val) {
node->left = leftRotate(node->left);
return rightRotate(node);
}
// RL失衡:先右旋再左旋
if (balance < -1 && val < node->right->val) {
node->right = rightRotate(node->right);
return leftRotate(node);
}
// 5. 未失衡,返回原节点
return node;
}
7. 中序遍历
AVL 树的中序遍历结果是有序的:
// 中序遍历
void inorderAVL(AVLNode* root) {
if (root == NULL) return;
inorderAVL(root->left);
printf("%d ", root->val);
inorderAVL(root->right);
}
六、完整代码示例
#include <stdio.h>
#include <stdlib.h>
// AVL树节点结构体
typedef struct AVLNode {
int val;
struct AVLNode* left;
struct AVLNode* right;
int height;
} AVLNode;
// 获取节点高度
int getHeight(AVLNode* node) {
if (node == NULL) return 0;
return node->height;
}
// 计算平衡因子
int getBalance(AVLNode* node) {
if (node == NULL) return 0;
return getHeight(node->left) - getHeight(node->right);
}
// 创建新节点
AVLNode* createAVLNode(int val) {
AVLNode* node = (AVLNode*)malloc(sizeof(AVLNode));
node->val = val;
node->left = NULL;
node->right = NULL;
node->height = 1;
return node;
}
// 右旋(LL失衡修复)
AVLNode* rightRotate(AVLNode* y) {
AVLNode* x = y->left;
AVLNode* T2 = x->right;
x->right = y;
y->left = T2;
y->height = 1 + (getHeight(y->left) > getHeight(y->right) ? getHeight(y->left) : getHeight(y->right));
x->height = 1 + (getHeight(x->left) > getHeight(x->right) ? getHeight(x->left) : getHeight(x->right));
return x;
}
// 左旋(RR失衡修复)
AVLNode* leftRotate(AVLNode* x) {
AVLNode* y = x->right;
AVLNode* T2 = y->left;
y->left = x;
x->right = T2;
x->height = 1 + (getHeight(x->left) > getHeight(x->right) ? getHeight(x->left) : getHeight(x->right));
y->height = 1 + (getHeight(y->left) > getHeight(y->right) ? getHeight(y->left) : getHeight(y->right));
return y;
}
// 插入节点
AVLNode* insertAVLNode(AVLNode* node, int val) {
if (node == NULL) return createAVLNode(val);
if (val < node->val) {
node->left = insertAVLNode(node->left, val);
} else if (val > node->val) {
node->right = insertAVLNode(node->right, val);
} else {
return node;
}
node->height = 1 + (getHeight(node->left) > getHeight(node->right) ? getHeight(node->left) : getHeight(node->right));
int balance = getBalance(node);
// LL失衡
if (balance > 1 && val < node->left->val) {
return rightRotate(node);
}
// RR失衡
if (balance < -1 && val > node->right->val) {
return leftRotate(node);
}
// LR失衡
if (balance > 1 && val > node->left->val) {
node->left = leftRotate(node->left);
return rightRotate(node);
}
// RL失衡
if (balance < -1 && val < node->right->val) {
node->right = rightRotate(node->right);
return leftRotate(node);
}
return node;
}
// 中序遍历
void inorderAVL(AVLNode* root) {
if (root == NULL) return;
inorderAVL(root->left);
printf("%d ", root->val);
inorderAVL(root->right);
}
// 释放AVL树内存
void freeAVLTree(AVLNode* root) {
if (root == NULL) return;
freeAVLTree(root->left);
freeAVLTree(root->right);
free(root);
}
int main() {
AVLNode* root = NULL;
// 插入有序数据,测试AVL树的自平衡能力
root = insertAVLNode(root, 10);
root = insertAVLNode(root, 20);
root = insertAVLNode(root, 30);
root = insertAVLNode(root, 40);
root = insertAVLNode(root, 50);
root = insertAVLNode(root, 25);
// 中序遍历结果应该是有序的
printf("中序遍历结果:");
inorderAVL(root); // 输出:10 20 25 30 40 50
printf("\n");
// 释放内存
freeAVLTree(root);
printf("内存已释放\n");
return 0;
}
七、AVL 树的实际应用场景
AVL 树在实际开发中应用非常广泛,常见场景包括:
- 数据库索引:早期的数据库索引使用 AVL 树,保证查询效率;
- C++ STL :
std::map和std::set的底层实现就是平衡树的思想(现代实现多为红黑树); - 文件系统:部分文件系统使用 AVL 树管理目录结构,提高查找效率;
- 编译器:编译器的符号表使用 AVL 树存储,保证符号查找的高效性;
- 实时系统:AVL 树的操作时间稳定,适合对响应时间要求严格的实时系统。
八、总结
AVL 树是第一个自平衡二叉搜索树,通过维护平衡因子和自动旋转,解决了普通二叉搜索树在插入有序数据时退化成链表的问题,让查找、插入、删除操作的时间复杂度始终维持在 O(log n)。
AVL 树的核心是四种旋转方式:LL(右旋)、RR(左旋)、LR(先左旋再右旋)、RL(先右旋再左旋),通过旋转修复失衡,保持树的平衡。
在实际开发中,AVL 树是平衡树的基础,后续的红黑树、B 树等都是在 AVL 树的基础上发展而来的,是算法和开发中不可或缺的基础数据结构。
希望这篇文章能帮助你深入理解 AVL 树的原理和实现!