平衡二叉树(AVL树)的实现

来源:互联网 发布:淘宝前100名半价怎么抢 编辑:程序博客网 时间:2024/04/30 04:54

一棵AVL树是其每个节点的左子树和右子树的高度最多差1的二叉查找树(空树的高度定义为-1)。AVL树是对二叉查找树的一种改进,虽然二叉查找树平均情况下比链表的查找速度要快,但是二叉查找树也可能出现所有结点都没有左孩子(或右孩子)的情况,此时树的高度为n,这就和链表可以等价为一种数据结构,为了避免这种情况的发生,AVL树就应运而生。


第一步:结点信息

与二叉搜索树的结点信息类似,只需要添加一个树高的参数,用于插入和删除操作过程的旋转。

struct AVLnode    {        T data;//结点的元素值        AVLnode *left;//指向左孩子        AVLnode *right;//指向右孩子        int height;//树高        //构造函数        AVLnode(T elem, AVLnode *l, AVLnode *r, int h=0):data(elem),left(l),right(r),height(h){}    };
第二步:平衡二叉树类的声明

主要包括树的插入删除,以及保持树平衡的各种旋转操作。

public://对外的接口函数    AVLTree():root(NULL){}    void insert(const T &x)//结点插入    {        insert(x, root);    }    void remove(const T &x)//结点删除    {        remove(x, root);    }private:    AVLnode *root;//根结点    //结点插入和删除    void insert(const T &x, AVLnode * &t);    void remove(const T &x, AVLnode * &t);    //树的旋转操作    void SingleRotateLeft(AVLnode * &t);    void SingleRotateRight(AVLnode * &t);    void DoubleRotateLR(AVLnode * &t);    void DoubleRotateRL(AVLnode * &t);
第三步:辅助函数

辅助函数有四个:两个用于旋转操作,一个是求树高,一个是求两个值的最大值;两个是用于删除操作,分别是求某一子树的最大值和最小值的结点。

//辅助函数    int height(AVLnode * &t)    {        //结点高度,空结点的高度为-1        return t==NULL?-1:t->height;    }    int max(int a, int b)    {        return a<b?b:a;    }    AVLnode * max_node(AVLnode *t)    {        if(t)            while(t->right)                t = t->right;        return pCur;    }    AVLTree * min_node(AVLnode *t)    {        if(t)            while(t->left)                t = t->left;        return t;    }
第四步:旋转

如果把必须重新平衡的结点叫k。由于任意结点最多有两个孩子,因此高度不平衡时,k点的两棵子树的高度差为2。容易看出,这种不平衡可能出现在下面四种情况中:

1)对k的左孩子的左子树进行一次插入。

2)对k的左孩子的右子树进行一次插入。

3)对k的右孩子的左子树进行一次插入。

4)对k的右孩子的右子树进行一次插入。

情形1和4是关于k点的镜像对称,而2和3是关于k点的镜像对称。因此,理论上只有两种情况,当然从编程的角度来看还是四种情况。

单旋转
对于情形1)和情形4),只需要进行一次旋转就可以恢复AVL树的平衡,以情形1)为例,阐述单旋转操作:

一次旋转就能恢复AVL树的平衡,不过还需要调整相应结点的height属性,并将结点s置为当前结点。这种旋转操作,就好像人为的把s结点向右旋转一次,所以该操作也叫做右旋转
左孩子的左子树插入结点失衡——右旋转
右孩子的右子树插入结点失衡——左旋转
单旋转实现代码如下所示:
template <typename T>void AVLTree<T>::SingleRotateLeft(AVLnode * &t){    AVLnode *s = t->right;    t->right = s->left    s->left = t;    t->height = max(height(t->left), height(t->right)) + 1;    s->height = max(t->height, height(s->right)) + 1;    t = s;}template <typename T>void AVLTree<T>::SingleRotateRight(AVLnode * &t){    AVLnode *s = t->left;    t->left = s->right;    s->right = t;    t->height = max(height(t->left), height(t->right)) + 1;    s->height = max(height(s->left), t->height);    t = s;}
双旋转
情形2)和3)稍微复杂一点,需要进行两次旋转才能恢复AVL树的平衡。以情形2)为例,阐述双旋转操作:

