AVL树的基本操作

来源:互联网 发布:30岁开始学编程 编辑:程序博客网 时间:2024/05/16 01:04

AvlTree的各种操作

AVL树是带有平衡条件的二叉查找树,要满足树中的每个节点的左子树和右子树的高度最多相差1。

  • 插入操作
    要使插入后生成的树为AVL树,就必须每插入一个节点,就判断一下左右子树的高度差是不是大于1,因此我们要记录每个节点的高度(这儿高的定义是一棵树ni的高是从ni到一片树叶的最长路径的长,空树的高度为-1树叶的高度为0)。
typedef struct AvlNode *AvlTree;struct AvlTree{    int x;    AvlTree Left;    AvlTree Right;    int Height;   //要记录高度};AvlTree Insert(int X,AvlTree T);

关键是插入后的判断,用T->Left->Height - T->Right->Height==2来判断左右子树的高度差是不对的,因为由于T的左右子树可能为NULL,因此要在用个计算节点高度的函数,对为NULL的情况讨论一下。

int Height(Position P){    if (P == NULL) {        return -1;      //这儿空树的高度为-1     }else {        return P->Height;    }}
AvlTree Insert(int X, AvlTree T){    if (T == NULL){        T = (AvlTree) malloc (sizeof(struct AvlNode));        T->x = X;        T->Left = NULL;        T->Right = NULL;        T->Height = 0;       }else if (T->x > X){        T->Left = Insert(X,T->Left);        if ( Height(T->Left) - Height(T->Right) == 2){            if ( X < T->Left->x ){                T = SingleRotateWithLeft(T);                }else{                T = DoubleRotateWithLeft(T);            }        }    }else if ( T->x < X){        T->Right = Insert(X, T->Right);        if ( Height(T->Right) - Height(T->Left) == 2 ){            if (X > T->Right->x){                T = SingleRotateWithRight(T);            }else{                T = DoubleRotateWithRight(T);            }        }    }    T->Height = Max(Height(T->Left),Height(T->Right))+1;            return T; //不要忘记更新高度和返回T}

接下来便是最关键的单旋转和双旋转操作,就像上面那样,即使判断出了高度差大于1了,那该怎么做使这个节点满足AVL树的性质呢?

  • 单旋转
    先考虑第一种情况,对节点k2的左儿子的 左子树 进行插入,使节点k2的左子树的高度k2右子树的高度高了2。
    左边是最简单的情况,当然了,k2也可能有右子树,k1也可能有右子树,所以都可以归结为中间那个图的情况,X Y Z是子树,Y和Z可能为空树,但不管怎样,k2的左子树一定比右子树高2.
    因此可以把k2这个节点降一层,k1这个节点升高一层,并改变一下Y的位置,就得到了最右边的图,这个过程就是SingleRotateWithLeft(T)。
    就像这样 这里写图片描述这里写图片描述
Position SingleRotateWithLeft(Position K2){    Position K1;    K1=K2->Left;           //要将K1的位置记录下来      K2->Left=K1->Right;    K1->Right=K2;              /*不要忘记更新节点的高度*/    K2->Height=Max( Height( K2->Left), Height(K2->Right)) +1;    K1->Height=Max( Height( K1->Left), Height(K1->Right)) +1;     //注意先更新K2,然后再更新 K1 ,因为 K1的高度得到与 K2有关     return K1;   //返回新的根     }

若对k2的右儿子的右子树进行插入,会出现下图所示的不平衡
这里写图片描述
可以看出,这种情况是与前一种对称的,所以有类似的过程:

Position SingleRotateWithRight(Position K2){    Position K1;    K1=K2->Right;    K2->Right=K1->Left;    K1->Left=K2;    K2->Height=Max( Height( K2->Left), Height(K2->Right)) +1;    K1->Height=Max( Height( K1->Left), Height(K1->Right)) +1;     return K1; } 
  • 双旋转
    现在还剩下两种情况,插入的元素比k2的右子树k1的值要小或者比K2的左子树K1的值要大。显然这两种情况也是对称的,考虑左边的情况。
    这里写图片描述
    若还是对K2进行单旋转,即让K1成为新的根,又会变成类似右图的那种情况,X还是太深(K1上升1层,K2下降一层,X的层数还是没变)不满足AVL树的条件。必须找到新的能作为新的根的节点。
    这里写图片描述
    由于有一个节点插入了子树X,因此X非空,所以通过变换可以让K3成为新的根。对于变换,只需要进行两次旋转即可。第一次对K1单旋转使K1降一层,K3升一层,第二次对K2进行单旋转,让K2也降了一层,使K3成为新的根。
    这里写图片描述
Position DoubleRotateWithLeft(Position K2){    K2->Left = SingleRotateWithRight(K2->Left);    return SingleRotateWithLeft(K2);}Position DoubleRotateWithRight(Position K2){    K2->Right = SingleRotateWithLeft(K2->Right);    return SingleRotateWithRight(K2);}
  • 删除操作
    下面来考虑删除操作,对于一个Avl树,节点的删除一般是下面三种情况A(树叶),B(只有一边的子树),C(左子树和右子树都有)。想一想可知,对于AB类型的正常删除即可,删除后仍然满足平衡树的性质。而对于C的情况,每一次执行删除操作后,只需判断一下是否(Height(T->Left) - Height(T->Right) == 2)即可,是的话执行相应的旋转即可。
    这里写图片描述
AvlTree Delete(int X, AvlTree T){    if(T==NULL){        return NULL;    }else if (T->x > X){        T->Left=Delete(X, T->Left);    }else if (T->x < X){        T->Right=Delete(X, T->Right);    }else {        if (T->Left!=NULL && T->Right!=NULL){            AvlTree temp=FindMin(T->Right);            T->x=temp->x;            T->Right=Delete(T->x, T->Right);            //删除后如果破坏了平衡便做相应的旋转            if(Height(T->Left) - Height(T->Right) == 2){                if (Height(T->Left->Left) > Height(T->Left->Right)){                    T = SingleRotateWithLeft(T);                }else{                    T = DoubleRotateWithLeft(T);                    }            }         }else {   //正常的删除操作            AvlTree temp=T;            if (T->Left==NULL){                T = T->Right;            }else if (T->Right==NULL){                T = T->Left;            }            free(temp);        }       }     if (T!=NULL)  T->Height = Max(Height(T->Left), Height(T->Right)) + 1;  //不是树叶被删除的话要更新一下高度    return T;}
原创粉丝点击