【Redis】求求你,别再问跳表了

简介: 【Redis】求求你,别再问跳表了

跳表

使用场景

跳表(Skiplist )是一个特殊的链表,相比一般的链表,有更高的查找效率,可比拟二叉查找树,

平均期望的查找、插入、删除时间复杂度都是O(log n),许多知名的开源软件(库)中的数据

结构均采用了跳表这种数据结构∶

  • Redis中的有序集合zset
  • LevelDB、RocksDB、HBase中Memtable
  • Apache Lucene中的Term Dictionary、Posting List

结构描述

我们拿我们以前的有序链表相比:

我们可以很清楚的看出,有序链表的查询比较慢,时间复杂度为O(n),它的删除和插入操作比较快,我们怎样才能让它的查询的时间复杂度也变快呢?

能不能将我们的链表实现二分的查找,也就是longN,答案是肯定的,也就是我们下面所说的这种跳表。

查询算法

跳表的查询和我之前做过的一道题特别类似,二维数组的查找

假设我们查询31这个数字

  • 我们的header开始,依次进行查询,
  • 31不在负无穷到17,往右边,到达17,继续比较,在17到正无穷之间,往下跑
  • …

整个查询过程如下所示

插入算法

对于插入来说,我们比如要插入一个21的数值

  • 类似查询算法,到达17这个点,进一步到达20这个点,然后在20的后面进行插入
  • 插入完了以后,这时候要考虑索引的问题,也就是我们需要将当前这个数值向上几层呢
  • 通过一个50%的概率决定,也就是绝大多数文章讲的抛硬币(_)

删除算法

对于删除来说,我们比如要插入一个17的数值

  • 先进行查询操作,找到这个数字
  • 将其及索引进行删除

时间复杂度

我们知道单链表查询的时间复杂度为O(n),而插入、删除操作需要先找到对应的位置,所以插入、删除的时间复杂度也是O(n)。

那么,跳表的时间复杂度是多少呢?

如果按照标准的跳表来看的话,每一级索引减少k/2个元素(k为其下面一级索引的个数),那么整个跳表的高度就是(log n)。

学习过平衡二叉树的同学都知道,它的时间复杂度与树的高度成正比,即O(log n)。

所以,这里跳表的时间复杂度也是O(log n)。(这里不一步步推倒了,只要记住,查询时每次减少一半的元素的时间复杂度都是O(log n),比如二叉树的查找、二分法查找、归并排序、快速排序)

空间复杂度

我们还是以标准的跳表来分析,每两个元素向上提取一个元素,那么,最后额外需要的空间就是:

n/2 + (n/2)^2 + (n/2)^3 + … + 8 + 4 + 2 = n - 2

所以,跳表的空间复杂度是O(n)。

总结

  • 跳表是可以实现二分查找的有序链表
  • 每个元素插入时随机生成它的索引的高度
  • 最低层包含所有的元素
  • 如果一个元素出现在level(x),那么它肯定出现在x以下的level中
  • 每个索引节点包含两个指针,一个向下,一个向右
  • 跳表查询、插入、删除的时间复杂度为O(log n),与平衡二叉树接近

Redis使用跳表而不是红黑树?

  • 插入元素
  • 删除元素
  • 查找元素
  • 有序输出所有元素
  • 查找区间内所有元素

前四项红黑树都可以完成,且时间复杂度与跳表一致

但是,最后一项,红黑树就不如跳表查询的快

在跳表中,要查找区间的元素,我们只要定位到两个区间端点在最低层级的位置,然后按顺序遍历元素就可以了,非常高效。

而红黑树只能定位到端点后,再从首位置开始每次都要查找后继节点,相对来说是比较耗时的。

跳表实现起来很容易且易读,红黑树实现起来相对困难,所以Redis选择使用跳表来实现有序集合。


相关文章
|
存储 NoSQL Redis
Redis常见面试题:ZSet底层数据结构,SDS、压缩列表ZipList、跳表SkipList
String类型底层数据结构,List类型全面解析,ZSet底层数据结构;简单动态字符串SDS、压缩列表ZipList、哈希表、跳表SkipList、整数数组IntSet
|
NoSQL 算法 Redis
redis7.0源码阅读(五):跳表(skiplist)
redis7.0源码阅读(五):跳表(skiplist)
1633 1
|
存储 NoSQL Redis
零基础手把手带你阅读Redis源代码系列-ZSet底层原理详解(跳表SkipList)
>之前就说了要来西索Redis,现在来辣! >本文的部分内容参考自《小林Coding》,部分地方根据源代码进行剖析。 >Redis源码地址:https://github.com/redis/redis.git
763 0
零基础手把手带你阅读Redis源代码系列-ZSet底层原理详解(跳表SkipList)
|
NoSQL Redis C++
Redis跳表
Redis跳表
354 0
|
NoSQL Go API
Golang实现redis系列-(4)实现跳表容器
Golang实现redis系列-(4)实现跳表容器
204 0
|
存储 NoSQL Redis
Redis数据结构之——跳表skiplist
Redis数据结构之——跳表skiplist
|
存储 NoSQL Redis
Redis 跳表skiplist
Redis 跳表skiplist
|
存储 NoSQL 算法
redis6.0源码分析:跳表skiplist
redis6.0源码分析:跳表skiplist
367 0
|
NoSQL Redis 索引
Redis zset 底层数据结构之跳表
0、zset数据结构 1、zset底层的数据结构 2、跳表介绍 3、跳表增删查的时间复杂度 4、什么时候使用压缩链表,什么时候使用跳表 5、跳表的内部实现及原理 6、为什么用跳表而不用红黑树或二叉树呢.........
833 0
Redis zset 底层数据结构之跳表