先是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;
}
不要直接调用求前驱的函数,貌似时间复杂度会变大= =