几种常用平衡树的概述(持续更新]
【咕咕咕】待更新
1. Treap
Treap是一种基于旋转来维护平衡的平衡树,它通过给每个节点赋一个随机值,并按照该值维护大(或)根堆,以此实现平衡;也因此,它叫Treap ( Binaty Search Tree + heap )
核心操作 : 左旋和右旋
左旋 : 对节点x进行左旋操作,即把x的右子节点旋到左边,具体操作就是把右子树的左子节点连到x,x的右子节点连在x和x的父亲之间;
右旋 : 旋左;
代码如下 :
1 //Author : 15owzLy1 2 //luogu3369.cpp 3 //2018 12 05 22:37:51 4 #include <iostream> 5 #include <cstdio> 6 #include <cstring> 7 #include <algorithm> 8 #include <cstdlib> 9 #define INF 2100000000 10 typedef long long ll; 11 typedef double db; 12 template<typename T>inline void read(T &_) { 13 _=0;int __=0;char ___=getchar(); 14 while(___<'0'||___>'9')__|=(___=='-'),___=getchar(); 15 while(___>='0'&&___<='9')_=(_<<1)+(_<<3)+(___^48),___=getchar(); 16 _=__?-_:_; 17 } 18 19 const int N = 100005; 20 int q, opt, y, res; 21 22 class Treap { 23 #define t a[p] 24 #define lson a[a[p].l] 25 #define rson a[a[p].r] 26 private : 27 int tot; 28 struct node { 29 int l, r, val, num, w, size; 30 }a[N]; 31 inline void push_up(int p) { 32 t.size=lson.size+rson.size+t.val; 33 } 34 inline void zig(int &p) { 35 int ts=lson.r, tmp=t.l; 36 lson.size=t.size; 37 lson.r=p, t.l=ts; 38 push_up(p); 39 p=tmp; 40 } 41 inline void zag(int &p) { 42 int ts=rson.l, tmp=t.r; 43 rson.size=t.size; 44 rson.l=p, t.r=ts; 45 push_up(p); 46 p=tmp; 47 } 48 inline void new_node(int &p, int x) { 49 t=(node){0, 0, 1, x, rand(), 1}; 50 } 51 public : 52 int rt; 53 void debug(int p) { 54 if(t.l) debug(t.l); 55 printf("num : %d p : %d\n", t.num, p); 56 if(t.r) debug(t.r); 57 } 58 void insert(int &p, int x) { 59 if(!p) { p=++tot; new_node(p, x); return ; } 60 ++t.size; 61 if(t.num==x) ++t.val; 62 else if(x<t.num) { 63 insert(t.l, x); 64 if(t.w<lson.w) zig(p); 65 } 66 else { 67 insert(t.r, x); 68 if(t.w<rson.w) zag(p); 69 } 70 } 71 void del(int &p, int x) { 72 if(t.num==x) { 73 if(t.val>1) --t.val, --t.size; 74 else if(t.l==0||t.r==0) p=t.l?t.l:t.r; 75 else if(lson.w>rson.w) zig(p), del(p, x); 76 else zag(p), del(p, x); 77 } 78 else if(x<t.num) { --t.size; del(t.l, x); } 79 else { --t.size; del(t.r, x); } 80 } 81 int query_rank(int p, int x) { 82 if(x==t.num) return lson.size+1; 83 else if(x<t.num) return query_rank(t.l, x); 84 else return query_rank(t.r, x)+lson.size+t.val; 85 } 86 int query_num(int p, int x) { 87 if(!p) return 0; 88 if(lson.size<x&&lson.size+t.val>=x) return t.num; 89 if(lson.size>=x) return query_num(t.l, x); 90 return query_num(t.r, x-t.val-lson.size); 91 } 92 void query_pre(int p, int x) { 93 if(!p) return ; 94 if(x<=t.num) query_pre(t.l, x); 95 else { 96 res=t.num; 97 query_pre(t.r, x); 98 } 99 } 100 void query_sub(int p, int x) { 101 if(!p) return ; 102 if(x>=t.num) query_sub(t.r, x); 103 else { 104 res=t.num; 105 query_sub(t.l, x); 106 } 107 } 108 }T; 109 110 int main() { 111 #ifndef ONLINE_JUDGE 112 freopen("luogu3369.in","r",stdin); 113 freopen("luogu3369.out","w",stdout); 114 #endif 115 srand(20030215); 116 read(q); 117 while(q--) { 118 read(opt), read(y); 119 if(opt==1) T.insert(T.rt, y); 120 else if(opt==2) T.del(T.rt, y); 121 else if(opt==3) printf("%d\n", T.query_rank(T.rt, y)); 122 else if(opt==4) printf("%d\n", T.query_num(T.rt, y)); 123 else if(opt==5) { 124 res=-INF; 125 T.query_pre(T.rt, y); 126 printf("%d\n", res); 127 } 128 else { 129 res=INF; 130 T.query_sub(T.rt, y); 131 printf("%d\n", res); 132 } 133 } 134 return 0; 135 }
2. Splay
Splay是一种基于旋转来维护平衡的平衡树,它通过旋转操作破坏当前存在的链维护相对平衡;
核心操作 : Splay ,rotate
Splay : 把x旋到根。如果是一个“之”字形,就旋两次x;如果是一条直链,就旋fa[x],再旋x;
rotate : 旋转操作;
Splay支持区间操作:处理l,r区间,可以先把节点l-1旋到根,再把r+1旋到l-1的右子树,r+1的左子树就是l,r区间;
代码如下:
1 //Author : 15owzLy1 2 //luogu3391.cpp 3 //2018 12 11 10:38:52 4 #include <cstdio> 5 #include <cstring> 6 #include <algorithm> 7 #define INF 2100000000 8 typedef long long ll; 9 typedef double db; 10 template<typename T>inline void read(T &_) { 11 _=0;int __=0;char ___=getchar(); 12 while(___<'0'||___>'9')__|=(___=='-'),___=getchar(); 13 while(___>='0'&&___<='9')_=(_<<1)+(_<<3)+(___^48),___=getchar(); 14 _=__?-_:_; 15 } 16 17 const int N = (int)1e5+5; 18 int n, q, x, y; 19 20 class Splay { 21 #define lson c[p][0] 22 #define rson c[p][1] 23 private : 24 int c[N][2], fa[N], size[N], num[N], lazy[N]; 25 inline void push_up(int p) { 26 size[p]=size[lson]+size[rson]+1; 27 } 28 inline void push_down(int p) { 29 if(lazy[p]) { 30 std::swap(lson, rson); 31 lazy[lson]^=1, lazy[rson]^=1; 32 lazy[p]=0; 33 } 34 } 35 inline void rotate(int x, int &p) { 36 int y=fa[x], z=fa[y], l, r; 37 l=(c[y][1]==x); r=l^1; 38 if(y==p) p=x; 39 else c[z][c[z][1]==y]=x; 40 fa[c[x][r]]=y, fa[x]=z, fa[y]=x; 41 c[y][l]=c[x][r], c[x][r]=y; 42 push_up(x), push_up(y); 43 } 44 public : 45 int rt; 46 inline void splay(int x, int &p) { 47 while(x!=p) { 48 int y=fa[x], z=fa[y]; 49 if(y!=p) { 50 if(c[y][0]==x^c[z][0]==y) rotate(x, p); 51 else rotate(y, p); 52 } 53 rotate(x, p); 54 } 55 } 56 int find(int p, int rank) { 57 push_down(p); 58 if(size[lson]+1==rank) return p; 59 else if(size[lson]>=rank)return find(lson, rank); 60 else return find(rson, rank-size[lson]-1); 61 } 62 inline void reverse(int x, int y) { 63 int l=find(rt, x), r=find(rt, y+2); 64 splay(l, rt), splay(r, c[l][1]); 65 lazy[c[r][0]]^=1; 66 } 67 void build(int l, int r, int father) { 68 if(l>r) return ; 69 int mid=l+r>>1; 70 c[father][mid>=father]=mid; 71 fa[mid]=father; size[mid]=1; 72 if(l==r) return ; 73 build(l, mid-1, mid); 74 build(mid+1, r, mid); 75 push_up(mid); 76 } 77 }T; 78 79 int main() { 80 #ifndef ONLINE_JUDGE 81 freopen("luogu3391.in","r",stdin); 82 #endif 83 read(n), read(q); 84 T.rt=(n+3)>>1; T.build(1, n+2, T.rt); 85 while(q--) { 86 read(x), read(y); 87 T.reverse(x, y); 88 } 89 for(int i=2;i<=n+1;i++) 90 printf("%d ", T.find(T.rt, i)-1); 91 return 0; 92 }
3. FHQ Treap ( Persistence Treap)
非旋Treap;
核心操作:split,merge
split:把当前的Treap分成两棵树,一棵上所有的节点大小<=k,另一棵>k;(递归实现
merge :把两棵Treap合并,此时节点的大小已经满足平衡树定义,所以需要和Treap一样维护堆;
通过split和merge,基本所有操作都可以暴力搞出来;
代码如下 :
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <cstdlib> 5 6 template<typename T>inline void read(T &x) { 7 x=0;int f=0;char c=getchar(); 8 while(c<'0'||c>'9')f|=(c=='-'), c=getchar(); 9 while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48), c=getchar(); 10 x=f?-x:x; 11 } 12 13 const int N = (int)1e5+5; 14 int q, opt, n; 15 16 class fhq_Treap { 17 #define t a[p] 18 #define lson a[a[p].l] 19 #define rson a[a[p].r] 20 private : 21 struct node { 22 int l, r, num, size, w; 23 }a[N]; 24 inline void push_up(int p) { 25 t.size=lson.size+rson.size+1; 26 } 27 void split(int p, int &x, int &y, int k) { 28 if(!p) { x=y=0; return ; } 29 if(t.num<=k) x=p, split(t.r, t.r, y, k); 30 else y=p, split(t.l, x, t.l, k); 31 push_up(p); 32 } 33 int merge(int &p, int x, int y) { 34 if(x==0||y==0) { p=x?x:y; return p; } 35 if(a[x].w<a[y].w) { p=x; merge(t.r, t.r, y); } 36 else { p=y; merge(t.l, x, t.l); } 37 push_up(p); 38 return p; 39 } 40 inline int new_node(int k) { 41 a[++tot]=(node){0, 0, k, 1, rand()}; 42 return tot; 43 } 44 public : 45 int rt, tot; 46 void debug(int p) { 47 if(t.l) debug(t.l); 48 printf(" %d %d %d %d\n", t.num, lson.num, rson.num, t.size); 49 if(t.r) debug(t.r); 50 } 51 inline void insert(int k) { 52 int x=0, y=0, z=new_node(k); 53 split(rt, x, y, k); 54 merge(rt, merge(x, x, z), y); 55 } 56 inline void remove(int k) { 57 int x=0, y=0, z=0; 58 split(rt, x, y, k); 59 split(x, x, z, k-1); 60 merge(rt, merge(x, x, merge(z, a[z].l, a[z].r)), y); 61 } 62 inline int find(int k) { 63 int x=0, y=0, ret; 64 split(rt, x, y, k-1); 65 ret=a[x].size+1; 66 merge(rt, x, y); 67 return ret; 68 } 69 inline int kth(int p, int k) { 70 while(lson.size+1!=k) { 71 if(lson.size>=k) p=t.l; 72 else k-=lson.size+1, p=t.r; 73 } 74 return t.num; 75 } 76 inline int query_pre(int k) { 77 int x=0, y=0, ret; 78 split(rt, x, y, k-1); 79 ret=kth(x, a[x].size); 80 merge(rt, x, y); 81 return ret; 82 } 83 inline int query_sub(int k) { 84 int x=0, y=0, ret; 85 split(rt, x, y, k); 86 ret=kth(y, 1); 87 merge(rt, x, y); 88 return ret; 89 } 90 }T; 91 92 int main() { 93 #ifndef ONLINE_JUDGE 94 freopen("fhq_treap.in", "r", stdin); 95 #endif 96 srand(20030215); 97 read(q); 98 while(q--) { 99 read(opt), read(n); 100 switch(opt) { 101 case 1 : T.insert(n);break; 102 case 2 : T.remove(n);break; 103 case 3 : printf("%d\n", T.find(n));break; 104 case 4 : printf("%d\n", T.kth(T.rt, n));break; 105 case 5 : printf("%d\n", T.query_pre(n));break; 106 case 6 : printf("%d\n", T.query_sub(n));break; 107 } 108 } 109 return 0; 110 }
4. 可持久化平衡树
由于要维护历史版本,基于旋转的平衡树就不适用;常用的有两种 fhq treap 和 替罪羊树(复杂度假的),这里主要介绍 fhq treap;
再fhq的split操作里,是唯一可能对历史版本造成影响的,所以只需要把这些节点复制,添加在当前的版本里,其他的直接用以前的版本即可;
至于 merge 操作,把新建的节点合并即可;
代码如下 :
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define INF 2147483647 5 6 template<typename T>inline void read(T &x) { 7 x=0;int f=0;char c=getchar(); 8 while(c<'0'||c>'9')f|=(c=='-'), c=getchar(); 9 while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48), c=getchar(); 10 x=f?-x:x; 11 } 12 13 const int N = (int)5e5+5; 14 int q, n, opt, tim; 15 16 class Persistable_Treap { 17 #define t a[p] 18 #define lson a[a[p].l] 19 #define rson a[a[p].r] 20 private : 21 struct node { 22 int l, r, num, size, w; 23 }a[N*50]; 24 int tot; 25 26 inline void push_up(int p) { t.size=lson.size+rson.size+1; } 27 inline int new_node(int k) { 28 a[++tot]=(node){0, 0, k, 1, rand()}; 29 return tot; 30 } 31 inline int c0py(int p) { 32 a[++tot]=t; 33 return tot; 34 } 35 void split(int p, int &x, int &y, int k) { 36 if(!p) { x=y=0; return ; } 37 if(t.num<=k) p=x=c0py(p), split(t.r, t.r, y, k); 38 else p=y=c0py(p), split(t.l, x, t.l, k); 39 push_up(p); 40 } 41 int merge(int x, int y) { 42 if(!x||!y) return x?x:y; 43 int p; 44 if(a[x].w<a[y].w) p=x, t.r=merge(t.r, y); 45 else p=y, t.l=merge(x, t.l); 46 push_up(p); 47 return p; 48 } 49 public : 50 int rt[N]; 51 void debug(int p) { 52 if(t.l) debug(t.l); 53 printf(" %d\n", t.num); 54 if(t.r) debug(t.r); 55 } 56 inline void insert(int &p, int k) { 57 int x, y; 58 split(p, x, y, k); 59 p=merge(merge(x, new_node(k)), y); 60 } 61 inline void remove(int &p, int k) { 62 int x, y, z; 63 split(p, x, y, k), split(x, x, z, k-1); 64 p=merge(merge(x, merge(a[z].l, a[z].r)), y); 65 } 66 inline int rank(int &p, int k) { 67 int x, y, ret; 68 split(p, x, y, k-1); 69 ret=a[x].size+1; 70 p=merge(x, y); 71 return ret; 72 } 73 inline int kth(int p, int k) { 74 while(lson.size+1!=k) 75 if(lson.size>=k) p=t.l; 76 else k-=lson.size+1, p=t.r; 77 return t.num; 78 } 79 inline int pre(int &p, int k) { 80 int x, y, ret; 81 split(p, x, y, k-1); 82 if(!x) return -INF; 83 ret=kth(x, a[x].size); 84 p=merge(x, y); 85 return ret; 86 } 87 inline int sub(int &p, int k) { 88 int x, y, ret; 89 split(p, x, y, k); 90 if(!y) return INF; 91 ret=kth(y, 1); 92 p=merge(x, y); 93 return ret; 94 } 95 }T; 96 97 int main() { 98 #ifndef ONLINE_JUDGE 99 freopen("luogu3835.in", "r", stdin); 100 #endif 101 srand(20021111); 102 read(q); 103 for(int i=1;i<=q;i++) { 104 read(tim), read(opt), read(n); 105 T.rt[i]=T.rt[tim]; 106 if(opt==1) T.insert(T.rt[i], n); 107 else if(opt==2) T.remove(T.rt[i], n); 108 else if(opt==3) printf("%d\n", T.rank(T.rt[i], n)); 109 else if(opt==4) printf("%d\n", T.kth(T.rt[i], n)); 110 else if(opt==5) printf("%d\n", T.pre(T.rt[i], n)); 111 else if(opt==6) printf("%d\n", T.sub(T.rt[i], n)); 112 } 113 return 0; 114 }