ps:想学一下非常平衡的平衡树,然后感觉AVL、RBT都很难,于是学了SBT~
再ps:水平极其有限,很多地方都没有细究,如果有错误或者不严谨的地方欢迎指正!
作用
节点大小平衡树(Size Balanced Tree,简称SBT),是陈启峰(Orz%%%)发明的非常高效的一种平衡树,据他本人所说是“这是目前为止速度最快的高级二叉搜索树”。顾名思义,SBT是利用了节点大小来制约的平衡树。
方法
SBT没有像RBT、Treap那样加了附加域,而是利用了十分重要的size域(我们都知道这个域基本是平衡树不可或缺的)。一棵SBT要么是空树,要么是每个节点均满足如下条件的树(s[i]表示i节点子树的节点个数,l[i]表示i的左儿子,r[i]表示i的右儿子):
1.s[r[i]]>=s[l[l[i]]],s[r[l[i]]]
2.s[l[i]]>=s[l[r[i]]],s[r[r[i]]]
图1
如图1,L,R是SBT,如果T是SBT,需要满足:
s[L]>=s[C],s[D]
s[R]>=s[A],s[B]
假设我们已经维护好了一棵SBT,这时候插入/删除导致不满足了,我们就需要用最核心的修正函数Maintain对不满足的树进行修正,使其重新成为SBT。
情况1:s[A]>s[R]
我们先将T右旋,如图2:
图2
然而这时候T依然不一定是SBT,所以我们Maintain(T),将T变为SBT。这时候虽然A和T是SBT了,但L依然不一定是SBT,所以再Maintain(L)。
情况2:s[B]>s[R]
这将牵扯到B的儿子E和F,如图3:
图3
我们将L左旋,如图4:
图4
再将T右旋,如图5:
图5
这时候就乱七八糟了,但是A,C,D,E,F还是SBT,所以我们只需要Maintain(L)和Maintain(T),最后再Maintain(B)就行了。
情况3:s[D]>s[L]
这个和情况1是对称的。
情况4:s[C]>s[L]
这个和情况2是对称的。
不过一次插入/删除只会影响一边,所以情况1,2和3,4不会同时不满足,我们可以分开来,减少判断次数,提高效率。在Maintain中再加一个参数flag,如果flag=0,表示只考虑1,2,如果flag=1,表示只考虑3,4。下面给出Maintain的代码:
void Maintain(P_node &p,bool fl)
{
P_node L=p->son[0],R=p->son[1]; //0:左儿子,1:右儿子
if (!fl)
if (L->son[0]->si>R->si) Rotate(p,1); else
if (L->son[1]->si>R->si) Rotate(p->son[0],0),Rotate(p,1); else
return;
else
if (R->son[1]->si>L->si) Rotate(p,0); else
if (R->son[0]->si>L->si) Rotate(p->son[1],1),Rotate(p,0); else
return;
Maintain(p->son[0],0);Maintain(p->son[0],1);
Maintain(p->son[1],0);Maintain(p->son[1],1);
Maintain(p,0);Maintain(p,1);
//ps:陈启峰神犇说Maintain(p->son[0],1)和Maintain(p->son[1],0)是不需要的
//再ps:但我不会证明,反正加上去不会错:P
}
然而为什么要这么旋转呢?而且Maintain显然是个递归函数,效率怎么样呢?会不会卡死呢?这些都放着,等下在讨论效率时一起讨论。
那么Insert是非常好写的,只需要在普通Insert后面加上Maintain即可:
void Insert(P_node &p,int k)
{
if (p==null) {p=new node(k,null);return;}
int d=p->cmp(k);Insert(p->son[d],k);
p->Pushup();Maintain(p,d);
}
Delete实际上也是普通的删除,如果找不到需要删除的节点,就删除最后一个访问到的节点,把这个节点的key记录下来。最后Maintain一下:
int Delete(P_node &p,int k)
{
int d=p->cmp(k),now;
if (k==p->key||p->son[d]==null)
{
now=p->key;P_node tem;
if (p->son[0]==null) tem=p->son[1],delete p,p=tem; else
if (p->son[1]==null) tem=p->son[0],delete p,p=tem; else
p->key=Delete(p->son[0],p->key),p->Pushup(),Maintain(p,1);
return now;
}
now=Delete(p->son[d],k);
p->Pushup();Maintain(p,d^1);
return now;
}
其他操作和普通二叉排序树均相同。
效率
设f[h]表示高度为h的SBT的最少节点个数,首先我们容易得出f[0]=1,f[1]=2。
还是拿图1说事:
设T是高度为h的SBT,那么L或R中肯定有一个是高度为h-1的SBT,不妨设为L。那么根据f的定义,得s[L]>=f[h-1]。因为L是高度为h-1的SBT,所以A或B中肯定有一个是高度为h-1的SBT。根据SBT的限制,得s[R]>=f[h-2]。所以s[T]>=f[h-1]+f[h-2]+1。
等号能否取到?显然是能的,只需要让L是高度为h-1的SBT,R是高度为h-2的SBT即可!所以f[h]=f[h-1]+f[h-2]+1。
实际上 f[h]=∑hi=0Fibonacci[i]