zoukankan      html  css  js  c++  java
  • 平衡树--替罪羊树 *

    平衡树–替罪羊树

    –yangkai


    身为平衡树却不做任何形式的旋转,替罪羊树可以称得上是最暴力的平衡树了。

    替罪羊树(SGT)保留有二叉搜索树的基本性质,即对于任意一个节点t,左儿子的所有节点比它小,右儿子的所有节点比它大。但是既然不基于翻转,它怎样维护平衡树的优秀复杂度呢?

    SDT基于一个叫做“重构”的操作,听起来很是优美暴力,那么我们要如何重构?

    首先,如果要时刻维持平衡树的平衡,即abs(siz[l]siz[r])<=1,显然我们平均每两次插入、删除操作就需要一次重构,这样的时间效率接近O(n2),所以我们可以取一个阈值[0.5,1.0],当且仅当左右子树超过阈值的限制重构子树。在这里我们可以发现,当阈值限制越紧[0.5,0.75],对修改少询问多有利;当阈值限定越松[0.75,1.0],对修改多查询少有利。

    根据阈值,我们就可以对子树进行重构了。

    但是问题又来了,在条根到叶子的链上可能有许许多多的“不平衡点”,我们要怎样确定最优的一个,实际上,我们只需要选择最靠近根节点的一个就好了,因为如果修改的层数太深,在总体上不会对树的结构产生多么大的影响,所以选最靠近根的一个相对来说比较优秀。

    那么维护平衡树性质的部分我们解决了,但是插入以及删除呢?

    首先,对于插入,直接参见动态开点大法,在向下插入的时候动态开点就好了(此处应该注意回收内存)

    其次,对于删除,我们既不能把它转到叶子结点(treap),也不能直接裂开丢掉再合并(非旋转treap),所以我们给它打上一个real标记,real标记存在说明这个节点信息为’真‘,否则为‘假’(已经删除),然后在重构子树的时候直接丢掉然后回收内存就好了

    其他操作rank,get_kth,pre,nxt之类的操作直接套用平衡树常见套路就好了


    然后写数组版的朋友们要注意
    需要维护父亲和儿子的关系
    不然会莫名其妙地丢失子树


    例题:BZOJ3224 Tyvj 1728 普通平衡树

    Description

    您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
    1. 插入x数
    2. 删除x数(若有多个相同的数,因只删除一个)
    3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
    4. 查询排名为x的数
    5. 求x的前驱(前驱定义为小于x,且最大的数)
    6. 求x的后继(后继定义为大于x,且最小的数)

    Input

    第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)

    Output

    对于操作3,4,5,6每行输出一个数,表示对应答案

    Sample Input

    10
    1 106465
    4 1
    1 317721
    1 460929
    1 644985
    1 84185
    1 89851
    6 81968
    1 492737
    5 493598

    Sample Output

    106465
    84185
    492737

    HINT

    1.n的数据范围:n<=100000

    2.每个数的数据范围:[-2e9,2e9]


    上板子

    //yangkai 
    #include<bits/stdc++.h>
    using namespace std;
    inline int read(){
        int data=0,w=1;char ch=getchar();
        while(!isdigit(ch)&&ch!='-')ch=getchar();
        if(ch=='-')w=-1,ch=getchar();
        while(isdigit(ch))data=(data<<1)+(data<<3)+(ch-'0'),ch=getchar();
        return data*w;
    }
    struct SGT{
        static const int N=100010;
        static const double alpha=0.75;
        //内存池 
        int st[N],top,tot;
        int get_place(){return top?st[top--]:++tot;}
        void del_place(int t){st[++top]=t;}
        void clean_st(){top=tot=0;}
        //SGT
        int root;
        int ls[N],rs[N],fa[N];
        int val[N],siz[N],all[N];
        bool real[N];
        void pushup(int t){
            siz[t]=siz[ls[t]]+siz[rs[t]]+real[t];
            all[t]=all[ls[t]]+all[rs[t]]+1;
        }
        bool check(int t){//返回1说明需要重构 
            if(all[ls[t]]>=alpha*all[t])return 1;
            if(all[rs[t]]>=alpha*all[t])return 1;
            return 0;
        }
        int newnode(int vl=0,int f=0){
            int t=get_place();
            ls[t]=rs[t]=0;
            val[t]=vl;
            siz[t]=all[t]=1;
            real[t]=1;
            fa[t]=f;
            return t;
        }
        //收集需要重构的下标 
        void collect(int t,vector<int> &v){
            if(!t)return;
            collect(ls[t],v);
            if(real[t])v.push_back(t);
            else del_place(t);
            collect(rs[t],v); 
        }
        //重构划分 
        int divide(int l,int r,vector<int> v){
            if(l>=r)return 0;
            int mid=(l+r)>>1;
            int t=v[mid];
            ls[t]=divide(l,mid,v);
            rs[t]=divide(mid+1,r,v);
            fa[ls[t]]=fa[rs[t]]=t;//维护父亲信息
            pushup(t);
            return t;
        }
        //重新建树 
        void rebuild(int &t){
            static vector<int> v;v.clear();
            int f=fa[t];
            collect(t,v);
            t=divide(0,v.size(),v);
            fa[t]=f;//维护父亲信息
        }
        //查询vl的排名 
        int rank(int vl){
            int t=root,ans=1;
            while(t){
                if(vl<=val[t])t=ls[t];
                else{
                    ans+=siz[ls[t]]+real[t];
                    t=rs[t];
                }
            }
            return ans;
        }
        //查询排名为k的数
        int get_kth(int k){
            int t=root;
            while(t){
                if(siz[ls[t]]+1==k&&real[t])return val[t];
                if(siz[ls[t]]>=k)t=ls[t];
                else k-=siz[ls[t]]+real[t],t=rs[t];
            }
        } 
        //插入vl值 
        int insert(int &t,int f,int vl){//返回深度最浅的不合法点 
            if(!t){t=newnode(vl,f);return 0;}
            int res;
            if(vl<=val[t])res=insert(ls[t],t,vl);
            else res=insert(rs[t],t,vl);
            pushup(t);
            //检查是否需要重构 
            if(check(t))res=t;
            return res;
        }
        void insert(int vl){
            int t=insert(root,0,vl);
            if(!t)return;
            if(t==root)rebuild(root);
            else{
                int f=fa[t];
                if(t==ls[f])rebuild(ls[f]);
                else rebuild(rs[f]);
            }
        }
        //删除排名为k的数 
        void erase(int t,int k){
            siz[t]--;
            if(real[t]&&k==siz[ls[t]]+real[t]){real[t]=0;return;}
            if(k<=siz[ls[t]])erase(ls[t],k);
            else erase(rs[t],k-siz[ls[t]]-real[t]);
        }
        //删除数vl 
        void erase(int vl){
            erase(root,rank(vl));
            if(siz[root]<alpha*all[root])rebuild(root);
        }
        //查询vl的前驱和后继 
        int pre(int vl){return get_kth(rank(vl)-1);} 
        int nxt(int vl){return get_kth(rank(vl+1));}
        //输出调试
        void out_put(int t){
            cout<<t<<"||"<<endl;
            if(ls[t])out_put(ls[t]);
            if(real[t])cout<<val[t]<<" L:"<<val[ls[t]]<<" R:"<<val[rs[t]]<<endl;
            if(rs[t])out_put(rs[t]);
        } 
    }sgt;
    int main(){
        int n=read();
        while(n--){
            int op=read(),t=read();
            switch(op){
                case 1:sgt.insert(t);break;
                case 2:sgt.erase(t);break;
                case 3:printf("%d
    ",sgt.rank(t));break;
                case 4:printf("%d
    ",sgt.get_kth(t));break;
                case 5:printf("%d
    ",sgt.pre(t));break;
                case 6:printf("%d
    ",sgt.nxt(t));break;
            }
        }
        return 0;
    } 
  • 相关阅读:
    asp.net(.net 4.0)+ json 分页
    在两张表(A表和B表)里面找出A中不存在B表的记录
    linq to sql 的List<Table> 数据表缓存
    linq 并发冲突概念
    阿里RocketMq节约成本
    阿里巴巴java手册异常日志
    阿里巴巴java手册安全规约
    阿里巴巴java手册单元测试
    Spring boot自定义starter
    MongoDB权限
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/9676397.html
Copyright © 2011-2022 走看看