【AVL树】AVL树的插入操作以及旋转

在讲解AVL树之前必须了解二叉搜索树,
可以看我之前的博客:二叉搜索树

AVL树是在二叉搜索树的基础上,在向二叉树排序树中插入新的结点,如果保证每个结点的左右子树的高度差的绝对值不超过1,即需要在插入的时候判断是否满足条件,然后对树进行旋转,从而可减低树的高度,减少平均搜索长度。

AVL树:(1)它的左右子树都是AVL树
(2)右子树和左子树的高度差(平衡因子)的绝对值不能超过1 (1 0 -1)。

给出实现AVL树的节点

template<class T>
struct BlancedBianryTreeNode
{
    BlancedBianryTreeNode(T data)
    : _data(data)
    , _bf(0)
    , _pLeft(NULL)
    , _pRight(NULL)
    , _pParent(NULL)
    {}
    T _data;
    int _bf; //平衡因子:右子树的高度-左子树的高度
    BlancedBianryTreeNode<T>* _pLeft;
    BlancedBianryTreeNode<T>* _pRight;
    BlancedBianryTreeNode<T>* _pParent;
};

下面讲解如何向平衡二叉树中插入一个元素, 大神勿喷。
步骤1:如果是空树,插入后即为根节点,插入后直接返回
步骤2:如果树不空,寻找插入位置,若在寻找的过程中找到key,则插入失败直接返回(注:与二叉搜索树的插入操作一样)
步骤3:插入结点,链接到树上,
步骤4:更新平衡因子
步骤5:对树进行调整:旋转

步骤2详解:在找的过程中,需要标记其找入插入位置的父节点pParent,因为在插入新节点pCur的时候,需要判断插入的位置(插入到父节点的左边还是右边)。直接看下面的代码

步骤4详解:插入新元素后该元素的平衡因子为0,但是其父节点的平衡因子可能改变,所以需要更新其平衡因子bf。
如果pCur为父节点pParent的左孩子,则pParent->_bf–;
如果pCur为父节点pParent的右孩子,则pParent->_bf++;
一直向上更新,直到更新到根节点或者父节点的bf为0。
如果父节点的bf为2或者-2的话,就不保持平衡,需要进行旋转。

步骤5详解 旋转有四种旋转方式:左旋,右旋,左右旋,右左旋
(1)左旋分为三种情况:
《【AVL树】AVL树的插入操作以及旋转》


步骤一:先把SubRL链接到pParent上,更新SubRL的父节点,注意SubRL可能为空
步骤二:修改subR,挂到原子树上,注意先得保存pParent的父节点,注意pParent可能为根节点,其父节点为NULL
步骤三:修改pParent,挂接到subR上
步骤四:更新pParent和SubR的平衡因子
从图中可以看出:三种方式的左旋后pParent和SubR的平衡因子都为0。所以旋转后,再更新pParent和SubR的平衡因子。
什么时候左旋:pParent->bf 为2 ,SubR->bf 为1。

代码

    void _RotateL(Node* pParent) //左旋
    {
        Node* SubR = pParent->_pRight; //当前节点pParent的左孩子
        Node* SubRL = SubR->_pLeft; //可能为空
        //先将 SubRL 链接到
        pParent->_pRight = SubRL;
        if (SubRL) //第三种情况
        {
            SubRL->_pParent = pParent;
        }
        //修改SubR
        Node *pPparent = pParent->_pParent;  //pParent可能为根节点
        SubR->_pParent = pPparent; //修改SubR的父节点
        //把SubR链接到原子树上
        if (pPparent == NULL)//可能为根节点
        {
            _pRoot = SubR;
        }
        else
        {
            if (pParent == pPparent->_pLeft) 
                pPparent->_pLeft = SubR;
            else
                pPparent->_pRight = SubR;
        }

        //修改pParent,
        SubR->_pLeft = pParent;
        pParent->_pParent = SubR;

        //更新SubR和pParent的平衡因子
        SubR->_bf = pParent->_bf = 0;
    }

(2)右旋:和左旋相似

《【AVL树】AVL树的插入操作以及旋转》

直接贴代码:步骤和左旋类似
什么时候左旋:pParent->bf 为-2 ,SubL->bf 为-1

