zoukankan      html  css  js  c++  java
  • 替罪羊树 知识总结

    替罪羊树 知识总结

    替罪羊树是一类神奇的平衡树。它最神奇的地方就在于,大部分平衡树都是用愚蠢的单旋来维护平衡,而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('
    ');break;
    			case 4:
    				write(GetVal(rt,x));putchar('
    ');break;
    			case 5:
    				write(GetVal(rt,Get_lwrb(rt,x)));putchar('
    ');break;
    			case 6:
    				write(GetVal(rt,Get_uprb(rt,x)));putchar('
    ');break;
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    Facebook ads_Business Manager
    思维导图学习5——参考书目
    思维导图学习4——技巧2
    思维导图学习3——思维导图示例
    思维导图学习2——思维导图的用途
    思维导图学习1——元素介绍
    广告词条解释
    Twitter OAuth
    Twitter开发2
    Twitter REST API, Streaming API
  • 原文地址:https://www.cnblogs.com/xiaoh105/p/12163392.html
Copyright © 2011-2022 走看看