跳表SkipList

跳表

目录

跳表的插入、删除和查找元素的时间复杂度都跟红黑树一样,是****O(logn)

1.单链表→跳表

下图中是一个有序单链表,查找的时间复杂度是O(n)

在这里插入图片描述

那么有没有什么办法可以提高链表的查找速度呢?如下图所示,可以增加一级索引,查找10这个元素的时候,可以先在第一级索引中找到 1、4、7、9,遍历到一级索引的 9 时,发现 9 的后继节点是 13,比 10 大,于是不往后找了,而是通过 9 找到原始链表的 9,然后再往后遍历找到了我们要找的 10,遍历结束。这样我们不需要对10之前的所有数据都进行遍历,提高了查找的效率

在这里插入图片描述

假如单链表有10000个元素,分别是 0~9999。我们建立如下的多级索引,就可以使得有序链表可以实现二分查找。由此可知,当元素数量较多时,索引提高的效率比较大,近似于二分查找。

在这里插入图片描述

所以说,跳表是可以实现二分查找的有序链表

2. 时间复杂度

跳表的时间复杂度 = 索引的高度 * 每层索引遍历元素的个数

先来求跳表的索引高度。如下图所示,假设每两个结点会抽出一个结点作为上一级索引的结点,原始的链表有n个元素,则一级索引有n/2 个元素、二级索引有 n/4 个元素、k级索引就有 n / 2 k n/2^{k} n/2k个元素。最高级索引一般有2个元素,即:最高级索引 h 满足 2 = n / 2 h n/2^{h} n/2h,即 h = l o g 2 n − 1 h = log_2n - 1 h=log2n1,最高级索引 h 为索引层的高度加上原始数据一层,跳表的总高度 h = l o g 2 n h = log_2n h=log2n

在这里插入图片描述

跳表的索引高度 h = l o g 2 n h = log_2n h=log2n,且每层索引最多遍历 3 个元素。所以跳表中查找一个元素的时间复杂度为 O(3*logn),省略常数即:O(logn)

3. 空间复杂度

假如原始链表包含 n 个元素,则一级索引元素个数为 n/2、二级索引元素个数为 n/4、三级索引元素个数为 n/8 以此类推。所以,索引节点的总和是:n/2 + n/4 + n/8 + … + 8 + 4 + 2 = n-2,空间复杂度是 O(n)

如果每三个结点抽一个结点做为索引,索引总和数就是 n/3 + n/9 + n/27 + … + 9 + 3 + 1= n/2,减少了一半。所以我们可以通过较少索引数来减少空间复杂度,但是相应的肯定会造成查找效率有一定下降,我们可以根据我们的应用场景来控制这个阈值,看我们更注重时间还是空间。

4. 插入数据

时间复杂度为查找元素的时间复杂度O(logn)

插入中需要解决的一个问题是,如何在插入数据的同时来更新索引,如果只往原始列表中添加数据,但是不更新索引,就可能出现两个索引节点之间数据非常多的情况。极端情况下,跳表退化成单链表。所以,在插入数据的同时,索引节点也需要相应的增加或者重建,来避免查找效率的退化

在这里插入图片描述

比较容易理解的做法是完全重建索引,每次插入数据后,都把这个跳表的索引删除全部重建,但是重建索引的时间复杂度是O(n),这是我们不能接受的。

那么,如果我们在原始链表中随机选取n/2个元素作为一级索引是不是也能通过索引来提高查找的效率呢?答案是肯定的。我们可以认为:当原始链表中元素数量足够大,且抽取足够随机的话,我们得到的索引是均匀的。所以,我们就可以维护一个这样的索引:随机选n/2个元素作为一级索引,随机选n/4个元素作为二级索引、随机选n/8个元素作为三级索引,以此类推

代码实现的时候:每次有数据要插入的时候,先通过概率算法告诉我们这个元素需要插入到几级索引中

可以实现一个randomlevel()方法,该方法会随机生成1~MAX_LEVEL之间的数,且有1/2的概率返回1,1/4的概率返回2,1/8的概率返回3,以此类推

randomlevel()方法的一个实现

在Redis的t_zset.c中也有类似函数的实现

在这里插入图片描述

Redis 作者 antirez 使用位运算来提高浮点数比较的效率。

4.总结

  1. 跳表是可以实现二分查找的有序链表
  2. 空间换时间
  3. 每个元素在插入时随机生成它的level
  4. level 0包含所有的元素
  5. 如果一个元素出现在level x,那么它肯定出现在x一下的level中
  6. 跳表源码在实现的时候并没有使用向下的指针,具体可以参考源码解析
  7. 跳表查询、插入和删除的时间复杂度为O(log n), 与平衡二叉树接近

5.源码解析

跳表源码解析

参考链接

学习跳表的时候从下文中借鉴颇多

SkipList跳表

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值