伸展树(Splay)复杂度证明
本文用势能法证明\(Splay\)的均摊复杂度,对\(Splay\)的具体操作不进行讲述。
为了方便本文的描述,定义如下内容:
在文中我们用\(T\)表示一棵完整的\(Splay\),并(不严谨地)用\(|T|\)表示\(T\)这棵\(Splay\)的节点数目。
如无特殊说明,小写英文字母(如\(x\),\(y\),\(z\))在本文中表示\(T\)的一个节点,并(不严谨地)用\(|x|\)表示以节点\(x\)为根的子树的大小,\(x\in T\)表示节点\(x\)在\(T\)中。
一般我们默认\(x'\)代表节点\(x\)在经过了上下文中描述的操作以后的状态,因此对应的\(x\)代表之前的状态。
我们用\(\Phi(T)\)表示整棵\(Splay\)的势能函数,\(\phi(x)\)则表示节点\(x\)对\(T\)贡献的势能。
=============================================
先来讲一下我们的势能函数,我们定义:
\[\phi(x)=\log|x|\]
\[\Phi(T)=\sum_{x\in T}\phi(x)\]
可以发现,对于任意时刻,因为\(|x|\geq 1\),因此\(\log|x|\geq 0\),从而得到\(\Phi(T)\geq 0\),因此势能函数是合法的。同时\(\forall |x|\leq |T|\),因此我们总有\(\Phi(T)\leq |T|\log|T|\)。这个上界是比较松的,但是对我们的分析没有影响。
下面考虑一次伸展操作对于势能函数的影响。由于我们可以把从根向下查找的代价计算到伸展过程中对应的旋转操作上,此时旋转操作复杂度不变,只是常数增大,从而忽略了查找对复杂度的影响。我们可以简单地通过增大势的单位来支配隐藏在操作中的常数。因此我们只需证明对于一次伸展操作的所有旋转操作,其复杂度是均摊\(O(\log|T|)\)的,我们就完成了对\(Splay\)复杂度的证明。
\(1\)、\(zig\)操作
由于\(zag\)操作与\(zig\)相似,因此只需要证明\(zig\)即可。
假设我们\(zig\)的对象是\(x\),其父亲为\(y\),显然在旋转以后,只有\(x\)和\(y\)的子树大小发生了变化。因此势能变化量为:
\[\Delta\Phi(T)=\phi(x')+\phi(y')-\phi(x)-\phi(y)\]
显然\(\phi(x')=\phi(y)\),且\(\phi(x')\geq \phi(y')\),因此消去\(\phi(x')\)与\(\phi(y)\),并将\(\phi(y')\)替换为\(\phi(x')\),有:
\[\Delta\Phi(T)\leq \phi(x')-\phi(x)\]
因此\(zig\)操作的均摊代价为\(O(1+\phi(x')-\phi(x))\),其中\(O(1)\)代表旋转操作本身的复杂度,而在一次伸展操作中也只会有一次\(zig\)操作,因此这额外的\(O(1)\)代价不会对分析造成影响,因此我们可以只关心其中的\(O(\phi(x')-\phi(x))\)。
\(2\)、\(zig-zig\)操作
由于\(zag-zag\)操作与\(zig-zig\)相似,因此只需要证明\(zig-zig\)即可。
假设我们\(zig-zig\)的对象是\(x\),其父亲为\(y\),其祖父为\(z\),与\(zig\)操作类似,势能变化量为:
\[\Delta\Phi(T)=\phi(x')+\phi(y')+\phi(z')-\phi(x)-\phi(y)-\phi(z)\]
同样地,由于\(\phi(x')=\phi(z)\),因此将它们消去:
\[\Delta\Phi(T)=\phi(y')+\phi(z')-\phi(x)-\phi(y)\]
而我们又有\(\phi(x')\geq \phi(y')\),\(\phi(x)\leq \phi(y)\),因此有:
\[\Delta\Phi(T)\leq \phi(x')+\phi(z')-2\phi(x)\]
推到这里,我们先来做一个小工作,来证明\(\phi(x)+\phi(z')-2\phi(x')\)(注意与上面的式子不一样)的值不大于\(-1\)。
假设\(|x|=a\),\(|z'|=b\),那么我们有:
\[\phi(x)+\phi(z')-2\phi(x')=\log|x|+\log|z'|-2\log|x'|\]
我们将\(\log\)合并,得到:
\[\phi(x)+\phi(z')-2\phi(x')=\log(\frac{|x||z'|}{|x'|^2})\]
由于\(|x'|\geq a+b\)(可以结合旋转过程思考一下),而\(\log\)是单调的,因此:
\[\phi(x)+\phi(z')-2\phi(x')\leq \log(\frac{ab}{(a+b)^2})\leq \log(\frac{ab}{2ab})\leq -1\]
证明完毕。现在我们已经知道\(zig-zig\)操作的摊还代价不大于:
\[O(1)+\phi(x')+\phi(z')-2\phi(x)\]
其中\(O(1)\)为旋转操作的复杂度。由于之前的推导我们可以知道\(\phi(x)+\phi(z')-2\phi(x')\leq -1\),因此\(-1-(\phi(x)+\phi(z')-2\phi(x'))\geq 0\),我们在摊还代价上加上这个非负数得到:
\[O(1)+\phi(x')+\phi(z')-2\phi(x)-1-(\phi(x)+\phi(z')-2\phi(x'))\]
化简一下,就得到:
\[O(1)+O(\phi(x')-\phi(x))-1\]
通过增大我们刚刚加的那个非负数以及势的单位,我们就可以支配隐藏在\(O(1)\)中的常数,因此一次\(zig-zig\)操作的摊还代价为:
\[O(\phi(x')-\phi(x))\]
\(3\)、\(zig-zag\)操作
分析的过程和\(zig-zig\)操作完全一样,之前分析用到的所有性质此时仍然适用,因此略过分析过程。其摊还代价依然为:
\[O(\phi(x')-\phi(x))\]
\(4\)、总结
综上所述,除了最后一次旋转可能增加\(O(1)\)的代价以外,其余操作的摊还代价只和我们伸展的对象\(x\)的势有关。我们假设旋转操作一共执行了\(n\)次,并用\(x_i\)来表示节点\(x\)在经过\(i\)次旋转后的状态,那么整一个伸展操作的摊还代价就为:
\[O\Big(1+\sum_{i=1}^n\phi(x_i)-\phi(x_{i-1})\Big)\]
显然除了\(\phi(x_n)\)与\(\phi(x_0)\)外,所有的势都被抵消了,因此摊还代价为:
\[O(1+\phi(x_n)-\phi(x_0))\]
至此,我们不必关心\(\phi(x_0)\)的值了。此时\(x_n\)是整棵\(Splay\)的根,因此\(\phi(x_n)=\log|T|\)。我们成功的证明了一次伸展操作的摊还代价为\(O(\log|T|)\)。
伸展树(Splay)复杂度证明的更多相关文章
- 纸上谈兵: 伸展树 (splay tree)[转]
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 我们讨论过,树的搜索效率与树的深度有关.二叉搜索树的深度可能为n,这种情况下,每 ...
- K:伸展树(splay tree)
伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(lgN)内完成插入.查找和删除操作.在伸展树上的一般操作都基于伸展操作:假设想要对一个二叉查找树执行一系列的查找操作,为了使 ...
- 高级搜索树-伸展树(Splay Tree)
目录 局部性 双层伸展 查找操作 插入操作 删除操作 性能分析 完整源码 与AVL树一样,伸展树(Splay Tree)也是平衡二叉搜索树的一致,伸展树无需时刻都严格保持整棵树的平衡,也不需要对基本的 ...
- 树-伸展树(Splay Tree)
伸展树概念 伸展树(Splay Tree)是一种二叉排序树,它能在O(log n)内完成插入.查找和删除操作.它由Daniel Sleator和Robert Tarjan创造. (01) 伸展树属于二 ...
- [Splay伸展树]splay树入门级教程
首先声明,本教程的对象是完全没有接触过splay的OIer,大牛请右上角.. 首先引入一下splay的概念,他的中文名是伸展树,意思差不多就是可以随意翻转的二叉树 PS:百度百科中伸展树读作:BoGa ...
- 伸展树(Splay tree)的基本操作与应用
伸展树的基本操作与应用 [伸展树的基本操作] 伸展树是二叉查找树的一种改进,与二叉查找树一样,伸展树也具有有序性.即伸展树中的每一个节点 x 都满足:该节点左子树中的每一个元素都小于 x,而其右子树中 ...
- 【BBST 之伸展树 (Splay Tree)】
最近“hiho一下”出了平衡树专题,这周的Splay一直出现RE,应该删除操作指针没处理好,还没找出原因. 不过其他操作运行正常,尝试用它写了一道之前用set做的平衡树的题http://codefor ...
- 伸展树Splay【非指针版】
·伸展树有以下基本操作(基于一道强大模板题:codevs维护队列): a[]读入的数组;id[]表示当前数组中的元素在树中节点的临时标号;fa[]当前节点的父节点的编号;c[][]类似于Trie,就是 ...
- ZOJ 3765 Lights (zju March I)伸展树Splay
ZJU 三月月赛题,当时见这个题目没辙,没学过splay,敲了个链表TLE了,所以回来好好学了下Splay,这道题目是伸展树的第二题,对于伸展树的各项操作有了更多的理解,这题不同于上一题的用指针表示整 ...
- [数据结构]伸展树(Splay)
#0.0 写在前面 Splay(伸展树)是较为重要的一种平衡树,理解起来也依旧很容易,但是细节是真的多QnQ,学一次忘一次,还是得用博客加深一下理解( #1.0 Splay! #1.1 基本构架 Sp ...
随机推荐
- 20155338 《网络攻防》 Exp7 网络欺诈防范
20155338 <网络攻防> Exp7 网络欺诈防范 基础问题回答 通常在什么场景下容易受到DNS spoof攻击 在一些公共场所,看到有免费的公用WIFI就想连的时候就容易受到 在日常 ...
- WPF解决按钮上被透明控件遮盖时无法点击问题
原文:WPF解决按钮上被透明控件遮盖时无法点击问题 IsHitTestVisible="False" 在控件上设置如上属性即可,即可让透明控件不触发点击效果
- 欧几里得算法(及扩展)&&快速幂(二分+位运算)
最近在二中苦逼地上课,天天听数论(当然听不懂) 但是,简单的还是懂一点的 1.欧几里得算法 说得这么高级干什么,gcd入门一个月的人都会吧,还需要BB? 证明可参照其他博客(不会),主要就是gcd(a ...
- shell脚本事例 -- 获取当前日期的前一天日期
记录一个shell脚本事例,事例中包括shell的一些语法(函数定义.表达式运算.if.case...) #!/bin/sh #获取当前时间 RUN_TIME=`date +%H%M%S` #取当前日 ...
- Docker原理探究
问题思考:-------------------------------------Docker浅显原理理解-------------------------------------P1. ubunt ...
- UE4添加植被Foliage Type
在UE4中的地形渲染上不可避免的需要添加植被,而如果采取手动添加StaticMesh植被的方式则会浪费大量的时间精力. UE4提供了一种批量添加地面植被类型的方式Foliage Type.在编辑器内容 ...
- 更新k8s镜像版本的三种方式
一.知识准备 更新镜像版本是在k8s日常使用中非常常见的一种操作,本文主要介绍更新介绍的三种方法 二.环境准备 组件 版本 OS Ubuntu 18.04.1 LTS docker 18.06.0-c ...
- 笛卡尔遗传规划Cartesian Genetic Programming (CGP)简单理解(1)
初识遗传算法Genetic Algorithm(GA) 遗传算法是计算数学中用于解决最优化的搜索算法,是进化算法的一种.进化算法借鉴了进化生物学中的一些现象而发展起来的,这些现象包括遗传.突变.自然选 ...
- springmvc 集成apache cxf 开发webservice 示例
今天需要在springmvc中增加webservice功能,试了多次axis2,和cxf都不行,后来发现在springmvc中最好用cxf集成非常方便,在又一次尝试后终于把demo整合到现有的项目中 ...
- ns3的输入输出奥秘(二) 命令行参数
命令行参数 (1) UdpEchoClientHelper echoClient (interfaces.GetAddress (1), 9); echoClient.SetAttribute (&q ...