void _RotateR(Node* pParent) //右旋
    {
        Node* SubL = pParent->_pLeft;
        Node* SubLR = SubL->_pRight; //可能为第三种情况

        //修改SubLR
        pParent->_pLeft = SubLR;
        if (SubLR)
        {
            SubLR->_pParent = pParent;
        }
        //修改subL
        Node* pPparent = pParent->_pParent;
        SubL->_pParent = pPparent;
        //subL链接到原子树上
        if (pPparent == NULL)
            _pRoot = SubL;
        else
        {
            if (pPparent->_pLeft == pParent)
                pPparent->_pLeft = SubL;
            else
                pPparent->_pRight = SubL;
        }
        //修改pparent
        SubL->_pRight = pParent;
        pParent->_pParent = SubL;

        //更新subl和pparent的平衡因子
        SubL->_bf = pParent->_bf = 0;
    }

(3)左右旋:先进行左旋,再进行右旋
《【AVL树】AVL树的插入操作以及旋转》

分析每种情况的pParent和SubL和SubLR的平衡因子,
找到规律,当未更新subLR->bf = -1时,
pParent->bf = 1; SubL->bf = 0;
当未更新subLR->bf = 1时,pParent->bf = 0; SubL->bf = -1;
**左右旋的时机:**pParent->bf 为-2 ,SubR->bf 为1

    void _RotateLR(Node* pParent) //左右旋 分三种
    {
        //根据 subLR的平衡因子:
        int bs = pParent->_pLeft->_pRight->_bf;
        Node* SubL = pParent->_pLeft;
        _RotateL(pParent->_pLeft);
        _RotateR(pParent);
        if (bs == 1)
        {
            pParent->_bf = 0;
            SubL->_bf = -1;
        }
        else if (bs == -1)
        {
            pParent->_bf = 1;
            SubL->_bf = 0;
        }

(4)右左旋:与左右旋类似,
《【AVL树】AVL树的插入操作以及旋转》

分析每种情况的pParent和SubR和SubRL的平衡因子,与左旋旋正好相反
找到规律,当未更新subRL->bf = -1时,
pParent->bf = 0; SubR->bf = 1;
当未更新subRL->bf = 1时,pParent->bf = -1; SubR->bf = 0;
右左旋时机:pParent->bf 为2 ,SubR->bf 为-1

void _RotateRL(Node* pParent) //右左旋转
    {
        int bs = pParent->_pRight->_pLeft->_bf;
        Node* SubR = pParent->_pRight;
        _RotateR(pParent->_pRight);
        _RotateL(pParent);
        if (bs == 1)
        {
            pParent->_bf = -1;
            SubR->_bf = 0;
        }
        else if (bs == -1)
        {
            pParent->_bf = 0;
            SubR->_bf = 1;
        }
    }

插入操作的整体代码:

bool Insert(T data)
    {
        if (_pRoot == NULL)
        {
            _pRoot = new Node(data);
            return true;
        }
        Node* pCur = _pRoot;
        Node* pParent = NULL;
        while (pCur)
        {
            if (data < pCur->_data)
            {
                pParent = pCur;
                pCur = pCur->_pLeft;
            }
            else if (data > pCur->_data)
            {
                pParent = pCur;
                pCur = pCur->_pRight;
            }
            else
                return false;
        }
        //找到插入节点,
        pCur = new Node(data);
        pCur->_pParent = pParent;
        if (data > pParent->_data)
            pParent->_pRight = pCur;
        else
            pParent->_pLeft = pCur;

        //更新平衡因子
        while (pParent)
        {
            if (pCur == pParent->_pLeft)
                pParent->_bf--;         
            else
                pParent->_bf++;
            //平衡因子改变
            if (pParent->_bf == 0)
                return true;
            else if (pParent->_bf <-1 || pParent->_bf > 1)
                break;
            else
            {
                pCur = pParent;
                pParent = pCur->_pParent;
            }           
        }
        //跳出循环 pParent的因子可能为2 或者 -2 则进行旋转
        //旋转
        //可能pParent为空,不需要进行旋转
        if (pParent)
        {
            if (pParent->_bf == 2)
            {
                if (pCur->_bf == 1)//进行左旋
                    _RotateL(pParent);
                else
                    _RotateRL(pParent);
            }
            else if (pParent->_bf == -2)
            {
                if (pCur->_bf == -1)
                    _RotateR(pParent);
                else
                    _RotateLR(pParent);
            }
        }   
        return true;
    }

如果有错误,欢迎大家指出来。

    原文作者:AVL树
    原文地址: https://blog.csdn.net/wenqiang1208/article/details/70215247
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