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
相关文章推荐
- pygame入门笔记
- Android: NDK编程入门笔记
- Arduino入门笔记【1】
- Android NDK编程入门笔记
- JAVA入门笔记02
- Redis入门指南笔记--主从复制
- Hadoop系列修炼---入门笔记5
- JSP入门笔记
- Unity Shader入门精要学习笔记 - 第2章 渲染流水线
- Lua脚本语言入门笔记
- Docker 入门笔记 10 - Control groups
- OpenGL入门笔记(十二)
- CUDA编程入门笔记
- 【知了堂学习笔记】_mybatis入门
- Visual Studio 2005 学习笔记一 入门
- django入门笔记5- model
- 2018年第一次笔记 LINUX的shell入门
- Python3萌新入门笔记(39)
- django入门笔记9 - auth用户验证&权限
- JS笔记 入门第三