定义

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. Spring中的事件监听实现

    在spring中我们可以自定义事件,并且可以使用ApplicationContext类型对象(就是spring容器container)来发布这个事件 事件发布之后,所有的ApplicaitonList ...

  2. NOI 2001 食物链 /// 并查集 oj22035

    Description 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1~N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到 ...

  3. USACO training course Mother's Milk /// DFS(有点意思) oj10120

    题目大意: 输入 A B C 为三个容器的容量 一开始A B是空的 C是满的 每一次倾倒只能在 盛的容器满 或 倒的容器空 时才停止 输出当A容器空时 C容器内剩余量的所有可能值 Sample Inp ...

  4. Python全栈开发:web框架们

    Python的WEB框架 Bottle Bottle是一个快速.简洁.轻量级的基于WSIG的微型Web框架,此框架只由一个 .py 文件,除了Python的标准库外,其不依赖任何其他模块. 1 2 3 ...

  5. AndroidStudio 添加翻译插件

    添加方式 第一步 在AndroidStudio的菜单栏里找到 File > Settings > 点击 . 第二步 点击Plugins > 在点击Marketplace 等待插件列表 ...

  6. Android 开发 Camera1_如何使用对焦功能

    前言 Camera1的自动对焦还是有一些坑值得开一个篇幅来讲解,一般对焦Mode有以下几种: Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO 连续自动对焦视 ...

  7. Python-函数基础(2)

    目录 可变长参数 形参 实参 函数对象 函数嵌套 名称空间与作用域 名称空间 内置名称空间 局部名称空间 全局名称空间 执行顺序 搜索顺序 作用域 全局作用域 局部作用域 global nonloca ...

  8. 第十二章 Odoo 12开发之报表和服务端 QWeb

    报表是业务应用非常有价值的功能,内置的 QWeb 引擎是报表的默认引擎.使用 QWeb 模板设计的报表可生成 HTML 文件并被转化成 PDF.也就是说我们可以很便捷地利用已学习的 QWeb 知识,应 ...

  9. LUOGU P2949 [USACO09OPEN]工作调度Work Scheduling (贪心)

    解题思路 明明一道比较简单的贪心结果挂了好几次23333,就是按照时间排序,然后拿一个小根堆维护放进去的,如果时间允许就入队并且记录答案.如果不允许就从堆里拿一个最小的比较. #include< ...

  10. JS 计算时间范围,最近一周、一个月

    //最近一周 getDay(-7) 返回的是距离当前日期的一周后的时间//一月 getDay(-30)//一年 getDay(-365) function getDay(day){ var today ...