定义

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^];//y指向要旋转到父节点的子节点
x->son[d^]=y->son[d],y->son[d]=x;//更新指向关系
y->size=x->size;//更新size值
x->size=size(x->son[])+size(x->son[])+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^])<size(x->son[d]->son[d^]))rotate(x->son[d],d),rotate(x,d^);
else if(size(x->son[d^])<size(x->son[d]->son[d]))rotate(x,d^);
else return;
maintain(x->son[],),maintain(x->son[],),maintain(x,),maintain(x,);//平衡子树后再平衡一次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[])+size(x->son[])+x->num;
return;
}
x->size--;
if(x->num>){x->num--;return;}//num
SBT *p=x;
if(x->son[]==NULL)x=x->son[],delete p;
else if(x->son[]==NULL)x=x->son[],delete p;
else{//用后继替换当前节点,删除后继
p=x->son[];
while(p->son[]){
p=p->son[];
}
x->num=p->num,x->key=p->key,p->num=,del(x->son[],p->key);
}
}

其他操作

int query_id(SBT *x,int key){//求数列中比key小的有几个
if(!x)return ;
if(x->key>key)return query_id(x->son[],key);
if(x->key==key)return size(x->son[]);
return query_id(x->son[],key)+size(x->son[])+x->num;
}
int query_k(SBT *x,int k){//求排第k的数
if(!x)return ;
if(size(x->son[])>=k)return query_k(x->son[],k);
if(size(x->son[])+x->num>=k)return x->key;
return query_k(x->son[],k-size(x->son[])-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[],num);
else pre(x->son[],num);
}
void suc(SBT *x,int num){//求后继
if(!x)return;
if(x->key>num)ans=x->key,suc(x->son[],num);
else suc(x->son[],num);
}
void mid_traversal(SBT *x){//中序遍历
if(x->son[])mid_traversal(x->son[]);
printf("%d ",x->key);
if(x->son[])mid_traversal(x->son[]);
}

模板

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

例题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[];
SBT(){
memset(this,,sizeof(SBT));
}
SBT(int x){
num=size=,key=x,son[]=son[]=;
}
}*root;
void rotate(SBT *&x,int d){//旋转操作,d=0表示左旋,d=1表示右旋
SBT *y=x->son[d^];//y指向要旋转到父节点的子节点
x->son[d^]=y->son[d],y->son[d]=x;//更新指向关系
y->size=x->size;//更新size值
x->size=size(x->son[])+size(x->son[])+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^])<size(x->son[d]->son[d^]))rotate(x->son[d],d),rotate(x,d^);
else if(size(x->son[d^])<size(x->son[d]->son[d]))rotate(x,d^);
else return;
maintain(x->son[],),maintain(x->son[],),maintain(x,),maintain(x,);//平衡子树后再平衡一次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[])+size(x->son[])+x->num;
return;
}
x->size--;
if(x->num>){x->num--;return;}//num>1直接num-1即可
SBT *p=x;
if(x->son[]==NULL)x=x->son[],delete p;
else if(x->son[]==NULL)x=x->son[],delete p;
else{//用后继替换当前节点,删除后继
p=x->son[];
while(p->son[]){
p=p->son[];
}
x->num=p->num,x->key=p->key,p->num=,del(x->son[],p->key);
}
}
int query_id(SBT *x,int key){//求数列中比key小的有几个
if(!x)return ;
if(x->key>key)return query_id(x->son[],key);
if(x->key==key)return size(x->son[]);
return query_id(x->son[],key)+size(x->son[])+x->num;
}
int query_k(SBT *x,int k){//求排第k的数
if(!x)return ;
if(size(x->son[])>=k)return query_k(x->son[],k);
if(size(x->son[])+x->num>=k)return x->key;
return query_k(x->son[],k-size(x->son[])-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[],num);
else pre(x->son[],num);
}
void suc(SBT *x,int num){//求后继
if(!x)return;
if(x->key>num)ans=x->key,suc(x->son[],num);
else suc(x->son[],num);
}
void mid_traversal(SBT *x){//中序遍历
if(x->son[])mid_traversal(x->son[]);
printf("%d ",x->key);
if(x->son[])mid_traversal(x->son[]);
}
bool f=;
void check(SBT *x){
if(!x)return;
check(x->son[]);
check(x->son[]);
if(x->size!=size(x->son[])+size(x->son[])+)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 :
insert(root,y);
break;
case :
del(root,y);
break;
case :
printf("%d\n",query_id(root,y)+);
break;
case :
printf("%d\n",query_k(root,y));
break;
case :
pre(root,y);printf("%d\n",ans);
break;
default:
suc(root,y);printf("%d\n",ans); }
// mid_traversal(root);printf("\n");
}
return ;
}

