AVL树:自平衡二叉搜索树的核心原理

前言:

AVL 树。它由两位苏联数学家 Adelson-Velsky 和 Landis 在 1962 年发明,是史上第一个自平衡二叉搜索树,也是理解红黑树和所有平衡树思想的基础。


1. 什么是 AVL 树?

AVL 树是一种严格平衡的二叉搜索树,它满足:

任意节点的左子树高度右子树高度之差(称为平衡因子)的绝对值不超过 1。

平衡因子 bf = height(left) - height(right),取值只能是 -1、0、1

因为高度差被严格限制,AVL 树能保证树高始终是 O(log n),从而所有基本操作(查找、插入、删除)的最坏时间复杂度都是 O(log n)


2. 为什么需要 AVL 树?

在普通 BST 中,依次插入 1, 2, 3, 4, 5 会变成:

1
 \
  2
   \
    3
     \
      4
       \
        5

高度变成 5,查找 5 要走 5 步。而 AVL 树通过旋转操作,会把树调整为近似完全二叉树的形态,保持树高在 1.44 * log2(n) 以内。同样的数据,AVL 树大概是:

    2
   / \
  1   4
     / \
    3   5

查找效率高得多。


3. AVL 树节点的定义

相比 BST 节点,我们需要增加一个 height 字段来记录以该节点为根的子树高度。高度定义为左右子树高度的最大值 + 1,空节点高度视为 0。

struct AVLNode {
    int key;
    AVLNode* left;
    AVLNode* right;
    int height;

    AVLNode(int k) : key(k), left(nullptr), right(nullptr), height(1) {}
};

常用辅助函数:

int height(AVLNode* node) {
    return node ? node->height : 0;
}

int getBalance(AVLNode* node) {
    return node ? height(node->left) - height(node->right) : 0;
}

void updateHeight(AVLNode* node) {
    if (node) {
        node->height = std::max(height(node->left), height(node->right)) + 1;
    }
}


4. 旋转操作 —— AVL 树的核心

当插入或删除节点后,某节点的平衡因子变为 2 或 -2,就需要通过旋转来恢复平衡。旋转共四种情况。

4.1 右旋(Right Rotation)——解决“左左”失衡

当节点的左子树的左侧插入导致不平衡(bf > 1 且左孩子的 bf >= 0),进行一次右旋。

     y                 x
    / \               / \
   x   T3   --->    T1   y
  / \                   / \
 T1  T2               T2  T3

代码:

AVLNode* rightRotate(AVLNode* y) {
    AVLNode* x = y->left;
    AVLNode* T2 = x->right;

    // 旋转
    x->right = y;
    y->left = T2;

    // 更新高度(先更新 y,再更新 x)
    updateHeight(y);
    updateHeight(x);

    return x;  // 新的根
}

4.2 左旋(Left Rotation)——解决“右右”失衡

当节点的右子树的右侧插入导致不平衡(bf < -1 且右孩子的 bf <= 0),进行一次左旋。

     x                 y
    / \               / \
  T1   y     --->    x   T3
      / \           / \
     T2  T3       T1  T2

代码:

AVLNode* leftRotate(AVLNode* x) {
    AVLNode* y = x->right;
    AVLNode* T2 = y->left;

    y->left = x;
    x->right = T2;

    updateHeight(x);
    updateHeight(y);

    return y;
}

4.3 左右旋(Left-Right Rotation)——解决“左右”失衡

当节点左子树的右侧插入导致不平衡(bf > 1 且左孩子的 bf < 0),需要先对左子树做左旋,再对当前节点做右旋

    y               y               z
    / \             / \             / \
   x   T3  --->    z   T3  --->    x   y
  / \             / \             / \ / \
 T1  z           x  T2          T1 T2 T3
    / \         / \
   T2 T3       T1 T2

代码(调用上面两个旋转):

AVLNode* leftRightRotate(AVLNode* y) {
    y->left = leftRotate(y->left);   // 左旋左孩子
    return rightRotate(y);           // 右旋自己
}

4.4 右左旋(Right-Left Rotation)——解决“右左”失衡

当节点右子树的左侧插入导致不平衡(bf < -1 且右孩子的 bf > 0),先对右子树做右旋,再对当前节点做左旋

     x               x               z
    / \             / \             / \
  T1   y    --->   T1  z    --->   x   y
      / \             / \         / \ / \
     z   T4          T2  y       T1 T2 T4
    / \                 / \
   T2 T3               T3 T4

代码:

AVLNode* rightLeftRotate(AVLNode* x) {
    x->right = rightRotate(x->right);
    return leftRotate(x);
}

5. 插入操作

插入和 BST 一样递归进行,插入后回溯更新高度,并检查平衡因子,一旦失衡立即做对应旋转。

AVLNode* insert(AVLNode* node, int key) {
    // 1. 普通 BST 插入
    if (!node)
        return new AVLNode(key);

    if (key < node->key)
        node->left = insert(node->left, key);
    else if (key > node->key)
        node->right = insert(node->right, key);
    else
        return node;   // 重复键不插入

    // 2. 更新当前节点高度
    updateHeight(node);

    // 3. 计算平衡因子,检查是否失衡
    int balance = getBalance(node);

    // 左左情况
    if (balance > 1 && key < node->left->key)
        return rightRotate(node);

    // 右右情况
    if (balance < -1 && key > node->right->key)
        return leftRotate(node);

    // 左右情况
    if (balance > 1 && key > node->left->key) {
        node->left = leftRotate(node->left);
        return rightRotate(node);
    }

    // 右左情况
    if (balance < -1 && key < node->right->key) {
        node->right = rightRotate(node->right);
        return leftRotate(node);
    }

    return node; // 未失衡,直接返回
}

重要细节:每个 if 里的条件 key < node->left->key 等判断,确保了旋转的正确性。如果重复键存在,代码会直接返回,不会插入,也无需平衡。


6. 删除操作

由于删除操作过于复杂,博主本人也还没完全搞明白,因此就不在这里讲解了。

7. 复杂度与性能特点

  • 时间复杂度:查找、插入、删除均为 O(log n),而且是最坏情况保证,不像 BST 可能退化为 O(n)。

  • 空间复杂度:每个节点需要额外存储高度(int),相比 BST 多出常数级内存。

  • 旋转开销:插入最多只需一次旋转(单旋或双旋),删除可能需要 O(log n) 次旋转(回溯到根)。

正因为插入/删除时旋转的常数开销比红黑树稍高,所以实际工程中红黑树更常见。但 AVL 树在查找密集型场景下性能更好,因为它更严格平衡,树更矮。

8. 小结

  • AVL 树是严格平衡的二叉搜索树,左右子树高度差不超过 1。

  • 通过四种旋转(左旋、右旋、左右旋、右左旋)在插入和删除后恢复平衡。

  • 所有操作均为 O(log n),适合查找频繁的场景。

  • 理解 AVL 树是理解一切平衡树的基础,也是数据结构面试的常客。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值