AVL树(旋转问题详解)

来源:互联网 发布:浙江卫视网络在线直播 编辑:程序博客网 时间:2024/06/06 02:52

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

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

可以,一个平衡因子就可以搞定,加了平衡因子,那么这颗二叉搜索树就是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 if (gparent->_pLeft == parent)            gparent->_pLeft = subR;        else            gparent->_pRight = subR;        //旋转之后将其清零——结合图理解        parent->_bf = 0;        subR->_bf = 0;    }

右单旋——左左

左左的意思是:在此节点的左子树的左侧,其实如果你理解了左单旋,你会发现,右单旋就和左单旋互换一个左右即可。

这里写图片描述

这里的20和35都是新插入的结点,同样的 ,我们也取出一个作为例子来看它的旋转

这里写图片描述

这个理念和做单选的一样,在这里做出一个建议,不要去尝试记忆两个,而要试着理解一个,那么另一个就像镜子里的自己,显而易见的。

双旋也就是两个单旋的组合,而左右双旋和右左双旋的区别就是左单旋和右单旋的顺序问题。

左右单旋:先进行左单旋,再进行右单旋

这里写图片描述

这就是一个可以进行左右单旋的例子,那么来看一下它的旋转吧

这里写图片描述

同样的右左双旋的理念也是这样的:先进行右旋,在进行左旋。

这里写图片描述

这里写图片描述

那么接下来我们就来看一下实现的过程吧,这个过程我debug的好多次,其中有各种的小毛病,注释上面都有,所以,看到的都要引以为戒。

AVLTree创建成功之后,我们得测验一下吧,就是判断这个二叉树是不是平衡树,按照二叉树的定义来,我们只需判断根结点的平衡因子是不是满足条件即可,然后再递归左子树和右子树。

不过,这里有一个需要注意的地方,就是平衡因子的计算是不是正确的,这个就是我刚才说的小毛病,所以,在判断平衡因子之前,我们再来计算一下平衡因子,如果和结点中的平衡因子不同,那么就不用在看其是否满足条件了。

// 检测二叉树是否为平衡树?    bool _IsBalanceTree(Node* pRoot)    {        if (pRoot == NULL)            return true;        //求出左右子树的高度        size_t left = _Height(pRoot->_pLeft);        size_t right = _Height(pRoot->_pRight);        //如果平衡因子计算错误  或  平衡因子大于1  那么不是平衡树        if ((right - left) != pRoot->_bf || pRoot->_bf > 1)            return false;        return _IsBalanceTree(pRoot->_pLeft) && _IsBalanceTree(pRoot->_pRight);    }

下面是全部的代码,这里的测试用例很好,基本上涵盖了所有的旋转问题。

