写在前面:我是巨菜……代码巨丑~请各种大牛不要鄙视……
Splay也就是传说中的伸展树~以前很想学,但是因为旋转比较晕,所以一直没学~这回因为决定学了,所以就看了看,写了写。经历了3个小时的摸索……终于写出来了……
首先说我用的题,是一道专门测平衡树的题:
给出N(N<=1000000)个操作,然后有N行,每行两个数,ch和k
ch=1表示插入一个值为k的数
ch=2表示查询第k小的数
ch=3表示删除值为k的数(k一定存在)
我之前的平衡树速度测试和学习都用的这道题(P.s.据说是陈启峰神牛的数据)。Splay是所以神奇就是因为它的灵活,我还是按照我的习惯来说说这个东西。想看的更具体一点呢,可以去看看sqybi神牛的经典Splay教程《The Magical Splay》(地址:http://www.oibh.org/bbs/viewthread.php?tid=23645&highlight=splay)
首先,平衡树的重要操作肯定少不了旋转,Splay的旋转就是个很悲剧的东西。因为splay的精髓在于把一个点旋转到任意一个位置,所以要记录每个点的父亲。但是一旦旋转中出现了父亲,很多东西都很IMBA~
还是一样,两种旋转ZIG和ZAG。ZIG(x)表示当x为它父亲的左儿子时,将x旋转到父亲的位置上的操作,ZAG则反之。(这个东西要认真写,差一点都会出大问题。)
然后就是伸展树的精髓,就是Splay(x,y)过程,就是将x旋转到y的儿子的位置,当y=0的时候旋转到根。这样就一共有6种情况,写写还可以。
然后就是怎么用它来维护树的平衡,说白了就是每次搞完了之后就将操作的那个点旋转到根去就好。所有操作中比较神奇的应该说是删除操作,首先找到您要删除的节点转到根上,然后找到比它大的最小的数和比它小的最大的数。(P.s.说白了就是找前驱和后继),这个时候要看这两个指针是否为空,如果是空,那么直接把root改下就好。反之,如果左右都有,那么将前驱转到根上,然后将后继转到前驱的右儿子上,细心的人这时候就发现,根的右儿子的左儿子只有一个元素,就是我们要删掉的那个元素。这样就结了,干掉这个点就好……
实测情况:
P.s.skyprophet那个是我以前的treap,cqf那个是陈启峰神牛的SBT,splay就是我这会写的东西了。~~
【代码】: