这是一篇个人笔记,不希望大家学习。
Treap
Treap是一个非常非常好写只是略微看运气的平衡树写法。
先说一句,这篇随笔是写个 XF 看的啦,不是属于正式的算法介绍,因此你们看不看的懂我也不能保证啦
一、
平衡树基于二叉搜索树,而对于一颗二叉搜索树,上面的所有节点均满足:左儿子节点右儿子,因此任意一颗平衡树一样的也有这样的性质。
二、
为了降低时间复杂度,Treap总是先遍历优先度高的节点,确定优先度的方法就是在建立一个新节点时,对它随机一个新节点。
因此,在每一次删除或者加入操作时,我们也要维护它的这个性质,这就要使用 rotate 了。
三、Rotate:旋转函数
可以选择左旋或者右旋,每次都将改变右/左儿子与其父亲的深度关系。
根据代码模拟下不就好了嘛,放什么图啊
inline void rotate(ll &id,ll d){//direction
ll tmp=ch[id][d^1];//这里解释一下:d^1可以使0变1,1变0
ch[id][d^1]=ch[tmp][d];
ch[tmp][d]=id;
id=tmp;
pushup(id);
pushup(ch[id][d]);
}
四、
新建节点:
inline ll New(ll x){
val[++tot]=x;
dat[tot]=rand();
size[tot]=cnt[tot]=1;
return tot;
}
inline void build(){
root=New(-INF),ch[root][1]=New(INF);
pushup(root);
}
inline void insert(ll &id,ll v){
if(!id){
id=New(v);
return;
}
if(val[id]==v) ++cnt[id];
else{
kk=v<val[id]?0:1;
insert(ch[id][kk],v);
if(dat[ch[id][kk]]>dat[id])
rotate(id,kk^1);
}
pushup(id);
}
更新信息:
inline void pushup(ll x){
size[x]=size[ch[x][1]]+size[ch[x][0]]+cnt[x];
}
删除节点:
inline void remove(ll &id,ll x){
if(!id) return;
else{
if(val[id]==x){
if(cnt[id]>1){
--cnt[id];
pushup(id);
return;
}
else{
if(ch[id][0]||ch[id][1]){
if(!ch[id][1]||dat[ch[id][0]]>dat[ch[id][1]])
rotate(id,1),remove(ch[id][1],x);
else
rotate(id,0),remove(ch[id][0],x);
pushup(id);
}
else id=0;
return;
}
}
}
kk=x<val[id]?0:1;
remove(ch[id][kk],x);
pushup(id);
}
查分/查排名
inline ll getrank(ll id,ll x){
if(!id)
return -INF;
if(val[id]==x)
return size[ch[id][0]]+1;
if(x<val[id])
return getrank(ch[id][0],x);
else
return getrank(ch[id][1],x)+size[ch[id][0]]+cnt[id];
}
inline ll getmark(ll id,ll x){
if(!id) return INF;
if(x<=size[ch[id][0]])
return getmark(ch[id][0],x);
if(x<=size[ch[id][0]]+cnt[id])
return val[id];
return getmark(ch[id][1],x-size[ch[id][0]]-cnt[id]);
}
查前驱
inline ll pre(ll x){
ll id=root,prev;
while(id){
if(val[id]<x)
prev=val[id],id=ch[id][1];
else
id=ch[id][0];
}
return prev;
}
这里如果当前节点的值比要寻找的值小的话,根据二叉搜索树的规则,应该选择往右边找,是否有更接近的值;如果大于的话,当然往左边找啦
后继把所有符号反过来就好啦。