伸展树的查找、插入、删除

在信息处理中,刚被访问的数据,很有可能在短时间内被再次访问。在计算机系统中,同样如此。在总结点数为n的AVL树中,访问m次相邻数据共需要mlogn的时间。如果我们把经常访问的数据都放在树的顶端,那么时间就会减少很多。在此,我们引入了伸展树这一概念。

伸展树的概念

最基本的概念,逐层伸展:节点一旦被访问,就将其转移至树根。这一操作可以经过若干次单旋Zig或Zag操作来完成。自下而上,逐层单旋,直至v被推送到根节点。

但是我们可能会遇到最坏情况,形似于n个节点阶梯状的树,与单链相近。此时我们若访问最坏节点(即最底端节点),则需要n时间。即使经过层层上移,在整个单链循环一边后,再次访问该节点,仍需要n时间。因此可以看出,旋转次数呈周期性,每一周期需要n^2,分摊到每一次操作仍需要n时间。这不仅比AVL树logn的时间要长,甚至已经与vector或list的时间相同。

Tarjan 提出了双层伸展方法。相比于单层伸展我们只考虑目标节点v和他的父亲p,我们此时还要关注他的爷爷g。这种方法在子孙异侧的情况,即Zig-Zag和Zag-Zig的效果并不明显,与AVL的双旋结果完全等效,与上面的逐层伸展别无二致,并看不出有多高明。

子孙同侧时,即Zig-Zig和Zag-Zag,此时,我们首先需要进行的是将节点g进行旋转,此时v和p同代,再将p进行旋转,得到的结果就是v经过两侧旋转成为(子)树根。这对于上面所说的最坏情况有了相当明显的改善。在一个足够长的单侧阶梯状树,我们访问最坏节点此时经过不断的双层伸展,节点v成为了新的树根,此时会发现树的高度会减小一半,同时伴随着的是最坏节点会不断的。这就是双层伸展的折叠效果:一旦访问坏节点,对应路径的长度会减小一半。单趟伸展操作经过时间分摊,都不会超过logn。

如果节点v只有父亲p,没有爷爷g呢?这种情况,只会发生在临近树根的位置,最多只会出现一次。此时我们只需要对v进行单层伸展,也就是对v进行一次Zig或Zag变换即可完成。Zig-Zig经过双层旋转会变成Zag-Zag,Zag-Zag经过双层旋转会变成Zig-Zig。

查找

时间复杂度O(logn)

相比于之前BST及AVL等的静态查找,在此我们将伸展树的查找变为动态。我们可以首先调用在BST中定义的search( )接口,在树中查找到目标结点的具体位置。

如果查找成功,返回的就是该节点,如果查找失败,search( )同样会返回一个查找终点_hot。不论查找成功失败与否,返回的节点必定属于四种情况之一,即Zig-Zig、Zig-Zag、Zag-Zag、Zag-Zig。视该节点所属情况而定,自下而上的,反复的将该节点通过双层伸展转移至树根。

此时的双层伸展的具体旋转操作可以说是与之前AVL树文章中所讲的3+4重构类似。我们只是通过两次旋转更好的理解双层伸展,而不需要真的在代码中写出旋转的操作。此时我们只需要将v、p、g以及他们所对应的四个子树根据理念中双层伸展后的结果进行直接绑定即可。比如说Zig-Zig情况,如果中序遍历对应的顺序为T1、v、T2、p、T3、g、T4,我们只需要将T3绑定为g的左孩子,T2绑定为p的左孩子,再将g绑定为p的右孩子,g绑定为v的右孩子即可实现双层伸展的具体效果。

插入

时间复杂度O(logn)

如果我们需要插入目标节点v,首先我们需要调用上面所写的伸展树的查找接口Splay::search( v )。此时若查找失败,返回的_hot节点在树根位置,而我们所插入的v必定是_hot的左孩子或右孩子。此时我们需要根据中序遍历的顺序,将v作为新的树根,将_hot对应的左子树或右子树和_hot作为v的左右子树根进行直接绑定。

删除

时间复杂度O(logn)

如果我们需要删除目标节点v,首先我们需要调用上面所写的伸展树的查找接口Splay::search( v )。如果查找成功,该节点必将在树根位置。由于我们需要将该节点删除,因此需要一个新的树根m代替v的位置。那么问题来了,我们如何选取m节点呢?我们可以选取v的后继节点m,m节点必将位于v右子树中的最左下角。我们需要将m作为v右子树的新的根节点。这一操作我们可以直接调用Splay::search( m )完成。当m成为v右子树根的同时,它的左子树必将为空,因此我们可以直接将v的左子树作为m的左子树进行绑定,这样m就成为了新的树根。

综合评价:

在局部性强的时候,如果有一庞大的数据集,数据个数为n,我们只需要远小于n的部分数据k,而我们需要对这些k中的操作次数m远大于n。(k<<n<m)此时,运用伸展树的效率极强,甚至越使用越快每次操作仅需要O(logk)。任何连续m次查找,我们仅需要O(mlogk+mlogn)。

BBST 与 AVL(插入、删除)link

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值