平衡树
一、二叉查找树
二叉查找树(Binary Search Tree)它或者是一棵空树;或者是具有下列性质的二叉树:
(1) 树中每个结点被赋予了一个权值;(下面假设不同结点的权值互不相同。)
(2) 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(3) 若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(4) 左、右子树也分别为二叉查找树;
二叉查找树能够支持多种动态集合操作,只要所维护的数据集合存在偏序关系(简单来
说就是定义了小于等于)。因此,在信息学竞赛中,二叉查找树起着非常重要的作用,它可
以被用来表示有序集合、建立索引或优先队列等。我们也常常利用它动态地维护一个有序数
集,利用二叉查找树找到新加入序列中的数插入的位置。
二、Splay模板
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int root,len;
//d为值,f为父亲的编号,c为控制的节点个数,n为同值的节点个数
struct trnode{
int d,f,c,n,son[2];//son存儿子节点的编号
}tr[110000];
//更新x所控制的节点数
void update(int x){
int lc=tr[x].son[0],rc=tr[x].son[1];
tr[x].c=tr[lc].c+tr[rc].c+tr[x].n;
}
//添加值为d的点,认编号为f的节点为父亲,同时,f也认它为孩子
void add(int d,int f){
len++;
tr[len].d=d,tr[len].n=1,tr[len].c=1,tr[len].f=f;
if(d<tr[f].d) tr[f].son[0]=len;
else tr[f].son[1]=len;
tr[len].son[0]=tr[len].son[1]=0;
}
//找值为d的节点的地址,如果不存在d,有可能是接近d的(或大或小)
int findip(int d){
int x=root;
while(tr[x].d!=d){
if(d<tr[x].d){
if(tr[x].son[0]==0) break;
else x=tr[x].son[0];
}
else{
if(tr[x].son[1]==0) break;
else x=tr[x].son[1];
}
}
return x;
}
//w=1为右旋,w=0为左旋
void rotate(int x,int w){
int f=tr[x].f,ff=tr[f].f;
int r,R;
r=tr[x].son[w],R=f,tr[R].son[w^1]=r;
if(r!=0) tr[r].f=R;
r=x; R=ff;
if(tr[ff].son[0]==f) tr[R].son[0]=r;
else tr[R].son[1]=r;
tr[r].f=R;
r=f;R=x;
tr[R].son[w]=r;
tr[r].f=R;
update(f),update(x);
}
void splay(int x,int rt){
while(tr[x].f!=rt){
int f=tr[x].f,ff=tr[f].f;
if(ff==rt){
if(x==tr[f].son[0]) rotate(x,1);
else rotate(x,0);
}
else{
if(tr[f].son[0]==x&&tr[ff].son[0]==f) rotate(f,1),rotate(x,1);
else if(tr[f].son[1]==x&&tr[ff].son[0]==f) rotate(x,0),rotate(x,1);
else if(tr[f].son[0]==x&&tr[ff].son[1]==f) rotate(x,1),rotate(x,0);
else if(tr[f].son[1]==x&&tr[ff].son[1]==f) rotate(f,0),rotate(x,0);
}
if(rt==0) root=x;
}
}
void ins(int d){
if(root==0){
add(d,len),root=len;
return;
}
int x=findip(d);
if(tr[x].d==d){
tr[x].n++;
update(x);
splay(x,0);
}
else{
add(d,x);
update(x);
splay(len,0);
}
}
void del(int d){
int x=findip(d);splay(x,0);
if(tr[x].d!=d) return;
if(tr[x].n>1){tr[x].n--;update(x);return;}
if(tr[x].son[0]==0&&tr[x].son[1]==0){len=0;root=0;}
else if(tr[x].son[0]!=0&&tr[x].son[1]==0){
root=tr[x].son[0];
tr[root].f=0;
}
else if(tr[x].son[1]!=0&&tr[x].son[0]==0){
root=tr[x].son[1];
tr[root].f=0;
}
else{
int p=tr[x].son[0];
while(tr[p].son[1]!=0) p=tr[p].son[1];
splay(p,x);
int r=tr[x].son[1],R=p;
tr[R].son[1]=r;
tr[r].f=R;
root=R,tr[root].f=0;
update(R);
}
}
int findpaiming(int d){
int x=findip(d);
splay(x,0);
return tr[tr[x].son[0]].c+1;
}
int findshuzi(int k){
int x=root;
while(1){
int lc=tr[x].son[0],rc=tr[x].son[1];
if(k<=tr[lc].c) x=lc;
else if(k>tr[lc].c+tr[x].n){k-=tr[lc].c+tr[x].n,x=rc;}
else break;
}
splay(x,0);
return tr[x].d;
}
int findqianqu(int d){
int x=findip(d);splay(x,0);
if(tr[x].d>=d&&tr[x].son[0]!=0){
x=tr[x].son[0];
while(tr[x].son[1]!=0) x=tr[x].son[1];
}
if(tr[x].d>=d) x=0;
return x;
}
int findhouji(int d){
int x=findip(d);splay(x,0);
if(tr[x].d<=d&&tr[x].son[1]!=0){
x=tr[x].son[1];
while(tr[x].son[0]!=0) x=tr[x].son[0];
}
if(tr[x].d<=d) x=0;
return x;
}
int main(){
int n;scanf("%d",&n);
root=0;len=0;
for(int i=1;i<=n;++i){
int cz,x;scanf("%d%d",&cz,&x);
if(cz==1) ins(x);
if(cz==2) del(x);
if(cz==3) printf("%d\n",findpaiming(x));
if(cz==4) printf("%d\n",findshuzi(x));
if(cz==5) printf("%d\n",tr[findqianqu(x)].d);
if(cz==6) printf("%d\n",tr[findhouji(x)].d);
}
return 0;
}
操作有:插入,删除,询问排名,询问第k大,查找前驱后继。
一、ROTATE
对于rotate(x,w),表示将x向上旋转一层,w=1为左旋(Zag),w=0为右旋(Zig);
以w=1为例(如下图所示),即x是f的左儿子,此时使用右旋(Zig)操作。
(反之,若x是f的右儿子,则使用左旋(Zag)操作,过程类似。)
int f=tr[x].f; //x的父亲
int ff=tr[f].f; //x的祖父
int r,R;
其中A、B为x的左右子树,C为f右子树
以下过程中可以把w=1代进去。(son[1]为右儿子,son[0]为左儿子)
第一步:处理x与f之间的关系
r=tr[x].son[w],R=f;
tr[R].son[w^1]=r;
if(r!=0) tr[r].f=R;
B原是x的右子树,变到f的左子树
第二步:处理x与ff之间的关系
r=x; R=ff;
if(tr[ff].son[0]==f) tr[R].son[0]=r;
else tr[R].son[1]=r;
tr[r].f=R;
把x设为ff的儿子,若f是ff的左儿子,x就是ff的左儿子,反之为右儿子
第三步:整理f和x的关系
r=f;R=x;
tr[R].son[w]=r;
tr[r].f=R;
x和f之间互相连上,就旋转完成了
二、Splay.
对于Splay(int x,int rt),表示将x旋转到rt的下面。
while(tr[x].f!=rt){
int f=tr[x].f,ff=tr[f].f;
if(ff==rt){
if(x==tr[f].son[0]) rotate(x,1);
else rotate(x,0);
}
else{
if(tr[f].son[0]==x&&tr[ff].son[0]==f) rotate(f,1),rotate(x,1);
else if(tr[f].son[1]==x&&tr[ff].son[0]==f) rotate(x,0),rotate(x,1);
else if(tr[f].son[0]==x&&tr[ff].son[1]==f) rotate(x,1),rotate(x,0);
else if(tr[f].son[1]==x&&tr[ff].son[1]==f) rotate(f,0),rotate(x,0);
}
if(rt==0) root=x;
}
①while语句:如果x的father不是rt,就把x往上旋。
while(tr[x].f!=rt)
②若x的祖父是rt,那么x的father就在rt的下面,只需要把x向上旋转一下,x就在rt下面了。
if(x==tr[f].son[0]) rotate(x,1);
else rotate(x,0);
如果x是它父亲的左儿子,则右旋;否则左旋。
③剩下的情况就是:x的祖父也不是rt,那么就把x往上旋转两次,根据父亲与儿子的关系而定。
----------------------------------------------------------------------------------------------
下面举旋转的一种情况。f是ff左儿子,x是f左儿子(如下图)。
if(tr[f].son[0]==x&&tr[ff].son[0]==f)
rotate(f,1),rotate(x,1);
原状态:
先把f右旋,变成这样:
然后把x右旋,就旋转完成了,变成这样:
最后,如果rt==0,就是把x旋转到0的下面,相当于把x旋转到根节点去,那么就要把root的值更新为x。