cqf大佬智慧的结晶
SBT
简介
SBT(子树大小平衡树)是一种通过子树大小来维持平衡的平衡树,由cqf大佬发明,是一种非常非常优秀的平衡树(反正比Splay快),与Splay的旋转类似,它通过旋转来平衡,但不同的是,Splay的旋转没有条件,而SBT的旋转是为了维护一个有关子树大小的性质。
定义
1 | struct SBT{ |
旋转
SBT的旋转与Splay类似,但略有不同,主要表现在它不记录父节点,而是通过传实参来更新与 $Z$ 的关系
代码如下:
1 | void r_rotate(int &t){ //右旋 |
性质
SBT和Splay的主要不同点是SBT维护了两个性质:
$size(r(t)) \ge size(l(l(t)))\ ,\ size(r(l(t)))$
$size(l(t)) \ge size(r(r(t)))\ ,\ size(l(r(t)))$
如图,就是说:
$size(R) \ge size(A)\ ,\ size(B)$
$size(L) \ge size(C)\ ,\ size(D)$
而为了在插入新节点后仍保持该性质,我们需要“机智”地旋转,假设我们用一个函数(命名为 $maintain(t)$ )可以做到使 $t$ 和它的子树符合以上性质,我们来考虑如何做到,无非有四种情况:
一、 $size(l(l(t))) > size(r(t))$ ,即 $size(A) > size(R)$ ,我们操作如下:
$r_rotate(t)$ ,完成后树的结构如下
完成后 $t,L,A,R$ 都已满足性质,但仍可能出现 $size(C) > size(B)$ 或 $size(D) > size(B)$ ,所以我们还需调用 $maintain(t)$ ,完成后 $t$ 所在子树满足了性质
此时 $L$ 的右子树可能已经被改变形状,不满足性质,所以必须再次 $maintain(t)$
二、 $size(r(l(t))) > size(r(t))$ ,即 $size(B) > size(R)$ ,此时情况较为复杂,需要考虑 $B$ 的子树:
$l_rotate(t)$ ,完成后树的结构如下:
然后 $r_rotate(t)$ ,完成后树的结构如下:
在两次旋转后,满足性质的有 $A,E,F,R$ ,我们调用 $maintain(L)$ 和 $maintain(t)$ 来修复 $B$ 的儿子
现在, $B$ 的儿子都是满足性质的了,只需再调用一次 $maintain(B)$
三、 $size(r(r(t))) > size(l(t))$ ,即 $size(D) > size(L)$ ,与情况一相反
四、 $size(r(r(t))) > size(l(t))$ ,即 $size(D) > size(L)$ ,与情况二相反
综上所述,我们可以写出伪代码:
1 | miantain(t){ |
这段代码有点复杂,在通常情况下,一棵树只会出现情况一、情况二或情况三、情况四,我们不妨多传一个 $bool$ 来简化代码:
1 | void maintain(int &t,bool flag){ |
至此我们还有两个问题:为啥 $maintain(l(t),true),maintain(r(t),false)$ 被省略了?而 $maintain(t)$ 的时间复杂度又是多少呢?下面我们来讨论。
首先是为何省略:
在情况一的图中(即右旋后的图),因为插入后 $size(A)>size(R)$ 所以 $size(B)$ 一定仍和插入前一样,即仍有 $size(B)\le size(R)$ ,故 $B$ 的子节点大小一定小于 $R$ ,即根本不需要 $maintain(r(t),false)$ 。其余情况同理。
然后我们来分析时间复杂度:
我们设 $SD$ 表示所有节点的深度和,明显, $SD$ 越小,BST(二叉搜索树)越优,并设 $T$ 表示 $maintain$ 中的旋转次数。
回顾整个 $maintain$ 我们发现在每次旋转后 $SD$ 总是在减小(不妨设减小了 $r$ ),所以,一个高度为 $O(\log{n})$ 的树,其深度和 $SD$ 总是保持在 $O(n\log{n})$ ,而在插入 $SD$ 仅增加 $O(log{n})$ ,故有: $SD=n \times O(\log{n})-T \times r=O(\log{n})$ ,取 $r$ 最小为 $1$ ,则 $T=O(\log{n})$ 。
于是,得到 $maintian$ 的平摊时间是:
$\frac{O(T)+O(n\log{n})+O(T)}{n\log{n}}=O(1) $
操作
SBT的基本操作如下:
1 | void insert(int &t,int v){ //插入 |
注:
在 $insert$ 操作中,如果插入了两个值相同的节点,BST并不会像Splay一样用一个 $cnt$ 来记录,而是直接新建节点,把两个相同的值放在两个不同的节点上
在 $del$ 操作中,有一个小优化,即注释掉 $miantain$ ,在删除后不去维护SBT的结构(但性质是一定满足了的),这样做树的高度变成了 $O(\log{K})$ ,这里 $K$ 表示插入的节点个数
时间分析
我们来分析SBT的时间复杂度(其实不用分析了,是个人都知道是 $O(log{n})$):
设 $f[h]$ 表示高度为 $h$ 的SBT包含的最少节点数,有:
$$
f[h]=\begin{cases}
1 & (h=0)\
2 & (h=1)\
f[h-1]+f[h-2]+1 & (h>1)
\end{cases}
$$
证明如下:
我们把一个高度为 $h-1$ 的SBT作为当前高度为 $h$ 的SBT的左子树,目前有 $f[h-1]+1$ 个节点,但为了满足SBT性质,右子树至少应有 $f[h-2]$ 个节点。
然后,我们可以由 $fibonacci$ 数列得到 $f[h]$ 计算公式:
$$f[h]=\sum_{i=0}^{h} Fibonacci[i]=\left | \frac{\alpha^{h+3}}{\sqrt{5}} \right |$$
其中 $\alpha=\frac{1+\sqrt{5}}{2}$
设 $maxh$ 表示有 n 个节点的SBT的最坏高度,有
$$f[maxh]=\left | \frac{\alpha^{h+3}}{\sqrt{5}} \right |-1 \le n \Rightarrow$$
$$\frac{\alpha^{h+3}}{\sqrt{5}} \le n+1.5 \Rightarrow$$
$$maxh \le \log_{\alpha}{\sqrt{5}(n+1.5)}-3 \Rightarrow$$
$$maxh \le 1.44\log_{2}{n+1.5}-1.33$$
明显,SBT高度为 $O(\log{n})$。
且SBT效率一般优于其他平衡树。
代码
1 |
|