Size Balanced Tree(节点大小平衡树)的更多相关文章

  1. Size Balanced Tree

    Size Balanced Tree(SBT)是目前速度最快的平衡二叉搜索树,且能够进行多种搜索操作,区间操作:和AVL.红黑树.伸展树.Treap类似,SBT也是通过对节点的旋转来维持树的平衡,而相 ...

  2. Size Balanced Tree(SBT) 模板

    首先是从二叉搜索树开始,一棵二叉搜索树的定义是: 1.这是一棵二叉树: 2.令x为二叉树中某个结点上表示的值,那么其左子树上所有结点的值都要不大于x,其右子树上所有结点的值都要不小于x. 由二叉搜索树 ...

  3. C基础 - 终结 Size Balanced Tree

    引言 - 初识 Size Balanced Tree 最近在抽细碎的时间看和学习 random 的 randnet 小型网络库. iamrandom/randnet - https://github. ...

  4. Size Balanced Tree(SBT树)整理

    不想用treap和Splay,那就用SB树把,哈哈,其实它一点也SB,厉害着呢. 先膜拜一下作者陈启峰.Orz 以下内容由我搜集整理得来. 一.BST及其局限性 二叉查找树(Binary Search ...

  5. 初学 Size Balanced Tree(bzoj3224 tyvj1728 普通平衡树)

    SBT(Size Balance Tree), 即一种通过子树大小(size)保持平衡的BST SBT的基本性质是:每个节点的size大小必须大于等于其兄弟的儿子的size大小: 当我们插入或者删除一 ...

  6. 手写一个节点大小平衡树(SBT)模板,留着用

    看了一下午,感觉有了些了解.应该没有错,有错希望斧正,感谢 #include<stdio.h> #include<string.h> struct s { int key,le ...

  7. 子树大小平衡树(Size Balanced Tree,SBT)操作模板及杂谈

    基础知识(包括但不限于:二叉查找树是啥,SBT又是啥反正又不能吃,平衡树怎么旋转,等等)在这里就不(lan)予(de)赘(duo)述(xie)了. 先贴代码(数组模拟): int seed; int ...

  8. Size Balance Tree(SBT模板整理)

    /* * tree[x].left 表示以 x 为节点的左儿子 * tree[x].right 表示以 x 为节点的右儿子 * tree[x].size 表示以 x 为根的节点的个数(大小) */ s ...

  9. 56. 2种方法判断二叉树是不是平衡二叉树[is balanced tree]

    [本文链接] http://www.cnblogs.com/hellogiser/p/is-balanced-tree.html [题目] 输入一棵二叉树的根结点,判断该树是不是平衡二叉树.如果某二叉 ...

随机推荐

  1. 19.SimLogin_case02

    # 模拟登录马蜂窝 import requests from lxml import etree session = requests.Session() phone_number = input(' ...

  2. 阿里云服务器安装Python3.8

    1.操作系统: CentOS 7.4 64位 2.下载python安装包 wget https://www.python.org/ftp/python/3.8.0/Python-3.8.0.tar.x ...

  3. 收藏的链接-Qt

    Qt编写的开源帖子集合(懒人专用) - QTCN开发网 - Powered by phpwind http://www.qtcn.org/bbs/read-htm-tid-85501.html?tds ...

  4. centos 7 开机优化shell

    vim  start_init.sh #!/bin/bash#####dns echo "nameserver 114.114.114.114" >/etc/resolv.c ...

  5. soj97 旅行

    题意:给你一棵n个点的树.m个操作,op 1:在点i上建立银行.op 2:询问从点x开始可以经过至少一个银行走到的点中编号第二大的点. n,m<=1e5. 标程: #include<bit ...

  6. js怎样截取字符串后几位以及截取字符串前几位

    想要截取字符串前几位与后几位,主要代码如下 截取字符串前几位 var disName ='开心一族漂亮家园'; var shortName = disName.substring(0,5); cons ...

  7. thinkphp 统计查询

    在应用中我们经常会用到一些统计数据,例如当前所有(或者满足某些条件)的用户数.所有用户的最大积分.用户的平均成绩等等,ThinkPHP为这些统计操作提供了一系列的内置方法,包括: 大理石平台检定规程 ...

  8. thinkphp 数据创建

    在进行数据操作之前,我们往往需要手动创建需要的数据,例如对于提交的表单数据: // 获取表单的POST数据 $data['name'] = $_POST['name']; $data['email'] ...

  9. 07.27NOIP模拟赛

    戳这里下载过去三次NOIP模拟赛总成绩 (别嘲笑垫底的我...解压密码为信奥生所在的两个班的班号,文档密码为机房开机用户名+密码) 又一次垫底…… 我难受. 上来感觉T1不可做,T2和蔼可亲,T3一脸 ...

  10. c++ static关键字的作用

    1.被申明的函数或值无法被其他源文件使用 2.static的第二个作用是保持变量内容的持久.(static变量中的记忆功能和全局生存期) 存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯 ...