(转)关于treap的板子理解
关于treap的板子理解:
关于结构体的定义:(一般平衡树无法理解的变量名):
v:节点的值;
size:子节点的个数(包括自己);
cnt:相同的值的副本数;
l:左儿子;
r:右儿子;
右旋:
父亲变成左儿子,左儿子变成父亲的右儿子;
1 void zig(int x) 2 { 3 int h=s[x].l; 4 s[x].l=s[x].r;s[h].r=x; 5 s[h].size=s[x].size; 6 up(x); 7 x=h; 8 return ; 9 }
左旋:
就是右旋相反就变成左旋;
void zag(int x) { int h=s[x].r; s[x].r=s[x].l;s[h].l=x; s[h].size=s[x].size; up(x); x=h; }
插入:
1.insert函数有两个函数,x和k;
这里k取地址会比较方便;
k就是目前节点,开始时就是根节点rt;
2.如果目前节点为0,即为上一个传过来的是0,那么这个值之前没有过,所以新建一个节点,就是
1 s[k].size=s[k].cnt=1; 2 s[k].ran=rand(); 3 s[k].v=x; 4 return ;
3.如果这个值之前出现过,那么副本cnt++;
众所周知s[k].l总是小于k,s[k].r总是大于k;
所以见代码
1 void insert(int x,int &k) 2 { 3 if(!k) 4 { 5 s[k].size=s[k].cnt=1; 6 s[k].ran=rand(); 7 s[k].v=x; 8 return ; 9 } 10 s[k].size++; 11 if(s[k].v==x) s[k].cnt++; 12 if(x>s[k].v) 13 { 14 insert(x,s[k].r); 15 if(s[s[k].l].ran<s[k].ran)zig(k); 16 } 17 else if(x<s[k].v) 18 { 19 insert(x,s[k].l); 20 if(s[s[k].l].ran<s[k].ran)zag(k); 21 } 22 return ; 23 }
删除节点:
1.如果就没有这个节点,就直接return;
2.如果这个节点就是这个值,并且这个点的副本>1, tr[k].cnt--;tr[k].size--;
3.如果左节点和右节点只有一个有值,这个点就是他的那个子节点(删除这个点之后就是他的子节点当这个点);
4.他的左儿子虚拟值(平衡树根据这个虚拟值可以提高效率)小于右儿子的虚拟值,那么右旋
5.else 左旋;
inline void del(int x,int &k) { if(!k) return ; if(tr[k].v==x) { if(tr[k].cnt>1) { tr[k].cnt--; tr[k].size--; } else if(tr[k].l*tr[k].r==0) k=tr[k].l+tr[k].r; else if(tr[tr[k].l].rnd<tr[tr[k].r].rnd) zig(k),del(x,k); else zag(k),del(x,k); return ; } tr[k].size--; if(tr[k].v>x) del(x,tr[k].l); else del(x,tr[k].r); return ; }
询问排名:
1.如果k==0,那么就return 0
2.如果这个值就是目前的节点的值,那么返回他的左儿子+1;(注意要加1,左儿子的是小于我的,所以+1,就是我的排名);
3.如果目前节点的值大于要查的值就递归他的左子树;
4.否则递归右子树;
inline int qrnk(int x,int k) { if(!k) return 0; if(tr[k].v==x) return tr[tr[k].l].size+1; else if(tr[k].v>x) return qrnk(x,tr[k].l); else return qrnk(x,tr[k].r)+tr[tr[k].l].size+tr[k].cnt; }
询问排名是x的值;
1.如果没有这个值,就return 0;
2.如果这个排名小于左子树的子树点的值并且排名小于左子树的子树点加上他的副本数的点;
那么就 return tr[k].v;
3.如果要查的值小于等于左子树的儿子值return qnum(x,tr[k].l);;
4.否则递归右子树;
inline int qnum(int x,int k) { if(!k) return 0; if(x>tr[tr[k].l].size&&x<=tr[tr[k].l].size+tr[k].cnt) return tr[k].v; else if(x<=tr[tr[k].l].size) return qnum(x,tr[k].l); else return qnum(x-tr[tr[k].l].size-tr[k].cnt,tr[k].r); }
询问前驱:
1.如果没有就return -inf;
2.如果小于这个点的值递归左子树;
否则返回 max(tr[k].v,qpre(x,tr[k].r));
询问后继:
1.(在这里偷懒一下)就是和询问前驱相反的;
inline int qnxt(int x,int k) { if(!k) return inf; if(x>=tr[k].v) return qnxt(x,tr[k].r); else return min(tr[k].v,qnxt(x,tr[k].l)); }
平衡树的主要作用就是:
- 插入 x 数;
- 删除 x 数(若有多个相同的数,因只删除一个);
- 查询 x 数的排名(若有多个相同的数,因输出最小的排名);
- 查询排名为 x 的数;
- 求 x 的前趋(前趋定义为小于 x ,且最大的数);
- 求 x 的后继(后继定义为大于 x ,且最小的数)。
有一道题叫普通平衡树;适合刚学平衡树的童鞋;
完整代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<string> 5 #include<cstdio> 6 #include<cstdlib> 7 #include<cmath> 8 #include<ctime> 9 using namespace std; 10 const int inf=0x7fffffff; 11 int n,rt; 12 int tot=0; 13 struct node{ 14 int v,cnt,l,r,rnd,size; 15 }tr[1000006]; 16 inline void up(int k) 17 { 18 tr[k].size=tr[tr[k].l].size+tr[tr[k].r].size+tr[k].cnt; 19 } 20 inline void zig(int &k) 21 { 22 int h=tr[k].l; 23 tr[k].l=tr[h].r,tr[h].r=k; 24 tr[h].size=tr[k].size; 25 up(k); 26 k=h; 27 return ; 28 } 29 inline void zag(int &k) 30 { 31 int h=tr[k].r; 32 tr[k].r=tr[h].l,tr[h].l=k; 33 tr[h].size=tr[k].size; 34 up(k); 35 k=h; 36 return ; 37 } 38 inline void insert(int x,int &k){ 39 if(!k){ 40 k=++tot; 41 tr[k].v=x; 42 tr[k].cnt=tr[k].size=1; 43 tr[k].rnd=rand(); 44 return ; 45 } 46 tr[k].size++; 47 if(x==tr[k].v) tr[k].cnt++; 48 else if(x<tr[k].v) 49 { 50 insert(x,tr[k].l); 51 if(tr[tr[k].l].rnd<tr[k].rnd) zig(k); 52 } 53 else if(x>tr[k].v) 54 { 55 insert(x,tr[k].r); 56 if(tr[tr[k].r].rnd<tr[k].rnd) zag(k); 57 } 58 return ; 59 } 60 inline void del(int x,int &k) 61 { 62 if(!k) return ; 63 if(tr[k].v==x) 64 { 65 if(tr[k].cnt>1) 66 { 67 tr[k].cnt--; 68 tr[k].size--; 69 } 70 else if(tr[k].l*tr[k].r==0) k=tr[k].l+tr[k].r; 71 else if(tr[tr[k].l].rnd<tr[tr[k].r].rnd) zig(k),del(x,k); 72 else zag(k),del(x,k); 73 return ; 74 } 75 tr[k].size--; 76 if(tr[k].v>x) del(x,tr[k].l); 77 else del(x,tr[k].r); 78 return ; 79 } 80 inline int qrnk(int x,int k) 81 { 82 if(!k) return 0; 83 if(tr[k].v==x) return tr[tr[k].l].size+1; 84 else if(tr[k].v>x) return qrnk(x,tr[k].l); 85 else return qrnk(x,tr[k].r)+tr[tr[k].l].size+tr[k].cnt; 86 } 87 inline int qnum(int x,int k) 88 { 89 if(!k) return 0; 90 if(x>tr[tr[k].l].size&&x<=tr[tr[k].l].size+tr[k].cnt) return tr[k].v; 91 else if(x<=tr[tr[k].l].size) return qnum(x,tr[k].l); 92 else return qnum(x-tr[tr[k].l].size-tr[k].cnt,tr[k].r); 93 } 94 inline int qpre(int x,int k) 95 { 96 if(!k) return -inf; 97 if(x<=tr[k].v) return qpre(x,tr[k].l); 98 else return max(tr[k].v,qpre(x,tr[k].r)); 99 } 100 inline int qnxt(int x,int k) 101 { 102 if(!k) return inf; 103 if(x>=tr[k].v) return qnxt(x,tr[k].r); 104 else return min(tr[k].v,qnxt(x,tr[k].l)); 105 } 106 int main() 107 { 108 srand(time(0)); 109 scanf("%d",&n); 110 for(int i=1;i<=n;i++) 111 { 112 int f,x; 113 scanf("%d%d",&f,&x); 114 if(f==1) insert(x,rt); 115 if(f==2) del(x,rt); 116 if(f==3) printf("%d\n",qrnk(x,rt)); 117 if(f==4) printf("%d\n",qnum(x,rt)); 118 if(f==5) printf("%d\n",qpre(x,rt)); 119 if(f==6) printf("%d\n",qnxt(x,rt)); 120 } 121 return 0; 122 }
蒟蒻理解,如有错误以其他大佬的为主
作者:lsc