Splay算法旋转操作的模拟

Splay算法旋转操作的模拟

本篇随笔简单讲解一下Splay算法维护平衡树时的旋转操作。重点集中在如何模拟旋转。

对Splay没有概念的同学请移步:

Splay详解

先上图再讲:

1596012176397

这是右旋。

针对一个节点,我们需要维护它的父亲、两个儿子、值和子树大小这些信息。在本文中用以下符号表示:

ch[x][0],ch[x][1]//x的左右儿子
fa[x]//x的爹
val[x]//x的值
size[x]//x的子树大小

我们观察旋转后的东西,发现对于全部节点来讲,他们的值都是没有变的(...)。所以我们不用修改值。

然后我们发现对于部分节点(D,C),它们的爹和儿子包括子树大小都没有任何变化。这俩点就不用管。

那么对于其他点来讲,我们需要按照上面这个图来进行模拟,修改它们的儿子、父亲和子树大小信息。怎么改呢?

改的时候要遵循几个规则:

首先,就是被赋值的信息,以后不能再被调用(因为已经被覆盖掉了),也就是变化的顺序问题。

比如如果上面的右旋,我们先把B的右儿子改成A,再把A的左儿子改成E。我们发现做不到,因为当B的右儿子变成A之后,E就被覆盖掉了,换句话说,我们找不到E了,自然就没办法把A的左儿子改成E了。

但是,如果先把A的左儿子改成E,再把B的右儿子改成A,因为B是我们要操作的节点,所以它无论如何是可以被找到的。这样我们就可以完成这个旋转的操作了。

别忘了还要更新父亲的信息和子树信息。

最后,还要特判,因为我们的Splay旋转还有单旋和双旋的区分,也就是说,虽然我们是三个点在旋转,但是在最后(马上到根节点)的情况下,是会出现只有两个点旋转的情况的,所以要特殊判断z节点(x节点的祖父节点)是否存在,存在的话才嫩更新其儿子,不存在的话没有儿子可以更新。

所以,代码就是:

void rotate(int x)
{
	int y=fa[x],z=fa[y];
	int k=get(x);//get(x)查询x是y的什么儿子。
	ch[y][k]=ch[x][k^1],ch[x][k^1]=y;
	fa[y]=x,fa[x]=z,fa[ch[y][k]]=y;
	if(z)
		ch[z][ch[z][1]==y]=x;
	maintain(y),maintain(x);
}
posted @ 2020-07-30 15:13  Seaway-Fu  阅读(348)  评论(0编辑  收藏  举报