Loading

旋转treap,AVL树、WBLT

口胡。

1. 旋转treap

treap 也可以用旋转来维护。

插入操作:
这是显然的,找到位置插入以后,把结点一路转上去,直到父亲结点的 key 值小于当前结点的 key 值就行了。
容易发现这样旋转没有破坏堆的性质。

删除操作:
如果只有一个儿子那就把儿子转上来,然后到子树进行操作。两个儿子转较小的那个。
配几张图。图中结点处标的都是 key 值。

好的我们要删除 2 这个结点。4<5,把 4 旋转上去。

这样 2 就往下了一些。同理,5<8,把 5 转上去。

只有一个儿子了。只能转 8。

没有儿子结点了。那就可以直接删掉了。

剩下的操作和普通的平衡树就没什么差别了,略。

2. AVL树

好像是最古老的一种平衡树。
AVL树的思路是让每个结点的两棵子树的高度差的绝对值不超过 1,所以也被叫做高度平衡树。
也就是说,当出现子树高度差的绝对值大于等于 2,即有一个子树过高时,我们就要通过旋转保持树的平衡。
具体分类如下:
LL:左子树过高且左子树的左子树比较高
LR:左子树过高且左子树的右子树比较高
RL:右子树过高且右子树的左子树比较高
RR:右子树过高且右子树的右子树比较高
这里只介绍 LL 和 LR 的处理,剩余两种同理。

LL:

如图所示,6 的左子树就过高了,并且 4 的左儿子比较高。因此这是 LL 的情况。
解决方法非常简单,只需把 4 旋转上去。
image
于是就平衡了。

LR:
image
LR 就不能单旋了。方法是,左旋转化为 LL。

然后就简单了。
image

实际上AVL树就已经讲完了。插入删除等操作同BST,但是要通过上面的操作维护平衡。

3. WBLT

WBLT 全名 Weight Balanced Leafy Tree。Weight Balanced Tree 顾名思义, Leafy 的意思类似于线段树,将原本的信息都放到叶子上。

WBLT 的非叶子结点都有两个儿子。原本的信息都存到叶子结点上,而非叶子结点存储的信息是它的两个儿子的合并。
具体到 WBLT 上,就是非叶子结点的权值和它的右儿子相同。(当然存 size 的时候还是要存子树中有效结点的多少,即叶子结点的个数)
但是 BST 的性质还是保留,也就是左子树的叶子结点的值都小于右子树的叶子结点的值。
画个图:

其中 1 2 3 4 就是存储的信息。

接下来我们看这棵树怎么旋转。
还是以上面的树为例,我们做一个左旋,把 4 转上去的操作是这样的:
image
记此时进行旋转的根结点为 x(这里是 4''),左儿子记作 l(x), 右儿子记作 r(x)。
首先我们新建一个结点合并 l(x) 和 l(r(x))。
image
将 l(x) 设为新建的结点。
image
将 r(x) 设为 r(r(x))。
这样就完成了左旋。原来的 r(x) 是不可能被遍历到的,和已经删除无异。(WBLT 中不储存父亲结点)
右旋同理。

然后我们就能用旋转来维护树的平衡了。如果左右子树存储的有效结点个数的比例失调,那么我们就可以通过旋转保持树的平衡。

好像双旋的复杂度才是正确的,我不太懂。

WBLT 的 find 操作要和当前结点的左儿子进行比较,这样才能找到正确的结点。

插入删除:
插入就是递归到叶子结点插进去就行了,然后建立兄弟结点,最后要像线段树一样 pushup 让结点满足 Leafy 的性质。
以上面旋转之后的树为例,我们要插入结点 5。最终结果如下图:
image
事实上我们就是插入了 5,把 4 挪到 5 的兄弟的位置,然后 pushup 的时候把上面的值都改成了 5。
删除也差不多。注意维护好 Leafy Tree 的性质就行。

剩下的操作略,注意结点存的 size 是子树中叶子结点的多少。

posted @ 2022-07-13 00:16  pjykk  阅读(421)  评论(0)    收藏  举报