skiplist 跳跃表

什么是跳跃表?

  SkipList在leveldb、redis以及lucence中都广为使用,是比较高效的数据结构。由于它的代码以及原理实现的简单性,更为人们所接受。我们首先看看SkipList的定义,为什么叫跳跃表?

  “     Skip lists  are data structures  that use probabilistic  balancing rather  than  strictly  enforced balancing. As a result, the algorithms  for insertion  and deletion in skip lists  are much simpler and significantly  faster  than  equivalent  algorithms  for balanced trees.   ”

  译文:跳跃表使用概率均衡技术而不是使用强制性均衡,因此,对于插入和删除结点比传统上的平衡树算法更为简洁高效。

跳跃表的理解

 有序表的搜索:考虑一个有序表

      

 从该有序表中搜索元素 < 23, 43, 59 > ,需要比较的次数分别为 < 2, 4, 6 >,总共比较的次数为 2 + 4 + 6 = 12 次。有没有优化的算法吗? 链表是有序的,但不能使用二分查找。类似二叉搜索树,我们把一些节点提取出来,作为索引。得到如下结构:

         

  提取出来作为一级索引,这样搜索的时候就可以减少比较次数了。 
 我们还可以再从一级索引提取一些元素出来,作为二级索引,三级索引…

 这里元素不多,体现不出优势,如果元素足够多,这种索引结构就能体现出优势来了。

跳表

 下面的结构是就是跳表: 
 其中 -1 表示 INT_MIN, 链表的最小值,1 表示 INT_MAX,链表的最大值。

 跳表具有如下性质:
  (1) 由很多层结构组成
  (2) 每一层都是一个有序的链表
  (3) 最底层(Level 1)的链表包含所有元素
  (4) 如果一个元素出现在 Level i 的链表中,则它在 Level i 之下的链表也都会出现。
  (5) 每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素。

跳表的搜索

 例子:查找元素 117
  (1) 比较 21, 比 21 大,往后面找
  (2) 比较 37, 比 37大,比链表最大值小,从 37 的下面一层开始找
  (3) 比较 71, 比 71 大,比链表最大值小,从 71 的下面一层开始找
  (4) 比较 85, 比 85 大,从后面找
  (5) 比较 117, 等于 117, 找到了节点。

具体的搜索算法如下:

/* 如果存在 x, 返回 x 所在的节点,
 * 否则返回 x 的后继节点 */
find(x)
{
    p = top;
    while (1) {
        while (p->next->key < x)
            p = p->next;
        if (p->down == NULL)
            return p->next;
        p = p->down;
    }
}

跳表的插入

 先确定该元素要占据的层数 K(采用丢硬币的方式,这完全是随机的) 
 然后在 Level 1 … Level K 各个层的链表都插入元素。 
 例子:插入 119, K = 2

 如果 K 大于链表的层数,则要添加新的层。 
 例子:插入 119, K = 4

 丢硬币决定 K 
 插入元素的时候,元素所占有的层数完全是随机的,通过一下随机算法产生:

int random_level()
{
    K = 1;  

    while (random(0,1))
        K++;  

    return K;
} 

  相当与做一次丢硬币的实验,如果遇到正面,继续丢,遇到反面,则停止,
  用实验中丢硬币的次数 K 作为元素占有的层数。显然随机变量 K 满足参数为 p = 1/2 的几何分布,
  K 的期望值 E[K] = 1/p = 2. 就是说,各个元素的层数,期望值是 2 层。

  跳表的高度。
    n 个元素的跳表,每个元素插入的时候都要做一次实验,用来决定元素占据的层数 K, 跳表的高度等于这 n 次实验中产生的最大 K,待续。。。
  

  跳表的空间复杂度分析
    根据上面的分析,每个元素的期望高度为 2, 一个大小为 n 的跳表,其节点数目的期望值是 2n。

跳表的删除
 在各个层中找到包含 x 的节点,使用标准的 delete from list 方法删除该节点。
 例子:删除 71

