图解平衡二叉搜索树

gitee仓库:https://gitee.com/WangZihao64/data-structure-and-algorithm/tree/master/avl

有如下一棵树(采用加入左结点平衡因子-1,加入右结点平衡因子+1的方式):

在这里插入图片描述

插入有以下几种情况:

1.平衡因子变为2

在这里插入图片描述

2.平衡因子变为0

在这里插入图片描述

3.平衡因子需要向上延伸(最多可延伸至父结点)

在这里插入图片描述

插入和二叉搜索树一样,平衡因子的更新规律如上图

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->left;
            }
            else if(cur->_kv.first<kv.first)
            {
                parent=cur;
                cur=cur->right;
            }
            //遇到相同的值,返回false
            else
            {
                return false;
            }
        }
        cur=new Node(kv);
        if(kv.first>parent->_kv.first)
        {
            parent->right=cur;
            cur->parent=parent;
        }
        else
        {
            parent->left=cur;
            cur->parent=parent;
        }
        //parent最多可以走到根节点
        while(parent)
        {
            //parent的左孩子,_bf-1
            //否则_bf+1
            if(parent->left==cur)
            {
                --parent->_bf;
            }
            else if(parent->right==cur)
            {
                ++parent->_bf;
            }
            //parent如果是0,表明是平衡的,不需要做任何操作,直接退出
            if(parent->_bf==0)
            {
                break;
            }
            else if(parent->_bf==-1||parent->_bf==1)
            {
                //如果不平衡,也会影响到父节点的_bf
                cur=parent;
                parent=parent->parent;
            }
            //如果是2/-2就需要特殊处理
            else if(parent->_bf==2||parent->_bf==-2)
            {
                
            }
        }
        return true;
    }

特殊处理有4中情况

  • 新节点插入较高左子树的左侧—左左:右单旋

在这里插入图片描述

上图在插入前,AVL树是平衡的,新节点插入到30的左子树(注意:此处不是左孩子)中,30左
子树增加了一层,导致以60为根的二叉树不平衡,要让60平衡,只能将60左子树的高度减少一层,右子树增加一层,即将左子树往上提,这样60转下来,因为60比30大,只能将其放在30的右子树,而如果30有右子树,右子树根的值一定大于30,小于60,只能将其放在60的左子树,旋转完成后,更新节点的平衡因子即可。在旋转过程中,有以下几种情况需要考虑:
1. 30节点的右孩子可能存在,也可能不存在
2. 60可能是根节点,也可能是子树
如果是根节点,旋转完成后,要更新根节点
如果是子树,可能是某个节点的左子树,也可能是右子树

//右旋和左旋是一样的
    void RotateR(Node* parent)
    {
        Node* subL=parent->left;
        Node* subLR=subL->right;
        Node* pparent=parent->parent;
        parent->left=subLR;
        subL->right=parent;
        if(subLR!= nullptr)
        {
            subLR->parent=parent;
        }
        parent->parent=subL;
        subL->parent=pparent;
        if(pparent== nullptr)
        {
            _root=subL;
        }
        else if(pparent!= nullptr)
        {
            if(pparent->left==parent)
            {
                pparent->left=subL;
            }
            else if(pparent->right==parent)
            {
                pparent->right=subL;
            }
        }
        parent->_bf=subL->_bf=0;
    }
  • 新节点插入较高右子树的右侧—右右:左单旋

在这里插入图片描述

void RotateL(Node* parent)
    {
        Node* subR=parent->right;
        //右子树的左子树
        Node* subRL=subR->left;
        //旋转的这棵树有可能是子树,所以需要保存parent的parent
        Node* pparent=parent->parent;
        parent->right=subRL;
        subR->left=parent;
        //subLR可能不存在,需要单独判断
        if(subRL!= nullptr)
        {
            subRL->parent = parent;
        }
        parent->parent=subR;
        subR->parent=pparent;
        //如果parent不是子树,就需要把_root给subR
        if(pparent== nullptr)
        {
            _root=subR;
        }
        //如果parent是子树的话,需要改变pparent的指向
        else if(pparent!= nullptr)
        {
            //子树是pparent的左子树/右子树,需要把pparent-left/->right指向subR
            if(pparent->left==parent)
            {
                pparent->left=subR;
            }
            else if(pparent->right==parent)
            {
                pparent->right=subR;
            }
        }
        //更新平衡因子
        subR->_bf=parent->_bf=0;
    }
  • 新节点插入较高左子树的右侧—左右:先左单旋再右单旋

在这里插入图片描述

注意:要提前记录60的平衡因子,60这个结点的平衡因子如果是-1,90的平衡因子就会+1,30、60的平衡因子是0,如果平衡因子是1的话,30的平衡因子会-1,60,90的平衡因子是0,但如果平衡因子是0的话,那么30,60,90的平衡因子也是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)
        {
            parent->_bf=0;
            subLR->_bf=0;
            subL->_bf=-1;
        }
        else if(bf==-1)
        {
            parent->_bf=1;
            subLR->_bf=0;
            subL->_bf=0;
        }
        else
        {
            assert(false);
        }
  • 新节点插入较高右子树的左侧—右左:先右单旋再左单旋

在这里插入图片描述

void RotateRL(Node* parent)
    {
        Node* subR=parent->right;
        Node* subRL=subR->left;
        int bf=subRL->_bf;
        RotateR(parent->right);
        RotateL(parent);
        //可以都更新为0
        if(bf==0)
        {
            subR->_bf=0;
            subRL->_bf=0;
            parent->_bf=0;
        }
        //bf=1,会导致parent少一个右结点
        else if(bf==1)
        {
            subR->_bf=0;
            parent->_bf=-1;
            subRL->_bf=0;
        }
        //bf=-1,会让subR少一个左结点
        else if(bf==-1)
        {
            subR->_bf=0;
            parent->_bf=0;
            subR->_bf=1;
        }
        else
        {
            //出错
            assert(false);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值