Size Balanced Tree,SBT,被广大算法竞赛选手“亲切”地成为傻逼树。当然作为由国内神犇陈启峰(%%%%%%)发明的一种优秀的二叉排序树,这种数据结构一点儿也不傻逼,甚至优于被公认为十分好写好用的Splay Tree。动动嘴皮子的事情谁都会,接下来就让我们来实现以下这个数据结构。
SBT主要的思想是,用子树的大小作为维护这棵平衡树的关键字。
首先SBT最基础的还是一类基本的左旋右旋操作。不过因为涉及到每一棵子树的大小,所以要在普通平衡树的基础上加上子树大小的转移。
#define N 1000001
int jis=0,r[N],l[N],s[N],d[N],host=0;//jis是总节点数,r是右孩子,l是左孩子,s代表字树的大小,d代表节点的值,host代表树的根节点
void zig(int &a){//右旋操作
int b=l[a];
l[a]=r[b];
r[b]=a;
s[b]=s[a];
s[a]=s[l[a]]+s[r[a]]+1;
a=b;
}
void zag(int &a){//左旋操作
int b=r[a];
r[a]=l[b];
l[b]=a;
s[b]=s[a];
s[a]=s[l[a]]+s[r[a]]+1;
a=b;
}
再来实现以下在树中插入新元素。
void insert(int &a,int data){
if(!a){
a=++jis;
d[a]=data;
s[a]=1;
r[a]=l[a]=0;
} else{
s[a]++;
if(data<d[a]) insert(l[a],data); else insert(r[a],data);
mt(a,data>=d[a]);
}
}
这个mt,即Maintain是个什么鬼呢?写完插入后,这棵树难免会有不平衡的情况出现,我们需要用Maintain函数来修复它。首先我们需要知道,SBT有这么两个性质。s[r[t]]≥s[l[l[t]]],s[r[l[t]]],和s[l[t]]≥s[l[r[t]]],s[r[r[t]]],如果不符合这两条性质,就要用左旋和右旋进行调整了!
void mt(int &t,bool pd){
if(!pd){
if(s[l[l[t]]]>s[r[t]]) zig(t);
else if(s[r[l[t]]]>s[r[t]]) zag(l[t]),zig(t);
else return;
}else{
if(s[r[r[t]]]>s[l[t]]) zag(t);
else if(s[l[r[t]]]>s[l[t]]) zig(r[t]),zag(t);
else return;
}
mt(l[t],0);
mt(r[t],1);
mt(t,0);
mt(t,1);
}
请手模体验这个神奇的函数。十分的工整和好写,又十分的神奇。
当我们维护好了这棵树之后呢,就可以使用这棵树的性质进行一些高级的操作,比如查找元素,查找第k大的元素,查找一个元素的前驱和后继或者删除一个元素。
int Find(int t,int data){
if(t==0||d[t]==data) return t;
if(d[t]>data) return Find(l[t],data);
else return Find(r[t],data);
}
int Delete(int &u,int v)//如果没有找到要删除的节点,就删除最后一个访问的节点并记录
{
s[u]--;
if((v==d[u])||(v<d[u]&&l[u]==0)||(v>d[u]&&r[u]==0))
{
int rr=d[u];
if(l[u]==0||r[u]==0)
u=l[u]+r[u];
else
d[u]=Delete(l[u],d[u]+1);
return rr;
}
else
{
if(v<d[u])
return Delete(l[u],v);
else
return Delete(r[u],v);
}
}
int Rank(int t,int data){
if(T==0) return 1;
if(data<=d[t]) return Rand(l[t],data);
else return s[l[t]]+1+rank(r[t],data);
}
int Succ(int t,int data){
if(t==0) return t;
if(data>=d[t]) return Succ(r[t],data);
else{
int cache=Succ(l[t],data);
return cache==0? t:cache;
}
}
int Pred(int t,int data){
if(t==0) return t;
if(data<=d[t]) return Pred(r[t],data);
else {
int cache=Pred(l[t],data);
return cache==0? t:cache;
}
}
int select(int a,int rank){
if(rank==s[l[a]]+1) return d[a];
if(rank<=s[l[a]]) return select(l[a],rank);
else return select(r[a],rank-1-s[l[a]]);
}
打完感觉手在冒烟。
参考资料