伸展树(Splay)复杂度证明

本文用势能法证明Splay的均摊复杂度,对Splay的具体操作不进行讲述。

为了方便本文的描述,定义如下内容:

在文中我们用T表示一棵完整的Splay,并(不严谨地)用|T|表示T这棵Splay的节点数目。

如无特殊说明,小写英文字母(如xyz)在本文中表示T的一个节点,并(不严谨地)用|x|表示以节点x为根的子树的大小,xT表示节点xT中。

一般我们默认x代表节点x在经过了上下文中描述的操作以后的状态,因此对应的x代表之前的状态。

我们用Φ(T)表示整棵Splay的势能函数,ϕ(x)则表示节点xT贡献的势能。

=============================================

先来讲一下我们的势能函数,我们定义:

ϕ(x)=log|x|Φ(T)=xTϕ(x)

可以发现,对于任意时刻,因为|x|1,因此log|x|0,从而得到Φ(T)0,因此势能函数是合法的。同时|x||T|,因此我们总有Φ(T)|T|log|T|。这个上界是比较松的,但是对我们的分析没有影响。

下面考虑一次伸展操作对于势能函数的影响。由于我们可以把从根向下查找的代价计算到伸展过程中对应的旋转操作上,此时旋转操作复杂度不变,只是常数增大,从而忽略了查找对复杂度的影响。我们可以简单地通过增大势的单位来支配隐藏在操作中的常数。因此我们只需证明对于一次伸展操作的所有旋转操作,其复杂度是均摊O(log|T|)的,我们就完成了对Splay复杂度的证明。

1zig操作

由于zag操作与zig相似,因此只需要证明zig即可。

假设我们zig的对象是x,其父亲为y,显然在旋转以后,只有xy的子树大小发生了变化。因此势能变化量为:

ΔΦ(T)=ϕ(x)+ϕ(y)ϕ(x)ϕ(y)

显然ϕ(x)=ϕ(y),且ϕ(x)ϕ(y),因此消去ϕ(x)ϕ(y),并将ϕ(y)替换为ϕ(x),有:

ΔΦ(T)ϕ(x)ϕ(x)

因此zig操作的均摊代价为O(1+ϕ(x)ϕ(x)),其中O(1)代表旋转操作本身的复杂度,而在一次伸展操作中也只会有一次zig操作,因此这额外的O(1)代价不会对分析造成影响,因此我们可以只关心其中的O(ϕ(x)ϕ(x))

2zigzig操作

由于zagzag操作与zigzig相似,因此只需要证明zigzig即可。

假设我们zigzig的对象是x,其父亲为y,其祖父为z,与zig操作类似,势能变化量为:

ΔΦ(T)=ϕ(x)+ϕ(y)+ϕ(z)ϕ(x)ϕ(y)ϕ(z)

同样地,由于ϕ(x)=ϕ(z),因此将它们消去:

ΔΦ(T)=ϕ(y)+ϕ(z)ϕ(x)ϕ(y)

而我们又有ϕ(x)ϕ(y)ϕ(x)ϕ(y),因此有:

ΔΦ(T)ϕ(x)+ϕ(z)2ϕ(x)

推到这里,我们先来做一个小工作,来证明ϕ(x)+ϕ(z)2ϕ(x)(注意与上面的式子不一样)的值不大于1

假设|x|=a|z|=b,那么我们有:

ϕ(x)+ϕ(z)2ϕ(x)=log|x|+log|z|2log|x|

我们将log合并,得到:

ϕ(x)+ϕ(z)2ϕ(x)=log(|x||z||x|2)

由于|x|a+b(可以结合旋转过程思考一下),而log是单调的,因此:

ϕ(x)+ϕ(z)2ϕ(x)log(ab(a+b)2)log(ab2ab)1

证明完毕。现在我们已经知道zigzig操作的摊还代价不大于:

O(1)+ϕ(x)+ϕ(z)2ϕ(x)

其中O(1)为旋转操作的复杂度。由于之前的推导我们可以知道ϕ(x)+ϕ(z)2ϕ(x)1,因此1(ϕ(x)+ϕ(z)2ϕ(x))0,我们在摊还代价上加上这个非负数得到:

O(1)+ϕ(x)+ϕ(z)2ϕ(x)1(ϕ(x)+ϕ(z)2ϕ(x))

化简一下,就得到:

O(1)+O(ϕ(x)ϕ(x))1

通过增大我们刚刚加的那个非负数以及势的单位,我们就可以支配隐藏在O(1)中的常数,因此一次zigzig操作的摊还代价为:

O(ϕ(x)ϕ(x))

3zigzag操作

分析的过程和zigzig操作完全一样,之前分析用到的所有性质此时仍然适用,因此略过分析过程。其摊还代价依然为:

O(ϕ(x)ϕ(x))

4、总结

综上所述,除了最后一次旋转可能增加O(1)的代价以外,其余操作的摊还代价只和我们伸展的对象x的势有关。我们假设旋转操作一共执行了n次,并用xi来表示节点x在经过i次旋转后的状态,那么整一个伸展操作的摊还代价就为:

O(1+i=1nϕ(xi)ϕ(xi1))

显然除了ϕ(xn)ϕ(x0)外,所有的势都被抵消了,因此摊还代价为:

O(1+ϕ(xn)ϕ(x0))

至此,我们不必关心ϕ(x0)的值了。此时xn是整棵Splay的根,因此ϕ(xn)=log|T|。我们成功的证明了一次伸展操作的摊还代价为O(log|T|)