redis 跳表

zset

zset是可排序的set。与hash的实现方式类似,如果元素个数不多且不大,就使用压缩列表ziplist来存储。不过由于zset包含了score的排序信息,所以在ziplist内部,是按照score排序递增来存储的。意味着每次插入数据都要移动之后的数据。

跳表

跳表(skiplist)是另一种实现dict的数据结构。跳表是对链表的一个增强。我们在使用链表的时候,即使元素的有序排列的,但如果要查找一个元素,也需要从头一个个查找下去,时间复杂度是O(N)。而跳表顾名思义,就是跳跃了一些元素,可以抽象多层。

如下图所示,比如我们要查找8,先在最上层L2查找,发现在1和9之间;然后去L1层查找,发现在5和9之间;然后去L0查找,发现在7和9之间,然后找到8。

当元素比较多时,使用跳表可以显著减少查找的次数。
在这里插入图片描述
同list类似,Redis内部也不是直接使用的跳表,而是使用了一个自定义的数据结构来持有跳表。下图左边蓝色部分是skiplist,右边是4个zskiplistNode。zskiplistNode内部有很多层L1、L2等,指针指向这一层的下一个结点。BW是回退指针(backward),用于查找的时候回退。然后下面是score和对象本身object。
在这里插入图片描述
在这里插入图片描述
可以看到一个是dict结构,主要key是其集合元素,而value就是对应分值,而zkiplist作为跳跃表,按照分值排序,方便定位成员
在这里插入图片描述
在这里插入图片描述
zskiplistNode中的robj指针指向具体元素,注意这个指针和dict中key指针指向同一个元素,其中backward后腿指针便于回溯

复杂度

第一层索引节点n/2,第二层索引节点n/4,第三层索引节点n/8,以此类推第n层节点n 得到h=logn - 1。
由上图可以知道,当我们要找到8号这个节点的时候,我们在k级索引发现5<8<9,故我们要到k-1索引下找,但是发现7<8<9,故我们要到原始链表找,且k索引层最多遍历1,5,9三个节点,k-1最多遍历5,7,9三个节点,故跳表的时间复杂度为O(3logn)=O(logn),索引节点总数为:n/2 + n/4 + n/8 + … + 8 + 4 + 2 =n,故空间复杂度为O(n)

为什么选择了跳跃表而不是红黑树

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

参考
https://blog.csdn.net/qq_38286618/article/details/102530020
https://blog.csdn.net/weixin_39030846/article/details/112433527
https://www.cnblogs.com/cjjjj/p/12751487.html (redis为什么选择了跳跃表而不是红黑树)

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis 跳表(Skip List)是一种有序数据结构,用于实现有序集合(Sorted Set)的数据存储和快速查找。 跳表的核心思想是在链表的基础上增加多级索引,通过索引来加速查找。每一级索引包含一部分节点,这些节点按照某种规则连接起来,形成一个类似于二叉树的结构。每一级索引的节点数逐级减少,最高级索引只有两个节点,分别指向整个跳表的头节点和尾节点。 在跳表中,每个节点除了保存值之外,还保存了指向同一层级或下一层级的节点的指针。通过这样的结构,跳表可以在不需要遍历所有节点的情况下,快速定位到目标节点。 Redis 使用跳表实现有序集合的数据结构,具体实现步骤如下: 1. 创建一个带有头节点和尾节点的空跳表。 2. 向跳表中插入新的元素时,从最高级索引开始,逐级向下查找插入位置。 3. 在每一级索引中,找到插入位置后,将新节点插入到该位置后面。 4. 根据概率随机算法,决定是否在更高级索引中插入新节点的指针。 5. 删除元素时,从最高级索引开始,逐级向下查找并删除节点。 6. 如果删除节点后某一级索引只剩下头节点和尾节点,则删除该级索引。 通过使用跳表Redis 在有序集合中执行插入、删除、查找等操作的时间复杂度可以达到 O(logN),相比于传统的有序数组或平衡二叉树,跳表具有更高的效率和简单的实现方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值