浅析AVL树

1.为什么提出AVL树

学习完搜索二叉树以后,我们应该想到一个问题,如果我们的搜索二叉树的趋向于单链的形式,类似于:
这里写图片描述
这两种情况随之而来的是时间复杂度太高,当我们在这两种当中搜索,时间复杂度为O(N),所以这样效率大大下降了!


2.二叉平衡树概念和结构

为了解决上述问题,所以提出了一个概念,叫做二叉平衡树。

二叉平衡树,相对于二叉搜索树,引入了一个叫做平衡因子的概念。
平衡因子:平衡因子就是右子树的深度-左子树的深度。

二叉平衡树的规则是:每一个节点的平衡因子的绝对值都要小于2。

所以我们需要给一个平衡因子。

二叉平衡树的结构:

template<typename K,typename V>
struct AVLBinaryTreeNode
{
    typedef AVLBinaryTreeNode<K, V> Node;
    AVLBinaryTreeNode(const K& key,const V& value)
        :_left(NULL)
        ,_right(NULL)
        , _parent(NULL)
        ,_key(key)
        , _bf(0)
        , _value(value)
    {}
    Node* _left;
    Node* _right;
    Node* _parent;

    int _bf;    //来保存右子树高度-左子树高度,平衡因子。
    K _key;
    V _value;
};

3.二叉平衡树的平衡化旋转

对于二叉平衡树来说,最重要的就是它的旋转算法,因为他要保证所有节点的平衡因子保持在0,1,-1,所以当平衡因子为2或者-2的时候,我们需要调整,这个时候就引入了旋转这个概念。

例如:
这里写图片描述
比如上面的这个例子,我们可以看出来在这我们的3已经不满足二叉平衡树的条件了,所以在这里我们应该进行旋转。

另外在这里需要知道,对于叶子节点,它的平衡因子都是0。


单旋转

首先我们来看单旋转,单旋转分为两种情况:左单旋和右单旋。

这里写图片描述

示例代码:

//左旋
    void _RotateL(Node *parent)
    {
        assert(parent);

        Node* SubR = parent->_right;
        Node* SubRL = SubR->_left;
        parent->_right = SubRL;
        if (SubRL)
            SubRL->_parent = parent;
        Node * ppNode = parent->_parent;            //记录根的父亲    
        SubR->_left = parent;       
        parent->_parent = SubR;
        if (ppNode==NULL)                       //考虑根节点的情况
        {
            _root = SubR;
            SubR->_parent = NULL;
        }
        else
        {
            if (ppNode->_left == parent)           //判断旋转以后的根应该链接在根的父亲的那边
            {
                ppNode->_left = SubR;
            }
            else
            {
                ppNode->_right = SubR;
            }
            SubR->_parent = ppNode;
        }

        parent->_bf = SubR->_bf = 0;              //重置平衡因子

    }
//右旋
    void _RotateR(Node *parent)
    {
        Node *SubL = parent->_left;
        Node *SubLR = SubL->_right;
        parent->_left = SubLR;
        if (SubLR)                  
        {
            SubLR->_parent = parent;
        }
        Node* ppNode = parent->_parent;         //记录根节点父亲
        SubL->_right = parent;
        parent->_parent = SubL;
        if (ppNode == NULL)                     //考虑根节点情况
        {
            _root = SubL;
            SubL->_parent = NULL;
        }
        else
        {
            if (ppNode->_left == parent)            //判断旋转以后的根应该链接在根的父亲的那边
            {
                ppNode->_left = SubL;
            }
            else
            {
                ppNode->_right = SubL;
            }
            SubL->_parent = ppNode;
        }


        parent->_bf = SubL->_bf = 0;               //重置平衡因子
    }

双旋转

平衡二叉树有时的旋转是双旋转,双旋转有一个特点,就是他的parent节点和下一个节点平衡因子需要异号。

左右双旋转

这里写图片描述
在这,根据sublr的平衡因子不同,分为了3种情况进行插入。接下来,我们对每一种情况进行分析,讲解。

  • 第一种情况:
    是sublr的平衡因子为0,这个时候就可以把sublr当作一个要插入的叶子节点来理解,这样subl的平衡因子为1,parentde平衡因子为-2,这样,就构成了双旋转。
    这里写图片描述

  • 第二种情况:
    sublr的平衡因子为-1,这个时候就是在sublr的左子树进行插入节点操作,这样,subl的平衡因子变为1,parent的平衡因子变为-2。
    这里写图片描述

  • 第三种情况:
    第三种情况所说的就是sublr的平衡因子为1,这个时候插入点是sublr的右树。这样,subl的平衡因子变为1,parent的平衡因子变为-2。
    这里写图片描述

上述的就是进行右左双旋时的三种情况,我们可以画一个表格总结下:
这里写图片描述

示例代码:

//左右双旋
    void _RotateLR(Node *parent)
    {
        //进行双旋转的时候通过在这里的旋转以后的根节点的bf进行判断
        Node* SubL = parent->_left;
        Node* SubLR = SubL->_right;
        //计算旋转以后的根节点的bf
        int bf = SubLR->_bf;

        _RotateL(SubL);
        _RotateR(parent);
        //如果bf为0,代表插入点就是这个节点,这个时候所有旋转后的bf皆为0
        if (bf == 0)
        {
            SubL->_bf = parent->_bf = 0;
        }
        //当bf为1时,这个时候相当于是在给bf的右树插入,所以插入以后subL旋转后的右边高度为h,

        else if (bf == 1)
        {
            SubL->_bf = -1;
            parent->_bf = 0;
        }
        //当bf为-1,这时相当于是给bf的左树进行插入
        else
        {
            SubL->_bf = 0;
            parent->_bf = 1;
        }
        SubLR->_bf = 0;
    }
右左双旋转

接下来我们进行另外一种双旋转的分析,右左双旋,有了上面分析的基础,我相信,下面的分析,你一看就能理解,通过单旋转,我们就可以看到,旋转是镜像的。
这里写图片描述
- subrl的bf==0
这里写图片描述
- subrl的bf==1

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值