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^];//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(节点大小平衡树)的更多相关文章
- Size Balanced Tree
Size Balanced Tree(SBT)是目前速度最快的平衡二叉搜索树,且能够进行多种搜索操作,区间操作:和AVL.红黑树.伸展树.Treap类似,SBT也是通过对节点的旋转来维持树的平衡,而相 ...
- Size Balanced Tree(SBT) 模板
首先是从二叉搜索树开始,一棵二叉搜索树的定义是: 1.这是一棵二叉树: 2.令x为二叉树中某个结点上表示的值,那么其左子树上所有结点的值都要不大于x,其右子树上所有结点的值都要不小于x. 由二叉搜索树 ...
- C基础 - 终结 Size Balanced Tree
引言 - 初识 Size Balanced Tree 最近在抽细碎的时间看和学习 random 的 randnet 小型网络库. iamrandom/randnet - https://github. ...
- Size Balanced Tree(SBT树)整理
不想用treap和Splay,那就用SB树把,哈哈,其实它一点也SB,厉害着呢. 先膜拜一下作者陈启峰.Orz 以下内容由我搜集整理得来. 一.BST及其局限性 二叉查找树(Binary Search ...
- 初学 Size Balanced Tree(bzoj3224 tyvj1728 普通平衡树)
SBT(Size Balance Tree), 即一种通过子树大小(size)保持平衡的BST SBT的基本性质是:每个节点的size大小必须大于等于其兄弟的儿子的size大小: 当我们插入或者删除一 ...
- 手写一个节点大小平衡树(SBT)模板,留着用
看了一下午,感觉有了些了解.应该没有错,有错希望斧正,感谢 #include<stdio.h> #include<string.h> struct s { int key,le ...
- 子树大小平衡树(Size Balanced Tree,SBT)操作模板及杂谈
基础知识(包括但不限于:二叉查找树是啥,SBT又是啥反正又不能吃,平衡树怎么旋转,等等)在这里就不(lan)予(de)赘(duo)述(xie)了. 先贴代码(数组模拟): int seed; int ...
- Size Balance Tree(SBT模板整理)
/* * tree[x].left 表示以 x 为节点的左儿子 * tree[x].right 表示以 x 为节点的右儿子 * tree[x].size 表示以 x 为根的节点的个数(大小) */ s ...
- 56. 2种方法判断二叉树是不是平衡二叉树[is balanced tree]
[本文链接] http://www.cnblogs.com/hellogiser/p/is-balanced-tree.html [题目] 输入一棵二叉树的根结点,判断该树是不是平衡二叉树.如果某二叉 ...
随机推荐
- P1736 创意吃鱼法 /// DP
题目大意: https://www.luogu.org/problemnew/show/P1736 题解 dplr[][] 当前点左边(副对角线时为右边)有多少个连续的0 dpup[][] 当前点上边 ...
- Go前言
Go语言为并发而生 硬件制造商正在为处理器添加越来越多的内核以来提高性能.所有数据中心都在这些处理器上运行,今天的应用程序使用多个微服务来维护数据库连接,消息队列和维护缓存.所以,开发的软件和编程语言 ...
- Miler-Rabbin素数判定
前言 素数判定? 小学生都可以打的出来! 直接暴力O(n)O(\sqrt n)O(n)-- 然后就会发现,慢死了-- 于是我们想到了筛法,比如前几天说到的詹欧筛法. 但是线性的时间和空间成了硬伤-- ...
- 2017.1.16【初中部 】普及组模拟赛C组总结
2017.1.16[初中部 ]普及组模拟赛C组 这次总结我赶时间,不写这么详细了. 话说这次比赛,我虽然翻了个大车,但一天之内AK,我感到很高兴 比赛 0+15+0+100=115 改题 AK 一.c ...
- Python基础笔记_变量类型
下面是W3C学习笔记 , , ) :] ]) :]) :]) :-]) :-]) ]) :]) :]) ) , , ]) :]) :]) ) , , , ]) :]) :]) ) ] = , ])) ...
- Laravel 不同环境加载不同的.env文件
假设有4个.env文件.分别为 .env.local .env.dev .env.test .env.prd 方式一 第一步:bootstrap\app.php 加入代码 $envs = ['loca ...
- VS2010-MFC(对话框:模态对话框及其弹出过程)
转自:http://www.jizhuomi.com/software/160.html 一.模态对话框和非模态对话框 Windows对话框分为两类:模态对话框和非模态对话框. 模态对话框是这样的对话 ...
- rsync命令集合
rsync -avz rsync://logs@211.151.78.206/www_logs/2014/03/27/* /mnt/hgfs/iautoslogs/
- PKU--2184 Cow Exhibition (01背包)
题目http://poj.org/problem?id=2184 分析:给定N头牛,每头牛都有各自的Si和Fi 从这N头牛选出一定的数目,使得这些牛的 Si和Fi之和TS和TF都有TS>=0 F ...
- mac使用ssh出现permission denied(publickey)
看出错信息是权限太开放的问题,google了一下,修改权限,不只是需要修改 .pem 文件的权限,还需要修改.ssh目录和用户目录 chmod go-w ~/ chmod ~/.ssh chmod ~ ...