树堆Treap
treap是平衡树的一种,简单好写而且速度快,是性价比很高的选择。
目录
算法思想
- treap采用了随机化思想,考虑到按照普通二叉搜索树构建时会由于数据而退化,treap在构建时,为每个节点重新
随机分配了一个权值
。这样构建时,节点就会相对平衡的分布了,所以treap复杂度是期望O(nlogn); - 具体说来,treap正如其名==tree+heap,是
二叉搜索树和堆的结合体
。对于每个节点的值来说,它是一棵二叉搜索树;而对于每个节点随机分配的权值来说,它又是一个堆(具体是大还是小根堆由个人喜好来定,不过小根堆会有一个小优点,这个稍后说); - treap之所以好写,其实就是因为它除了这一点不同,就是一个普通的二叉搜索树,如果学了
splay
,会左右旋
这个直接写是很容易的。
算法实现
插入
- 按照普通的二叉排序树先插入节点,同时为其随机分配一个权值;
- 为了维护堆的性质,和该节点的父亲比较,如果不符合条件就上旋;
- 通常我们建成小根堆,因为rand()函数值域为[0,32767],而根节点的父亲0号节点的权值没有修改、为0,我们上旋时就无需考虑是否已经转到根节点上了。
删除
- 有些类似堆的删除,因为我们无需考虑要删除的节点对treap的影响,所以直接相堆一样把它换到叶节点然后删除即可;
- 具体说就是在两个儿子里挑一个权值最优的(大根堆挑最大的,小根堆挑最小的)把它旋上来作为被删节点的替代,如此反复直至被删节点旋转到叶节点,最后把它删除即可。
其它操作
- 和
splay
基本一样,这也是学完splay后treap好学的原因; - 不过要注意的一点是,splay每次都把新更改的节点旋转到了根节点,所以递推写法也很好更新子树大小,但treap不同,推荐写递归版的,递推版更新真是要死人。
不过我图zhao省ma事fan,直接按之前写的splay改了,是递推版…
- 和
代码
#include <cstdio>
#include <cstdlib>
using namespace std;
const int MAXN=100005;
struct node{
int s[2],w,val,cnt,sz,fa;
};
class treap{
private:
int sz,cnt,rt;
node d[MAXN];
void sgl_upd(const int &u){
d[u].sz=d[d[u].s[0]].sz+d[d[u].s[1]].sz+d[u].cnt;
}
void path_upd(int u){
while(u){
sgl_upd(u);
u=d[u].fa;
}
}
void rot(int u){
int ufa=d[u].fa;
bool lr= d[ufa].s[1]==u;
d[u].fa=d[ufa].fa;
if(d[ufa].fa)
d[d[ufa].fa].s[d[d[ufa].fa].s[1]==ufa]=u;
else rt=u;
d[ufa].s[lr]=d[u].s[lr^1];
d[d[ufa].s[lr]].fa=ufa;
d[u].s[lr^1]=ufa;
d[ufa].fa=u;
sgl_upd(ufa),sgl_upd(u);
}
void mt(const int &u){
while(d[u].w<d[d[u].fa].w) rot(u);
}
int find(const int &val){
int u=rt;
while(u){
if(d[u].val==val) return u;
u=d[u].s[val>d[u].val];
}
return 0;
}
public:
void ist(const int &val){
++sz;
int u=rt,v=0;
while(u){
if(val==d[u].val){
++d[u].cnt;
path_upd(u);
return;
}
v=u;
u=d[u].s[val>d[u].val];
}
u=++cnt;
d[u].cnt=d[u].sz=1;
d[u].val=val;
d[u].w=rand();
if(v){
d[u].fa=v;
d[v].s[val>d[v].val]=u;
mt(u);
path_upd(u);
}
else rt=u;
}
void del(const int &val){
int u=find(val);
if(!u) return;
if(d[u].cnt>1){
--d[u].cnt;
path_upd(u);
return;
}
int &ls=d[u].s[0],&rs=d[u].s[1];
while(ls|rs){
if(ls && rs)
rot(d[u].s[d[rs].w<d[ls].w]);
else rot(ls|rs);
}
d[d[u].fa].s[d[d[u].fa].s[1]==u]=0;
path_upd(d[u].fa);
if(--sz==0) rt=0;
}
int low_b(const int &val){
int u=rt,v=0;
while(u){
if(d[u].val<val)
v=u,u=d[u].s[1];
else u=d[u].s[0];
}
return d[v].val;
}
int up_b(const int &val){
int u=rt,v=0;
while(u){
if(d[u].val>val)
v=u,u=d[u].s[0];
else u=d[u].s[1];
}
return d[v].val;
}
int rank(const int &val){
int u=rt,s=1;
while(u){
if(d[u].val<val){
s+=d[d[u].s[0]].sz+d[u].cnt;
u=d[u].s[1];
}
else u=d[u].s[0];
}
return s;
}
int get_val(int k){
int u=rt;
while(k<=d[d[u].s[0]].sz || d[d[u].s[0]].sz+d[u].cnt<k){
if(k<=d[d[u].s[0]].sz)
u=d[u].s[0];
else{
k-=d[d[u].s[0]].sz+d[u].cnt;
u=d[u].s[1];
}
}
return d[u].val;
}
}T;
int main(){
int n;
scanf("%d",&n);
for(int i=1,opt,x;i<=n;++i){
scanf("%d%d",&opt,&x);
switch(opt){
case 1: T.ist(x); break;
case 2: T.del(x); break;
case 3: printf("%d\n",T.rank(x)); break;
case 4: printf("%d\n",T.get_val(x)); break;
case 5: printf("%d\n",T.low_b(x)); break;
default:printf("%d\n",T.up_b(x));
}
}
return 0;
}