AVL树笔记(二):maintain,delete

来源:互联网 发布:91.v6p.co index.php 编辑:程序博客网 时间:2024/05/21 04:19

先是maintain操作,它可以维护AVLTree的平衡。
有了maintain,AVLTree的insert和delete就可以不马上维护平衡,而是操作完再维护平衡了。

void maintain(int &r){    if(tree[tree[r].lc].h==tree[tree[r].rc].h+2)    {        int t=tree[r].lc;        if(tree[tree[t].lc].h==tree[tree[r].rc].h+1)r=zig(r);        else if(tree[tree[t].rc].h==tree[tree[r].rc].h+1)r=zagzig(r);    }    else if(tree[tree[r].rc].h==tree[tree[r].lc].h+2)    {        int t=tree[r].rc;        if(tree[tree[t].rc].h==tree[tree[r].lc].h+1)r=zag(r);        else if(tree[tree[t].lc].h==tree[tree[r].lc].h+1)r=zigzag(r);    }    tree[r].h=max(tree[tree[r].lc].h,tree[tree[r].rc].h)+1;}

仔细看会发现和昨天的insert的维护平衡比较相似。
就是这次不能凭借tree[r].v和插入值x来比较x在左子树还是右子树了,要根据h来判断。
然后insert就是二叉查找树的insert了:

int insert(int r,int x){    if(r==0)    {        tree[++cnt].v=x;        tree[cnt].h=1;        return cnt;    }    if(tree[r].v>x)tree[r].lc=insert(tree[r].lc,x);    else if(tree[r].v<x)tree[r].rc=insert(tree[r].rc,x);    maintain(r);    return r;}

接下来是delete操作:
首先我们要find,find到了再delete。
要delete的点如果是叶子,那么直接删掉,如果只有一个儿子,那么把这个点删掉,把它的父亲直接连到它的儿子。
如果左右儿子都有,就比较麻烦了。
可以找到它的前驱,把这个点的值修改成前驱的值,最后删掉前驱。
可以证明的是,前驱一定不存在有左右2个儿子都有的状况。
可行是因为删掉这个点,前驱就会上来。还不如直接把这个点改成前驱,再删掉以前那个前驱。
实际实现可以参考find。
find到了,如果是前2种状况,那么删掉,如果是第三种,那么递归删去当前点的前驱。
删完记得维护h值。

int del(int &r,int x){    int tv;    if(x==tree[r].v||(x<tree[r].v&&tree[r].lc==0)||(x>tree[r].v&&tree[r].rc==0))    {        if(tree[r].lc==0||tree[r].rc==0)        {            tv=tree[r].v;            r=tree[r].lc+tree[r].rc;            return tv;        }        else tree[r].v=del(tree[r].lc,x);    }    else    {        if(tree[r].v>x)tv=del(tree[r].lc,x);        else tv=del(tree[r].rc,x);    }    maintain(r);    return tv;}

不要直接调用求前驱的函数,貌似时间复杂度会变大= =

0 0
原创粉丝点击