您的位置:首页 > 理论基础 > 数据结构算法

数据结构之伸展树

2017-01-17 16:55 211 查看
本文转载自:http://blog.csdn.net/niuox/article/details/8018280

二叉查找树(Binary Search Tree)能够支持多种动态集合操作。因此,在信息学竞赛中,二叉排序树起着非常重要的作用,它可以被用来表示有序集合、建立索引或优先队列等。

作用于二叉查找树上的基本操作的时间是与树的高度成正比的。对一个含n各节点的完全二叉树,这些操作的最坏情况运行时间为O(log n)。但如果树是含n个节点的线性链,则这些操作的最坏情况运行时间为O(n)。而有些二叉查找树的变形,其基本操作在最坏情况下性能依然很好,比如红黑树、AVL树等等。

本文将要介绍的伸展树(Splay Tree),也是对二叉查找树的一种改进,虽然它并不能保证树一直是“平衡”的,但对于伸展树的一系列操作,我们可以证明其每一步操作的平摊复杂度都是O(log n)。所以从某种意义上说,伸展树也是一种平衡的二叉查找树。而在各种树状数据结构中,伸展树的空间要求与编程复杂度也都是很优秀的。

【伸展树的基本操作】

伸展树是二叉查找树的一种改进,与二叉查找树一样,伸展树也具有有序性。即伸展树中的每一个节点x都满足:该节点左子树中的每一个元素都小于x,而其右子树中的每一个元素都大于x。与普通二叉查找树不同的是,伸展树可以自我调整,这就要依靠伸展操作Splay(x,S)。

伸展操作 Splay(x,S)

伸展操作Splay(x,S)是在保持伸展树有序性的前提下,通过一系列旋转将伸展树S中的元素x调整至树的根部。在调整的过程中,要分以下三种情况分别处理:

情况一:节点x的父节点y是根节点。这时,如果x是y的左孩子,我们进行一次Zig(右旋)操作;如果x 是y 的右孩子,则我们进行一次Zag(左旋)操作。经过旋转,x成为二叉查找树S的根节点,调整结束。即:如果当前结点父结点即为根结点,那么我们只需要进行一次简单旋转即可完成任务,我们称这种旋转为单旋转。如图1所示



(图1)

情况二:节点x 的父节点y 不是根节点,y 的父节点为z,且x 与y 同时是各自父节点的左孩子或者同时是各自父节点的右孩子。这时,我们进行一次Zig-Zig操作或者Zag-Zag操作。即:设当前结点为X , X 的父结点为Y ,Y 的父结点为Z ,如果Y 和X 同为其父亲的左孩子或右孩子,那么我们先旋转Y ,再旋转X 。我们称这种旋转为一字形旋转。如图2所示



(图2)

情况三:节点x的父节点y不是根节点,y的父节点为z,x与y中一个是其父节点的左孩子而另一个是其父节点的右孩子。这时,我们进行一次Zig-Zag操作或者Zag-Zig 操作。即:这时我们连续旋转两次X 。我们称这种旋转为之字形旋转。如图3所示



(图3)

如图4所示,执行Splay(1,S),我们将元素1 调整到了伸展树S 的根部。再执行Splay(2,S),如图5 所示,我们从直观上可以看出在经过调整后,伸展树比原来“平衡”了许多。而伸展操作的过程并不复杂,只需要根据情况进行旋转就可以了,而三种旋转都是由基本得左旋和右旋组成的,实现较为简单。



(图4)



(图5)

利用Splay操作,我们可以在伸展树S上进行如下运算:

(1)Find(x,S):判断元素x是否在伸展树S表示的有序集中。

首先,与在二叉查找树中的查找操作一样,在伸展树中查找元素x。如果x在树中,则再执行Splay(x,S)调整伸展树。

(2)Insert(x,S):将元素x插入伸展树S表示的有序集中。

首先,也与处理普通的二叉查找树一样,将x 插入到伸展树S中的相应位置上,再执行Splay(x,S)。

(3)Delete(x,S):将元素x从伸展树S所表示的有序集中删除。

首先,用在二叉查找树中查找元素的方法找到x的位置。如果x没有孩子或只有一个孩子,那么直接将x删去,并通过Splay操作,将x节点的父节点调整

到伸展树的根节点处。否则,则向下查找x的后继y,用y替代x的位置,最后执行Splay(y,S),将y调整为伸展树的根。

(4)Join(S1,S2):将两个伸展树S1与S2合并成为一个伸展树。其中S1的所有元素都小于S2的所有元素。首先,我们找到伸展树S1 中最大的一个元素x,再通过Splay(x,S1)将x 调整到伸展树S1 的根。然后再将S2 作为x 节点的右子树。这样,就得到了新的伸展树S。如图6所示



(图6)

(5)Split(x,S):以x 为界,将伸展树S 分离为两棵伸展树S1 和S2,其中S1中所有元素都小于x,S2中的所有元素都大于x。首先执行Find(x,S),将元素x 调整为伸展树的根节点,则x 的左子树就是S1,而右子树为S2。如图7所示



(图7)

除了上面介绍的五种基本操作,伸展树还支持求最大值、求最小值、求前趋、求后继等多种操作,这些基本操作也都是建立在伸展操作的基础上的。

通常来说,每进行一种操作后都会进行一次Splay 操作,这样可以保证每次操作的平摊时间复杂度是O(log n)。关于证明可以参见相关书籍和论文。

既然可以把任何一个结点转到根,那么也就可以把任意一个结点转到其到根路径上任何一个结点的下面(特别地,转到根就是转到空结点Null 的下面)。下面的利用伸展树维护数列就要用到将一个结点转到某个结点下面。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据结构