【C++ 学习 ㉒】- 超详解 AVL 树的插入、平衡调整以及删除(含源代码)

目录

一、AVL 树的概念

二、AVL 树节点的定义

三、AVL 树的插入

四、AVL 树的平衡调整

五、AVL 树的删除

六、AVL 树的实现

6.1 - AVL.h

6.2 - test.cpp


 


一、AVL 树的概念

二叉搜索树查找算法的性能取决于二叉树搜索树的形状,而二叉搜索树的形状则取决于数据集。如果数据呈有序排列,则二叉搜索树为单支树,查找的时间复杂度为 O(n);反之,如果二叉搜索树的形状合理,则查找速度较快,查找的时间复杂度为 O(log_2^n)。事实上,树的高度越小,查找速度越快,因此,希望二叉树的高度尽可能小。下面将讨论一种特殊类型的二叉搜索树,称为平衡二叉树(Balanced Binary Tree 或 Height-Balanced Tree),因由前苏联数学家 Adelson-VelskiiLandis 提出,所以又称 AVL 树

平衡二叉树或者是空树,或者是具有如下特征的二叉搜索树:

  1. 左子树和右子树的高度之差的绝对值不超过 1

  2. 左子树和右子树也是平衡二叉树。

若将二叉树上节点的平衡因子(BF, Balance Factor)定义为该节点左子树和右子树的高度之差,则平衡二叉树上的所有节点的平衡因子只可能是 -1、0 和 1。只要二叉树上有一个节点的平衡因子的绝对值大于 1,则该二叉树就是不平衡

例如,下面图 (a) 所示为两棵平衡二叉树,图 (b) 所示则为两棵不平衡的二叉树,节点中的值为该结点的平衡因子。

因为 AVL 树上任何节点的左右子树的高度之差都不超过 1,则可以证明它的深度和 log_2^n 是同数量级的(其中 n 为节点个数)。由此,其查找的时间复杂度是 O(log_2^n)。


二、AVL 树节点的定义

template<class K, class V>
struct AVLNode
{
    AVLNode<K, V>* _left;
    AVLNode<K, V>* _right;
    AVLNode<K, V>* _parent;
    std::pair<K, V> _kv;
    int _bf;
​
    AVLNode(const std::pair<K, V>& kv = std::pair<K, V>())
        : _left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _bf(0)
    { } 
};


三、AVL 树的插入

当新增的节点 *cur(指向节点的指针为 cur) 插入到其双亲节点 *parent(指向节点的指针为 parent)的左边时,双亲节点的平衡因子 +1;反之,当新增的节点插入到其双亲节点的右边时,双亲节点的平衡因子 -1。

  1. 若双亲节点的平衡因子原来为 -1 或者 1,在它的左边或者右边插入新增的节点后,它的平衡因子变为 0。由于以 *parent 为根节点的子树的高度没有发生变化,因此也不会影响除 *parent 以外的其它祖先节点的平衡因子,插入完成。

  2. 若双亲节点的平衡因子原来为 0,在它的左边或者右边插入新增的节点后,它的平衡因子变为 1 或者 -1。由于以 *parent 为根节点的子树的高度增高了 1,此时也就会影响其它祖先节点的平衡因子,需要往上更新。

    往上更新其它祖先节点的平衡因子的方式和一开始插入新增节点 *cur 时更新其双亲节点 *parent 的平衡因子的方式是一样的,即如果 *cur 在祖先节点的左子树中,则祖先节点的平衡因子 +1;如果 *cur 在祖先节点的右子树中,则祖先节点的平衡因子 -1。

    • 如果祖先节点的平衡因子被更新为 0,则说明更新完成了,也意味着插入完成了

    • 如果祖先节点的平衡因子被更新为 1 或者 -1,则说明还需要继续往上更新,直到某个祖先节点的平衡因子被更新为 0,或者直到更新完根节点,当根节点的平衡因子被更新为 0、1 或者 -1 时,意味着更新和插入也都完成了

    • 如果祖先节点的平衡因子被更新为 2 或者 -2(即其较高的子树增高了),就需要对以该节点为根节点的子树(称为最小不平衡子树)做平衡调整来恢复平衡


四、AVL 树的平衡调整

假设最小不平衡子树的根节点为 A,则失去平衡后进行调整的规律可归纳为下列 4 种情况。

  1. LL 型:由于在 A 左子树根节点的左子树上插入节点,A 的平衡因子由 1 增至 2,致使以 A 为根节点的子树失去平衡,则需进行一次向右的顺时针旋转操作

    void LL(Node* pA)
    {
        Node* pB = pA->_left;
        Node* pBR = pB->_right;
    ​
        pA->_left = pBR;
        if (pBR)
            pBR->_parent = pA;
    ​
        pB->_right = pA;
        Node* tmp = pA->_parent;
        pA->_parent = pB;
        pB->_parent = tmp;
    ​
        if (tmp == nullptr)  // 或者 _root == pA
        {
            _root = pB;
        }
        else
        {
            if (tmp->_left == pA)
                tmp->_left = pB;
            else
                tmp->_right = pB;
        }
    ​
        pA->_bf = pB->_bf = 0;
    }
  2. RR 型:由于在 A 的右子树根节点的右子树上插入节点,A 的平衡因子由 -1 变成 -2,致使以 A 为根节点的子树失去平衡,则需进行一次向左的逆时针旋转操作

    void RR(Node* pA)
    {
        No
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值