平衡搜索树:AVLTree的实现

为什么使用AVLTree?
对于一般的搜索二叉树而言,在理想的状态下,寻找一个数的复杂度为o(lgn)(这个树为满二叉树),最坏为o(n),如下图所示:
这里写图片描述
所以我们需要限制它的左右高度,防止它出现不平衡状态。
AVL树的性质
1. 左子树和右子树的高度之差的绝对值不超过1
2. 树中的每个左子树和右子树都是AVL树
3. 每个节点都有一个平衡因子(我用br表示),任一节点的平衡因子是-1,0,1。(每个节点的平衡因子等于右子树的高度减去左子树的高度 )

AVL树的难点在于如何维护AVL树的性质,以及正确的更新节点的平衡因子。我们一般使用旋转的方法来维护。

如果你还对节点之间的旋转不太清楚的话,你可以看看这篇博客:http://blog.csdn.net/fengasdfgh/article/details/52917671

现在我们主要讨论插入时的情况(删除可以看作是插入的逆向,你学会了insert,就可以自己琢磨出erase)。

在这里你还可能对平衡因子的作用没有什么感触,为了引起你的重视,我在这里说明平衡因子的意义。
就如性质3所说的,平衡因子为节点的左右高度差,它可能的值如下:
1 : 说明该节点的右子树比左子树高度高1.
0 : 右子树和左子树一样高。
-1 : 左子树比右子树高1.
2 : 右子树高度比左子树高度高2,这已经违背了性质 1,我们必须 通过旋转来调整这个子树来维持性质。
-2 : 左子树比右子树高2,这同样也违背了性质1.

我们重点讨论新节点插入后怎么更新节点的br(平衡因子)以及如何维护性质1.

首先我们可以推出新节点的br为0.
这里写图片描述

如果你以为这就结束了,那未免也太简单了。新插入的节点会导致其父节点的左右高度差改变,继而父节点的平衡因子改变。
那么这种改变如果影响到了其祖先节点的左右高度差改变,它是否会影响到其祖先节点那?
我们又如何去判断?
什么时候不会修改?
如果修改了,怎么去更新它的br值?
我用图片说明:
这里写图片描述

如图中所示:因为d节点的插入,因为d是c的右节点,说明c的右子树高度一定发生了变化,所以c的平衡因子发生了变化。
注意,因为c的平衡因子发生变化,那么对于节点b来说,b的右子树高度一定发生变化,所以它的平衡因子发生变化。
既然b的平衡因子改变了,为什么没有影响到a?因为b的平衡因子变为了0,这说明它的左右子树高度相等,但这对于a来说,它的右子树高度是没有变的,a的左右子树高度都没变,则它的平衡因子不会变。

比如:在d点插入之前各点状况如下:
左子树高度 右子树高度
a 2 3
b 2 1
c 0 0

d点插入之后
左子树高度 右子树高度
a 2 3
b 2 2
c 0 1

这里我们可以总结出规律:
如果一个点的平衡因子发生变化且变为1,-1,那么它一定对它的父节点产生了影响,是0则不然。
可以看出对平衡因子的调节是一个从下向上调整的过程。

    void adjust(Node *child)//调整函数
    {
        Node *father = child->_father;
         //从孩子到父亲,向上调整。
        while (child != _root)
        {
            if (father->_left == child)
                father->_br -= 1;
            else father->_br += 1;

            if (0 == father->_br)
                break;
            else if (1 == father->_br || -1 == father->_br)
            {
                child = father;
                father = father->_father;

            }
        }
    }

如果你认为这就完了,那么再请拉回去看一下平衡因子还有可能的取值,没错我们还没有讨论平衡因子为2,-2时的状况。

平衡因子变为2,-2,这说明这棵树(具体地说是这棵子树,但它会导致这棵树不为AVLTree)不是AVLTree.那么我们就需要来维护这个二叉树了。如图:
这里写图片描述
类似的,在右子树插入也是一样的。
我们设新插入的节点为child,则它的父亲为为father。

我们可以从上面找到以下规律;

此时说到的br(平衡因子)值都已经是更新过的,我们从新插入的结点开始向上更新,遇到有节点的br值为2,-2时边调用旋转函数,遇到0
终止,遇到1,-1继续向上调节。

while child != root            //最多到达根节点

//更新father的br值代码此处省略

//判断father更新后的br值
if father的br(平衡因子值)为2
    if father的br为1
        进行左旋;
    else 进行右左双旋;

else  if father的br(平衡因子值)为-2
        if father的br为-1
        进行右旋;
    else 进行左右双旋;  
else if father的br(平衡因子值)为1,-1
    child = father;
else   break;

C++代码如下:

    void adjust(Node *child)
    {
        Node *father = child->_father;

        while (child != _root)
        {
            if (father->_left == child)
                father->_br -= 1;
            else father->_br += 1;

            if (0 == father->_br)
                break;
            else if (1 == father->_br || -1 == father->_br)
            {
                child = father;
                father = father->_father;

            }
            else
            {
                adjust(child, father);

                break;
            }
        }
    }
    void adjust(Node *father, Node *grandfather)
    {

        if (2 == grandfather->_br)//进行左旋转
        {
            if (-1 == father->_br)//进行右左旋转
            {

                rotate_right_left(father);
                //调整相关节点的高度因子
                father->_br = grandfather->_br = 0;

            }

            else
            {
                rotate_left(father);
                father->_br = grandfather->_br = 0;
            }


        }
        else if (-2 == grandfather->_br)//进行右旋转
        {
            if (1 == father->_br)//进行左右旋转
            {

                rotate_left_right(father);
                //调整相关节点的高度因子
                father->_br = grandfather->_br = 0;

            }
            else
            {
                rotate_right(father);
                father->_br = grandfather->_br = 0;

            }

        }

    }

基本上到这里就可以说AVL树基本完成了,但是这样没问题吗?我们可以测试一下,思路是我们在构造完AVL树后,对每个节点进行遍历,
判断它的左右子树高度差righthigh- lefthigh是否不超过1.并且也判断是否等于该节点的br值。
判断函数如下:

    bool Isblance()
    {
        int height = 0;
        return Isblance(_root, height);
    }

    bool Isblance(Node *root, int& height)
    {
        if (NULL == root)
        {
            return true;
            height = 0;
        }
        int leftmax = 0;
        //判断左右子树是否平横
        if (!Isblance(root->_left, leftmax))
            return false;
        int rightmax = 0;
        if (!Isblance(root->_right, rightmax))
            return false;
        //判断该节点的平衡因子值是否正确,把所有平衡因子错误的节点打印出来。
        if (root->_br != rightmax - leftmax)
        {
            cout << "don't blance" << ends;
            cout << root->_key << endl;
        }
        //判断该子树是否平横
        if (abs(rightmax - leftmax) >= 2)
            return false;
        height = (max(leftmax, rightmax)) + 1;
        return true;


    }

测试函数如下:
void test()
{

AVLtree<int, int> a;

vector<int> Test = {4, 2, 6, 1, 3, 5, 15, 7, 16 ,14};
for (auto p : Test)
{

    a.insert(p);
}

a.Inorder();//中序遍历
a.Isblance() ? cout << "blance" << endl : cout << "noblance" << endl;

}

结果如下(vs2015):

这里写图片描述

好吧,这里出现了问题,虽然这棵树现在的结构正确,但它节点的平衡因子已经有了问题,这势必会有隐患。
我们现在需要知道哪里出现问题,加入调试语句,调试情况如下:

void test()
{

    AVLtree<int, int> a;

    vector<int> Test = {4, 2, 6, 1, 3, 5, 15, 7, 16 ,14};
    for (auto p : Test)
    {

        a.insert(p);
        cout << p << endl;
        a.Isblance();
        cout << "-----------------" << endl;
    }

}

这里写图片描述

可以看到,插入节点14的时侯出现问题,这是为什们那,我们只要把节点14插入的情况画出来既可:
这里写图片描述

我们可以看出,节点的br值没有更新正确。
在我们的此前的函数里,我们只是简单在旋转后把相关节点的br值变为0,这是有问题的,我们必须进行完善。
我们可以很容易把所有特殊情况推出:
这里写图片描述
图中的红节点并不是新插入节点,它只是我们这里逻辑上的child节点。

我们和双旋转的图对比一下,就可以发现,这个特殊情况就是child的一个子节点不为NULL。我们把所有情况列举出来,并且得出结果:

设c = child, b = father, a = grandfather
child的br:-1, father的br : 1, grandfather的br:-2
更新后为:child.br = 0, father.br = 0, grandfather.br = 1.

child的br:1, father的br : 1, grandfather的br:-2
更新后为:child.br = 0, father.br = -1, grandfather.br = 0.

child的br:-1, father的br : -1, grandfather的br:2
更新后为:child.br = 0, father.br = 1, grandfather.br = 0.

child的br:-1, father的br : 1, grandfather的br:2
更新后为:child.br = 0, father.br = 0, grandfather.br = -1.

完善后的调整函数为:

 //当需要调整时调用adjust的重载函数。
    void adjust(Node *father, Node *grandfather)
    {
        if (2 == grandfather->_br)//进行左旋转
        {
            if (-1 == father->_br)//进行右左旋转
            {
                Node *leftchild = father->_left;
                rotate_right_left(father);
                //调整相关节点的高度因子
                if (0 == leftchild->_br)
                {
                    leftchild->_br = father->_br = grandfather->_br = 0;

                }
                else if (1 == leftchild->_br)
                {
                    leftchild->_br = 0;
                    father->_br = 0;
                    grandfather->_br = -1;
                }
                else
                {
                    leftchild->_br = 0;
                    father->_br = 1;
                    grandfather->_br = 0;
                }


            }

            else
            {
                rotate_left(father);
                father->_br = grandfather->_br = 0;
            }


        }
        else if (-2 == grandfather->_br)//进行右旋转
        {
            if (1 == father->_br)//进行左右旋转
            {
                Node *leftchild = father->_right;
                rotate_left_right(father);
                //调整相关节点的高度因子
                if (0 == leftchild->_br)
                {
                    leftchild->_br = father->_br = grandfather->_br = 0;

                }
                else if (1 == leftchild->_br)
                {
                    leftchild->_br = 0;
                    father->_br = -1;
                    grandfather->_br = 0;
                }
                else
                {
                    leftchild->_br = 0;
                    father->_br = 0;
                    grandfather->_br = 1;
                }

            }

            else
            {
                rotate_right(father);
                father->_br = grandfather->_br = 0;

            }

        }

    }。

全部代码如下(只实现了插入操作):

#pragma once

#include <iostream>
#include <stack>
#include <algorithm>
using namespace std;
//节点
template<typename K, typename T = int>
struct AVLNode
{
    typedef AVLNode<K, T> Node;

    AVLNode(const K& key, const T value = T())
        :_key(key)
        ,_value(value)
        ,_br(0)
        ,_left(NULL)
        ,_right(NULL)
        ,_father(NULL)
    {

    }

    const K _key;
    T _value;
    int _br;(平衡因子)
    Node *_left;
    Node *_right;
    Node *_father;
};


//AVLTree
template<class K, class T = int>
class AVLtree
{
public:


    typedef AVLtree<K, T> tree;
    typedef AVLNode<K, T> Node;

    AVLtree()
        :_root(NULL)
    {};

    bool insert(const K& key, const T& value = T())
    {
        Node *p = new Node(key, value);
        if (NULL == _root)
        {
            _root = p;
            return true;
        }
        Node *cur    = _root;
        Node *father = cur;

        while (cur)
        {
            father = cur;
            if (key == cur->_key)
                return false;
            else if (key > cur->_key)
                cur = cur->_right;
            else cur = cur->_left;

        }
        if (father->_key > key)
            father->_left = p;
        else father->_right = p;
        p->_father = father;

        //每次插入完成后进行调整
        adjust(p);
        return true;



    }




    void middle_display()
    {
        middle_display(_root);
        cout << endl;
    }

    bool Isblance()
    {
        int height = 0;
        return Isblance(_root, height);
    }

protected:

