替罪羊树 知识总结

替罪羊树 知识总结

替罪羊树是一类神奇的平衡树。它最神奇的地方就在于,大部分平衡树都是用愚蠢的单旋来维护平衡,而fhq-Treap则是用split和merge维护平衡的,可替罪羊树却是用一种神奇的操作维护平衡的,那就是重构Rebuild。每次插入和删除元素的时候,检查子树大小,若失衡则直接重购以维护整棵树平衡。
在讲思路前,我们先要注意一个点:替罪羊树仅能用重构来维持平衡,因而若节点被删除后,无法将其高效的移除,所以我们采取懒惰删除,每次仅将节点的计数器--即可。
先来看一下我们的变量区和基础的函数吧:
struct node{
    int l,r;//左右儿子
    int sd,sz;//sd为节点所在子树的实际大小,而sz则为包括所有删除的元素在内的子树大小
    int cnt,val;//cnt为数量,val为键值
};
inline void update(int p)
{
    f[p].sd=f[f[p].l].sd+f[f[p].r].sd+f[p].cnt;
    f[p].sz=f[f[p].l].sz+f[f[p].r].sz+f[p].cnt;
}
接下来我们来看看重构操作吧。我们预先设定一个阈值α,一般取[0.7,0.8]左右,若检查到某个节点的儿子所在子树的sz占这个节点sz的比例超过α时,就重构。由于当被删除节点过多时,搜索树效率也会显著降低,于是当一个节点的sd占其sz的比例超过α时,亦重构。
inline bool Can_Rbu(int p)
{
    return f[p].cnt&&(f[p].sz*alpha<=(double)max(f[f[p].l].sz,f[f[p].r].sz)||f[p].sz*alpha>=(double)f[p].sd);
}
那么怎么重构呢?我们已经知道了,对于一棵平衡树,它的中序遍历是排好序的。所以我们把它的中序遍历拉到数组中,然后重新二分建树即可:
int tmp[100010];
void Rbu_Flatten(int &num,int p)
{
    if(!p) return;//空儿子返回
    Rbu_Flatten(num,f[p].l);//搜左儿子
    if(f[p].cnt) tmp[num++]=p;//加入当前节点
    Rbu_Flatten(num,f[p].r);//搜右儿子
}
int Rbu_Build(int l,int r)
{
    if(l>=r) return 0;//二分边界
    int mid=(l+r)>>1;
    f[tmp[mid]].l=Rbu_Build(l,mid);//建左儿子
    f[tmp[mid]].r=Rbu_Build(mid+1,r);//建右儿子
    update(tmp[mid]);//更新信息
    return tmp[mid];
}
void Rbu(int &p)
{
    int x=0;
    Rbu_Flatten(x,p);
    p=Rbu_Build(0,x);//这里注意下标从0开始可以节省很多事情
}
以上就是基本函数啦!其余的部分神似普通的平衡树,我们的介绍和注释就稍微简略一些了咯。
首先是插入和删除。我们在遍历路径上每一个节点时,都检查是否满足重构条件,并重构即可。其余与普通二叉搜索时雷同。
void Insert(int &p,int val)
{
    if(!p)
    {
        p=++tot;
        f[p].cnt=f[p].sd=f[p].sz=1;
        f[p].val=val;
        f[p].l=f[p].r=0;
    }
    else
    {
        if(f[p].val==val) f[p].cnt++;
        else if(val<f[p].val) Insert(f[p].l,val);
        else Insert(f[p].r,val);
        update(p);
        if(Can_Rbu(p)) Rbu(p);
    }
}
void del(int &p,int val)
{
    if(!p) return;
    f[p].sd--;
    if(f[p].val==val)
    {
        if(f[p].cnt) f[p].cnt--;
    }
    else
    {
        if(val<f[p].val) del(f[p].l,val);
        else del(f[p].r,val);
        update(p);
    }
    if(Can_Rbu(p)) Rbu(p);
}
接下来是由权值查询对应的值。依旧是与普通二叉搜索树完全相同的操作,在此不再赘述了,不懂直接看代码吧QAQ
int GetVal(int p,int rk)
{
    if(!p) return 0;
    if(rk>f[f[p].l].sd&&rk<=f[f[p].l].sd+f[p].cnt) return f[p].val;
    else if(rk>f[f[p].l].sd+f[p].cnt) return GetVal(f[p].r,rk-f[f[p].l].sd-f[p].cnt);
    else return GetVal(f[p].l,rk);
}
最后是查询前后驱的函数。在此我们稍微的改变一下函数的返回值:将返回数值改为返回下标。这样以后查询排名就可以用前驱坐标+1即可。代码依旧与普通平衡树相同……
int Get_uprb(int p,int val)
{
    if(!p) return 1;
    if(f[p].val==val&&f[p].cnt) return f[f[p].l].sd+f[p].cnt+1;
    else if(val<f[p].val) return Get_uprb(f[p].l,val);
    else return f[f[p].l].sd+f[p].cnt+Get_uprb(f[p].r,val);
}
int Get_lwrb(int p,int val)
{
    if(!p) return 0;
    if(f[p].val==val&&f[p].cnt) return f[f[p].l].sd;
    else if(val<f[p].val) return Get_lwrb(f[p].l,val);
    else return f[f[p].l].sd+f[p].cnt+Get_lwrb(f[p].r,val);
}
最后就是完整版代码啦:
#include<bits/stdc++.h>
using namespace std;
template <typename T>
void read(T &x) {
    x = 0;
    int f = 1;
    char ch = getchar();
    while (!isdigit(ch)) {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while (isdigit(ch)) {
        x = x * 10 + (ch ^ 48);
        ch = getchar();
    }
    x *= f;
    return;
}
template <typename T>
void write(T x)
{
    if(x < 0) {
        putchar('-');
        x = -x;
    }
    if(x > 9)
        write(x/10);
    putchar(x % 10 + '0');
    return;
}
int n;
int rt,tot=0;
struct node{
    int l,r;
    int sd,sz;
    int cnt,val;
}f[100010];
const double alpha=0.75;
inline void update(int p)
{
    f[p].sd=f[f[p].l].sd+f[f[p].r].sd+f[p].cnt;
    f[p].sz=f[f[p].l].sz+f[f[p].r].sz+f[p].cnt;
}
inline bool Can_Rbu(int p)
{
    return f[p].cnt&&(f[p].sz*alpha<=(double)max(f[f[p].l].sz,f[f[p].r].sz)||f[p].sz*alpha>=(double)f[p].sd);
}
int tmp[100010];
void Rbu_Flatten(int &num,int p)
{
    if(!p) return;
    Rbu_Flatten(num,f[p].l);
    if(f[p].cnt) tmp[num++]=p;
    Rbu_Flatten(num,f[p].r);
}
int Rbu_Build(int l,int r)
{
    if(l>=r) return 0;
    int mid=(l+r)>>1;
    f[tmp[mid]].l=Rbu_Build(l,mid);
    f[tmp[mid]].r=Rbu_Build(mid+1,r);
    update(tmp[mid]);
    return tmp[mid];
}
void Rbu(int &p)
{
    int x=0;
    Rbu_Flatten(x,p);
    p=Rbu_Build(0,x);
}
void Insert(int &p,int val)
{
    if(!p)
    {
        p=++tot;
        f[p].cnt=f[p].sd=f[p].sz=1;
        f[p].val=val;
        f[p].l=f[p].r=0;
    }
    else
    {
        if(f[p].val==val) f[p].cnt++;
        else if(val<f[p].val) Insert(f[p].l,val);
        else Insert(f[p].r,val);
        update(p);
        if(Can_Rbu(p)) Rbu(p);
    }
}
void del(int &p,int val)
{
    if(!p) return;
    f[p].sd--;
    if(f[p].val==val)
    {
        if(f[p].cnt) f[p].cnt--;
    }
    else
    {
        if(val<f[p].val) del(f[p].l,val);
        else del(f[p].r,val);
        update(p);
    }
    if(Can_Rbu(p)) Rbu(p);
}
int Get_uprb(int p,int val)
{
    if(!p) return 1;
    if(f[p].val==val&&f[p].cnt) return f[f[p].l].sd+f[p].cnt+1;
    else if(val<f[p].val) return Get_uprb(f[p].l,val);
    else return f[f[p].l].sd+f[p].cnt+Get_uprb(f[p].r,val);
}
int Get_lwrb(int p,int val)
{
    if(!p) return 0;
    if(f[p].val==val&&f[p].cnt) return f[f[p].l].sd;
    else if(val<f[p].val) return Get_lwrb(f[p].l,val);
    else return f[f[p].l].sd+f[p].cnt+Get_lwrb(f[p].r,val);
}
int GetVal(int p,int rk)
{
    if(!p) return 0;
    if(rk>f[f[p].l].sd&&rk<=f[f[p].l].sd+f[p].cnt) return f[p].val;
    else if(rk>f[f[p].l].sd+f[p].cnt) return GetVal(f[p].r,rk-f[f[p].l].sd-f[p].cnt);
    else return GetVal(f[p].l,rk);
}
int main()
{
    read(n);
    for(int i=1;i<=n;i++)
    {
        int op;
        read(op);
        int x;
        read(x);
        switch(op)
        {
            case 1:
                Insert(rt,x);break;
            case 2:
                del(rt,x);break;
            case 3:
                write(Get_lwrb(rt,x)+1);putchar('\n');break;
            case 4:
                write(GetVal(rt,x));putchar('\n');break;
            case 5:
                write(GetVal(rt,Get_lwrb(rt,x)));putchar('\n');break;
            case 6:
                write(GetVal(rt,Get_uprb(rt,x)));putchar('\n');break;
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/xiaoh105/p/12163392.html