C++ 模拟实现 AVL 树

一、AVL 树基础概念

1. 什么是 AVL 树?

AVL 树是自平衡二叉搜索树,由 Adelson-Velsky 和 Landis 提出。

核心规则:

  1. 满足二叉搜索树性质(左 < 根 < 右)

  2. 每个节点的左右子树高度差的绝对值 ≤ 1

  3. 每个节点维护一个平衡因子(Balance Factor, BF)

    • BF = 右子树高度 - 左子树高度

    • 合法值:-1、0、1

    • 出现 2 / -2 说明失衡,需要旋转调整

2. 平衡因子(BF)规则

  • 插入左子树 → BF -1

  • 插入右子树 → BF +1

  • BF = 0 → 高度不变,停止更新

  • BF = ±1 → 高度变化,继续向上更新

  • BF = ±2 → 失衡,必须旋转调整


二、AVL 树节点结构定义

复制代码
#pragma once
#include<iostream>
#include<assert.h>
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)
    {}
};

节点核心成员:

  • pair<K, V>:存储 key-value 数据

  • _left/_right/_parent三叉链结构,方便旋转回溯

  • _bf:平衡因子,控制平衡


三、AVLTree 类整体结构

复制代码
template<class K,class V>
class AVLTree
{
    typedef AVLTreeNode<K, V> Node;
public:
    // 插入接口
    bool Insert(const pair<K, V>& kv);
    // 左旋
    void RotateL(Node* parent);
    // 右旋
    void RotateR(Node* parent);
    // 右左旋
    void RotateRL(Node* parent);
    // 左右旋
    void RotateLR(Node* parent);
    // 求树高度
    int Height();
    int Height(Node* root);
    // 判断是否平衡
    bool IsBalance();
    bool IsBalance(Node* root);
​
private:
    Node* _root = nullptr; // 根节点
​
public:
    int _rotateCount = 0; // 旋转次数统计
};

四、插入(Insert)核心逻辑

插入分为 3 步

  1. 按照二叉搜索树规则插入节点

  2. 更新平衡因子 BF

  3. 如果失衡 → 旋转调整

1. BST 插入部分

复制代码
// 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 // key 重复,不插入
        return false;
}
​
// 3. 链接新节点
cur = new Node(kv);
if (parent->_kv.first < cur->_kv.first)
    parent->_right = cur;
else
    parent->_left = cur;
​
cur->_parent = parent;

2. 平衡因子更新 + 旋转判断

复制代码
// 回溯更新平衡因子
while (parent)
{
    // 左孩子插入 → BF -1
    if (cur == parent->_left)
        parent->_bf--;
    // 右孩子插入 → BF +1
    else
        parent->_bf++;
​
    // ====================== 情况1 ======================
    // BF = 0:高度没变,停止更新
    if (parent->_bf == 0)
    {
        break;
    }
​
    // ====================== 情况2 ======================
    // BF = ±1:高度变了,继续向上更新
    else if (parent->_bf == 1 || parent->_bf == -1)
    {
        cur = parent;
        parent = parent->_parent;
    }
​
    // ====================== 情况3 ======================
    // BF = ±2:失衡 → 旋转
    else if (parent->_bf == 2 || parent->_bf == -2)
    {
        // 旋转调整
        if (parent->_bf == 2 && cur->_bf == 1)
        {
            RotateL(parent); // 左单旋
        }
        else if (parent->_bf == -2 && cur->_bf == -1)
        {
            RotateR(parent); // 右单旋
        }
        else if (parent->_bf == -2 && cur->_bf == 1)
        {
            RotateLR(parent); // 左右旋
        }
        else if (parent->_bf == 2 && cur->_bf == -1)
        {
            RotateRL(parent); // 右左旋
        }
        break;
    }
    else
        assert(false); // 非法 BF
}

3. 四种旋转判断总结

失衡类型 条件 旋转方式
左左 parent.*bf = -2,cur.*bf = -1 右旋
右右 parent.*bf = 2,cur.*bf = 1 左旋
左右 parent.*bf = -2,cur.*bf = 1 先左旋后右旋
右左 parent.*bf = 2,cur.*bf = -1 先右旋后左旋

五、旋转实现(核心)

1. 左旋(RotateL)

适用场景:右右失衡

复制代码
void RotateL(Node* parent)
{
    ++_rotateCount;
    Node* cur = parent->_right;
    Node* curleft = cur->_left;
​
    // 1. cur 左孩子 给 parent 右孩子
    parent->_right = curleft;
    if (curleft)
        curleft->_parent = parent;
​
    // 2. parent 做 cur 的左孩子
    cur->_left = parent;
​
    // 3. 处理祖父节点
    Node* ppnode = parent->_parent;
    parent->_parent = cur;
​
    if (parent == _root)
    {
        _root = cur;
        cur->_parent = nullptr;
    }
    else
    {
        if (ppnode->_left == parent)
            ppnode->_left = cur;
        else
            ppnode->_right = cur;
        cur->_parent = ppnode;
    }
​
    // 旋转后平衡因子重置为 0
    parent->_bf = cur->_bf = 0;
}

2. 右旋(RotateR)

适用场景:左左失衡

复制代码
void RotateR(Node* parent)
{
    ++_rotateCount;
    Node* cur = parent->_left;
    Node* curright = cur->_right;
​
    // 1. cur 右孩子 给 parent 左孩子
    parent->_left = curright;
    if (curright)
        curright->_parent = parent;
​
    // 2. parent 做 cur 的右孩子
    cur->_right = parent;
    parent->_parent = cur;
​
    // 3. 处理祖父节点
    Node* ppnode = parent->_parent;
    if (ppnode == nullptr)
    {
        _root = cur;
        cur->_parent = nullptr;
    }
    else
    {
        if (ppnode->_left == parent)
            ppnode->_left = cur;
        else
            ppnode->_right = cur;
        cur->_parent = ppnode;
    }
​
    parent->_bf = cur->_bf = 0;
}

3. 右左旋(RL)

适用场景:右左失衡

复制代码
void RotateRL(Node* parent)
{
    Node* cur = parent->_right;
    Node* curleft = cur->_left;
    int bf = curleft->_bf;
​
    RotateR(parent->_right);
    RotateL(parent);
​
    // 精细调整平衡因子
    if (bf == 0)
        cur->_bf = curleft->_bf = parent->_bf = 0;
    else if (bf == 1)
    {
        cur->_bf = 0;
        curleft->_bf = 0;
        parent->_bf = -1;
    }
    else if (bf == -1)
    {
        cur->_bf = 1;
        curleft->_bf = 0;
        parent->_bf = 0;
    }
    else
        assert(false);
}

4. 左右旋(LR)

适用场景:左右失衡

复制代码
void RotateLR(Node* parent)
{
    Node* cur = parent->_left;
    Node* curright = cur->_right;
    int bf = curright->_bf;
​
    RotateL(parent->_left);
    RotateR(parent);
​
    if (bf == 0)
        parent->_bf = cur->_bf = curright->_bf = 0;
    else if (bf == -1)
    {
        parent->_bf = 1;
        cur->_bf = 0;
        curright->_bf = 0;
    }
    else if (bf == 1)
    {
        parent->_bf = 0;
        cur->_bf = -1;
        curright->_bf = 0;
    }
}

六、验证 AVL 树正确性

1. 求树高度

复制代码
int Height()
{
    return Height(_root);
}
​
int Height(Node* root)
{
    if (root == nullptr)
        return 0;
​
    int leftHeight = Height(root->_left);
    int rightHeight = Height(root->_right);
​
    return max(leftHeight, rightHeight) + 1;
}

2. 判断是否为合法 AVL 树

检查两点:

  1. 每个节点左右高度差 ≤ 1

  2. 平衡因子 BF 计算正确

复制代码
bool IsBalance()
{
    return IsBalance(_root);
}
​
bool IsBalance(Node* root)
{
    if (root == nullptr)
        return true;
​
    int leftHight = Height(root->_left);
    int rightHight = Height(root->_right);
​
    // 检查平衡因子是否正确
    if (rightHight - leftHight != root->_bf)
    {
        cout << "平衡因子异常:" << root->_kv.first << "->" << root->_bf << endl;
        return false;
    }
​
    // 检查高度差
    return abs(rightHight - leftHight) < 2
        && IsBalance(root->_left)
        && IsBalance(root->_right);
}

七、完整测试代码

复制代码
void TestAVLTree()
{
    AVLTree<int, int> t;
    int a[] = { 4, 2, 6, 1, 3, 5, 7 };
    for (auto e : a)
    {
        t.Insert(make_pair(e, e));
    }
​
    cout << "IsBalance: " << t.IsBalance() << endl;
    cout << "Height: " << t.Height() << endl;
    cout << "RotateCount: " << t._rotateCount << endl;
}
​
int main()
{
    TestAVLTree();
    return 0;
}

八、AVL 树知识点总结

1. 核心流程

插入 → 更新 BF → 判断失衡 → 旋转调整 → 验证平衡

2. 四种旋转

  • 左左 → 右旋

  • 右右 → 左旋

  • 左右 → 左右旋

  • 右左 → 右左旋

3. 平衡因子规则

  • BF = 右高 - 左高

  • 合法:-1/0/1

  • 失衡:±2

4. 性能

  • 查找/插入/删除:O(logN)

  • 效率高于普通 BST,不会退化成链表


九、代码完整可运行版

复制代码
#pragma once
#include<iostream>
#include<assert.h>
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;
​
    AVLTreeNode(const pair<K, V>& kv)
        :_kv(kv)
        ,_left(nullptr)
        ,_right(nullptr)
        ,_parent(nullptr)
        ,_bf(0)
    {}
};  
​
template<class K,class V>
class AVLTree
{
    typedef AVLTreeNode<K, V> Node;
​
public:
    bool Insert(const pair<K, V>& kv)
    {
        if (_root == nullptr)
        {
            _root = new Node(kv);
            return true;
        }
        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;
            }
        }
​
        cur = new Node(kv);
        if (parent->_kv.first < cur->_kv.first)
        {
            parent->_right = cur;
        }
        else
        {
            parent->_left = cur;
        }
​
        cur->_parent = parent;
​
        while (parent)
        {
            if (cur == parent->_left)
            {
                parent->_bf--;
            }
            else
            {
                parent->_bf++;
            }
​
            if (parent->_bf == 0)
            {
                break;
            }
            else if (parent->_bf == 1 || parent->_bf == -1)
            {
                cur = parent;
                parent = parent->_parent;
            }
            else if (parent->_bf == 2 || parent->_bf == -2)
            {
                if (parent->_bf == 2 && cur->_bf == 1)
                {
                    RotateL(parent);
                }
                else if (parent->_bf == -2 && cur->_bf == -1)
                {
                    RotateR(parent);
                }
                else if (parent->_bf == -2 && cur->_bf == 1)
                {
                    RotateLR(parent);
                }
                else if (parent->_bf == 2 && cur->_bf == -1)
                {
                    RotateRL(parent);
                }
                break;
            }
            else assert(false);
        }
        return true;
    }
​
    void RotateL(Node* parent)
    {
        ++_rotateCount;
        Node* cur = parent->_right;
        Node* curleft = cur->_left;
​
        parent->_right = curleft;
        if (curleft)
        {
            curleft->_parent = parent;
        }
        cur->_left = parent;
​
        Node* ppnode = parent->_parent;
        parent->_parent = cur;
​
        if (parent == _root)
        {
            _root = cur;
            cur->_parent = nullptr;
        }
        else
        {
            if (ppnode->_left == parent)
            {
                ppnode->_left = cur;
            }
            else
            {
                ppnode->_right = cur;
            }
            cur->_parent = ppnode;
        }
        parent->_bf = cur->_bf = 0;
    }
