• Size Balanced Tree(节点大小平衡树)


    定义

    SBT也是一种自平衡二叉查找树,它的平衡原理是每棵树的大小不小于其兄弟树的子树的大小

    即size(x->l)$ge$size(x->r->l),size(x->r->r),右边同理size(x->r)$ge$size(x->l->l),size(x->l->r)

    具体操作

      旋转

        旋转几乎是所有平衡树所共有的操作,操作方法也基本相同

        

        

    void rotate(SBT *&x,int d){//旋转操作,d=0表示左旋,d=1表示右旋
        SBT *y=x->son[d^1];//y指向要旋转到父节点的子节点 
        x->son[d^1]=y->son[d],y->son[d]=x;//更新指向关系 
        y->size=x->size;//更新size值
        x->size=size(x->son[0])+size(x->son[1])+x->num;
        x=y;//别忘了将进入子树的指针指到y上 
    }

      平衡维护

        SBT的平衡维护是SBT所特有的操作,具体有两种情况(左右对称算一种)

          1.size(x->l)<size(x->r->r),即下图中的size(2)<size(7)

          

          这时我们只需要把3旋转到根即可

          

          这时size(7)>size(2),size(6),但size(6)不一定>size(4),size(5),所以要维护一下节点1,然后再维护一遍节点3

          2.size(x->l)<size(x->r->l),即下图中的size(2)<size(6)

          

          我们先把子树3右旋,6旋到3的位置

          

          这时size(2)还不一定大于size(3),size(8),于是我们把子树1左旋,将6变为根

          

          这时size(1)>size(9),size(7),但是子树1和3不一定平衡,所以平衡1,3,然后再平衡6

    void maintain(SBT *&x,int d){//平衡操作,检查(x->son[d]的子树是否比x->son[d^1]大)
        if(x->son[d]==NULL)return;
        if(size(x->son[d^1])<size(x->son[d]->son[d^1]))rotate(x->son[d],d),rotate(x,d^1);
        else if(size(x->son[d^1])<size(x->son[d]->son[d]))rotate(x,d^1);
        else return;
        maintain(x->son[0],0),maintain(x->son[1],1),maintain(x,0),maintain(x,1);//平衡子树后再平衡一次x 
    }

       插入

          和二叉查找树的插入差不多,只是在插入后要平衡一下

    void insert(SBT *&x,int key){
        if(!x){x=new SBT(key);return;}
        x->size++;
        if(x->key==key){x->num++;return;}
        int d=key>x->key;
        insert(x->son[d],key);
        maintain(x,d);//插入后平衡一遍 
    }

       删除

          如果要删除的节点有子节点为空,则用另一个子节点代替要删除的节点
          否则,用后继代替当前节点,然后递归删除后继

    void del(SBT *&x,int key){
        if(x->key!=key){
            del(x->son[key>x->key],key);
            x->size=size(x->son[0])+size(x->son[1])+x->num; 
            return;
        }
        x->size--;
        if(x->num>1){x->num--;return;}//num
        SBT *p=x;
        if(x->son[0]==NULL)x=x->son[1],delete p;
        else if(x->son[1]==NULL)x=x->son[0],delete p;
        else{//用后继替换当前节点,删除后继 
            p=x->son[1];
            while(p->son[0]){
                p=p->son[0];
            }
            x->num=p->num,x->key=p->key,p->num=1,del(x->son[1],p->key);
        }
    }

    其他操作

    int query_id(SBT *x,int key){//求数列中比key小的有几个 
        if(!x)return 0;
        if(x->key>key)return query_id(x->son[0],key);
        if(x->key==key)return size(x->son[0]);
        return query_id(x->son[1],key)+size(x->son[0])+x->num;
    }
    int query_k(SBT *x,int k){//求排第k的数 
        if(!x)return 0;
        if(size(x->son[0])>=k)return query_k(x->son[0],k);
        if(size(x->son[0])+x->num>=k)return x->key;
        return query_k(x->son[1],k-size(x->son[0])-x->num);
    }
    int ans;
    void pre(SBT *x,int num){//求num的前驱(即小于num的最大的数),并存在ans里 
        if(!x)return;
        if(x->key<num)ans=x->key,pre(x->son[1],num);
        else pre(x->son[0],num);
    }
    void suc(SBT *x,int num){//求后继 
        if(!x)return;
        if(x->key>num)ans=x->key,suc(x->son[0],num);
        else suc(x->son[1],num);
    }
    void mid_traversal(SBT *x){//中序遍历
        if(x->son[0])mid_traversal(x->son[0]);
        printf("%d ",x->key);
        if(x->son[1])mid_traversal(x->son[1]);
    }

    模板

    #include<cstdio>
    #include<cstring>
    using namespace std;
    #define size(x) (x?x->size:0)
    struct SBT{
        int key,size,num;
        SBT *son[2];
        SBT(){
            memset(this,0,sizeof(SBT));
        }
        SBT(int x){
            num=size=1,key=x,son[0]=son[1]=0;
        }
    }*root;
    void rotate(SBT *&x,int d){//旋转操作,d=0表示左旋,d=1表示右旋
        SBT *y=x->son[d^1];//y指向要旋转到父节点的子节点 
        x->son[d^1]=y->son[d],y->son[d]=x;//更新指向关系 
        y->size=x->size;//更新size值
        x->size=size(x->son[0])+size(x->son[1])+x->num;
        x=y;//别忘了将进入子树的指针指到y上 
    }
    void maintain(SBT *&x,int d){//平衡操作,检查(x->son[d]的子树是否比x->son[d^1]大)
        if(x->son[d]==NULL)return;
        if(size(x->son[d^1])<size(x->son[d]->son[d^1]))rotate(x->son[d],d),rotate(x,d^1);
        else if(size(x->son[d^1])<size(x->son[d]->son[d]))rotate(x,d^1);
        else return;
        maintain(x->son[0],0),maintain(x->son[1],1),maintain(x,0),maintain(x,1);//平衡子树后再平衡一次x 
    }
    void insert(SBT *&x,int key){
        if(!x){x=new SBT(key);return;}
        x->size++;
        if(x->key==key){x->num++;return;}
        int d=key>x->key;
        insert(x->son[d],key);
        maintain(x,d);//插入后平衡一遍 
    }
    void del(SBT *&x,int key){
        if(x->key!=key){
            del(x->son[key>x->key],key);
            x->size=size(x->son[0])+size(x->son[1])+x->num; 
            return;
        }
        x->size--;
        if(x->num>1){x->num--;return;}//num>1直接num-1即可 
        SBT *p=x;
        if(x->son[0]==NULL)x=x->son[1],delete p;
        else if(x->son[1]==NULL)x=x->son[0],delete p;
        else{//用后继替换当前节点,删除后继 
            p=x->son[1];
            while(p->son[0]){
                p=p->son[0];
            }
            x->num=p->num,x->key=p->key,p->num=1,del(x->son[1],p->key);
        }
    }
    int query_id(SBT *x,int key){//求数列中比key小的有几个 
        if(!x)return 0;
        if(x->key>key)return query_id(x->son[0],key);
        if(x->key==key)return size(x->son[0]);
        return query_id(x->son[1],key)+size(x->son[0])+x->num;
    }
    int query_k(SBT *x,int k){//求排第k的数 
        if(!x)return 0;
        if(size(x->son[0])>=k)return query_k(x->son[0],k);
        if(size(x->son[0])+x->num>=k)return x->key;
        return query_k(x->son[1],k-size(x->son[0])-x->num);
    }
    int ans;
    void pre(SBT *x,int num){//求num的前驱(即小于num的最大的数),并存在ans里 
        if(!x)return;
        if(x->key<num)ans=x->key,pre(x->son[1],num);
        else pre(x->son[0],num);
    }
    void suc(SBT *x,int num){//求后继 
        if(!x)return;
        if(x->key>num)ans=x->key,suc(x->son[0],num);
        else suc(x->son[1],num);
    }
    void mid_traversal(SBT *x){//中序遍历
        if(x->son[0])mid_traversal(x->son[0]);
        printf("%d ",x->key);
        if(x->son[1])mid_traversal(x->son[1]);
    }
    bool f=0;
    void check(SBT *x){
        if(!x)return;
        check(x->son[0]);
        check(x->son[1]);
        if(x->size!=size(x->son[0])+size(x->son[1])+1)printf("woring");
    }
    int main(){
        return 0;
    }

    例题P3369 【模板】普通平衡树(Treap/SBT)

    #include<cstdio>
    #include<cstring>
    using namespace std;
    #define size(x) (x?x->size:0)
    struct SBT{
        int key,size,num;
        SBT *son[2];
        SBT(){
            memset(this,0,sizeof(SBT));
        }
        SBT(int x){
            num=size=1,key=x,son[0]=son[1]=0;
        }
    }*root;
    void rotate(SBT *&x,int d){//旋转操作,d=0表示左旋,d=1表示右旋
        SBT *y=x->son[d^1];//y指向要旋转到父节点的子节点 
        x->son[d^1]=y->son[d],y->son[d]=x;//更新指向关系 
        y->size=x->size;//更新size值
        x->size=size(x->son[0])+size(x->son[1])+x->num;
        x=y;//别忘了将进入子树的指针指到y上 
    }
    void maintain(SBT *&x,int d){//平衡操作,检查(x->son[d]的子树是否比x->son[d^1]大)
        if(x->son[d]==NULL)return;
        if(size(x->son[d^1])<size(x->son[d]->son[d^1]))rotate(x->son[d],d),rotate(x,d^1);
        else if(size(x->son[d^1])<size(x->son[d]->son[d]))rotate(x,d^1);
        else return;
        maintain(x->son[0],0),maintain(x->son[1],1),maintain(x,0),maintain(x,1);//平衡子树后再平衡一次x 
    }
    void insert(SBT *&x,int key){
        if(!x){x=new SBT(key);return;}
        x->size++;
        if(x->key==key){x->num++;return;}
        int d=key>x->key;
        insert(x->son[d],key);
        maintain(x,d);//插入后平衡一遍 
    }
    void del(SBT *&x,int key){
        if(x->key!=key){
            del(x->son[key>x->key],key);
            x->size=size(x->son[0])+size(x->son[1])+x->num; 
            return;
        }
        x->size--;
        if(x->num>1){x->num--;return;}//num>1直接num-1即可 
        SBT *p=x;
        if(x->son[0]==NULL)x=x->son[1],delete p;
        else if(x->son[1]==NULL)x=x->son[0],delete p;
        else{//用后继替换当前节点,删除后继 
            p=x->son[1];
            while(p->son[0]){
                p=p->son[0];
            }
            x->num=p->num,x->key=p->key,p->num=1,del(x->son[1],p->key);
        }
    }
    int query_id(SBT *x,int key){//求数列中比key小的有几个 
        if(!x)return 0;
        if(x->key>key)return query_id(x->son[0],key);
        if(x->key==key)return size(x->son[0]);
        return query_id(x->son[1],key)+size(x->son[0])+x->num;
    }
    int query_k(SBT *x,int k){//求排第k的数 
        if(!x)return 0;
        if(size(x->son[0])>=k)return query_k(x->son[0],k);
        if(size(x->son[0])+x->num>=k)return x->key;
        return query_k(x->son[1],k-size(x->son[0])-x->num);
    }
    int ans;
    void pre(SBT *x,int num){//求num的前驱(即小于num的最大的数),并存在ans里 
        if(!x)return;
        if(x->key<num)ans=x->key,pre(x->son[1],num);
        else pre(x->son[0],num);
    }
    void suc(SBT *x,int num){//求后继 
        if(!x)return;
        if(x->key>num)ans=x->key,suc(x->son[0],num);
        else suc(x->son[1],num);
    }
    void mid_traversal(SBT *x){//中序遍历
        if(x->son[0])mid_traversal(x->son[0]);
        printf("%d ",x->key);
        if(x->son[1])mid_traversal(x->son[1]);
    }
    bool f=0;
    void check(SBT *x){
        if(!x)return;
        check(x->son[0]);
        check(x->son[1]);
        if(x->size!=size(x->son[0])+size(x->son[1])+1)printf("woring");
    }
    int main(){
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
        int n,x,y;scanf("%d",&n);
        while(n--){
            scanf("%d%d",&x,&y);
            switch(x){
                case 1:
                    insert(root,y);
                    break;
                case 2:
                    del(root,y);
                    break;
                case 3:
                    printf("%d
    ",query_id(root,y)+1);
                    break;
                case 4:
                    printf("%d
    ",query_k(root,y));
                    break;
                case 5:
                    pre(root,y);printf("%d
    ",ans);
                    break;
                default:
                    suc(root,y);printf("%d
    ",ans);
                
            }
    //        mid_traversal(root);printf("
    ");
        }
        return 0;
    }
  • 相关阅读:
    mobilebone.js使用笔记
    js笔记:匿名函数
    java中==和equals和hashcode的区别详解
    JVM基础学习之基本概念、可见性与同步
    面向对象-抽象类和接口解析
    maven使用deploy发布到本地仓库
    Jackson 时间格式化,时间注解 @JsonFormat 与 @DatetimeFormat 用法、时差问题说明
    cxf动态调用webservice设置超时,测试线程安全
    再谈API GateWay服务网关
    微服务实战-使用API Gateway
  • 原文地址:https://www.cnblogs.com/bennettz/p/8366315.html
Copyright © 2020-2023  润新知