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 [题目] 输入一棵二叉树的根结点,判断该树是不是平衡二叉树.如果某二叉 ...
随机推荐
- 6_2.springboot2.x整合Druid和配置数据源监控
简介 Druid首先是一个数据库连接池.Druid是目前最好的数据库连接池,在功能.性能.扩展性方面,都超过其他数据库连接池,包括DBCP.C3P0.BoneCP.Proxool.JBoss Data ...
- Kindle电子书制作
text.html: <!DOCTYPE html> <html lang="en"> <head> <meta http-equiv=& ...
- spring:AOP面向切面编程(注解)03
使用注解写aop时最好使用环绕通知写 切面类: /** * 用于记录日志的工具类,它里面提供了公共的代码 */ @Component("logger") @Aspect //表示当 ...
- selenium学习笔记——driver.get(url) 页面加载时间太长
# 两个同时设置才行 # 实现效果:加载状态停止,进行代码下一步操作 driver.set_page_load_timeout(10) driver.set_script_timeout(10) # ...
- [转]Java多线程
一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的图: 上图中基本上囊括了Java中多线程各重要知识点.掌握了上图中的各知识点,Java中的多线程也就基本上掌 ...
- LoadRunner穿过防火墙运行Vuser和进行监控
LoadRunner穿过防火墙运行Vuser和进行监控 LoadRunner穿过防火墙进行测试,总结下来是2个方法:1. 在controller和Vuser的LAN中的防火墙都打开54345端口即 ...
- [JZOJ3293] 【SHTSC2013】阶乘字符串
题目 题目大意 给你一个字符串,判断这个字符串是否为"阶乘字符串". 就是子序列包含字符集的全排列的字符串. n≤26n\leq 26n≤26 ∣S∣≤450|S|\leq 450 ...
- 介绍一下再Apache下的Tomcat负载均衡的一些使用问题
在负载均衡技术中,硬件设备是比较昂贵的,对于负载均衡的学习者如果不是在企业中应用或者是学员中学习,很少有机会能碰到实际操作的训练.(http://xz.8682222.com)所以,很多朋友都会选择软 ...
- 有关阿里云对SaaS行业的思考,看这一篇就够了
过去二十年,随着改革开放的深化,以及中国的人口红利等因素,中国诞生了大批To C的高市值互联网巨头,2C的领域高速发展,而2B领域一直不温不火.近两年来,在C端流量饱和,B端数字化转型来临的背景下,中 ...
- python 排序算法总结及实例详解
python 排序算法总结及实例详解 这篇文章主要介绍了python排序算法总结及实例详解的相关资料,需要的朋友可以参考下 总结了一下常见集中排序的算法 排序算法总结及实例详解"> 归 ...