​
    void RotateR(Node* parent)
    {
        ++_rotateCount;
        Node* cur = parent->_left;
        Node* curright = cur->_right;
​
        parent->_left = curright;
        if (curright)
            curright->_parent = parent;
​
        Node* ppnode = parent->_parent;
        cur->_right = parent;
        parent->_parent = cur;
​
        if (ppnode == nullptr)
        {
            _root = cur;
            cur->_parent = nullptr;
        }
        else
        {
            if (ppnode->_left == parent)
            {
                ppnode->_left = cur;
            }
            else
            {
                ppnode->_right = cur;
            }
            cur->_parent = ppnode;
        }
        parent->_bf = cur->_bf = 0;
    }
​
    void RotateRL(Node* parent)
    {
        Node* cur = parent->_right;
        Node* curleft = cur->_left;
        int bf = curleft->_bf;
​
        RotateR(parent->_right);
        RotateL(parent);
​
        if (bf == 0)
        {
            cur->_bf = 0;
            curleft->_bf = 0;
            parent->_bf = 0;
        }
        else if (bf == 1)
        {
            cur->_bf = 0;
            curleft->_bf = 0;
            parent->_bf = -1;
        }
        else if (bf == -1)
        {
            cur->_bf = 1;
            curleft->_bf = 0;
            parent->_bf = 0;
        }
        else
        {
            assert(false);
        }
    }
​
    void RotateLR(Node* parent)
    {
        Node* cur = parent->_left;
        Node* curright = cur->_right;
        int bf = curright->_bf;
​
        RotateL(parent->_left);
        RotateR(parent);
​
        if (bf == 0)
        {
            parent->_bf = 0;
            cur->_bf = 0;
            curright->_bf = 0;
        }
        else if (bf == -1)
        {
            parent->_bf = 1;
            cur->_bf = 0;
            curright->_bf = 0;
        }
        else if (bf == 1)
        {
            parent->_bf = 0;
            cur->_bf = -1;
            curright->_bf = 0;
        }
    }
​
    int Height()
    {
        return Height(_root);
    }
​
    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;
    }
​
    bool IsBalance()
    {
        return IsBalance(_root);
    }
​
    bool IsBalance(Node* root)
    {
        if (root == nullptr)
            return true;
​
        int leftHight = Height(root->_left);
        int rightHight = Height(root->_right);
​
        if (rightHight - leftHight != root->_bf)
        {
            cout << "平衡因子异常:" << root->_kv.first << "->" << root->_bf << endl;
            return false;
        }
​
        return abs(rightHight - leftHight) < 2
            && IsBalance(root->_left)
            && IsBalance(root->_right);
    }
​
private:
    Node* _root = nullptr;
​
public:
    int _rotateCount = 0;
};
​
void TestAVLTree()
{
    AVLTree<int, int> t;
    int a[] = { 4, 2, 6, 1, 3, 5, 7 };
    for (auto e : a)
    {
        t.Insert(make_pair(e, e));
    }
​
    cout << "IsBalance: " << t.IsBalance() << endl;
    cout << "Height: " << t.Height() << endl;
    cout << "RotateCount: " << t._rotateCount << endl;
}
​
int main()
{
    TestAVLTree();
    return 0;
}
相关推荐
李日灐1 小时前
< 7 > Linux 开发工具:git 版本控制器 和 cgdb/gdb 调试器
linux·运维·服务器·开发语言·git·调试器·gdb/cgdb
会编程的土豆1 小时前
洛谷题单 入门1 顺序结构(go语言)
开发语言·后端·golang·洛谷
jieyucx1 小时前
Go 语言 switch 条件语句详解
开发语言·c++·golang
AC赳赳老秦1 小时前
网安工程师提效:用 OpenClaw 实现漏洞扫描报告生成、安全巡检自动化、日志合规审计
java·开发语言·前端·javascript·python·deepseek·openclaw
初心未改HD1 小时前
Go语言defer机制深度解析
开发语言·golang
万法若空1 小时前
C++ <iomanip> 库全方位详解
开发语言·c++
c++之路1 小时前
C++ 模板
linux·开发语言·c++
幻影七幻1 小时前
js中send的作用和使用 $.ajax的作用
开发语言·前端·javascript
鸿儒5171 小时前
记录一个C++ Windows程序移植到Linux系统的bug
开发语言·c++·bug