    //左旋
    void rotate_left(Node *father)
    {
        Node *grandfather = father->_father;
        Node *great_father = grandfather->_father;

        grandfather->_right = father->_left;
        if (father->_left != NULL)
            father->_left->_father = grandfather;

        father->_left = grandfather;
        grandfather->_father = father;

        if (NULL == great_father)
        {
            _root = father;

        }
        else 
        {
            if (great_father->_left == grandfather)
                great_father->_left = father;
            else great_father->_right = father;

        }
        father->_father = great_father;

    }
    //右旋
    void rotate_right(Node *father)
    {
        Node *grandfather = father->_father;
        Node *great_father = grandfather->_father;

        grandfather->_left = father->_right;
        if (father->_right != NULL)
            father->_right->_father = grandfather;

        father->_right = grandfather;
        grandfather->_father = father;

        if (NULL == great_father)
        {
            _root = father;
            _root->_father = NULL;
        }
        else
        {
            if (great_father->_left == grandfather)
                great_father->_left = father;
            else great_father->_right = father;
        }
        father->_father = great_father;
    }
    //左右旋
    void rotate_left_right(Node *father)
    {
        father = father->_right;
        rotate_left(father);
        rotate_right(father);
    }
    //右左旋
    void rotate_right_left(Node *father)
    {
        father = father->_left;
        rotate_right(father);
        rotate_left(father);
    }
    //调整函数
    void adjust(Node *child)
    {
        Node *father = child->_father;

        while (child != _root)
        {
            if (father->_left == child)
                father->_br -= 1;
            else father->_br += 1;

            if (0 == father->_br)
                break;
            else if (1 == father->_br || -1 == father->_br)
            {
                child = father;
                father = father->_father;

            }
            else
            {
                adjust(child, father);

                break;
            }
        }
    }
    //adjust的重载函数
    void adjust(Node *father, Node *grandfather)
    {

        if (2 == grandfather->_br)//进行左旋转
        {
            if (-1 == father->_br)//进行右左旋转
            {
                Node *leftchild = father->_left;
                rotate_right_left(father);
                //调整相关节点的高度因子
                if (0 == leftchild->_br)
                {
                    leftchild->_br = father->_br = grandfather->_br = 0;

                }
                else if (1 == leftchild->_br)
                {
                    leftchild->_br = 0;
                    father->_br = 0;
                    grandfather->_br = -1;
                }
                else
                {
                    leftchild->_br = 0;
                    father->_br = 1;
                    grandfather->_br = 0;
                }


            }

            else
            {
                rotate_left(father);
                father->_br = grandfather->_br = 0;
            }


        }
        else if (-2 == grandfather->_br)//进行右旋转
        {
            if (1 == father->_br)//进行左右旋转
            {
                Node *leftchild = father->_right;
                rotate_left_right(father);
                //调整相关节点的高度因子
                if (0 == leftchild->_br)
                {
                    leftchild->_br = father->_br = grandfather->_br = 0;

                }
                else if (1 == leftchild->_br)
                {
                    leftchild->_br = 0;
                    father->_br = -1;
                    grandfather->_br = 0;
                }
                else
                {
                    leftchild->_br = 0;
                    father->_br = 0;
                    grandfather->_br = 1;
                }

            }

            else
            {
                rotate_right(father);
                father->_br = grandfather->_br = 0;

            }

        }

    }



    void Inorder(Node *root)
    {
        if (NULL == root)
            return;
        middle_display(root->_left);
        cout << root->_key << ends;

        Inorder(root->_right);
    }


    bool Isblance(Node *root, int& height)
    {
        if (NULL == root)
        {
            return true;
            height = 0;
        }
        int leftmax = 0;
        if (!Isblance(root->_left, leftmax))
            return false;
        int rightmax = 0;
        if (!Isblance(root->_right, rightmax))
            return false;
        if (root->_br != rightmax - leftmax)
        {
            cout << "don't blance" << ends;
            cout << root->_key << endl;
        }
        if (abs(rightmax - leftmax) >= 2)
            return false;
        height = (max(leftmax, rightmax)) + 1;
        return true;


    }


private:

    Node *_root;
};

AVL数虽然在很大程度上保持了平衡,但是它却太难理解,维护代价高。RBTree相比AVLTree则更为简单。

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值