update notes on splay, avl and btree.
This commit is contained in:
@@ -5,6 +5,22 @@
|
||||
|
||||
> Splay树基本操作的分摊时间复杂度为`O(logn)`,如何证明这个结论?
|
||||
|
||||
对于任意一棵伸展树`S`,引入势能函数$\Phi(S) = \Sigma_{v \in S} rank(v)$,其中$rank(v) = log(size(v))$,即以`v`为树根的子树的规模的对数。通过简单的分析可以看到,`S`越平衡,则它的势能越小,`S`越失衡,则它的势能越大。特殊地,当`S`是一条单链的时候,$\Phi(S) = O(nlogn)$;而`S`是一棵满二叉树的时候,$\Phi(S) = O(n)$。
|
||||
|
||||
将第`i`次操作的分摊时间复杂度$A_i$,写作实际的时间复杂度与势能变化量之和,即
|
||||
|
||||
$$
|
||||
A_i = T_i + \Delta\Phi
|
||||
$$
|
||||
|
||||
然后就伸展树当中存在的三种元操作,即`zig`,`zigzig`以及`zigzag`(其余的操作无非是这三个操作的对称情况),分别证明满足
|
||||
|
||||
$$
|
||||
A_i \le 3\cdot [rank_i(v) - rank_{i - 1}(v)]
|
||||
$$
|
||||
|
||||
这样,对于伸展树中一次基本操作(如`search`),其作用无非是将节点`v`逐层伸展到了根节点,因此分摊时间复杂度为$A \le 1 + 3\cdot[rank(r) - rank(v)] = O(logn)$。
|
||||
|
||||
## AVL树
|
||||
|
||||
> 高度为h的AVL树,至少拥有`fib(h + 3) - 1`个节点,此时对应了一棵`fib-AVL`数。
|
||||
@@ -43,4 +59,26 @@
|
||||
|
||||
## B树
|
||||
|
||||
关于B树,首先需要明确高度的定义,乃是加上了外部节点后的树的高度。其次是说这里有两种统计口径,即节点数量和关键码数量。节点数量是指超级节点的数量,而关键码则是指一个数据的关键码,两者的关系是:一个超级节点中含有多个关键码。
|
||||
|
||||
> 对于含有`N`个关键码的B树,其高度`h`的上下限。
|
||||
|
||||
$$
|
||||
log_m^{N + 1} \le h \le log_{\lceil{\frac{m}{2}}\rceil}^{\lfloor\frac{N + 1}{2}\rfloor} + 1
|
||||
$$
|
||||
|
||||
这个结论的推导要会。需要注意的是,推导的过程,是通过高度为`h`的B树,所含有最多节点数量和最少节点数量,来得到`h`应该满足的关系的。为了将推导过程中使用的节点数量,转化为关键码数量,利用了B树外部节点的性质——外部节点的数量等于关键码的数量加一。实际上,这个性质的本质是二叉树二度节点数量与零度节点数量的关系。
|
||||
|
||||
> 关于B树插入和删除过程中的分裂和合并次数
|
||||
|
||||
考虑一次分裂操作,有两种情况,即在根节点分裂以及在非根节点分裂。如果是在非根节点分裂,一次分裂后B树的高度`h`不变,但是节点数量加一;如果是在根节点分裂,分裂后B树的高度`h`加一,节点数量加二。无论如何,一次分裂以后,`n - h`的值都会增加一,故可以之作为B树分裂次数的计数器。
|
||||
|
||||
同样地可以证明,一次B树的合并操作后,`n - h`的值将会减少一。
|
||||
|
||||
因此,如果对一棵初始状态`h = 1`的B树连续插入`N`个关键码,最终得到一棵高度为`h`,节点数量为`n`的B树,则累计的的分裂次数为`n - h`,平均每次插入的分裂次数为$\frac{n - h}{N} \le \frac{n - h}{n(\lceil \frac{m}{2} \rceil - 1)} = \frac{1}{\lceil \frac{m}{2} \rceil - 1}$。因此,平均$\lceil \frac{m}{2} \rceil - 1$次插入操作才会导致一次分裂。对于删除与合并操作,也有同样的结论。
|
||||
|
||||
尽管如此,单次的插入和删除操作,至多仍需要`O(logn)`次分裂或合并。在最坏情况下,对于任意的`m`为奇数,都可以构造一棵B树,交替地对它进行插入和删除操作,每次操作都需要`O(logn)`次分裂或者合并调整。
|
||||
|
||||
> 为什么B树的插入不采用旋转策略,而是统一采用分裂策略?
|
||||
|
||||
分裂策略总是可行,程序逻辑简单。根据上面的分析,分裂操作通常不会频繁发生。空间利用率也不至于显著降低,因为至少有50%。树高也不至于明显增加,根据上面分析,树高主要取决于关键码的总数,而与节点数量几乎没有关系。
|
||||
|
||||
Reference in New Issue
Block a user