高级搜索树

伸展树

动机:更好地利用程序的时间局部性原理,将每次访问的路径都压缩至树根处,使最近访问的数据总是集中在前k层。

此外伸展树的双层伸展还能对拓扑结构有一定的修复作用,即对已退化为链表的树来说有一定的折叠效果:
对于zig-zig或zag-zag的情况,Tarjan建议不再采用原始的一步一步往上爬的操作,而是改用先让爷爷结点旋转,再让爸爸把自己旋转上去的策略,有点像卷被子的感觉。

在某些场合下,时间复杂度应考虑均摊的时间复杂度,而非最大的时间复杂度。如对于伸展树来说:
最大的时间复杂度:O(n)(伸展树为从大到小的单链时,升序查找的第一次时间复杂度为n,下一轮升序查找的第一次时间复杂度也仍为n,原因在于经过一轮升序查找后,其拓扑结构又恢复为原来的单链情况)
均摊的时间复杂度:O(logn)


B树

动机:更好地利用硬盘按数据块批量读入的特性,尽可能地减少I/O操作。

插入操作发生上溢需要对结点进行分裂;
删除操作发生下溢需要对结点进行旋转或合并操作。
插入和删除均会反复出现上溢现象和下溢现象,因此最多都需要调整logn次。

在单个结点处,尽管数组中的关键码已经是有序的,但依然会使用顺序查找的算法,而非二分查找。原因在于一般单个结点对应着不超过一个数据块的大小,而数据块的大小通常是几个KB,对应于数组的大小则大概是几百,因此顺序查找是完全有能力应付如此规模的数据的,相反采用二分查找的话反而会因为出现cache不命中的情况而导致效率更低。

对于b树如何选取合适的阶数问题:对于查找当前结点来说是顺序的,因此查找的效率等价于+1,而对于查找下一层结点来说范围则直接从m*m(当前结点所有分叉的元素总和)缩小至了m(某一分叉的元素总和),因此查找的效率等价于*m。所以如果内存的读取速度是硬盘读取速度的m+1倍的话,则需要增加m,使内存多工作;而如果内存的读取速度是硬盘读取速度的m-1倍的话,则需要减小m,使硬盘多工作。总的来说即找到一个合适的m,使得同样的数据用内存处理所花费的代价与用硬盘处理所花费的代价大致相当,否则就通过调整m使之往代价更小的一方倾斜。

注:叶子结点 = 外部结点 = 失败结点,B树的高度不包括外部结点那一层


B+树

动机:树中不再包含所有信息量较大的数据,而是将这些较大数据的地址统一由叶子结点进行存储,原因在于存储单个结点的数据块大小有限,而若希望m能够尽可能地大,则须尽量精简结点中所存储的内容。而m越大也即意味着树的高度可以越低,从而可以尽可能大地减小硬盘所带来的开销。

同时由于访问所有数据都需要从根节点出发至叶子结点,所以访问每一个数据的速度都是恒定的,也是公平的。此外,由于所有叶子结点都被额外地链接起来,所以也能更好地支持范围查找,对于范围查找来说,只需把最小的元素找出来,再沿着链指针依次遍历到最大的元素即可。


红黑树

动机:对于AVL树而言,删除操作所产生的失衡有可能一直向上传播至树根处,也就意味着其在一次操作后,拓扑结构有可能会发生logn次的变化。而红黑树无论是插入还是删除都只可能令拓扑结构发生常数次变化,虽然红黑树的本质是(2, 4)B树,即插入和删除都可能会产生多达logn次的上溢和下溢,但红黑树却巧妙地用染色的方法避免了拓扑结构变化的传播。

插入结点可能会导致双红缺陷(父亲和我都是红色):
1.父亲的兄弟也是红色,则红色上浮(会向上传播)。
2.父亲的兄弟是黑色的,此时若我在父亲和爷爷之间,则我上升两层;反之,父亲上升一层。双红缺陷

删除结点可能会导致双黑缺陷(对换的后继结点为黑色,且其父亲也为黑色,则此时其父亲为双重黑身份):
1.父亲的兄弟是黑色的,且他的儿子都是黑色,则黑色上浮(会向上传播)。
2.父亲的兄弟是黑色的,且有红色的儿子,此时若红色的儿子在父亲和该兄弟之间,则该儿子上升两层;反之,父亲的兄弟上升一层。(两个红儿子时也可让父亲的兄弟上升一层,站在B树的角度来说就是父亲下来顶替了我,然后再一次性移动两个元素来顶替父亲原来的结点)
3.父亲的兄弟是红色的,父亲的兄弟上升一层(此法可为父亲找到一个黑色的兄弟)。双黑缺陷

posted @ 2023-03-21 23:09  Kelvin-Wu  阅读(22)  评论(0编辑  收藏  举报