跳跃表解析

本文详细解析了Redis中跳跃表(跳表)的数据结构及其应用,包括跳表的基本思想、创建过程、节点与结构、插入和删除节点的步骤,以及跳表的复杂度分析。跳表作为Redis zset的底层数据结构,通过多层有序链表提高查找效率,以牺牲空间换取快速查找。文章还介绍了Redis创建跳表时如何确定节点层高、插入和删除节点时如何调整跳表结构,以及跳表在有序集合中的应用和性能分析。
摘要由CSDN通过智能技术生成

写在前面

跳表的应用很多 redis zset的底层数据结构的一种就是其应用
面试被问到两次 一次不知道 一次没太讲清除 尴尬
所以专门找了redis书里的跳表进行学习
以下主要基于Redis5设计与源码分析 这本书
直接看书也可以 这篇博客是对书内容的一个解析
算是我的笔记 是我对于书内容的理解 希望能帮到你
里面有些图太丑 不要嫌弃 有些图可能因为github 加载会慢点
关于java实现跳表可以看视频:https://www.bilibili.com/video/BV1Er4y1P7k1?from=search&seid=12607675317732896430

基本思想与跳表什么样?

跳表效率堪比红黑树 但实现比红黑树简单

跳表:有序链表部分节点分层 每层都是一个有序链表

基本思想:查找时从最高层开始 如果next是空 或比key大则降一层继续查找

最终发现查找效率比有序链表高

实现过程:

在这里插入图片描述

如图

性质:

  • 多层次构成

  • 有一个header(头结点),头节点中有一个64层的结构,每层的结构包含指向本层的下个节点的指针,指向本层下个节点中间所跨越的节点个数为本层的跨度(span)见图

  • 除头节点外,层数最多的节点的层高为跳跃表的高度(level),图3-3中跳跃表的高度为3。

  • 每层都是一个有序链表,数据递增

  • 除header节点外,一个元素在上层有序链表中出现,则它一定会在下层有序链表中出现。

  • 跳跃表每层最后一个节点指向NULL,表示本层有序链表的结束

  • 跳跃表拥有一个tail指针,指向跳跃表最后一个节点

  • 最底层的有序链表包含所有节点,最底层的节点个数为跳跃表的长度(length)(不包括头节点),图3-3中跳跃表的长度为7。

  • 每个节点包含一个后退指针,头节点和第一个节点指向NULL;其他节点指向最底层的前一个节点。

跳表每个维护了多个指向其他节点的指针?

所以在跳跃表进行查找、插入、删除操作时可以跳过一些节点,快速找到操作需要的节点。

归根结底,跳跃表是以牺牲空间的形式来达到快速查找的目的。

Redis中的跳表实现 节点与结构

跳表节点
typedef struct zskiplistNode {
   
     sds ele; 						//存储字符串类型的数据
     double score;					//存储排序的分值
     struct zskiplistNode *backward;//后退指针 指向当前节点最底层的前一个节点 才									后向前遍历跳表时使用
     struct zskiplistLevel {
   
         struct zskiplistNode *forward;
         unsigned int span;
 	 } level[];							//柔性数组 每个节点的数组长度不同 生成跳
					//跃表节点时,随机生成一个1~64的值,值越大出现的概率越低。?
    		//level struct zskiplistLevel类型的数组 每项包含两个元素:1.forward: 指向本层下一个节点 尾结点的forward指向NULL
    				 (2) .span: forward指向的节点与本节点之间的元素个数。span值
							越大,跳过的节点个数越多。

} zskiplistNode;

跳表是Redis zset的底层数据结构 则ele存储zset的成员member值

score存储成员score值 节点分值升序排序 score相同 节点按member排序

zset: ZADD key score member

image-20210418180247133

跳表结构

管理节点的结构

结构体:

typedef struct zskiplist {
   
     struct zskiplistNode *header, *tail; 
    	//header:跳表头节点 其level数组元素有64个 
    		//其不存储member和score ele==NULL score==0 不计入跳表总长度 跳表多层			次的开始 初始化时 64个元素的forward指向null  span==0
    	//tail:跳表尾结点
     unsigned long length; //跳表长度 最底层的节点数(除头结点外)
     int level; 		   //跳表高度
} zskiplist;

可在O(1)的时间复杂度下,快速获取到跳跃表的头节点、尾节点、长度和高度。 因为是直接展示的

跳表如何创建

表里有节点 层高(层高决定节点中Level数组要有几个元素)需要创建节点 层高 先层高 再节点(节点中有数组level[]放之后的节点)

tail,
lenth,
level : zslRandomLevel
header : level[]:span 
    			 forward节点:ele
                     		score
                     		backward
         ele
         score
         backward

tail,length,level,header ---> level[] ---> sapn , forward节点 --->ele,backward,score

1. 创建层高

决定节点的level[]数组中元素个数

节点层高最小1 最大ZSKIPLIST_MAXLEVEL Redis5中最大为64

#define ZSKIPLIST_MAXLEVEL 64

通过zslRandomLevel随机生成1~64的值 作为新建节点的高度 值越大出现概率越低

层高确定后不会修改

代码:

#define ZSKIPLIST_P 0.25 /* Skiplist P = 1/4 */
int zslRandomLevel(void) {
   
 int level = 1;  //层高初始值为1
 while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF)) //生成随机值 取这个值的低16位作为x,当x小于0.25倍的0xFFFF时,level的值加1;
 level += 1;  
 return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}

层高初始值为1 通过while循环 每次生成一个随机值

取这个值的低16位作为x,当x小于0.25倍的0xFFFF时,level的值加1;

否则退出while循环。

最终返回level和ZSKIPLIST_MAXLEVEL两者中的最小值

2. 创建节点

首先分配内存 节点里有ele,score,zskiplistLevel

zskiplistNode *zn =
zmalloc(sizeof(*zn
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值