平衡树
fhq treap(无旋treap)
treap:其中每个点有输入值与随机值两种值,输入值满足二叉树性质,随机值满足堆性质。
而fhq如何实现平衡呢
它需要两个操作:分裂与合并
分裂:
目的:将其分成所有权值都大于或小于等于某一值的两棵平衡树
我们现在需要两个指针,每一次走到该点权值是否大于该值的反方向
点击查看代码
//pushup()一般是用来更改每一点的size...
void split(int &a,int &b,int p,int val){
if(p==0){
a=b=0;
return ;
}
if(e[p].sm<=val){//此处val为进行分离的那一个值
a=p;
split(e[p].r,b,e[p].r,val);
}
else{
b=p;
split(a,e[p].l,e[p].l,val);
}
pushup(p);
}
合并:
保证,两颗平衡树中的某一颗所有值都比另一颗的大
仍使用两个指针指向两个平衡树,
对于当前指向有两种连接方式:把小的作为大的左儿子;把大的作为小的的右儿子
这是我们把随机值小的放上面
这个位置直接连接到上一节点并把指针放到该位置上。
点击查看代码
int merge(int x,int y){
if(!x||!y) return x+y;
if(e[y].dat<=e[x].dat){
e[y].l=merge(x,e[y].l);
pushup(y);
return y;
}
else{
e[x].r=merge(e[x].r,y);
pushup(x);
return x;
}
}
接下来就是一些基础操作:
add:
按插入值分裂,再分别把该值与两棵树合并
点击查看代码
void add(int sm){
int x,y;
split(x,y,rt,sm);
rt=merge(x,merge(newnode(sm),y));
}
delete:
按删除值大于分裂,再按其-1大于分裂,中间树删根或大小-1,再全部合并起来。
点击查看代码
void delete(int sm){
int x,y,z;
split(x,z,rt,sm);
split(x,y,x,sm-1);
y=merge(e[y].l,e[y].r);
rt=merge(x,merge(y,z));
}
值查询排名(rank):
按该值分裂,观察左子树size。
点击查看代码
void rank(int sm){
int x,y,ans;
split(x,y,rt,sm);
ans=e[e[x].l].siz;
rt=merge(x,y);
return ans;
}
排名查询值(rerank):
所有二叉平衡树的写法都一样。(略)
点击查看代码
int rerank(int p,int sm){
if(e[e[p].l].siz+1<sm) return rerank(e[p].r,sm-e[e[p].l].siz-1);
if(e[e[p].l].siz<=sm) return rerank(e[p].l,sm);
return e[p].sm;
}
前驱(fpre):
按值分裂,直接在A子树中rerank最后一名。
点击查看代码
int fpre(int sm){
int x,y,ans;
split(x,y,rt,sm-1);
ans=rerank(x,e[x].siz);
rt=merge(x,y);
return ans;
}
后继(fnxt):
类似前驱。
点击查看代码
按值分裂,直接在A子树中rerank最后一名。
int fnxt(int sm){
int x,y,ans;
split(x,y,rt,sm);
ans=rerank(y,1);
rt=merge(x,y);
return ans;
}