C++:AVL树实现

前言:

●在普通二叉搜索树的使用过程中,数据有序插入极易退化成一条单链表,查询、插入的时间复杂度直接劣化为O(N)O(N)O(N),效率大打折扣。为解决二叉搜索树的失衡缺陷,1962年前苏联科学家G. M. Adelson-VelskyE. M. Landis 正式提出AVL树,这也是计算机领域诞生的第一款自平衡二叉搜索树。

AVL树本质依旧是二叉搜索树 ,在此基础上新增了严苛的平衡约束:整棵树为空树,或是左右子树均为合法AVL树,且同一节点左右两棵子树的高度差值绝对值必须≤1 。为了直观管控平衡状态,我们引入平衡因子平衡因子 = 右子树高度 − 左子树高度,一颗合法AVL树所有节点的平衡因子只允许为-1、0、1三个数值。

●或许有人会产生疑问:平衡因子全部为0、左右子树高度完全一致,平衡效果不是最优吗?实际场景里该条件无法永久达成,像2个节点、4个节点的树形结构,天然无法做到左右高度完全相等,因此限定高度差不超过1是兼顾合理性与平衡性的最优方案。

●依靠这套平衡机制,AVL树的整体树高被稳定约束在log⁡N\log NlogN区间,插入、删除、查找所有操作的时间复杂度稳定维持O(log⁡N)O(\log N)O(logN),彻底解决了普通BST极端情况下效率崩盘的问题,也成为后续学习红黑树等平衡树结构的重要基石。


一、AVL树的结构

(1)代码:

cpp 复制代码
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:
    //...

private:
    Node* _root = nullptr;
};

(2)逐段详解

1、AVLTreeNode 节点结构体(每个树节点的成员)

成员 作用说明
pair<K, V> _kv 存储键值对,K为关键字(用于二叉搜索树排序),V为对应数据
_left / _right 左右孩子指针,搭建二叉树拓扑结构
_parent 父节点指针,核心作用:向上回溯更新祖先节点的平衡因子、失衡时向上查找需要旋转处理的节点
int _bf 平衡因子,标准定义:bf = 右子树高度 - 左子树高度;合法AVL节点bf只能是 -1、0、1
构造函数 初始化键值,左右、父指针全部置空,平衡因子默认初始化为0(新建叶子节点左右都为空,高度差为0)

2、外层 AVLTree 管理类

  1. typedef AVLTreeNode<K, V> Node;
    类型别名,简化代码书写,后续直接用Node代替长长的节点全名。
  2. 公开区域public:对外接口(插入、删除、查找、遍历等功能函数)
  3. 私有成员Node* _root = nullptr;
    整棵AVL树的根节点指针,初始为空代表空树,封装在private中禁止外部直接修改根节点。

二、AVL树的插入

(1)AVL树插入一个值的大概过程

  1. 按BST(二叉搜索树)规则插入新节点:比较key找到空位,生成叶子节点,双亲、左右指针绑定,平衡因子初始化为0。
  2. 沿父节点向上回溯更新平衡因子:新节点在左,祖先bf-1;在右,祖先bf+1。
  3. 校验平衡因子
    • 祖先bf为0:上层高度无变化,直接结束;
    • 祖先为±1:继续向上遍历;
    • 祖先为±2:节点失衡,进入旋转修正。
  4. 根据失衡类型执行对应旋转:右旋(RotateR)、左旋(RotateL)、左右旋(RotateLR)、右左旋(RotateRL),旋转后重置相关节点平衡因子。
  5. 旋转完成,整棵树恢复平衡,插入流程结束。

(2)平衡因更新

基础公式:

平衡因子 bfbfbf = 右子树高度 − 左子树高度

插入节点仅会升高对应分支高度,顺着父节点向上逐层修改bfbfbf:

  1. 新节点在父节点右子树 :父bf+1bf\boldsymbol{+1}bf+1
  2. 新节点在父节点左子树 :父bf−1bf\boldsymbol{-1}bf−1

三种终止更新的情况:

1. 更新后 bf = 0

插入补齐了矮的一侧,当前子树总高度不变,上层节点不受影响,更新停止。

  1. 更新后 bf = 1 / -1
    子树高度变大,会继续影响上层祖先,必须向上继续更新
  2. 更新后 bf = 2 / -2(失衡)
    子树失去平衡,执行对应旋转;旋转后子树高度复原,无需向上更新,插入流程直接结束。

如果一路更新到根节点,根节点bf为±1时直接终止更新。

(3)插入结点及更新平衡因子的代码实现

代码:

cpp 复制代码
bool Insert(const pair<K, V>& kv)
{
    // 1、空树,直接新建根节点
    if (_root == nullptr)
    {
        _root = new Node(kv);
        return true;
    }

    Node* parent = nullptr;
    Node* cur = _root;
    // 2、BST查找插入位置
    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;
        }
    }

    // 3、创建新节点,挂载到父节点左右孩子
    cur = new Node(kv);
    if (parent->_kv.first < kv.first)
    {
        parent->_right = cur;
    }
    else
    {
        parent->_left = cur;
    }
    cur->_parent = parent;

    // 4、向上回溯更新平衡因子
    while (parent)
    {
        // 新节点在左:bf-1;在右:bf+1
        if (cur == parent->_left)
            parent->_bf--;
        else
            parent->_bf++;

        if (parent->_bf == 0)
        {
            // 子树高度不变,无需向上更新,直接退出循环
            break;
        }
        else if (parent->_bf == 1 || parent->_bf == -1)
        {
            // 子树高度升高,继续向上更新祖先bf
            cur = parent;
            parent = parent->_parent;
        }
        else if (parent->_bf == 2 || parent->_bf == -2)
        {
            // 平衡被破坏,需要执行旋转矫正,跳出循环去处理旋转
            break;
        }
        else
        {
            // 合法AVL不会走到这里,断言报错
            assert(false);
        }
    }

    return true;
}

分段讲解

(1)BST标准插入逻辑(前半段代码)

  1. 树为空:直接new节点作为根节点。
  2. while(cur)遍历树:按照二叉搜索树规则,小值走左、大值走右,记录目标位置的父节点parent
  3. 遇到相等key:直接return false,AVL不允许重复关键字。
  4. 找到空位后创建新节点,绑定父子双向指针。

(2)平衡因子更新核心逻辑(while(parent)循环)

① 修改父节点平衡因子

新节点是父节点左孩子bf -= 1

新节点是父节点右孩子bf += 1

② 三种分支判断(对应之前的更新规则)

  1. parent->_bf == 0
    插入补齐了矮的一侧,当前子树整体高度没变,上层节点不受影响,break结束更新。
  2. parent->_bf == ±1
    当前子树高度增加,会影响上层所有祖先,把parent赋值给cur,继续向上循环更新祖辈平衡因子。
  3. parent->_bf == ±2
    树已经失衡,终止因子更新,跳出循环,后续编写左旋/右旋函数完成树的平衡修复。
  4. else分支:正常运行不会进入,用assert(false)捕获异常bug。

三、旋转

旋转主要讲右单旋(RotateR)和左右双旋(RotateLR),剩下的左单旋(RotateL)和右左双旋(RotateRL)和他们大同小异。

(1)右单旋

1、模型讲解

  1. 模型通用性

    以节点10(失衡节点parent)为根,a、b、c为三棵高度均为h的合法AVL子树,该抽象模型可以囊括所有LL失衡、需要右单旋的全部场景;节点10既可以是整棵树根,也可以只是树内某个局部子树根。

  2. 失衡产生原因

    a子树插入新节点 → a高度由h升高为h+1,向上逐层更新平衡因子,最终节点10平衡因子由-1变为-2,左右子树高度差大于1,触发AVL失衡。

  3. 右单旋核心操作(依托数值有序性:5 < b子树全部值 < 10)

    ① 将b(subLR)挂载为失衡节点parent(10)的左子树;

    ② 把原失衡节点parent(10)设置为subL(5)的右子树;

    subL(5)升级成为该局部树的新根。

    整套操作全程保留二叉搜索树的有序特性。

总模型:

  1. 旋转带来的效果
    ① 树形重新满足AVL平衡约束;
    ② 该局部树整体高度回落至插入节点前的数值;
    ③ 若10原本只是上层树的局部子树,本次旋转后无需再向上修正祖先平衡因子,插入流程直接结束。

情况1:

情况2:

情况3:

情况4:

情况···(以此类推)

一定要记住总模型

cpp 复制代码
void RotateR(Node* parent)
{
    Node* subL = parent->_left;
    Node* subLR = subL->_right;

    // 1、将subLR挂载到parent左孩子
    parent->_left = subLR;
    if (subLR)
        subLR->_parent = parent;

    Node* parentParent = parent->_parent;
    // 2、parent降级为subL的右孩子
    subL->_right = parent;
    parent->_parent = subL;

    // 3、subL对接上层节点parentParent
    if (parentParent == nullptr)
    {
        // parent原本是整棵树的根
        _root = subL;
        subL->_parent = nullptr;
    }
    else
    {
        // 判断原parent是上层的左/右孩子,替换成subL
        if (parent == parentParent->_left)
        {
            parentParent->_left = subL;
        }
        else
        {
            parentParent->_right = subL;
        }
        subL->_parent = parentParent;
    }

    // 4、LL右旋后两个节点平衡因子全部置0
    parent->_bf = subL->_bf = 0;
}

