什么是伸展树?
首先,伸展树(splay tree)是一颗二叉搜索树,它的定义是建立在二叉搜索树之上,并且它是基于类似程序局部性原理的假设:一个节点在一次被访问后,这个节点很可能不久再次被访问。那么伸展树的做法就是在每次一个节点被访问后,我们就把它推到树根的位置。正像程序局部性原理的实际效率被广泛证明一样,伸展树在实际的搜索效率上也是非常高效的。尽管存在最坏情况下单次操作会花费O(N)的时间,但是这种情况并不是经常发生,而实际证明伸展树能够保证M次连续操作最多花费O(MlogN)的时间。
相比于平衡二叉树,伸展树有差不多的平均性能,其他的优势在于:不需要存储平衡信息。另外如果采用自顶向下的调整方式,还能简略额外的栈开销。
如何在查找的一个节点时调整树?
大多数博客上来就说单旋转,一字型,之字形的情况以及如何调整,我觉得这没有说清楚为什么要用这样的调整策略,并不利于我们深入理解这种策略背后的考量。要理解这个问题,我们需要自底向上的调整方式说起。
如果让我们自己设计伸展树查找后的伸展策略,目标是将查找到的节点调整到根节点的位置,我们的第一想法很简单,直接单旋转就好了,也就两种情况,一直循环到被查到的节点到根节点便是,但是这种做法会把不是被查找节点推到和原来被查询节点差不多的位置。一个简单的例子就是查询只有左子树的有序二叉查找树1,2……,9,
1. 如果我们查询1,则1需要9次比较,查询结束后,将1逐步单旋转到树根位置,此时的树形是1为树根只有右孩子9,9节点只有左子树。
2. 如果继续查询2,则也需要9次比较,查询结束后使用单旋转将2转到树根位置
3. 如果继续查询节点3,则需要8次比较
也就是说以此类推,如果节点数是N的话,我们需要 N+∑Ni=2i