#include<iostream>using namespace std;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};template<class K, class V>class AVLTree{    typedef AVLTreeNode<K, V> Node;public:    AVLTree()        : _pRoot(NULL)    {}    bool Insert(const K& key, const V& value)    {        if (_pRoot == NULL)//如果树为空,直接插入        {            _pRoot = new Node(key, value);            return true;        }        //找插入位置        Node* pCur = _pRoot;        Node* parent = NULL;        while (pCur)        {            if (key < pCur->_key)            {                parent = pCur;                pCur = pCur->_pLeft;            }            else if (key>pCur->_key)            {                parent = pCur;                pCur = pCur->_pRight;            }            else                return false;        }        //插入        pCur = new Node(key, value);//不要忘        if (key < parent->_key)            parent->_pLeft = pCur;        else            parent->_pRight = pCur;        pCur->_pParent = parent;//插入之后将pCur的双亲的指针指向parent        while (parent != NULL)        {            //平衡树可能不平衡了,重新调整            if (parent->_pLeft == pCur)                parent->_bf--;//不管pCur的兄弟结点是否存在,直接--,即可            else                parent->_bf++;            //分情况讨论            if (parent->_bf == 0)//层数没有增加                return true;            else if (parent->_bf == 1 || parent->_bf == -1)//层数加一,此时这个子树还是平衡的,继续向上调整            {                pCur = parent;                parent = parent->_pParent;            }            else//  2||-2   已经不平衡了,需要进行旋转处理            {                if (parent->_bf == 2)                {                    if (pCur->_bf == 1)                        _RotateL(parent);//右右——左单旋                    else                        _RotateRL(parent);//右左——右左双旋                }                else                {                    if (pCur->_bf == -1)                        _RotateR(parent);//左左——右单旋                    else                        _RotateLR(parent);//左右——左右双旋                }                //插入只是影响树的局部,调整之后,树平衡                break;            }        }    }    void InOrder()    {        cout << "InOrder: ";        _InOrder(_pRoot);        cout << endl;    }    size_t Height()    {        return _Height(_pRoot);    }    bool IsBalanceTree()    {        return _IsBalanceTree(_pRoot);    }private:    // 检测二叉树是否为平衡树?    bool _IsBalanceTree(Node* pRoot)    {        if (pRoot == NULL)            return true;        //求出左右子树的高度        size_t left = _Height(pRoot->_pLeft);        size_t right = _Height(pRoot->_pRight);        //如果平衡因子计算错误  或  平衡因子大于1  那么不是平衡树        if ((right - left) != pRoot->_bf || pRoot->_bf > 1)            return false;        return _IsBalanceTree(pRoot->_pLeft) && _IsBalanceTree(pRoot->_pRight);    }    size_t _Height(Node* pRoot)    {        if (pRoot == NULL)            return 0;        if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)            return 1;        size_t left = _Height(pRoot->_pLeft);        size_t right = _Height(pRoot->_pRight);        return left > right ? left + 1 : right + 1;    }    void _InOrder(Node* pRoot)    {        if (pRoot)        {            _InOrder(pRoot->_pLeft);            cout << pRoot->_key << " ";            _InOrder(pRoot->_pRight);        }    }    //左单旋——右右    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 if (gparent->_pLeft == parent)            gparent->_pLeft = subR;        else            gparent->_pRight = subR;        //旋转之后将其清零——结合图理解        parent->_bf = 0;        subR->_bf = 0;    }    //右单旋——左左(在较高左子树的左插入结点)    void _RotateR(Node* parent)    {        Node* subL = parent->_pLeft;        Node* subLR = subL->_pRight;        parent->_pLeft = subLR;        if (subLR)            subLR->_pParent = parent;        subL->_pRight = parent;        Node* gparent = parent->_pParent;        parent->_pParent = subL;        subL->_pParent = gparent;//总共三个,一个都不能忘        if (gparent == NULL)            _pRoot = subL;        else if (gparent->_pLeft == parent)            gparent->_pLeft = subL;        else            gparent->_pRight = subL;        parent->_bf = 0;        subL->_bf = 0;    }    //左右双旋——左右    void _RotateLR(Node* parent)    {        Node* subL = parent->_pLeft;        Node* subLR = subL->_pRight;        int bf = subLR->_bf;        _RotateL(subL);        _RotateR(parent);        if (-1 == bf)            parent->_bf = 1;        else if (1 == bf)            subL->_bf = -1;        else//只有三个结点的时候            return;    }    //右左双旋——右左    void _RotateRL(Node* parent)//debug    {        Node* subR = parent->_pRight;        Node* subRL = subR->_pLeft;        int bf = subRL->_bf;        _RotateR(subR);        _RotateL(parent);        if (1 == bf)            parent->_bf = -1;        else if (-1 == bf)            subR->_bf = 1;        else            return;    }private:    Node* _pRoot;};void FunTest(){    int array[] = {16, 3, 7, 11, 9, 26, 18, 14, 15};    AVLTree<int, int> t;    for (size_t idx = 0; idx < sizeof(array) / sizeof(array[0]); ++idx)        t.Insert(array[idx], idx);    t.InOrder();    if (t.IsBalanceTree())    {        cout << "是AVL树" << endl;    }    else    {        cout << "不是AVL树" << endl;    }}int main(){    FunTest();    return 0;}
原创粉丝点击