skiplist与平衡树、哈希表的比较

  • skiplist和各种平衡树(如AVL、红黑树等)的元素是有序排列的,而哈希表不是有序的。因此,在哈希表上只能做单个key的查找,不适宜做范围查找。所谓范围查找,指的是查找那些大小在指定的两个值之间的所有节点。
  • 在做范围查找的时候,平衡树比skiplist操作要复杂。在平衡树上,我们找到指定范围的小值之后,还需要以中序遍历的顺序继续寻找其它不超过大值的节点。如果不对平衡树进行一定的改造,这里的中序遍历并不容易实现。而在skiplist上进行范围查找就非常简单,只需要在找到小值之后,对第1层链表进行若干步的遍历就可以实现。
  • 平衡树的插入和删除操作可能引发子树的调整,逻辑复杂,而skiplist的插入和删除只需要修改相邻节点的指针,操作简单又快速。
  • 从内存占用上来说,skiplist比平衡树更灵活一些。一般来说,平衡树每个节点包含2个指针(分别指向左右子树),而skiplist每个节点包含的指针数目平均为1/(1-p),具体取决于参数p的大小。如果像Redis里的实现一样,取p=1/4,那么平均每个节点包含1.33个指针,比平衡树更有优势。
  • 查找单个key,skiplist和平衡树的时间复杂度都为O(log n),大体相当;而哈希表在保持较低的哈希值冲突概率的前提下,查找时间复杂度接近O(1),性能更高一些。所以我们平常使用的各种Map或dictionary结构,大都是基于哈希表实现的。
  • 从算法实现难度上来比较,skiplist比平衡树要简单得多。

Redis为什么用skiplist而不用平衡树?

There are a few reasons:

1) They are not very memory intensive. It’s up to you basically. Changing parameters about the probability of a node to have a given number of levels
will make then less memory intensive than btrees.

2) A sorted set is often target of many ZRANGE or ZREVRANGE operations, that is, traversing the skip list as a linked list. With this operation the cache
locality of skip lists is at least as good as with other kind of balanced trees.

3) They are simpler to implement, debug, and so forth. For instance thanks to the skip list simplicity I received a patch (already in Redis master)
with augmented skip lists implementing ZRANK in O(log(N)). It required little changes to the code.

  这里从内存占用、对范围查找的支持和实现难易程度这三方面总结的原因。  

资料出处:https://blog.csdn.net/u014427196/article/details/52454462

原文地址:https://www.cnblogs.com/myseries/p/11442335.html

时间: 2024-10-11 14:16:47

skiplist 跳跃表的相关文章

SkipList (跳跃表)解析及其实现

目录 导言 查找结点的效率如何提升? 什么是跳跃表? 跳跃表必须是完美的? 预备知识 抛硬币实验 模拟建表 操作解析 伪代码 代码实现 柔性数组 跳跃表的创建与销毁 跳跃表表头结构体定义 跳跃表结点结构体定义 建立跳跃表表头操作 操作解析 伪代码 代码实现 创建单个结点操作 操作解析 代码实现 销毁操作 操作解析 代码实现 插入操作 操作解析 时间复杂度 伪代码 代码实现 删除操作 操作解析 时间复杂度 伪代码 代码实现 查找操作 操作解析 伪代码 代码实现 时间复杂度分析 简单应用 跳跃字母表

浅析SkipList跳跃表原理及代码实现

本文将总结一种数据结构:跳跃表.前半部分跳跃表性质和操作的介绍直接摘自<让算法的效率跳起来--浅谈“跳跃表”的相关操作及其应用>上海市华东师范大学第二附属中学 魏冉.之后将附上跳跃表的源代码,以及本人对其的了解.难免有错误之处,希望指正,共同进步.谢谢. 跳跃表(Skip List)是1987年才诞生的一种崭新的数据结构,它在进行查找.插入.删除等操作时的期望时间复杂度均为O(logn),有着近乎替代平衡树的本领.而且最重要的一点,就是它的编程复杂度较同类的AVL树,红黑树等要低得多,这使得其

