您的位置:首页 > 其它

Treap入门笔记

2018-02-10 22:07 197 查看

Treap是平衡树的一种,又叫做树堆,因为tree+heap=treap。

Treap的原理是为每一个节点赋一个随机值使其满足堆的性质, Treap 是一种高效的平衡树算法,在常数大小与代码复杂度上好于 Splay。

Treap数组:

struct node{
int l,r,ct,val,siz,ran;
//l、r表示左、右子结点;ct表示该点出现次数;val是点值;siz是该子树的大小;ran是我们随机赋值的随机值
}f[MAXN];


Treap添加点和删点:

用Treap添加点时的操作和普通二叉搜索树类似,只是需要赋值一个随机值。

void ins(int &k,int x){
if(!k){
k=++num;
f[k].siz=f[k].ct=1;f[k].ran=ran();f[k].val=x;
return;
}
f[k].siz++;
if(f[k].val==x) f[k].ct++;
else if(f[k].val<x){
ins(f[k].r,x);
if(f[k].ran>f[rc].ran) l_turn(k);
}else
{
ins(f[k].l,x);
if(f[k].ran>f[lc].ran) r_turn(k);
}
}


Treap删点:

void del(int &k,int x){
if(!k) return;
if(f[k].val==x){
if(f[k].ct>1) f[k].ct--,f[k].siz--;
else{
if(lc==0||rc==0) k=lc+rc;
else if(f[lc].ran<f[rc].ran) r_turn(k),del(k,x);
else l_turn(k),del(k,x);
}
}
else if(x>f[k].val) f[k].siz--,del(rc,x);
else f[k].siz--,del(lc,x);
}


我们会发现再用Treap添加和删除完点后会出现图不满足堆的性质的情况,所以这里需要用到Treap的旋转,通过旋转可以是Treap保持堆的性质,从而也完成了对二叉搜索树的平衡。

Treap的旋转:



如图,我们以x为根节点进行右旋,此时点node成了点x的右儿子。因此,我们要删掉x原来的右儿子p,由于p>x且p< node,所以p成为了node的左儿子。这样的操作就完成了旋转,既保持了二叉搜索树的性质,也维护了堆,同时起到平衡的作用。

左旋与此类似

void l_turn(int &k){   //右旋
int x=f[k].r;f[k].r=f[x].l;f[x].l=k;
f[x].siz=f[k].siz;up(k);k=x;
}

void r_turn(int &k){  //左旋
int x=f[k].l;f[k].l=f[x].r;f[x].r=k;
f[x].siz=f[k].siz;up(k);k=x;
}


完成插入,左旋和右旋后,Treap也就十分简单了,其余的操作类似于普通二叉搜索树

模板:

#define lc f[k].l
#define rc f[k].r
#define INF 2000000000

struct node{
int l,r,ct,val,siz,ran;
}f[100005];

inline int ran(){   //随机化赋值
static int seed=2333;
return seed=(int)((((seed^998244353)+19260817ll)*19890604ll)%1000000007);
}

int num,ans,n,i,root;

inline void up(int k){f[k].siz=f[lc].siz+f[rc].siz+f[k].ct;}   //用于更新旋转后点的子树大小值

void l_turn(int &k){   //左旋
int x=f[k].r;f[k].r=f[x].l;f[x].l=k;
f[x].siz=f[k].siz;up(k);k=x;
}

void r_turn(int &k){   //右旋
int x=f[k].l;f[k].l=f[x].r;f[x].r=k;
f[x].siz=f[k].siz;up(k);k=x;
}

void ins(int &k,int x){   //插入
if(!k){
k=++num;
f[k].siz=f[k].ct=1;f[k].ran=ran();f[k].val=x;
return;
}
f[k].siz++;
if(f[k].val==x) f[k].ct++;
else if(f[k].val<x){
ins(f[k].r,x);
if(f[k].ran>f[rc].ran) l_turn(k);
}else
{
ins(f[k].l,x);
if(f[k].ran>f[lc].ran) r_turn(k);
}
}

void del(int &k,int x){   //删除
if(!k) return;
if(f[k].val==x){
if(f[k].ct>1) f[k].ct--,f[k].siz--;
else{
if(lc==0||rc==0) k=lc+rc;
else if(f[lc].ran<f[rc].ran) r_turn(k),del(k,x);
else l_turn(k),del(k,x);
}
}
else if(x>f[k].val) f[k].siz--,del(rc,x);
else f[k].siz--,del(lc,x);
}

int findth(int k,int x){   //询问数x是第几小的数
if(k==0) return 0;
if(f[k].val==x) return f[lc].siz+1;
if(f[k].val<x) return f[lc].siz+f[k].ct+findth(rc,x);
else return findth(lc,x);
}

int findnum(int k,int x){  //询问第x小的数是几
if(k==0) return 0;
if(x<=f[lc].siz) return findnum(lc,x);
x-=f[lc].siz;
if(x<=f[k].ct) return f[k].val;
x-=f[k].ct;
return findnum(rc,x);
}

int findbefore(int k,int x){ //询问x的前驱(前驱定义为小于x,且最大的数)
if(!k) return -INF;
if(x>f[k].val) return max(f[k].val,findbefore(rc,x));
else if(x<=f[k].val) return findbefore(lc,x);
}

int findafter(int k,int x){  //询问x的的后继(后继定义为大于x,且最小的数)
if(!k) return INF;
if(x>=f[k].val) return findafter(rc,x);
else return min(findafter(lc,x),f[k].val);
}


板子题:BZOJ3224

推荐博客:https://www.cnblogs.com/fengzhiyuan/articles/7994428.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Treap