关于平衡树的一些总结

平衡树是个大专题啊qwq。。最近也学了一些很有用的平衡树,写个总结吧。。

 

一.splay

学的第一个平衡树,复习一下。。

splay是一个功能很强大的二叉搜索树。其实讲道理splay并不算平衡树吧,因为它并没有任何关于树高的限制。splay的原理就是,每次插入或查询一个结点,就把它旋转到根结点,这样与搜索引擎的原理类似,查询次数较多的结点就能更靠近根。splay并不能保证每次操作严格复杂度都是O(logn),但是每次操作的均摊复杂度确实可以是O(log)。。然而我也不知道为什么。。不过splay好像有可能会被卡来着。。但是splay在序列上的操作可以说几乎完美,因为它可以任意旋转。splay的编程复杂度也是很小的。

splay的核心在于splay操作,即调用splay(x,f),就是把x旋转到f的儿子上(特别地,如果把x旋到根那么f=0)。splay的旋转分为3种——zig,zig-zig,zig-zag。zig操作,就是当x的父亲已经是f的儿子时,直接旋转x。zig-zig,当x与x的父亲位于同一侧时(就是说,x是x的父亲的左儿子,x的父亲是x的父亲的父亲的左儿子,反之也成立)。先旋转x的父亲,再旋转x。zig-zag,当x与x的父亲位于不同侧时,直接旋转两次x。这里的旋转,就是指,如果x是它父亲的左儿子,那么就把他右旋,否则左旋。感觉上个图会更容易理解些。。

 

然后就是上代码了。。

 1 il void rotate(RG int x){
 2     RG int y=fa[x],z=fa[y],k=ch[y][0]==x;
 3     ch[z][ch[z][1]==y]=x,fa[x]=z;
 4     ch[y][k^1]=ch[x][k],fa[ch[x][k]]=y;
 5     ch[x][k]=y,fa[y]=x,pushup(y),pushup(x); return;
 6 }
 7 
 8 il void splay(RG int x,RG int goal){
 9     RG int top=0; st[++top]=x;
10     for (RG int i=x;fa[i]!=goal;i=fa[i]) st[++top]=fa[i];
11     for (RG int i=top;i;--i) if (lazy[st[i]]) pushdown(st[i]);
12     while (fa[x]!=goal){
13     RG int y=fa[x],z=fa[y];
14     if (fa[y]!=goal){
15         ((ch[z][0]==y)^(ch[y][0]==x)) ? rotate(x) : rotate(y);
16     }
17     rotate(x);
18     }
19     if (!goal) rt=x; return;
20 }

splay还是很好写的吧,还能对一个序列进行分裂和合并这种神奇的操作qwq。。

 

二.替罪羊树

替罪羊树也是一种很好写的平衡树qwq。。替罪羊树的核心思想就是重构。即当一棵子树的平衡被破坏,那么就把这棵树拍平,也就是树高为O(logn)的完美二叉树形态。这样看似复杂度很高,实则不然。可以证明,替罪羊树每次重构的复杂度都是均摊O(logn)的。反正我也不会证明,证明还要用物理学知识。。

---------------------------------------------------------------------------------------------------------------------------------------------------

具体是这样操作的:我们设定一个参数α,满足α[0.5,1)。(其实如果你设定α为0.5那就会T飞~)

α高度平衡:树高hlog(1/a)n

α大小平衡:对于一个结点p满足max(sizelp,sizerp)αsizep

如果每个结点均满足α大小平衡,假设最深的点为dd,那么有1nα^depd

那么depdlogα(1/n)=log(1/a)n,即满足α高度平衡。

插入和普通BST类似,只需要判断插入操作是否导致了这一条链上结点的大小平衡被破坏,如果有的话将深度最浅的点所在子树暴力重建。重建最简单的方法就是对这棵子树中序遍历后分治建树。通过势能分析可以证明每一次插入的均摊复杂度为logn

查询和普通BST并无区别。

删除操作比较巧妙。对于一次删除,我们并不马上移除这个点,而是直接这个点上打上删除标记,查询时跳过该点。重构时可以顺便删除打了删除标记的点。当被删除结点的个数超过总结点数的(1α)倍时可以选择重构整棵树进行结构优化,并且显然这部分重构的复杂度不会超过O(nlogn)。

---------------------------------------------------------------------------------------------------------------------------------------------------

