diff --git a/thu_dsa/chp7/notes.md b/thu_dsa/chp7/notes.md new file mode 100644 index 0000000..9a68635 --- /dev/null +++ b/thu_dsa/chp7/notes.md @@ -0,0 +1,46 @@ +关于几个平衡搜索树的深入探讨 +========================= + +## Splay树 + +> Splay树基本操作的分摊时间复杂度为`O(logn)`,如何证明这个结论? + +## AVL树 + +> 高度为h的AVL树,至少拥有`fib(h + 3) - 1`个节点,此时对应了一棵`fib-AVL`数。 + +可以归纳地证明该结论。 + +> 对于任意一棵高度为h的AVL树,其叶节点的高度深度为$\lceil \frac{h}{2} \rceil$。 + +可以归纳地证明该结论。 + +> 对于一棵高度为h的AVL树进行删除,至多只会有一个节点失衡,但是至多需要进行`O(h) = O(logn)`次调整。具体说来,至多需要$\lfloor \frac{h}{2} \rfloor$次调整操作。 + +可以构造一棵高度为`h`的`fib-AVL`树,删除它的最左侧节点。当`h`为偶数时,该节点是叶节点,作为深度最低的一个节点,它的深度恰好是`AVL`树的深度下界,即$\lceil \frac{h}{2} \rceil = \frac{h}{2}$,对它进行删除后,该节点的所有祖先节点都需要一次调整,故一共需要$\frac{h}{2}$次调整操作。 + +当`h`为奇数时,最左侧节点`m`并非叶节点,它拥有一个右孩子,该右孩子是深度最低的叶节点。因此`m`的深度为$\lceil \frac{h}{2} \rceil - 1 = \lfloor \frac{h}{2} \rfloor$。将`m`删除,其实质是删除它的右孩子,这将导致`m`的所有祖先节点都进行一次调整操作,故一共需要$\lfloor \frac{h}{2} \rfloor$次调整。 + +综上,删除操作后最多进行的调整次数为$\lfloor \frac{h}{2} \rfloor$。 + +需要注意的是这里说的是调整操作,而不是旋转操作。在`fib-AVL`树中,每次这样的调整都可以通过单旋来完成。但是,可以通过对`fib-AVL`树的结构做简单的改变,使得这样的调整需要双旋来完成。这样,总体的旋转次数为$2 \cdot \lfloor \frac{h}{2} \rfloor$次。 + +> 证明:按照递增次序,将$2^{h + 1} - 1$个关键码依次插入到一棵初始为空的`AVL`树中,必然得到高度为`h`的一棵满二叉树。 + +下面归纳地证明该结论。 + ++ 当`h = 0`时,结论显然成立。 ++ 假设对于任意高度不大于`h`的`AVL`树,该结论都是成立的。 ++ 下面讨论将$2^{h + 2} - 1$个节点以递增次序插入到一棵初始为空的`AVL`树的过程。 + + - 首先插入前$[0, 2^{h + 1} - 1)$个关键码。根据归纳假设,此时将形成一棵高度为`h`的`AVL`树。 + - 然后插入$[2^{h + 1} - 1, 3\cdot 2^{h} - 1)$的关键码。这些关键码将全部被插入到右子树中,而对左子树没有任何影响。根据归纳假设,此时右子树形成了一棵高度为`h`的`AVL`树,因此全树的高度为`h + 1`。 + - 插入第$3\cdot 2^{h} - 1$个关键码。此时在根节点不满足`AVL`树的平衡条件,因此需要做一次单旋调整。调整以后,左子树成为一棵高度为`h`的满二叉树,右子树的高度也为`h`,但是在最底层只有一个关键码,即刚刚插入的关键码。 + - 继续插入$[3\cdot 2^{h} - 1, 2^{h + 2} - 1)$个关键码,同样,这些关键码将被插入到右子树,而与左子树无关。根据归纳假设,插入完成后,右子树成为了一棵高度为`h`的满二叉树,此时全树也就成为了一棵高度为`h + 1`的满二叉树。 +证毕。 + +实际上,可以类似地证明,将$2^{h + 1} - 1$个关键码以递减次序,依次插入到一棵初始为空的`AVL`树中,也可以得到一棵高度为`h`的满二叉树。 + +## B树 + +