平衡搜索树:AVLTree的实现

来源:互联网 发布:快递热敏打印软件 编辑:程序博客网 时间:2024/05/21 16:12

为什么使用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;};//AVLTreetemplate<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则更为简单。

这里写图片描述

0 0
原创粉丝点击