讲解:

①定义核心指针

cpp 复制代码
Node* subL = parent->_left;    // parent(失衡点)的左孩子,旋转后上位成为局部新根
Node* subLR = subL->_right;    // subL的右子树,需要过继给parent

②交接subLR子树

cpp 复制代码
parent->_left = subLR;
if (subLR)
    subLR->_parent = parent;

把subL原本的右子树subLR,交给失衡节点parent作为左孩子;

subLR不为空时必须修改自身父指针,双向绑定指针。

③parent降级,挂载到subL右侧

cpp 复制代码
subL->_right = parent;
parent->_parent = subL;

原失衡节点parent变成subL的右子节点,完成上下级互换。

④subL对接上层父节点 parentParent

  • if(parentParent == nullptr) :parent是整棵AVL树的根节点,直接把类成员_root更新为subL
  • else:判断原来parent是上层节点的左/右分支,用subL替换parent的位置,并赋值subL的父指针

⑤平衡因子刷新

cpp 复制代码
parent->_bf = subL->_bf = 0;

LL型右旋完成后,parent、subL左右子树高度全部平齐,平衡因子固定归零,上层不需要继续向上更新bf,插入流程直接结束。

(2)左单旋

总模型:

代码展示:

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

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

    Node* parentParent = parent->_parent;
    subR->_left = parent;
    parent->_parent = subR;

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

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

(3)左右双旋

通过情况1和情况2可以看到,左边高时,如果插入位置不是在a子树,而是插入在b子树,b子树高度从h变成h+1,引发旋转,右单旋无法解决问题 ,右单旋后,我们的树依旧不平衡 。右单旋解决的纯粹的左边高,但是插入在b子树中,10为跟的子树不再是单纯的左边高,对于10是左边高,但是对于5是右边高,需要用两次旋转才能解决 ,以5为旋转点进行一个左单旋,以10为旋转点进行一个右单旋,这棵树这棵树就平衡了。

图1和图2分别为左右双旋中h=0和h=1具体场景分析,下面我们将a/b/c子树抽象为高度h的AVL子树进行分析,另外我们需要把b子树的细节进一步展开为8和左子树高度为h-1的e和f子树,因为我们要对b的父亲5为旋转点进行左单旋,左单旋需要动b树中的左子树。b子树中新增结点的位置不同,平衡因子更新的细节也不同,通过观察8的平衡因子不同,这里我们要分三个场景讨论

  • 场景1:h >= 1时,新增结点插入在e子树,e子树高度从h-1并为h并不断更新8->5->10平衡因子,引发旋转,其中8的平衡因子为-1,旋转后8和5平衡因子为0,10平衡因子为1。

  • 场景2:h >= 1时,新增结点插入在f子树,f子树高度从h-1变为h并不断更新8->5->10平衡因子,引发旋转,其中8的平衡因子为1,旋转后8和10平衡因子为0,5平衡因子为-1。

  • 场景3:h == 0时,a/b/c都是空树,b自己就是一个新增结点,不断更新5->10平衡因子,引发旋转,其中8的平衡因子为0,旋转后8和10和5平衡因子均为0。

代码:

