AVL树(旋转问题详解)

本文介绍了AVL树的概念,强调了平衡因子的重要性,解释了左单旋、右单旋、左右双旋和右左双旋四种旋转操作的原理。通过示例详细阐述了插入节点后如何判断和调整平衡树状态,以及旋转后的平衡因子更新。最后,提供了AVL树的实现代码并附带测试用例。
摘要由CSDN通过智能技术生成

二叉搜索树给我们了一个搜索一个点的方法,同时,也将二叉树中的节点排序了,但是,对于一些特殊的二叉树来说,使用二叉搜索树会很费空间,比如说:

左单支的这种情况,你说是二叉树,还不如说是单链表呢,还节省了一大波的空间呢(右指针域),同样的,对于右单支的情况也是如此,那么现在我们就要想能不能避免这个问题。

可以,一个平衡因子就可以搞定,加了平衡因子,那么这颗二叉搜索树就是AVLTree了

那么现在我们就来分析一下AVLTree,首先还是看一下平衡因子吧:平衡因子的实质就是右子树的高度减去左子树的高度的差值(当然你也可以用左减去右),既然是平衡因子,那么就有一个范围,这里的范围就是:-1,0,1。每一个结点的平衡因子都是这三个值中的一个,不是,那么就不平衡。那么为什么会是这三个呢?

下面我们来分析一下,首先0就不用说了,表示树的左右子树高度相等的,当然是平衡的,那么1和-1呢,这其实都是一个类型的,我们之说一个就行了,就按照-1来说吧,我们举一个最简单的例子。如果一个树只有两个节点,那么会出现什么结果呢?

这里写图片描述

这就是-1的结果,因此,所有,结点的右子树的高度减去左子树的高度的差值的绝对值如果大于1,那么他就不是平衡树。

了解了这些之后,我们就来构建一颗AVLTree吧

首先还是构建一个树的结点,其实二叉搜索树都已经写过了,这个都很简单了,这就比二叉搜索树多了一个平衡因子而已嘛。

template<class K, class V>
struct AVLTreeNode
{
   
    AVLTreeNode(const K& key, const V& value)
    : _pLeft(NULL)
    , _pRight(NULL)
    , _pParent(NULL)
    , _key(key)
    , _value(value)
    , _bf(0)
    {}


    AVLTreeNode<K, V> *_pLeft;  //左子树
    AVLTreeNode<K, V> *_pRight;  // 右子树
    AVLTreeNode<K, V> *_pParent;  //双亲
    K _key; 
    V _value;
    int _bf;       // 平衡因子:right-left
};

然后我们按照一定的规则来插入元素,其实这些规则都是二叉搜索树的,无非就是当你的Key小于当前节点的Key的时候,从左边遍历,大于就从右边遍历,找到之后就插入,不过这里在插入之后要判断是否满足平衡树的条件,如果不满足的话,那么需要调平。

那么我们就重点来看一下怎样调平的吧:

首先插入进去之后平衡因子会变化,那么都有哪些结点的平衡因子变化了呢,我们不妨仔细看看,

这里写图片描述

这张图中红色的线链接的那个结点是新插入的结点,在插入之前结点:30、40、50的平衡因子都是0,但是插入之后,他们就变成了-1,他们还有一个共同点,那就是,他们都是新插入节点的祖先结点,也就是说,新插入一个结点,那么从根结点到此节点这条链的所有节点的平衡因子都变化。

至于说,如何变化的,显然如果是其左子树,那么-1,右子树+1

最后就是调平了,我们二叉树中的一条链上的结点的平衡因子变化了,那么就得看一下变化之后是不是还满足AVLTree的条件。

总体来说还是得分情况:

1、如果此节点的平衡因子变成0,那么直接就不用调了。

这里写图片描述

2、如果此节点是1或-1的话,还是平衡树,那么继续向上边遍历,看是否有超过1的

3、如果此节点的平衡因子的绝对值超过1,显然不满足平衡树的条件,这就需要我们做一下旋转处理了。

旋转大致分为四种:左单旋——右右,右单旋——左左,左右双旋——左右,右左双旋——右左。

那么我们来一个一个看,首先来看一下左单旋

右右的意思是:在此节点右子树的右侧插入一个节点。

这里写图片描述

如图所示,其中65或80这两个结点都满足这个旋转条件,为了方便起见,我们只取一个,来看一下它的旋转处理:

这里写图片描述

我们将55这个结点的连接到50的右,同时将50连接到60的左。不过,在做这些之前,我们需要将一些节点先保存起来,这样,变化之后,我们再将50和60这两个结点的平衡因子都置为0。

//左单旋——右右
    void _RotateL(Node* parent)
    {
        Node* subR = parent->_pRight;
        Node* subRL = subR->_pLeft;//有可能不存在

        parent->_pRight = subRL;
        if (subRL)
            subRL->_pParent = parent;

        subR->_pLeft = parent;
        Node* gparent = parent->_pParent;//保存parent的双亲
        parent->_pParent = subR;
        subR->_pParent = gparent;

        if (gparent == NULL)//parent是根结点
            _pRoot = subR;
        else
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值