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\n",query_id(root,y)+1);
                break;
            case 4:
                printf("%d\n",query_k(root,y));
                break;
            case 5:
                pre(root,y);printf("%d\n",ans);
                break;
            default:
                suc(root,y);printf("%d\n",ans);
            
        }
//        mid_traversal(root);printf("\n");
    }
    return 0;
}

转载于:https://www.cnblogs.com/bennettz/p/8366315.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值