零、前言
以学习为借口,是不做题摆烂的最好方式。毕竟
SBT
\textit{SBT}
SBT 在我的
O
I
\rm OI
OI 生涯里就没使用过。今天才学的说。参考原论文,但是纯英文。
壹、怎么平衡
利用一个比较奇葩的性质,侄子子树大小更小。写成式子即 size ( x . lson . lson/rson ) ⩽ size ( x . rson ) \text{size}(x.\text{lson}.\text{lson/rson})\leqslant\text{size}(x.\text{rson}) size(x.lson.lson/rson)⩽size(x.rson),对于另一侧也成立。
然后即可证明树高是
O
(
log
n
)
\mathcal O(\log n)
O(logn) 的。需要逆向思考一下,记
f
(
h
)
f(h)
f(h) 为高度为
h
h
h 的
SBT
\text{SBT}
SBT 至少需多少个节点。不妨设左儿子高度为
(
h
−
1
)
(h{-}1)
(h−1),根据性质,右儿子至少大小为
f
(
h
−
2
)
f(h{-}2)
f(h−2),所以有
f
(
h
)
⩾
f
(
h
−
1
)
+
f
(
h
−
2
)
+
1
f(h)\geqslant f(h{-}1)+f(h{-}2)+1
f(h)⩾f(h−1)+f(h−2)+1
初值 f ( 1 ) = 1 , f ( 2 ) = 2 f(1)=1,\;f(2)=2 f(1)=1,f(2)=2 易知。显然这是指数级增长,故树高 O ( log n ) \mathcal O(\log n) O(logn) 。
顺便聊两句:发明者本人声称 SBT \text{SBT} SBT 不属于 Weight Balanced Tree \text{Weight Balanced Tree} Weight Balanced Tree 。
贰、怎么维护平衡
记方法 maintain ( x ) \texttt{maintain}(x) maintain(x) 为,通过 旋转 使得 x x x 的子树为 SBT \text{SBT} SBT 。具体调整方法可见原论文,因为其中附有图片。直观来说就是将 size \text{size} size 过大的侄子子树旋转到高处。
下面给出代码。不存储父节点,类似于自顶向下旋转,故 rotate \texttt{rotate} rotate 函数非常简单。
void rotate(Node* &o, int d){
Node* const k = o->ch[d];
o->ch[d] = k->ch[d^1], k->ch[d^1] = o;
k->size = o->size, o->pushUp(), o = k;
}
/// @brief check for lighter part
void maintain(Node* &o, int d){
if(o->ch[d^1]->ch[d^1]->size > o->ch[d]->size) rotate(o,d^1);
else if(o->ch[d^1]->ch[d]->size > o->ch[d]->size)
rotate(o->ch[d^1],d), rotate(o,d^1); else return;
maintain(o->ch[0],1), maintain(o->ch[1],0);
maintain(o,0), maintain(o,1);
}
至于为什么它是对的,那就是子树已经
BST
\text{BST}
BST 了,放缩一下可知。不想深究,能用就行。注意:空节点应为分配了内存的特殊地址,即 Node* _null
为自定义值。
insert \texttt{insert} insert 时,要自顶向上 maintain \texttt{maintain} maintain 。而 erase \texttt{erase} erase 的实现方式是,若删去点不是叶子结点,就递归删除左子树的最大值(或删去右子树最小值),然后放到该节点上,最后 maintain \texttt{maintain} maintain 。
一个有趣的事情是, erase \texttt{erase} erase 之后不 maintain \texttt{maintain} maintain 的复杂度仍然正确:均摊复杂度变为 O ( log ∣ insert ∣ ) \mathcal O(\log|\text{insert}|) O(log∣insert∣) 。
叁、时间复杂度
记 Φ = ∑ i ∈ T depth ( i ) \Phi=\sum_{i\in T}\text{depth}(i) Φ=∑i∈Tdepth(i),可以发现 maintain \texttt{maintain} maintain 中的 rotate \texttt{rotate} rotate 会使得 Φ \Phi Φ 至少减少 1 1 1 。
而 Φ \Phi Φ 只在 insert \texttt{insert} insert 时增加树高 O ( log n ) \mathcal O(\log n) O(logn),故 rotate \texttt{rotate} rotate 复杂度可以均摊到 insert \texttt{insert} insert 操作上,而后 maintain \texttt{maintain} maintain 是均摊 O ( 1 ) \mathcal O(1) O(1) 的。