SBT

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<

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值