AVL树笔记(二):maintain,delete

先是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;
}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值