c 复制代码
void RotateLR(Node* parent)
{
    Node* subL = parent->_left;
    Node* subLR = subL->_right;
    int bf = subLR->_bf;

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

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

详细讲解:

1. 函数整体作用

RotateLR左右双旋 ,用于修正AVL树左子树的右子树插入节点 造成的LR失衡(父节点左孩子偏高、左孩子的右孩子偏高),修复树平衡并正确更新三个关键节点的平衡因子_bf

2. 变量定义

c 复制代码
Node* subL = parent->_left;    // parent的左孩子(第一次左旋的旋转支点)
Node* subLR = subL->_right;    // subL的右孩子,整个失衡结构的中间节点(最终上位的根)
int bf = subLR->_bf;           // 记录旋转前subLR的平衡因子,用来区分三种插入场景

3. 双旋转本体

c 复制代码
RotateL(parent->_left);  // 第一步:对parent的左子树执行【左单旋】,把LR型结构转为LL型
RotateR(parent);         // 第二步:对parent整体执行【右单旋】,LL型失衡直接修复

左右双旋固定顺序:先左旋左孩子 → 再右旋父节点

4. 依据bf分场景更新平衡因子(对应上图3种场景)

场景1:bf == 0(原文场景3,h=0,新增节点就是subLR本身)
c 复制代码
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 0;

插入点刚好是subLR节点,旋转后三个节点左右子树等高,平衡因子全部归零。

场景2:bf == -1(原文场景1,节点插入subLR的左子树e)
c 复制代码
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 1;

subLR左子树更高,旋转后:原左孩子subL平衡因子0、中间节点subLR平衡因子0、原父节点parent平衡因子1。

场景3:bf == 1(原文场景2,节点插入subLR的右子树f)
c 复制代码
subL->_bf = -1;
subLR->_bf = 0;
parent->_bf = 0;

subLR右子树更高,旋转后:原左孩子subL平衡因子-1、中间节点subLR平衡因子0、原父节点parent平衡因子0。

else分支

c 复制代码
assert(false);

正常插入导致的LR失衡,subLR的bf只可能是-1/0/1,其余数值属于程序异常,断言报错终止程序排查bug。

(4)右左双旋

跟左右双旋类似,下面我们将a/b/c子树抽象为高度h的AVL子树进行分析,另外我们需要把b子树的细节进一步展开为12和左子树高度为h-1的e和f子树,因为我们要对b的父亲15为旋转点进行右单旋,右单旋需要动b树中的右子树。b子树中新增结点的位置不同,平衡因子更新的细节也不同,通过观察12的平衡因子不同,这里我们要分三个场景讨论。

  • 场景1:h >= 1时,新增结点插入在e子树,e子树高度从h-1变为h并不断更新12->15->10平衡因子,引发旋转,其中12的平衡因子为-1,旋转后10和12平衡因子为0,15平衡因子为1。
  • 场景2:h >= 1时,新增结点插入在f子树,f子树高度从h-1变为h并不断更新12->15->10平衡因子,引发旋转,其中12的平衡因子为1,旋转后15和12平衡因子为0,10平衡因子为-1。
  • 场景3:h == 0时,a/b/c都是空树,b自己就是一个新增结点,不断更新15->10平衡因子,引发旋转,其中12的平衡因子为0,旋转后10和12和15平衡因子均为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 == 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树的查找

那二叉搜索树逻辑实现即可,搜索效率为O(logN)O(logN)O(logN)

代码:

c 复制代码
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;
}

五、AVL树平衡检测

代码:

cpp 复制代码
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 _IsBalanceTree(Node* root)
{
    // 空树也是AVL树
    if (nullptr == root)
        return true;

    // 计算root结点的平衡因子:root左右子树的高度差
    int leftHeight = _Height(root->_left);
    int rightHeight = _Height(root->_right);
    int diff = rightHeight - leftHeight;

    // 高度差绝对值≥2 → 树失衡,不是合法AVL
    if (abs(diff) >= 2)
    {
        cout << root->_kv.first << "高度差异异常" << endl;
        return false;
    }

    // 代码存储的平衡因子 和 实际计算高度差不一致,说明维护bf出错
    if (root->_bf != diff)
    {
        cout << root->_kv.first << "平衡因子异常" << endl;
        return false;
    }

    // 递归校验左右子树全部平衡,整棵树才是AVL树
    return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
}

逐段讲解:

1. _Height 函数:递归计算一棵树的实际高度

cpp 复制代码
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;
}
  • 递归基准:空节点高度 = 0
  • 后序递归:先算出左子树、右子树高度
  • 节点自身高度 = 较高子树高度 + 1(自身节点)
  • 用途:校验平衡因子是否正确,不能直接用节点存储的_bf当作真实高度差

2. _IsBalanceTree:递归校验整棵树是否为标准AVL树

① 基线条件
cpp 复制代码
if (nullptr == root)
    return true;

空树天然满足AVL平衡规则,直接返回true。

② 计算真实平衡差值
cpp 复制代码
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
int diff = rightHeight - leftHeight;

平衡因子定义:右子树高度 − 左子树高度 ,和你旋转代码里_bf定义保持统一。

③ 校验1:高度差是否超标
cpp 复制代码
if (abs(diff) >= 2)
{
    cout << root->_kv.first << "高度差异异常" << endl;
    return false;
}

AVL硬性规则:任意节点左右高度差绝对值必须<2,≥2说明旋转逻辑失效,树已经失衡。

④ 校验2:校验代码维护的_bf数值是否准确
cpp 复制代码
if (root->_bf != diff)
{
    cout << root->_kv.first << "平衡因子异常" << endl;
    return false;
}

