7.AVL 树:第一个自平衡二叉搜索树

一、什么是 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 树在实际开发中应用非常广泛,常见场景包括:

  1. 数据库索引:早期的数据库索引使用 AVL 树,保证查询效率;
  2. C++ STLstd::mapstd::set的底层实现就是平衡树的思想(现代实现多为红黑树);
  3. 文件系统:部分文件系统使用 AVL 树管理目录结构,提高查找效率;
  4. 编译器:编译器的符号表使用 AVL 树存储,保证符号查找的高效性;
  5. 实时系统:AVL 树的操作时间稳定,适合对响应时间要求严格的实时系统。

八、总结

AVL 树是第一个自平衡二叉搜索树,通过维护平衡因子和自动旋转,解决了普通二叉搜索树在插入有序数据时退化成链表的问题,让查找、插入、删除操作的时间复杂度始终维持在 O(log n)

AVL 树的核心是四种旋转方式:LL(右旋)、RR(左旋)、LR(先左旋再右旋)、RL(先右旋再左旋),通过旋转修复失衡,保持树的平衡。

在实际开发中,AVL 树是平衡树的基础,后续的红黑树、B 树等都是在 AVL 树的基础上发展而来的,是算法和开发中不可或缺的基础数据结构。

希望这篇文章能帮助你深入理解 AVL 树的原理和实现!

相关推荐
rannn_1111 小时前
【力扣hot100】二叉树专题+总结
java·算法·leetcode·二叉树
啊嘞嘞?1 小时前
力扣(岛屿数量)
算法·leetcode
SomeB1oody1 小时前
【RustyML入门】5.3. 聚类指标
开发语言·后端·机器学习·rust·教程
国科安芯1 小时前
星载网络化控制的总线脊梁:四路CANFD在分布式载荷管理中的架构优势
分布式·单片机·嵌入式硬件·架构·系统架构·canfd·低轨卫星星座
OPEN-F1 小时前
Python进阶教程:Web开发入门(Flask)
开发语言·python
神仙别闹1 小时前
基于C++ MFC实现动态分区分配存储管理
开发语言·c++·mfc
zander2582 小时前
LeetCode 198. 打家劫舍
算法·leetcode·深度优先
for_ever_love__2 小时前
python基础语法学习: 类型注解
开发语言·python·学习
吃着火锅x唱着歌2 小时前
LeetCode 648.单词替换
算法·leetcode·职场和发展