——以上内容蒯自xLightGod学长(http://blog.xlightgod.com/%E3%80%90bzoj3224%E3%80%91%E6%99%AE%E9%80%9A%E5%B9%B3%E8%A1%A1%E6%A0%91/)

 所以呢,上代码吧。。http://www.cnblogs.com/wfj2048/p/6498757.html(bzoj3224 普通平衡树)

  1 //It is made by wfj_2048~
  2 #include <algorithm>
  3 #include <iostream>
  4 #include <complex>
  5 #include <cstring>
  6 #include <cstdlib>
  7 #include <cstdio>
  8 #include <vector>
  9 #include <cmath>
 10 #include <queue>
 11 #include <stack>
 12 #include <map>
 13 #include <set>
 14 #define N (100010)
 15 #define il inline
 16 #define RG register
 17 #define ll long long
 18 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
 19 
 20 using namespace std;
 21 
 22 int ch[N][2],cover[N],sz[N],val[N],del[N],st[N],Q,rt,top,tot;
 23 const double alpha=0.75;
 24 
 25 il int gi(){
 26     RG int x=0,q=1; RG char ch=getchar(); while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
 27     if (ch=='-') q=-1,ch=getchar(); while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x;
 28 }
 29 
 30 il void pushup(RG int x){
 31     sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+(!del[x]);
 32     cover[x]=cover[ch[x][0]]+cover[ch[x][1]]+1; return;
 33 }
 34 
 35 il void dfs(RG int x){
 36     if (ch[x][0]) dfs(ch[x][0]);
 37     if (!del[x]) st[++top]=x;
 38     if (ch[x][1]) dfs(ch[x][1]);
 39     sz[x]=cover[x]=ch[x][0]=ch[x][1]=0;
 40     return;
 41 }
 42 
 43 il int divide(RG int l,RG int r){
 44     if (l>r) return 0; RG int mid=(l+r)>>1;
 45     ch[st[mid]][0]=divide(l,mid-1);
 46     ch[st[mid]][1]=divide(mid+1,r);
 47     pushup(st[mid]); return st[mid];
 48 }
 49 
 50 il void rebuild(RG int &x){ top=0,dfs(x),x=divide(1,top); return; }
 51 
 52 il int qrank(RG int x,RG int k){
 53     RG int res=1;
 54     while (x){
 55     if (k<=val[x]) x=ch[x][0];
 56     else res+=sz[ch[x][0]]+(!del[x]),x=ch[x][1];
 57     }
 58     return res;
 59 }
 60 
 61 il int find(RG int x,RG int k){
 62     while (x){
 63     if (!del[x] && k==sz[ch[x][0]]+1) return val[x];
 64     if (k<=sz[ch[x][0]]) x=ch[x][0];
 65     else k-=sz[ch[x][0]]+(!del[x]),x=ch[x][1];
 66     }
 67     return val[x];
 68 }
 69 
 70 il int* Insert(RG int &x,RG int k){
 71     if (!x){ val[x=++tot]=k,sz[x]=cover[x]=1; return NULL; }
 72     sz[x]++,cover[x]++; int *p=Insert(ch[x][val[x]<=k],k);
 73     if (max(cover[ch[x][0]],cover[ch[x][1]])>cover[x]*alpha) p=&x;
 74     return p;
 75 }
 76 
 77 il void insert(RG int k){ int *x=Insert(rt,k); if (x) rebuild(*x); return; }
 78 
 79 il void Erase(RG int x,RG int k){
 80     while (x){
 81     sz[x]--; if (!del[x] && k==sz[ch[x][0]]+1){ del[x]=1; return; }
 82     if (k<=sz[ch[x][0]]) x=ch[x][0]; else k-=sz[ch[x][0]]+(!del[x]),x=ch[x][1];
 83     }
 84     return;
 85 }
 86 
 87 il void erase(RG int k){ Erase(rt,qrank(rt,k)); if (sz[rt]<cover[rt]*alpha) rebuild(rt); return; }
 88 
 89 il void work(){
 90     Q=gi(); RG int type,x;
 91     while (Q--){
 92     type=gi(),x=gi();
 93     if (type==1) insert(x);
 94     if (type==2) erase(x);
 95     if (type==3) printf("%d\n",qrank(rt,x));
 96     if (type==4) printf("%d\n",find(rt,x));
 97     if (type==5) printf("%d\n",find(rt,qrank(rt,x)-1));
 98     if (type==6) printf("%d\n",find(rt,qrank(rt,x+1)));
 99     }
100     return;
101 }
102 
103 int main(){
104     File("tree");
105     work();
106     return 0;
107 }

 

三.SBT

一种很快的平衡树,而且平衡条件很奇葩。。

SBT的平衡要求是,对于一个结点,它的size必须大于等于它的兄弟的左右儿子的size。

如果不满足这个平衡性,我们就要对这个点进行maintain操作,也就是修复操作。

假设我们修复ch[x][1],ch[x][0]的情况不做讨论,因为是对称的。

1.size[ch[x][1]]<size[ch[ch[x][0]][0]],直接将x右旋。

2.size[ch[x][1]]<size[ch[ch[x][0]][1]],先将ch[x][0]左旋,然后再将x右旋。

最后,我们再重新修复x的儿子,以及x。

我们记一个k,k为1表示修复x的右儿子,k为0表示修复x的左儿子,k=0与上述情况类似。

下面上代码:(注意这里的旋转和splay的旋转不一样!!SBT的旋转是把当前点往下旋转~)

 1 il void rotate(RG int &x,RG int k){
 2     RG int y=ch[x][k^1]; ch[x][k^1]=ch[y][k],ch[y][k]=x;
 3     s[y]=s[x],s[x]=s[ch[x][0]]+s[ch[x][1]]+1,x=y;
 4     return;
 5 }
 6 
 7 il void maintain(RG int &x,RG int k){
 8     if (s[ch[ch[x][k^1]][k^1]]>s[ch[x][k]]) rotate(x,k);
 9     else if (s[ch[ch[x][k^1]][k]]>s[ch[x][k]]) rotate(ch[x][k^1],k^1),rotate(x,k);
10     else return;
11     maintain(ch[x][0],1),maintain(ch[x][1],0);
12     maintain(x,1),maintain(x,0); return;
13 }

可以说,SBT的代码量还是很短的,而且很方便。复杂度证明。。好像和斐波那契数列有关系来着。。SBT可以做到树高最多为O(logn),真的很神奇啊。。

我感觉很醉,连treap都没学,如果考可持久化怎么办。。

posted @ 2017-03-05 11:18  wfj_2048  阅读(11048)  评论(7编辑  收藏  举报