深入理解AVL树:平衡调整机制与性能优化实战


🌈 say-fall:个人主页 🚀 专栏:《手把手教你学会C++》 | 《系统深入Linux操作系统》 | 《数据结构与算法》 | 《小游戏与项目》 💪 格言:做好你自己,才能吸引更多人,与他们共赢,这才是最好的成长方式。

文章目录

  • [🔥 前言](#🔥 前言)
  • [📚 一、AVL树的概念](#📚 一、AVL树的概念)
  • [🏗️ 二、AVL树的结构设计](#🏗️ 二、AVL树的结构设计)
  • [⚙️ 三、插入操作详解](#⚙️ 三、插入操作详解)
    • [3.1 插入的基本思路](#3.1 插入的基本思路)
    • [3.2 平衡因子的更新](#3.2 平衡因子的更新)
    • [3.3 旋转操作](#3.3 旋转操作)
      • [📍 左单旋(情况:右边高,且右孩子的右子树高)](#📍 左单旋(情况:右边高,且右孩子的右子树高))
      • [📍 右单旋(情况:左边高,且左孩子的左子树高)](#📍 右单旋(情况:左边高,且左孩子的左子树高))
      • [📍 左右双旋(情况:左边高,但左孩子的右子树高)](#📍 左右双旋(情况:左边高,但左孩子的右子树高))
      • [📍 右左双旋(情况:右边高,但右孩子的左子树高)](#📍 右左双旋(情况:右边高,但右孩子的左子树高))
  • [📊 四、四种旋转的判断](#📊 四、四种旋转的判断)
  • [🧪 五、完整代码实现](#🧪 五、完整代码实现)
    • [AVLTree.h 完整代码](#AVLTree.h 完整代码)
    • [测试代码 Test.cpp](#测试代码 Test.cpp)
  • [📈 六、性能分析](#📈 六、性能分析)
  • [🎯 七、总结](#🎯 七、总结)
  • 本节完...

🔥 前言

AVL树,听起来很高大上?其实它是平衡二叉搜索树的一种经典实现!今天我们就来手撕AVL树,用C++实现一个完整版本 🚀


📚 一、AVL树的概念

什么是AVL树?

AVL树(由发明者 G.M. Adelson-Velsky 和 E.M. Landis 命名)是一种自平衡的二叉搜索树
核心特点:左右子树的高度差(平衡因子)的绝对值不超过 1

简单来说,就是:

复制代码
        ✅ AVL树          ❌ 非AVL树
           4                 4
          / \               /
         2   6              2
        / \                 \
       1   3                 3
                              \
                               5

为什么需要AVL树?

普通的二叉搜索树在极端情况下会退化成链表,时间复杂度从 O(log n) 退化成 O(n)

操作 理想情况(平衡) 最坏情况(退化成链表)
查找 O(log n) O(n)
插入 O(log n) O(n)
删除 O(log n) O(n)

🏗️ 二、AVL树的结构设计

节点结构

cpp 复制代码
template <class K, class V>
struct AVLTreeNode
{
    pair<K, V> _kv;           // 键值对
    AVLTreeNode <K,V>* _left;    // 左孩子
    AVLTreeNode <K,V>* _right;   // 右孩子
    AVLTreeNode <K,V>* _parent;  // 父节点(用于回溯)

    int _bf; // Balance Factor 平衡因子 = 右子树高度 - 左子树高度

    AVLTreeNode(const pair<K, V>& kv)
        : _kv(kv)
        , _left(nullptr)
        , _right(nullptr)
        , _parent(nullptr)
        , _bf(0)
    {
    }
};

小贴士:平衡因子 _bf 的取值只能是 -1, 0, 1,否则就需要旋转调整!


⚙️ 三、插入操作详解

3.1 插入的基本思路

复制代码
📌 插入步骤:
   ① 按BST规则找到插入位置
   ② 将新节点插入并链接父节点
   ③ 从插入位置向上回溯,更新平衡因子
   ④ 当平衡因子变为 ±2 时,进行旋转调整

3.2 平衡因子的更新

关键点:平衡因子的更新是理解AVL树的核心!

cpp 复制代码
// 更新平衡因子
if (parent->_left == cur)  // 左子树插入
{
    parent->_bf--;
}
else  // 右子树插入
{
    parent->_bf++;
}
平衡因子变化 含义 处理方式
0 原本一边轻一边重,插入后平衡了 ✅ 停止回溯
±1 原本平衡,现在一边更高了 🔄 继续向上更新
±2 失衡了!需要旋转 🔧 旋转调整

3.3 旋转操作

旋转的本质:让树重新恢复平衡,同时保持BST的有序性

📍 左单旋(情况:右边高,且右孩子的右子树高)

复制代码
旋转前:                    旋转后:
      p                        p'       
     / \                      / \       
    ...  subR       →         ...  subR  
        / \                      / \     
      ...  subRL               ... subRL 
           ↓                        ↓
         (新节点)                  (新节点)
cpp 复制代码
void RotateL(Node* parent)
{
    Node* subR = parent->_right;
    Node* subRL = subR->_left;
    Node* Pparent = parent->_parent;

    // 重新链接
    parent->_right = subRL;
    if (subRL) subRL->_parent = parent;
    
    subR->_left = parent;
    parent->_parent = subR;

    // 处理根节点
    if (parent == _root)
    {
        _root = subR;
        subR->_parent = nullptr;
    }
    else
    {
        subR->_parent = Pparent;
        if (Pparent->_left == parent)
            Pparent->_left = subR;
        else
            Pparent->_right = subR;
    }

    subR->_bf = 0;
    parent->_bf = 0;
}

📍 右单旋(情况:左边高,且左孩子的左子树高)

复制代码
旋转前:                    旋转后:
      p                        p'       
     / \                      / \       
  subL   ...       →       subL   ...  
  / \                          / \     
subLR  ...                  subLR  ...
      ↓                           ↓
    (新节点)                    (新节点)
cpp 复制代码
void RotateR(Node* parent)
{
    Node* cur = parent->_left;
    Node* subLR = cur->_right;
    Node* Pparent = parent->_parent;

    // 重新链接
    cur->_right = parent;
    parent->_parent = cur;
    parent->_left = subLR;
    if (subLR) subLR->_parent = parent;

    // 处理根节点
    if (parent == _root)
    {
        _root = cur;
        cur->_parent = nullptr;
    }
    else
    {
        cur->_parent = Pparent;
        if (Pparent->_left == parent)
            Pparent->_left = cur;
        else
            Pparent->_right = cur;
    }

    cur->_bf = 0;
    parent->_bf = 0;
}

📍 左右双旋(情况:左边高,但左孩子的右子树高)

复制代码
旋转前:                    先左旋:           再右旋:
      p                        p                   p'
     / \                      / \                / \
   subL subR      →         cur  subR   →      subL subR
   / \                      / \                / \
 ...  subLR              ...  ...            ...  ...
      ↓                      ↓                  ↓
   (新节点)                (新节点)            (新节点)
cpp 复制代码
void RotateLR(Node* parent)
{
    Node* subL = parent->_left;
    Node* subLR = subL->_right;
    int bf = subLR->_bf;  // 保存旋转前的平衡因子,用于后续调整

    RotateL(parent->_left);  // 先左旋
    RotateR(parent);         // 再右旋

    // 根据不同情况调整平衡因子
    if (bf == -1)  // subLR插入在左子树
    {
        parent->_bf = 1;
        subL->_bf = 0;
    }
    else if (bf == 1)  // subLR插入在右子树
    {
        parent->_bf = 0;
        subL->_bf = -1;
    }
    else if (bf == 0)  // subLR就是新插入的节点
    {
        parent->_bf = 0;
        subL->_bf = 0;
    }
}

📍 右左双旋(情况:右边高,但右孩子的左子树高)

这是左右双旋的镜像情况,原理相同!

cpp 复制代码
void RotateRL(Node* parent)
{
    Node* subR = parent->_right;
    Node* subRL = subR->_left;
    int bf = subRL->_bf;

    RotateR(parent->_right);  // 先右旋
    RotateL(parent);           // 再左旋

    if (bf == 1)
    {
        subRL->_bf = 0;
        parent->_bf = -1;
        subR->_bf = 0;
    }
    else if (bf == -1)
    {
        subRL->_bf = 0;
        parent->_bf = 0;
        subR->_bf = 1;
    }
    else if (bf == 0)
    {
        subRL->_bf = 0;
        parent->_bf = 0;
        subR->_bf = 0;
    }
}

📊 四、四种旋转的判断

旋转类型判断口诀:看爹看儿,爹2儿1/爹-2儿-1

父平衡因子 子平衡因子 旋转类型 图示
-2 -1 右单旋 左左
2 1 左单旋 右右
-2 1 左右双旋 左右
2 -1 右左双旋 右左
cpp 复制代码
// 旋转判断代码
if (parent->_bf == -2 && cur->_bf == -1)   // 左左 → 右单旋
{
    RotateR(parent);
}
else if (parent->_bf == 2 && cur->_bf == 1)  // 右右 → 左单旋
{
    RotateL(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1) // 左右 → 左右双旋
{
    RotateLR(parent);
}
else if (parent->_bf == 2 && cur->_bf == -1) // 右左 → 右左双旋
{
    RotateRL(parent);
}

🧪 五、完整代码实现

AVLTree.h 完整代码

cpp 复制代码
#pragma once
#include<iostream>
#include<utility>
#include<cassert>
using namespace std;

template <class K, class V>
struct AVLTreeNode
{
    pair<K, V> _kv;
    AVLTreeNode <K,V>* _left;
    AVLTreeNode <K,V>* _right;
    AVLTreeNode <K,V>* _parent;

    int _bf; // Balance Factor

    AVLTreeNode(const pair<K, V>& kv)
        : _kv(kv)
        , _left(nullptr)
        , _right(nullptr)
        , _parent(nullptr)
        , _bf(0)
    {
    }
};

template <class K, class V>
class AVLTree
{
    using Node = AVLTreeNode<K, V>;
public:
    // 插入
    bool Insert(const pair<K,V>& kv)
    {
        // 1. 空树直接插入
        if (_root == nullptr)
        {
            _root = new Node(kv);
            return true;
        }

        // 2. 找到插入位置
        Node* parent = nullptr;
        Node* cur = _root;
        while (cur)
        {
            if (cur->_kv.first < kv.first)
            {
                parent = cur;
                cur = cur->_right;
            }
            else if (cur->_kv.first > kv.first)
            {
                parent = cur;
                cur = cur->_left;
            }
            else
            {
                return false;  // -key已存在
            }
        }

        // 3. 插入新节点
        cur = new Node(kv);
        if (parent->_kv.first < kv.first)
            parent->_right = cur;
        else
            parent->_left = cur;
        cur->_parent = parent;

        // 4. 向上更新平衡因子
        while (parent)
        {
            if (parent->_left == cur)
                parent->_bf--;
            else
                parent->_bf++;

            // 判断是否需要继续更新或旋转
            if (parent->_bf == 0)
                break;  // 平衡了,停止
            else if (parent->_bf == 1 || parent->_bf == -1)
            {
                // 继续向上更新
                cur = parent;
                parent = cur->_parent;
            }
            else if (parent->_bf == 2 || parent->_bf == -2)
            {
                // 需要旋转
                if (parent->_bf == -2 && cur->_bf == -1)
                    RotateR(parent);
                else if (parent->_bf == 2 && cur->_bf == 1)
                    RotateL(parent);
                else if (parent->_bf == -2 && cur->_bf == 1)
                    RotateLR(parent);
                else if (parent->_bf == 2 && cur->_bf == -1)
                    RotateRL(parent);
                else
                    assert(false);
                break;
            }
            else
            {
                assert(false);
            }
        }
        return true;
    }

    // 中序遍历
    void InOrder()
    {
        _InOrder(_root);
        cout << endl;
    }

    // 获取高度
    int Height()
    {
        return _Height(_root);
    }

    // 获取节点数
    int Size()
    {
        return _Size(_root);
    }

    // 检查是否是AVL树
    bool IsBalanceTree()
    {
        return _IsBalanceTree(_root);
    }

    // 查找
    Node* Find(const K& key)
    {
        Node* cur = _root;
        while (cur)
        {
            if (cur->_kv.first < key)
                cur = cur->_right;
            else if (cur->_kv.first > key)
                cur = cur->_left;
            else
                return cur;
        }
        return nullptr;
    }

private:
    // 右单旋
    void RotateR(Node* parent)
    {
        Node* cur = parent->_left;
        Node* subLR = cur->_right;
        Node* Pparent = parent->_parent;

        cur->_right = parent;
        parent->_parent = cur;
        parent->_left = subLR;
        if (subLR) subLR->_parent = parent;

        if (parent == _root)
        {
            _root = cur;
            cur->_parent = nullptr;
        }
        else
        {
            cur->_parent = Pparent;
            if (Pparent->_left == parent)
                Pparent->_left = cur;
            else
                Pparent->_right = cur;
        }

        cur->_bf = 0;
        parent->_bf = 0;
    }

    // 左单旋
    void RotateL(Node* parent)
    {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
        Node* Pparent = parent->_parent;

        parent->_right = subRL;
        if (subRL) subRL->_parent = parent;
        subR->_left = parent;
        parent->_parent = subR;

        if (parent == _root)
        {
            _root = subR;
            subR->_parent = nullptr;
        }
        else
        {
            subR->_parent = Pparent;
            if (Pparent->_left == parent)
                Pparent->_left = subR;
            else
                Pparent->_right = subR;
        }

        subR->_bf = 0;
        parent->_bf = 0;
    }

    // 左右双旋
    void RotateLR(Node* parent)
    {
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
        int bf = subLR->_bf;

        RotateL(parent->_left);
        RotateR(parent);

        if (bf == -1)
        {
            parent->_bf = 1;
            subL->_bf = 0;
        }
        else if (bf == 1)
        {
            parent->_bf = 0;
            subL->_bf = -1;
        }
        else if (bf == 0)
        {
            parent->_bf = 0;
            subL->_bf = 0;
        }
    }

    // 右左双旋
    void RotateRL(Node* parent)
    {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
        int bf = subRL->_bf;

        RotateR(parent->_right);
        RotateL(parent);

        if (bf == 1)
        {
            subRL->_bf = 0;
            parent->_bf = -1;
            subR->_bf = 0;
        }
        else if (bf == -1)
        {
            subRL->_bf = 0;
            parent->_bf = 0;
            subR->_bf = 1;
        }
        else if (bf == 0)
        {
            subRL->_bf = 0;
            parent->_bf = 0;
            subR->_bf = 0;
        }
    }

    void _InOrder(Node* root)
    {
        if (root == nullptr) return;
        _InOrder(root->_left);
        cout << root->_kv.first << ":" << root->_kv.second << endl;
        _InOrder(root->_right);
    }

    int _Height(Node* root)
    {
        if (root == nullptr) return 0;
        int leftHeight = _Height(root->_left);
        int rightHeight = _Height(root->_right);
        return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
    }

    int _Size(Node* root)
    {
        if (root == nullptr) return 0;
        return _Size(root->_left) + _Size(root->_right) + 1;
    }

    bool _IsBalanceTree(Node* root)
    {
        if (root == nullptr) return true;
        int leftHeight = _Height(root->_left);
        int rightHeight = _Height(root->_right);
        int diff = rightHeight - leftHeight;

        if (abs(diff) >= 2)
        {
            cout << root->_kv.first << "高度差异常" << endl;
            return false;
        }

        if (root->_bf != diff)
        {
            cout << root->_kv.first << "平衡因子异常" << endl;
            return false;
        }

        return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
    }

    Node* _root = nullptr;
};

测试代码 Test.cpp

cpp 复制代码
#include "AVLTree.h"

void TestAVLTree1()
{
    AVLTree<int, int> t;
    // 常规测试用例
    int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
    // 双旋场景测试用例
    //int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };

    for (auto e : a)
    {
        t.Insert({ e, e });
    }

    t.InOrder();
    cout << "是否平衡: " << t.IsBalanceTree() << endl;
}

// 性能测试:插入100万个随机数
void TestAVLTree2()
{
    const int N = 1000000;
    vector<int> v;
    v.reserve(N);
    srand(time(0));
    for (size_t i = 0; i < N; i++)
    {
        v.push_back(rand() + i);
    }

    size_t begin2 = clock();
    AVLTree<int, int> t;
    for (auto e : v)
    {
        t.Insert(make_pair(e, e));
    }
    size_t end2 = clock();

    cout << "插入耗时: " << end2 - begin2 << " ms" << endl;
    cout << "是否平衡: " << t.IsBalanceTree() << endl;
    cout << "树高度: " << t.Height() << endl;
    cout << "节点数: " << t.Size() << endl;

    size_t begin1 = clock();
    for (auto e : v)
    {
        t.Find(e);
    }
    size_t end1 = clock();
    cout << "查找耗时: " << end1 - begin1 << " ms" << endl;
}

int main()
{
    TestAVLTree2();
    return 0;
}

📈 六、性能分析

AVL树各项操作的时间复杂度都是 O(log n)!

操作 时间复杂度 说明
查找 O(log n) 始终保持平衡
插入 O(log n) 最多旋转两次
删除 O(log n) 最多旋转O(log n)次
获取高度 O(n) 需要遍历子树
中序遍历 O(n) 遍历所有节点

🎯 七、总结

🎉 恭喜你掌握了AVL树!

复制代码
📌 今日收获:
   ✅ 理解了AVL树的自平衡原理
   ✅ 掌握了四种旋转操作(左单旋、右单旋、左右双旋、右左双旋)
   ✅ 学会了如何更新平衡因子
   ✅ 能够实现完整的AVL树

💡 思考题:AVL树和红黑树有什么区别?什么时候该用AVL树,什么时候该用红黑树?


本节完...

如果觉得有帮助,记得 点赞 + 关注 + 收藏 哦!

相关推荐
鱼鳞_1 小时前
Java学习笔记_Day33(高级流)
java·笔记·学习
赖在沙发上的熊2 小时前
Python数据序列
开发语言·python
Hello--_--World2 小时前
Js面试题目录表
开发语言·javascript·ecmascript
tankeven2 小时前
HJ180 游游的最长稳定子数组
c++·算法
聆风吟º2 小时前
【C标准库】深入理解C语言strcmp函数:字符串比较的核心用法
c语言·开发语言·库函数·strcmp
kyle~2 小时前
机器人广域网通信---MQTT技术
大数据·c++·机器人·ros2
Fanfanaas2 小时前
Linux 进程篇 (四)
linux·运维·服务器·开发语言·c++·学习
Sylvia-girl2 小时前
C++中类与对象
开发语言·c++
2501_944934732 小时前
咨询行业怎样提升自己?
学习