【转】浅析SkipList跳跃表原理及代码实现

SkipList在Leveldb以及lucence中都广为使用,是比较高效的数据结构.由于它的代码以及原理实现的简单性,更为人们所接受.首先看看SkipList的定义,为什么叫跳跃表? "Skip lists  are data structures  that use probabilistic  balancing rather  than  strictly  enforced balancing. As a result, the algorithms  for insertion  a

跳跃表SkipList

SkipList在各种开源引擎中用处普遍,例如redis的sortedset容器.luence里面的索引字典等均用到了skiplist. 1.SkipList     在数据结构里面,我们知道有两种基本数据存储结构:数组和链表.它们均有其各自的特点,数组(特别是有序数组),可以进行快速查询,但不便于删除操作;链表,可以进行快速的增删操作,但是又不便于查询.那有没可能存在一种数据结构,结合两者各自的优点呢?     基于这样的思路,William Pugh这位马里兰大学的计算机教授,于1989年提

跳跃表skiplist简析

1.简述 跳跃表(skiplist)最初由William Pugh发表在ACM通讯上的论文<Skip lists: a probabilistic alternative to balanced trees >中,作者给出的定义是:跳表是在很多应用中有可能替代平衡树而作为实现方法的一种数据结构.跳跃列表的算法有同平衡树一样的渐进的预期时间边界,并且更简单.更快速和使用更少的空间.其实,跳表,顾名思义,跳跃的链表,跳表在链表的基础上增加了前向指针,对于链表而言,顺序查找的时间复杂度为线性时间,为

redis 5.0.7 源码阅读——跳跃表skiplist

redis中并没有专门给跳跃表两个文件.在5.0.7的版本中,结构体的声明与定义.接口的声明在server.h中,接口的定义在t_zset.c中,所有开头为zsl的函数. 一.数据结构 单个节点: typedef struct zskiplistNode { //key,唯一 sds ele; //分值,可重复 double score; //后退指针 struct zskiplistNode *backward; //层 struct zskiplistLevel { //前进指针 struc

查找——图文翔解SkipList(跳跃表)

跳跃表 跳跃列表(也称跳表)是一种随机化数据结构,基于并联的链表,其效率可比拟于二叉查找树(对于大多数操作需要O(logn)平均时间). 基本上,跳跃列表是对有序的链表增加上附加的前进链接,增加是以随机化的方式进行的,所以在列表中的查找可以快速的跳过部分列表元素,因此得名.所有操作都以对数随机化的时间进行. 如下图所示,是一个即为简单的跳跃表.传统意义的单链表是一个线性结构,向有序的链表中插入一个节点需要O(n)的时间,查找操作需要O(n)的时间.如果我们使用图中所示的跳跃表,就可以大大减少减少

redis源码分析4---结构体---跳跃表

redis源码分析4---结构体---跳跃表 跳跃表是一种有序的数据结构,他通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的: 跳跃表支持平均O(logN),最坏O(N)复杂度的节点查找,还可以通过顺序性操作来批量处理节点.性能上和平衡树媲美,因为事先简单,常用来代替平衡树. 在redis中,只在两个地方使用了跳跃表,一个是实现有序集合键,另一个是在集群节点中用作内部数据结构. 1 跳跃表节点 1.1 层 层的数量越多,访问其他节点的速度越快: 1.2 前进指针 遍历举例

跳跃表Skip List【附java实现】

skip list的原理 Java中的LinkedList是一种常见的链表结构,这种结构支持O(1)的随机插入及随机删除, 但它的查找复杂度比较糟糕,为O(n). 假如我们有一个有序链表如下,如果我们想找到值为59的节点,需要查找7次.怎么提高查询效率呢?通常的做法是使用二分法,但LinkedList的随机访问时间复杂度同样为O(n),因此朴素的二分法并不适用.那怎么办呢? 我们可以在节点中增加额外的跳跃节点,如下: 这样我们可以根据跳跃节点查询,只需要查找3次.至于查询47,我们先根据跳跃节点