首页 > 代码库 > 平衡二叉树(AVL树)的实现

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

一棵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 = http://www.mamicode.com/max_node(t->left)->data;>