关于Splay旋转的一些理解

看旋转看了好久...

Splay主要有三种旋转

zig,zig-zig,zig-zag

zig:当目标节点是根节点的左子节点或右子节点时,进行一次单旋转,将目标节点调整到根节点的位置。

 

zig-zig

x是它爸的什么儿子,它爸就是它爷爷的什么儿子(它们的线是直的)

zig-zag

x是它爸什么儿子,它爸就不是它爷爷什么儿子(它们是弯的)

 

 注意这里的zig(x)是指把x往上提

所以说代码怎么写

先看一个构架

void splay(int x,int goal){将x转到goal的儿子
    while(x的爸爸不是goal){
        int y=x爸爸,z=x爷爷=y爸爸,res1,res2;
        if(z不是goal)//x只需一步就到了他爷爷的儿子
        {
            if(x是爸爸的左儿子) res1=0;
            else res1=1;
            if(y是爸爸的左儿子) res2=0;
            else res2=1;
            if(res1==1&&res2==1) zig_zig(x,1);右直旋(自己编的)
            else if(res1==0&&res2==0) zig_zig(x,0);左直旋
            else if(res1==1) zig_zag(x,1);右弯旋
            else zig_zag(x,0);//左弯旋
        }
        else
        {
            if(x是z的左儿子) zig(x,0);左单旋
            else zig(x,0);//右单旋
        }
    }
}

我们想想中间的一些怎么压缩

1.他们是直的还是弯的只需要res1,res2异或一下就好

2.zig-zig(x,1)==zig(y,1)然后zig(x,1)//看上面的图

3.我们的旋转函数可以优化,自动搞出是左儿子还是右儿子

4.我们发现无论哪种情况,最后一项都是zig(x)  

void splay(int x,int goal)//将x旋转为goal的儿子,如果goal是0则旋转到根
{
    while(t[x].ff!=goal)//一直旋转到x成为goal的儿子
    {
        int y=t[x].ff,z=t[y].ff;//父节点祖父节点

        if(z!=goal)//如果Y不是根节点,则分为上面两类来旋转
            (t[z].ch[0]==y)^(t[y].ch[0]==x)?rotate(x):rotate(y);
        
        如果是直的就先转y,弯的就先转x

        rotate(x);//无论怎么样最后的一个操作都是旋转x

        注意上面的图,zig_zig就是一次y一次x,zig_zag就是两次x

        这里的rotate(x)不在if里面,因为如果x要到爷爷的儿子,zig(x)一次就好
    }
    if(goal==0)root=x;//如果goal是0,则将根节点更新为x
}

现在来看rotate

 

void rotate(int x)//X是要旋转的节点
{
    int y=t[x].ff;//X的父亲
    int z=t[y].ff;//X的祖父
    int k=(t[y].ch[1]==x);//X是Y的哪一个儿子 0是左儿子 1是右儿子
    t[z].ch[t[z].ch[1]==y]=x;//Z的原来的Y的位置变为X
    t[x].ff=z;//X的父亲变成Z
    t[y].ch[k]=t[x].ch[k^1];//X的与X原来在Y的相对的那个儿子变成Y的儿子
    t[t[x].ch[k^1]].ff=y;//更新父节点
    t[x].ch[k^1]=y;//X的 与X原来相对位置的儿子变成 Y
    t[y].ff=x;//更新父节点
}

 巧妙啊

猜你喜欢

转载自blog.csdn.net/sslz_fsy/article/details/82056253