Skip List(跳表) 分析

最近上课讲了一个挺陌生的概念,叫做Skip List。搜索了一下,中文名称作跳表 

写这个题目的原因: 

不过中文blog讲得都很浅: 

http://hideto.javaeye.com/blog/297625 

http://www.kernelchina.org/?q=node/239 

(刚刚baidu搜索了一下,百度文库里面有很好的介绍Skip List的文章: 

1http://wenku.baidu.com/view/7285945f804d2b160b4ec0a8.html —— 线段跳表 —— 跳表的一个拓展 by 石家庄二中 李骥扬  

2http://wenku.baidu.com/view/d1bd5a0e7cd184254b3535af.html —— 让算法的效率跳起来—— by 华东师范大学第二附属中学 魏冉 

不过,都缺乏了对应的分析部分。)

 

英文站点还是有很多详细的解释的: 

3http://courses.csail.mit.edu/6.046/spring04/handouts/skiplists.pdf 

 

我来尝试写一下,自己对于Skip List的理解。希望能补全下不足的中文资料。 

 

Skip List的原理:

(如果感觉这里对于Skip List说明的不完整,请移步参考资料【1】/【2】/亲自google,本文重点在于理论分析+证明)

产生动机: 

我们知道,如果用有序数组进行二分查找(Binary Search),则用时为Olog n)。 

但有序数组的问题是:

    1. 它的容量有限,不能插入比它更多的元素;

    2. 每次增加一个元素,需要On)的时间,这样花费很大

所以我们会选择用链表(Linked-List)来实现数据存储。但链表的问题是,因为只能进行linear search,查找耗时On)。

因此,我们希望一种存储方式,能够在链表上实现Olog n)的查找时间。

一个最初的想法:

每次查找的时候,因为元素是有序排列的,所以在进行查找的时候,比如查找数字7——即使跳过数字1~6,也不妨碍对7的发现。所以,在查找元素的时候,如果能够看到几个元素之后的值,就能够决定是否要跳过这些元素。从而缩短了查找的时间。

就好像火车,有快车有慢车;快车停得站少,慢车停得多。所以,从一个地方到另一个地方,我们需要先乘坐快车,之后换乘慢车。

 

假设我们建立的一层快车,每两个元素直接跳过b个元素,那么每次查找的时间消耗(worst-case)T1 = n/b + b

易知,当n/b = b时,T1最小。此时b = sqrt(n)T1 = 2sqrt(n) = O(sqrt(n))

 

假设我们建立两层快车,第二层比第一层多跳过b个元素,那么每次查找时间(worst-case)

T2 = n/b + n/b2 + b;(先放在这里,之后会统一求)


现在假设我们建立了m层快车,每层的两个元素之间跳过上一层的b个元素;那么每次查找时间(worst-case):

(在平板电脑上手写的,图片加载时间可能会比较长)

《Skip List(跳表) 分析》

因此,合适的Skip-List情形,是每次跳过2个元素(b),一共log(n)层。

但是呢~如果依照这个想法建立SkipList,需要记录每次的高度;而且在插入新元素的时候,需要更新所有的层的所有相关节点……这样的任务量是非常大的!

怎么办呢?

神奇的地方

于是……神奇的地方出现了~我们让概率来决定——每次增加一个新的元素,我们就扔一枚硬币,看看是不是要向上增长一层~之后从概率上可以证明,这样能保持Skip List的性质~

Skip List的性质

在进行下一阶段之前,这是对于Skip List结构性质的总结(摘自【2】):

  • 跳跃表由多条链构成(S0,S1,S2 ……,Sh),且满足如下三个条件:
  1. 每条链必须包含两个特殊元素:+∞ 和 -∞
  2. S0包含所有的元素,并且所有链中的元素按照升序排列。
  3. 每条链中的元素集合必须包含于序数较小的链的元素集合

 《Skip List(跳表) 分析》

(图片来自【2】)


 

Skip List的时空效率:

(摘自【2】)

  • 空间复杂度: O(n)     (期望)
  • 跳跃表高度: O(log n)     (期望)

ü  相关操作的时间复杂度:

  • 查找: O(log n)     (期望)
  • 插入:  O(log n)     (期望)
  • 删除: O(log n)     (期望)

关于时空效率的证明:

1. 空间复杂度 O(n):

    对于每层的期待:第一层n,第二层n/2,第三层n/22,…,直到 n/2log n=1。所以,总空间需求:

    S = n + n/2 + n/22 + … + n/2log n < n(1 + 1/2 + 1/22 + … + 1/2) =2n

    因此他的空间复杂度为 2n = O(n)

2. Skip List高度:

    对每层来说,它会向上增长的概率为1/2,则第m层向上增长的概率为1/2m;n个元素,则在m层元素数目的期待为Em = n/2m;当Em = 1,m = log2n即为层数的期待。故其高度期待为 Eh = O(log n)。

*关于“高概率(high probability)”的定义:

    (参考【3】【4】)

    对于事件A,如果它发生的概率至少为1-cα/nα,其中cα仅取决于α,那么我们称它为高概率。

         在牺牲时间和/或空间的情况下,我们可以选择α的值。

 

(3~5条目证明起来很困难……先放在这里吧)

3. 查找的复杂度:

claim:在高概率的前提下,查找的时间复杂度为O(log n)

 

4. 插入的复杂度:

claim:在高概率的前提下,插入的时间复杂度为O(log n)

首先通过查找找到要插入的位置:O(log n)

之后进行插入,同时对每层进行对应的更新(依照概率决定是否向上增长):o(log n)

3. 删除的复杂度:

claim:在高概率的前提下,删除的时间复杂度为O(log n)

 


References:

  【1】 线段跳表 —— 跳表的一个拓展 by 石家庄二中 李骥扬, taken on 2011223日,fromhttp://wenku.baidu.com/view/7285945f804d2b160b4ec0a8.html

  【2】 让算法的效率跳起来—— by 华东师范大学第二附属中学 魏冉, taken on 2011223日,fromhttp://wenku.baidu.com/view/d1bd5a0e7cd184254b3535af.html

  

 

    原文作者:算法小白
    原文地址: https://www.cnblogs.com/flyfy1/archive/2011/02/24/1963347.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