同样,旋转操作之后,还要修改结点的height属性值,并将q设置为当前结点。从上图可以看出,双旋转实际上就是两次简单的单旋转,所以上述操作可以通过两次单旋转函数的调用来实现
t->left = SingleRotateLeft(t->left);return SingleRotateRight(t);
左孩子的右子树插入结点导致不平衡——先左旋转,再右旋转
右孩子的左子树插入结点导致不平衡——先右旋转,再左旋转
双旋转的代码实现如下所示:
template <typename T>void AVLTree<T>::DoubleRotateLR(AVLnode* &t){    AVLnode *p = t->left;    AVLnode *q = p->right;    t->left = q->right;    p->right = q->left;    q->left = p;    q->right = t;    t->height = max(height(t->left), height(t->right)) + 1;    p->height = max(height(p->left), height(p->right)) + 1;    q->height = max(p->height, t->height) + 1;    t = q;}template <typename T>void AVLTree<T>::DoubleRotateRL(AVLnode* &t){    AVLnode *p = t->right;    AVLnode *q = p->left;    t->right = q->left;    p->left = q->right;    q->left = t;    q->right = p;    t->height = max(height(t->left), height(t->right)) + 1;    p->height = max(height(p->left), height(p->right)) + 1;    q->height = max(t->height, p->height) + 1;    t = q;}
第五步:插入和删除操作
插入和删除操作与普通二叉搜索树一样,只是需要在插入和删除操作之后,为了保持树的平衡性,进行相应的旋转操作。
插入和删除操作的代码实现如下所示:
template <typename T>void AVLTree<T>::insert(const T &x, AVLnode * &t){    if(!t)//找到插入点        t = new AVLnode(x, NULL, NULL);    else if(x < t->data)    {        insert(x, t->left);        if(height(t->left) - height(t->right) == 2)        {            if(x < t->left->data)//结点插在当前结点左孩子的左子树                SingleRotateRight(t);//右单旋转            else//结点插在当前结点左孩子的右子树                DoubleRotateLR(t);//先左旋再右旋        }        else//不需要调整,只需要重新计算树的高度            t->height = max(height(t->left), height(t->right)) + 1;    }    else if(x > t->data)    {        insert(x, t->right);        if(height(t->right) - height(t->left) == 2)        {            if(x > t->right->data)//结点插在当前结点右孩子的右子树                SingleRotateLeft(t);//左单旋转            else//结点插在当前结点左孩子的右子树                DoubleRotateRL(t);//先右旋再左旋        }        else//不需要调整,只需要重新计算树的高度            t->height = max(height(t->left), height(t->right)) + 1;    }    else;//不考虑重复的值}template <typename T>void AVLTree<T>::remove(const T &x, AVLnode * &t){    if(!t)return;//找到叶结点以下,即没有要删除的结点,啥也不做    if(x < t->data)    {        remove(x, t->left);        if(height(t->right) - height(t->left) == 2)        {            AVLnode * temp = t->right;            if(height(temp->right) > height(temp->left))//类似于结点插在当前结点右孩子的右子树上                SingleRotateLeft(t);//左单旋转            else//类似结点插在当前结点右孩子的左子树                DoubleRotateRL(t);//先右旋再左旋        }        else//不需要调整,只需要重新计算树的高度            t->height = max(height(t->left), height(t->right)) + 1;    }    else if(x > t->data)    {        remove(x, t->right);        if(height(t->left) - height(t->right) == 2)        {            AVLnode * temp = t->left;            if(height(temp->left) > height(temp->right))//类似于结点插在当前结点左孩子的左子树上                SingleRotateRight(t);//右单旋转            else//类似结点插在当前结点左孩子的右子树                DoubleRotateLR(t);//先左旋再右旋        }        else//不需要调整,只需要重新计算树的高度            t->height = max(height(t->left), height(t->right)) + 1;    }    else//要删除结点等于当前结点    {        if(t->left && t->right)        //t的左右子树都非空,把remove操作转移到只有一个非空子树的结点或者叶子结点上        {            if(height(t->left) > height(t->right))            {                t->data = max_node(t->left)->data;                remove(t->data, t->left);            }                    else            {                t->data = min_node(t->right)->data;                remove(t->data, t->right);            }        }        else        {             AVLnode *oldnode = t;             t = t->left?t->left:t->right;             delete oldnode;             oldnode = NULL;        }    }}




0 0
原创粉丝点击