平衡树——Treap,Splay
二叉查找树(BST)的两条性质:
1.该节点的关键码不小于它的左子树中任意节点的关键码
2.该节点的关键码不大于它的右子树中任意节点的关键码
显然,二叉树的中序遍历是关键码单调递增排列的序列。
为避免越界,在BST中插入关键码为正无穷和负无穷的结点为初始情况。
基本操作:
1.检索
递归:
(1)找到结点,返回值
(2)未找到节点且无路可走(子结点为空),返回
(3)结点偏大,向左继续递归
(4)结点偏小,向右继续递归
ps:也可以用循环写,只是我习惯用深搜而已
2.插入
若发现关键值的结点不存在时,新建节点。
3.排名(Kth)
根据平衡树的性质,一个结点的排名即左子树大小+1。
4.前驱/后继
首先找到结点,根据平衡树的性质:
若找其前驱,先向左走一步(比结点小的左子树中找),随
后不断向右走直到叶节点,即为比结点小的数中最大的一个。
后既同理。
5.删除
首先,检索得到结点。
若p的子结点个数小于2,则直接删除p,令子结点直接代替其 位置,与p的父节点相连。
若p既有左子树又有右子树,则求出p的后继结点next。若next 没有左子树,直接删除next,并令next的右子树代替next的位置。 最后, 再让next节点代替p结点, 删除p即可。
Treap(单旋转)
下面是用treap写普通平衡树(BZOJ3224)的代码,代码几乎是李煜东的《算法竞赛进阶指南》上面的样子(最喜欢的一本书)。
1 #include<iostream> 2 #include<cstdlib> 3 using namespace std; 4 const int SIZE = 100005; 5 struct Treap{ 6 int l, r; 7 int val, dat; 8 int size, cnt; 9 }a[SIZE]; 10 int tot, n, root, INF = 0x7fffffff; 11 int New(int val){ 12 a[++tot].val = val; 13 a[tot].size = a[tot].cnt = 1; 14 a[tot].dat = rand(); 15 return tot; 16 } 17 void Update(int p){ 18 a[p].size = a[a[p].l].size + a[a[p].r].size + a[p].cnt; 19 } 20 21 void Build(){ 22 New(-INF), New(INF); 23 root = 1, a[1].r = 2; 24 Update(root); 25 } 26 27 void zig(int &p){ 28 int q = a[p].l; 29 a[p].l = a[q].r, a[q].r = p, p = q; 30 Update(a[p].r), Update(p); 31 } 32 void zag(int &p){ 33 int q = a[p].r; 34 a[p].r = a[q].l, a[q].l = p, p = q; 35 Update(a[p].l), Update(p); 36 } 37 void Insert(int &p, int val){ 38 if(p == 0) { 39 p = New(val); 40 return ; 41 } 42 if(val == a[p].val){ 43 a[p].cnt++, Update(p); 44 return ; 45 } 46 if(val < a[p].val){ 47 Insert(a[p].l, val); 48 if(a[p].dat < a[a[p].l].dat) zig(p); 49 } 50 else{ 51 Insert(a[p].r, val); 52 if(a[p].dat < a[a[p].r].dat) zag(p); 53 } 54 Update(p); 55 } 56 void Remove(int &p, int val){ 57 if(p == 0) return; 58 if(val == a[p].val){ 59 if(a[p].cnt > 1){ 60 a[p].cnt--, Update(p); 61 return; 62 } 63 if(a[p].l || a[p].r){ 64 if(a[p].r == 0 || a[a[p].l].dat > a[a[p].r].dat) 65 zig(p), Remove(a[p].l, val); 66 else 67 zag(p), Remove(a[p].r, val); 68 Update(p); 69 }else p = 0; 70 } 71 val < a[p].val ? Remove(a[p].l, val) : Remove(a[p].r, val); 72 Update(p); 73 } 74 int GetRankByVal(int p, int val){ 75 if(p == 0) return 0; 76 if(a[p].val == val) return a[a[p].l].size; 77 if(val < a[p].val) return GetRankByVal(a[p].l, val); 78 return GetRankByVal(a[p].r, val) + a[a[p].l].size + a[p].cnt; 79 } 80 int GetValByRank(int p, int rank){ 81 if(p == 0) return INF; 82 if(a[a[p].l].size >= rank) return GetValByRank(a[p].l, rank); 83 if(a[a[p].l].size + a[p].cnt >= rank) return a[p].val; 84 return GetValByRank(a[p].r, rank - a[a[p].l].size - a[p].cnt); 85 } 86 87 int GetPrev(int val){ 88 int ans = 1; 89 int p = root; 90 while(p){ 91 if(val == a[p].val){ 92 if(a[p].l > 0){ 93 p = a[p].l; 94 while(a[p].r) p = a[p].r; 95 ans = p; 96 } 97 break; 98 } 99 if(a[p].val < val && a[p].val > a[ans].val) ans = p; 100 p = val < a[p].val ? a[p].l : a[p].r; 101 } 102 return a[ans].val; 103 } 104 int GetNext(int val){ 105 int ans = 2; 106 int p = root; 107 while(p){ 108 if(val == a[p].val){ 109 if(a[p].r > 0){ 110 p = a[p].r; 111 while(a[p].l) p = a[p].l; 112 ans = p; 113 } 114 break; 115 } 116 if(a[p].val > val && a[p].val < a[ans].val) ans = p; 117 p = val < a[p].val ? a[p].l : a[p].r; 118 } 119 return a[ans].val; 120 } 121 122 int main(){ 123 Build(); 124 int n; 125 cin >> n; 126 while(n--){ 127 int f, x; 128 cin >> f >> x; 129 switch(f){ 130 case 1: 131 Insert(root, x); 132 break; 133 case 2: 134 Remove(root, x); 135 break; 136 case 3: 137 cout << GetRankByVal(root, x) <<"\n"; 138 break; 139 case 4: 140 cout << GetValByRank(root, x+1) <<"\n"; 141 break; 142 case 5: 143 cout << GetPrev(x) <<"\n"; 144 break; 145 case 6: 146 cout << GetNext(x) <<"\n"; 147 break; 148 } 149 } 150 return 0; 151 }
splay(伸展树)
伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(log n)内完成插入、查找和删除操作。
概括的说其操作——把一个结点旋转到根。
概括的说其转法——如果结点是父结点的左子节点,则进行右旋;如果结点是父节点的右子节点,则进行左旋。
逐渐可以理解转的原理的。
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int _=1e5+20; 5 6 int n,opt,x; 7 8 struct Splay 9 { 10 int root,cnt,val[_],ch[_][2],siz[_],sz[_],par[_]; 11 12 bool chk(int x) 13 { 14 return ch[par[x]][1]==x; 15 } 16 17 void pushup(int x) 18 { 19 if (!x) return; 20 siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+sz[x]; 21 //cout<<x<<' '<<siz[x]<<" "; 22 } 23 24 int newnode(int x) 25 { 26 ++cnt; 27 val[cnt]=x,sz[cnt]=siz[cnt]=1; 28 ch[cnt][0]=ch[cnt][1]=0; 29 return cnt; 30 } 31 32 void rotate(int x) 33 { 34 int y=par[x],z=par[y],k=chk(x),w=ch[x][k^1]; 35 ch[y][k]=w,par[w]=y; 36 ch[z][chk(y)]=x,par[x]=z; 37 ch[x][k^1]=y,par[y]=x; 38 pushup(y),pushup(x); 39 } 40 41 void splay(int x,int goal=0) 42 { 43 while (par[x]!=goal) 44 { 45 int y=par[x],z=par[y]; 46 if (z!=goal) 47 { 48 if (chk(x)==chk(y)) rotate(y); 49 else rotate(x); 50 } 51 rotate(x); 52 } 53 if (!goal) root=x; 54 } 55 56 void insert(int x) 57 { 58 int cur=root,p=0; 59 while (cur && x!=val[cur]) p=cur,cur=ch[cur][x>val[cur]]; 60 if (cur) ++sz[cur],splay(cur); 61 else cur=ch[p][x>val[p]]=newnode(x),par[cur]=p,splay(cur); 62 } 63 64 void find(int x) 65 { 66 if (!root) return; 67 int cur=root; 68 while (ch[cur][x>val[cur]] && x!=val[cur]) cur=ch[cur][x>val[cur]]; 69 splay(cur); 70 } 71 72 int rank(int x) 73 { 74 find(x); 75 return siz[ch[root][0]]+1; 76 } 77 78 int kth(int k) 79 { 80 int cur=root; 81 while (true) 82 { 83 if (ch[cur][0] && k<=siz[ch[cur][0]]) cur=ch[cur][0]; 84 else if (k>siz[ch[cur][0]]+sz[cur]) k-=siz[ch[cur][0]]+sz[cur],cur=ch[cur][1]; 85 else return cur; 86 } 87 } 88 89 int pre(int x) 90 { 91 find(x); 92 if (val[root]<x) return root; 93 int cur=ch[root][0]; 94 while (ch[cur][1]) cur=ch[cur][1]; 95 return cur; 96 } 97 98 int suc(int x) 99 { 100 find(x); 101 if (val[root]>x) return root; 102 int cur=ch[root][1]; 103 while (ch[cur][0]) cur=ch[cur][0]; 104 return cur; 105 } 106 107 void remove(int x) 108 { 109 int u=pre(x),v=suc(x); 110 //cout<<u<<' '<<v<<endl; 111 splay(u),splay(v,u); 112 int w=ch[v][0]; 113 if (sz[w]>1) --sz[w],splay(w); 114 else siz[0]=sz[0]=ch[v][0]=0; 115 //pushup(v),pushup(u); 116 } 117 118 void maintain(int x) 119 { 120 if (!ch[x][0] && !ch[x][1]) siz[x]=sz[x]; 121 if (ch[x][0]) maintain(ch[x][0]); 122 if (ch[x][1]) maintain(ch[x][1]); 123 pushup(x); 124 } 125 126 void print(int x) 127 { 128 cout<<x<<' '<<val[x]<<' '<<sz[x]<<' '<<siz[x]<<' '<<ch[x][0]<<' '<<ch[x][1]<<' '<<endl; 129 if (ch[x][0]) print(ch[x][0]); 130 if (ch[x][1]) print(ch[x][1]); 131 } 132 }T; 133 134 int main() 135 { 136 T.insert(-0x3f3f3f3f);T.insert(0x3f3f3f3f); 137 cin>>n; 138 for (int i=1; i<=n; i++) 139 { 140 cin>>opt>>x; 141 if (opt==1) T.insert(x); 142 else if (opt==2) T.remove(x); 143 else if (opt==3) cout<<T.rank(x)-1<<'\n'; 144 else if (opt==4) cout<<T.val[T.kth(x+1)]<<'\n'; 145 else if (opt==5) cout<<T.val[T.pre(x)]<<'\n'; 146 else if (opt==6) cout<<T.val[T.suc(x)]<<'\n'; 147 } 148 return 0; 149 }