浅谈splay的双旋
昨晚终于明白了splay双旋中的一些细节,今日整理如下
注:题目用的2002HNOI营业额统计,测试结果均来及codevs 网站的评测结果 http://codevs.cn/problem/1296/
本题完整代码请见http://www.cnblogs.com/TheRoadToTheGold/p/6372009.html
1、这是旋转部分的代码
inline void splay(int x,int goal) { while(pre[x]!=goal) { int y=pre[x]; int kind=ch[y][0]==x; if(pre[y]==goal) rot(x,kind);//父节点是目标节点,单旋 else { kind=ch[pre[y]][0]==y; if(ch[y][!kind]==x)//一字型 { rot(y,kind); rot(x,kind); } else//之字型 { rot(x,!kind); rot(x,kind); } } } root=x; }
理解较浅,目前认为之字形旋转的本质就是单旋,一字型才算双旋
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次
看图,
可以发现连续转自己两次,每次使自己上移一层
而如果先转父节点,可以发现自己原来在哪儿还在那儿,只是父节点和爷爷节点换了而已,相当于这一次白转了
当然你也可以先转父节点,再转自己(两次旋转方向相同),因为的正确性是有保证的
对比一下,上边是先转父节点,再转自己,下边是连续转自己两次(一字型旋转均是先转父节点,再转自己)
上边空间明显大,时间也多点儿