跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ AVL 树底层原理与实现

C++ 中 AVL 树的底层原理与实现。AVL 树是一种自平衡二叉搜索树,通过平衡因子控制左右子树高度差不超过 1。文章涵盖了 AVL 树的概念、三大性质、节点结构定义、插入逻辑及四种旋转操作(左单旋、右单旋、左右双旋、右左双旋)以维持平衡。此外还展示了查找、遍历、高度和大小计算等功能的代码实现,并分析了其查找效率高但插入删除涉及旋转操作较复杂的优缺点。

RustyLab发布于 2026/3/27更新于 2026/10/24106 浏览
C++ AVL 树底层原理与实现

C++ AVL 树底层原理与实现

AVL 树示意图

一、AVL 树的概念

AVL 树是一种高度平衡的平衡二叉树,相比于搜索二叉树,它的特点在于左右子树都为 AVL 树且树的高度差的绝对值不超过 1。

这里我们会引入一个新的概念叫做平衡因子。平衡因子也就是左右子树的高度差,我们可以通过平衡因子方便我们后续去观察和控制树是否平衡。

平衡因子示例

二、AVL 树的性质

AVL 树主要有三大性质:

  1. 每棵树的左右子树都是 AVL 树。
  2. 左子树和右子树的高度之差的绝对值不超过 1。
  3. 每个节点都会有一个平衡因子,且任何一个节点的平衡因子都为 1、0、-1。

三、AVL 树的实现

1. 树的基本结构

AVL 树的结点包含了左右节点的指针以及父亲节点的指针,同时还有 key、value 以及代表树平衡的平衡因子。

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) {}
};

2. 树的插入

树的插入按照搜索二叉树的规则进行插入。插入节点后更新平衡因子,如果没有违反规则 (即没有导致节点的平衡因子变成 2/-2),则插入结束;如果违反规则,则树会不平衡,需要进行旋转操作。

平衡因子的更新规则:

  1. 平衡因子 = 右子树高度 - 左子树高度。
  2. 只有子树高度的变化才会影响当前节点的平衡因子。
  3. 插入节点后会增加数的高度,若新增节点在 parent 的右子树,则 parent 的平衡因子 ++,相反在 parent 的左子树时,则平衡因子 --。

每更新完一个节点的平衡因子都需要进行以下判断:

  1. 如果 parent 的平衡因子等于 1/-1 时,说明 parent 原先的平衡因子为 0,插入节点后左子树或右子树的高度增加了,说明还需要向上更新平衡因子。
  2. 如果 parent 的平衡因子等于 0,说明 parent 原先的平衡因子为 1/-1,插入节点后左右两棵子树从不平衡变平衡了,说明无需更新平衡因子。
  3. 如果 parent 的平衡因子等于 2/-2 时,说明 parent 原先的平衡因子等于 1/-1,插入节点后插入到了较高的那一棵子树,说明此时以 parent 为根节点的子树已经不平衡了,需要进行旋转处理。

插入过程示意图

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 < 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) {
                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);
            }
        } else {
            assert(false);
        }
        break;
    }
    return true;
}

3. 树的旋转

旋转的目的就是让树从失衡到平衡,降低树的高度。 旋转主要分为四种,分别为左单旋、右单旋、左右双旋和右左双旋。下面我们具体讲讲每一种旋转的内部逻辑。

• 左单旋

条件:新节点插入到子树较高的右侧。 我们用图来感受一下其旋转的过程:

  1. 先将 15 的左子树的节点 12 变成 10 的右子树。
  2. 再将 10 变成 15 的左子树,15 成为新树的根节点。
  3. 更新平衡因子。

由于左单旋的情况很多,我们也可以用一张抽象图来表示:

左单旋示意图

代码所示:

// 左单旋
void RotateL(Node* parent) {
    Node* subR = parent->_right;
    Node* subRL = subR->_left;
    parent->_right = subR;
    if (subRL) subRL->_parent = parent;
    Node* pparent = parent->_parent;
    if (pparent == nullptr) {
        _root = subR;
        subR->_parent = nullptr;
    } else {
        if (pparent == parent->_right) {
            parent->_right = subR;
        } else {
            parent->_left = subR;
        }
        subR->_parent = pparent;
    }
    parent->_bf = subR->_bf = 0;
}
• 右单旋

条件:新节点插入到子树较高的左侧 用图来感受旋转的过程:

右单旋示意图

我们会发现和左单旋和相似

  1. 先将 5 的右子树的值 b 变成 10 的左子树。
  2. 再将 10 变成 5 的右子树,旋转完后 5 成为整棵树的根节点。
  3. 更新平衡因子。 代码所示:
// 右单旋
void RotateR(Node* parent) {
    Node* subL = parent->_left;
    Node* subLR = subL->_right;
    parent->_left = subLR;
    if (subLR) subLR->_parent = parent;
    Node* pparent = parent->_parent;
    subL->_right = parent;
    parent->_parent = subL;
    if (pparent == nullptr) {
        _root = subL;
        subL->_parent = nullptr;
    } else {
        if (pparent == parent->_left) {
            parent->_left = subL;
        } else {
            parent->_right = subL;
        }
        subL->_parent = pparent;
    }
    parent->_bf = subL->_bf = 0;
}
• 左右双旋

条件:新节点插入到较高左子树的右侧。 下面我们用图来演示一下其旋转过程:

  1. 插入新节点

左右双旋步骤 1

  1. 以节点 5 为旋转点进行左单旋

左右双旋步骤 2

  1. 以节点为 10 进行右单旋

左右双旋步骤 3

  1. 旋转完后更新平衡因子。 平衡因子又分为三种情况:
  2. 当 subLR 的平衡因子为 -1 时,左右双旋后 parent、subL,subLR 的平衡因子分别为 1、0、0。
  3. 当 subLR 的平衡因子为 1 时,左右双旋后 parent、subL,subLR 的平衡因子分别为 0、-1、0。
  4. 当 subLR 的平衡因子为 0 时,左右双旋后 parent、subL,subLR 的平衡因子分别为 0、0、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) {
        subLR->_bf = 0;
        subL->_bf = 0;
        parent->_bf = 1;
    } else if (bf == 1) {
        subLR->_bf = 0;
        subL->_bf = -1;
        parent->_bf = 0;
    } else if (bf == 0) {
        subLR->_bf = 0;
        subL->_bf = 0;
        parent->_bf = 0;
    } else {
        assert(false);
    }
}
• 右左双旋

条件:插入到较高右子树的左侧 其旋转过程和左右双旋类似,这就不一一列举了。 旋转完过后也是需要更新平衡因子,平衡因子也是跟左右双旋一样有三种情况。 代码所示:

// 右左双旋
void RotateRL(Node* parent) {
    Node* subR = parent->_right;
    Node* subRL = subR->_left;
    int bf = subRL->_bf;
    RotateR(parent);
    RotateL(parent);
    if (bf == 0) {
        subR->_bf = 0;
        subRL->_bf = 0;
        parent->_bf = 0;
    } else if (bf == 1) {
        subR->_bf = 0;
        subRL->_bf = 0;
        parent->_bf = -1;
    } else if (bf == -1) {
        subR->_bf = 1;
        subRL->_bf = 0;
        parent->_bf = 0;
    } else {
        assert(false);
    }
}

四、AVL 树的其它功能

1. 树的查找

定义一个 cur 指针从树的根节点开始查找,按一下规则进行查找:

  1. 当 key 的值小于当前节点的值时,则在该节点的左边进行查找。
  2. 当 key 的值大于当前节点的值时,则在该节点的右边进行查找。
  3. 若 key 的值等于当前节点的值时,则说明查找成功,返回 true。
  4. 若遍历完还没查找到该节点的值,则说明没有此节点,返回 false。 代码所示:
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;
}

2. 树的遍历

我们遍历方式有前序、中序、后序、层序等方式,我们在这就采用中序遍历的方式来遍历树的每一节点。 代码所示:

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

3. 树的高度

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;
}

4. 树的大小

返回左子树 + 右子树再加上根节点即可。

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

五、总结

1. AVL 树的优缺点

优点:

  1. 查找效率高: 由于 AVL 树总是保持平衡,其高度相对较低,因此查找操作的时间复杂度为 O(log N),效率较高。
  2. 结构稳定: AVL 树的平衡性使得其结构相对稳定,不会出现极端不平衡的情况,从而保证了操作的稳定性和可靠性。

缺点:

  1. 插入和删除复杂: AVL 树在插入和删除节点时,可能需要通过旋转操作来保持树的平衡,比较复杂。
  2. 可能导致性能下降: 在频繁插入和删除的场景下,AVL 树需要不断地进行旋转操作来保持平衡,这就有可能导致性能降低。

2. 完整代码

#include <iostream>
#include <assert.h>
using namespace std;

template <class K, class V>
struct AVLTreeNode {
    // 需要 parent 指针,后续更新平衡因子可以看到
    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 {
    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 < 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) {
                    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);
                }
            } else {
                assert(false);
            }
            break;
        }
        return true;
    }

    // 右单旋
    void RotateR(Node* parent) {
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
        parent->_left = subLR;
        if (subLR) subLR->_parent = parent;
        Node* pparent = parent->_parent;
        subL->_right = parent;
        parent->_parent = subL;
        if (pparent == nullptr) {
            _root = subL;
            subL->_parent = nullptr;
        } else {
            if (pparent == parent->_left) {
                parent->_left = subL;
            } else {
                parent->_right = subL;
            }
            subL->_parent = pparent;
        }
        parent->_bf = subL->_bf = 0;
    }

    // 左单旋
    void RotateL(Node* parent) {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
        parent->_right = subR;
        if (subRL) subRL->_parent = parent;
        Node* pparent = parent->_parent;
        if (pparent == nullptr) {
            _root = subR;
            subR->_parent = nullptr;
        } else {
            if (pparent == parent->_right) {
                parent->_right = subR;
            } else {
                parent->_left = subR;
            }
            subR->_parent = pparent;
        }
        parent->_bf = subR->_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) {
            subLR->_bf = 0;
            subL->_bf = 0;
            parent->_bf = 1;
        } else if (bf == 1) {
            subLR->_bf = 0;
            subL->_bf = -1;
            parent->_bf = 0;
        } else if (bf == 0) {
            subLR->_bf = 0;
            subL->_bf = 0;
            parent->_bf = 0;
        } else {
            assert(false);
        }
    }

    // 右左双旋
    void RotateRL(Node* parent) {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
        int bf = subRL->_bf;
        RotateR(parent);
        RotateL(parent);
        if (bf == 0) {
            subR->_bf = 0;
            subRL->_bf = 0;
            parent->_bf = 0;
        } else if (bf == 1) {
            subR->_bf = 0;
            subRL->_bf = 0;
            parent->_bf = -1;
        } else if (bf == -1) {
            subR->_bf = 1;
            subRL->_bf = 0;
            parent->_bf = 0;
        } else {
            assert(false);
        }
    }

    void Inorder() {
        _Inorder(_root);
        cout << endl;
    }

    void Hight() {
        return _Hight(_root);
    }

    void Size() {
        return _Size(_root);
    }

private:
    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;
    }

    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:
    Node* _root = nullptr;
};

目录

  1. C++ AVL 树底层原理与实现
  2. 一、AVL 树的概念
  3. 二、AVL 树的性质
  4. 三、AVL 树的实现
  5. 1. 树的基本结构
  6. 2. 树的插入
  7. 3. 树的旋转
  8. • 左单旋
  9. • 右单旋
  10. • 左右双旋
  11. • 右左双旋
  12. 四、AVL 树的其它功能
  13. 1. 树的查找
  14. 2. 树的遍历
  15. 3. 树的高度
  16. 4. 树的大小
  17. 五、总结
  18. 1. AVL 树的优缺点
  19. 2. 完整代码

更多推荐文章

查看全部
  • OpenClaw 系统架构分析
  • 鸿蒙金融理财全栈项目:风险控制、合规审计与产品创新
  • Hunyuan-MT-7B-WEBUI 企业级翻译部署与实战
  • Java 面试题及答案(208 道)
  • 线性 DP 五大经典模型:LIS、LCS、合唱队形、编辑距离详解
  • C++ STL 进阶:unordered_set 与 unordered_map 用法及底层模拟
  • C++ 类型转换与 IO 流核心解析
  • OpenAI Whisper 语音转文字完全使用指南
  • Qwen3-VL WEBUI 环境配置与部署实战
  • 闲置 Mac Mini 部署 OpenClaw 实现金融 AI 分析助手
  • Python 机器人避障算法实战:掌握 5 种核心算法与智能路径规划
  • 30 岁程序员转行大模型:可行性分析与职业转型建议
  • Qwen-Multiple-Angles:角色与产品多视角生成插件,96 种相机角度控制
  • 大型语言模型(LLM)训练全流程指南
  • 计算机专业大学生必参加的六大编程竞赛指南
  • IntelliJ IDEA 安装与配置 Java 17 指南
  • VS Code 中配置 GitHub Copilot Agent Skills 指南
  • 自然语言处理在医疗领域的应用与实战
  • 人工智能产品经理:AI 时代的产品经理进阶手册
  • Flutter 实现 TabBar 主菜单功能开发指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online