本文用势能法证明\(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)复杂度证明的更多相关文章

  1. 纸上谈兵: 伸展树 (splay tree)[转]

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢!  我们讨论过,树的搜索效率与树的深度有关.二叉搜索树的深度可能为n,这种情况下,每 ...

  2. K:伸展树(splay tree)

      伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(lgN)内完成插入.查找和删除操作.在伸展树上的一般操作都基于伸展操作:假设想要对一个二叉查找树执行一系列的查找操作,为了使 ...

  3. 高级搜索树-伸展树(Splay Tree)

    目录 局部性 双层伸展 查找操作 插入操作 删除操作 性能分析 完整源码 与AVL树一样,伸展树(Splay Tree)也是平衡二叉搜索树的一致,伸展树无需时刻都严格保持整棵树的平衡,也不需要对基本的 ...

  4. 树-伸展树(Splay Tree)

    伸展树概念 伸展树(Splay Tree)是一种二叉排序树,它能在O(log n)内完成插入.查找和删除操作.它由Daniel Sleator和Robert Tarjan创造. (01) 伸展树属于二 ...

  5. [Splay伸展树]splay树入门级教程

    首先声明,本教程的对象是完全没有接触过splay的OIer,大牛请右上角.. 首先引入一下splay的概念,他的中文名是伸展树,意思差不多就是可以随意翻转的二叉树 PS:百度百科中伸展树读作:BoGa ...

  6. 伸展树(Splay tree)的基本操作与应用

    伸展树的基本操作与应用 [伸展树的基本操作] 伸展树是二叉查找树的一种改进,与二叉查找树一样,伸展树也具有有序性.即伸展树中的每一个节点 x 都满足:该节点左子树中的每一个元素都小于 x,而其右子树中 ...

  7. 【BBST 之伸展树 (Splay Tree)】

    最近“hiho一下”出了平衡树专题,这周的Splay一直出现RE,应该删除操作指针没处理好,还没找出原因. 不过其他操作运行正常,尝试用它写了一道之前用set做的平衡树的题http://codefor ...

  8. 伸展树Splay【非指针版】

    ·伸展树有以下基本操作(基于一道强大模板题:codevs维护队列): a[]读入的数组;id[]表示当前数组中的元素在树中节点的临时标号;fa[]当前节点的父节点的编号;c[][]类似于Trie,就是 ...

  9. ZOJ 3765 Lights (zju March I)伸展树Splay

    ZJU 三月月赛题,当时见这个题目没辙,没学过splay,敲了个链表TLE了,所以回来好好学了下Splay,这道题目是伸展树的第二题,对于伸展树的各项操作有了更多的理解,这题不同于上一题的用指针表示整 ...

  10. [数据结构]伸展树(Splay)

    #0.0 写在前面 Splay(伸展树)是较为重要的一种平衡树,理解起来也依旧很容易,但是细节是真的多QnQ,学一次忘一次,还是得用博客加深一下理解( #1.0 Splay! #1.1 基本构架 Sp ...

随机推荐

  1. vue-cli 使用 font-awesome 字体插件

    在 cmd 中,运行:cnpm install font-awesome在 main.js 里添加import "font-awesome/css/font-awesome.css" ...

  2. Caffe+Windows 环境搭建收集

    Caffe+Anconda3+VS2015+Win10(64位)环境搭建 Caffe on Windows (Visual Studio 2015+CUDA8.0+cuDNNv5) Win10+VS2 ...

  3. [Zlib]_[初级]_[使用zlib库压缩和解压STL string]

    场景 1.一般在使用文本json传输数据, 数据量特别大时,传输的过程就特别耗时, 因为带宽或者socket的缓存是有限制的, 数据量越大, 传输时间就越长. 网站一般使用gzip来压缩成二进制. 说 ...

  4. Linux 学习日记 1

    这是我第一次系统地学习Linux,希望通过这个学习日记收获一些东西把-- @_@ Grub - 启动管理器   在启动时让用户选择要启动的系统.(但是windows比较霸道--重装windows后会将 ...

  5. IP 解析器(IpParser) test 和 生产环境 实现

    注意:之前我maven居然没有引入 StringUtils 的包,然后引入了一个路径类似,但其实包路径不一样的 StringUtils ,居然是划掉的状态,像这样 StringUtils ,这个其实不 ...

  6. golang高性能端口扫描

    前言 最近有个小项目的需要,使用golang写了个端口扫描工具,不得不说golang的效率确实比python快的太多了.在使用一段时间golang之后,感觉有三个方面是优于python的: 一个方面是 ...

  7. Serverless架构详解:开发者如何专注于业务代码本身?

    本文来自腾讯云技术沙龙,本次沙龙主题为Serverless架构开发与SCF部署实践 演讲嘉宾:黄文俊,曾负责企业级存储.企业级容器平台等产品的架构与开发,目前主要负责SCF腾讯无服务器云函数产品相关. ...

  8. 复习下VLAN的知识

    转载:来自百度百科 VLAN一般指虚拟局域网 VLAN(Virtual Local Area Network)的中文名为"虚拟局域网". 虚拟局域网(VLAN)是一组逻辑上的设备和 ...

  9. Bitcoin区块验证

    目录 区块的生成 区块的验证链接 验证过程 Merkle Tree结构 区块的生成 矿工在挖矿前要组建区块 将coinbase交易打包进区块 将交易池中高优先级的交易打包进区块 优先级 = 交易的额度 ...

  10. 《Linux内核分析与设计》读书笔记二

    第五章 5.1 与内核通信57 系统调用在用户空间进程和硬件设备之间添加了一个中间层,该层主要作用有三个: 首先它为用户空间提供了一种硬件的抽象接口,举例来说当需要读写文件的时候,应用程序就可以不去管 ...