浅谈splay的双旋
昨晚终于明白了splay双旋中的一些细节,今日整理如下
注:题目用的2002HNOI营业额统计,测试结果均来及codevs 网站的评测结果 http://codevs.cn/problem/1296/
本题完整代码请见http://www.cnblogs.com/TheRoadToTheGold/p/6372009.html
1、这是旋转部分的代码
1 inline void splay(int x,int goal) { 2 while(pre[x]!=goal) { 3 int y=pre[x]; 4 int kind=ch[y][0]==x; 5 if(pre[y]==goal) rot(x,kind); //父节点是目标节点,单旋 6 else { 7 kind=ch[pre[y]][0]==y; 8 if(ch[y][!kind]==x) { //一字型 9 rot(y,kind); 10 rot(x,kind); 11 } else { //之字型 12 rot(x,!kind); 13 rot(x,kind); 14 } 15 } 16 } 17 root=x; 18 }
理解较浅,目前认为之字形旋转的本质就是单旋,一字型才算双旋
2、双旋与单旋的区别
先看看运行结果
左边是单旋,右边是双旋,空间几乎一样,时间差距很大
why?
图1:
图2的单旋结果
图2:
双旋结果
可以发现,双旋之后的层数要比单旋之后的层数少1
这只是5层(本图中第6层对5不干扰),如果层数更多呢
所以,双旋层数比单旋要更少
手推一下,上图中第1次旋转结果的树的形态与单旋结果的树的形态是一样的,只是4和3换了个位
所以双旋的优越性在一字型层数>4时才会体现
为什么双旋层数要少?
继续观察图2的第2个和第3个,发现少的那一层是因为4和1处在了同一层
即双旋 使 待旋转点的旋转方向的孩子 与待旋转点的爷爷节点 处在同一层
换用字母表示:
设a的旋转方向的孩子是b,a的父节点是c,c的父节点是d
那么双旋使b、d处在同一层,因为b、d都成为了c的孩子
为什么?分析双旋的两次旋转
先旋转父节点,使爷爷节点和自己都成为父节点的孩子,此时父节点和自己处在同一层,原来的爷爷节点成为自己的父节点
然后旋转自己,自己旋转方向的孩子节点成为自己现在父节点(原先爷爷节点)的孩子,也就是和自己原先父节点处在了同一层
这也是我认为之字型旋转也是单旋的原因,因为之字型旋转是让自己旋转2次,与单旋并无不同
3、旋转顺序
A、代码中一字型旋转顺序是先转父节点,再转自己
①不能改成先转自己,再转父节点
先转自己让自己到父节点的位置,父节点到自己的孩子位置,再转父节点,又转回去了,跟没转一个样
②可以改成旋转自己两次,但那样就相当于单旋
B、之字型旋转的旋转顺序是连续转自己2次
看图,
可以发现连续转自己两次,每次使自己上移一层
而如果先转父节点,可以发现自己原来在哪儿还在那儿,只是父节点和爷爷节点换了而已,相当于这一次白转了
当然你也可以先转父节点,再转自己(两次旋转方向相同),因为的正确性是有保证的
对比一下,上边是先转父节点,再转自己,下边是连续转自己两次(一字型旋转均是先转父节点,再转自己)
上边空间明显大,时间也多点儿
以上内容转自这里QWQ
---------------------------------------------------我-是-分-割-线-----------------------------------------------------------
下面说一点我自己的思考QWQ
我们假设有这样一棵二叉搜索树
现在要把4节点转到根节点
按照一般的方法,会一直旋转4节点直到旋转到根
转完后是酱紫的
(中间的过程我太懒省略了,有兴趣的可以自己动手画画)
但是我们通过观察发现,可以先对4节点进行一次单旋
然后就可以愉快地双旋辣QWQ
转完后是酱紫的
结构一下子变得高效了起来
这只是一条不太长的链,所以层数只减少了一层。如果数据很大,这一优化就会更明显
代码只需要做很小的修改
1 //一般的双旋 2 inline void splay(int x, int goal) { //将 x 旋转到 goal 的子节点 3 for (int i; (i = t[x].f) != goal; rotate(x)) 4 if (t[i].f != goal) 5 rotate(get(x) == get(i) ? i : x); 6 if (!goal) 7 rt = x; 8 }
1 //单双混用 2 inline void splay(int x, int goal) { 3 for (int i; (i = t[x].f) != goal; rotate(x)) 4 if (t[i].f != goal && get(x) == get(i)) 5 rotate(i); 6 if (!goal) 7 rt = x; 8 }
按照以上分析,这样写程序会变得更加高效,然而在实际评测时单双混用却要稍微慢一点,空间使用也略大
【例1】洛谷P3391 【模板】文艺平衡树(Splay)
上面是一般的双旋,下面是单双混用
【例2】洛谷P3369 【模板】普通平衡树
上面是一般的双旋, 下面是单双混用
测试了几次都是这样,十分玄学