插入/旋转时我们手动修改了每个节点的_bf变量,这里用真实高度差反向核对:

如果存起来的_bf和真实计算的diff对不上,代表更新平衡因子的代码写bug了

⑤ 递归校验左右子树
cpp 复制代码
return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);

当前节点合法,还要保证左、右两颗子树也全部合法;只有左右都返回true,整棵树才是合格AVL树。


六、完整代码与测试

AVLTree.h:

cpp 复制代码
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
template<class K, class V>

struct AVLTreeNode
{
	pair<K,V> _kv;
	AVLTreeNode<K,V>* _parent;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	int _bf;

	AVLTreeNode(const pair<K,V>& kv)
		:_kv(kv)
		,_parent(nullptr)
		,_left(nullptr)
		,_right(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* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (cur->_kv.first<kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if(kv.first < cur->_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);
				}
				break;
				
			}
			else
			{
				assert(false);
			}
		}
		return true;
	}

	//右单旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;
		Node* parentP = parent->_parent;
		subL->_right = parent;
		parent->_parent = subL;

		if (parent == _root)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (parentP->_left == parent)
			{
				parentP->_left = subL;
			}
			else
			{
				parentP->_right = subL;
			}
			subL->_parent = parentP;
		}
		parent->_bf = subL->_bf = 0;
	}

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

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

		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 == 0)
		{
			subL->_bf = 0;
			subLR->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 1)
		{
			subL->_bf = -1;
			subLR->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == -1)
		{
			subL->_bf = 0;
			subLR->_bf = 0;
			parent->_bf = 1;
		}
		else
		{
			assert(false);
		}
	}
	//右左双旋
	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;
		RotateR(parent->_right);
		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);
		}
	}
	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;
	}

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

	int Height()
	{
		return _Height(_root);
	}

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

	bool IsBalanceTree()
	{
		return _IsBalanceTree(_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;
	}

	bool _IsBalanceTree(Node* root)
	{
		if (nullptr == root)
			return true;
		// 计算pRoot结点的平衡因子:即pRoot左右子树的高度差
		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);
		int diff = rightHeight - leftHeight;

		// 如果计算出的平衡因子与pRoot的平衡因子不相等,或者
		// pRoot平衡因子的绝对值超过1,则一定不是AVL树
		if (abs(diff) >= 2)
		{
			cout << root->_kv.first << "高度差异常" << endl;
			return false;
		}

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

		// pRoot的左和右如果都是AVL树,则该树一定是AVL树
		return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
	}

private:
	Node* _root=nullptr;
};

test.c:

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include"AVLTree.h"
#include<vector>

// 测试代码
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;
}

// 插入一堆随机值,测试平衡,顺便测试一下高度和性能等
void TestAVLTree2()
{
    const int N = 100000;
    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 << "Insert:" << end2 - begin2 << endl;
    cout << t.IsBalanceTree() << endl;
    cout << "Height:" << t.Height() << endl;
    cout << "Size:" << t.Size() << endl;

    size_t begin1 = clock();
    // 确定在的值
    /*for (auto e : v)
    {
        t.Find(e);
    }*/
    // 随机值
    for (size_t i = 0; i < N; i++)
    {
        t.Find((rand() + i));
    }

    size_t end1 = clock();
    cout << "Find:" << end1 - begin1 << endl;
}
int main()
{
    //TestAVLTree1();
    TestAVLTree2();
	return 0;
}
相关推荐
ShineWinsu2 小时前
对于Linux:传输层协议UDP原理的解析
linux·c++·面试·udp·协议·传输层·计算机系统
LingzhiPi2 小时前
零知派ESP32--AS5600磁吸旋钮音量控制器
c++·单片机·嵌入式硬件
小保CPP2 小时前
OpenCV C++车型识别1-图像预处理
c++·人工智能·opencv·计算机视觉
库克克3 小时前
【C++】C++11 包装器function 与 绑定器 bind
开发语言·c++
小小龙学IT3 小时前
C++ Placement New 与显式析构:手动对象生命周期管理的艺术
c++·windows·mfc
小保CPP3 小时前
OpenCV C++车型识别2-形状匹配
c++·人工智能·opencv·计算机视觉
醉城夜风~5 小时前
手撕 C++ string:从零实现一个简易字符串类
开发语言·c++·算法
nLif5 小时前
进程管道通讯-伪终端方式
c++·windows
颜x小5 小时前
[C#]泛型类与泛型方法
开发语言·c++·c#