跳跃表(跳表)是什么

为什么要有跳表

正常链表只能一个一个往下走但是如果我直到我的目标位置就在链表的中部但是我还得一步一步走过去很浪费时间,所以跳表就是在正常链表的基础上添加了多步跳跃的指针。

什么是跳表

跳表(Skip List)是一种概率型的数据结构,它是基于链表的,通过创建多个层次的链表来加快搜索速度。每个节点不仅有指向下一节点的指针,还可能有指向更高层次节点的指针,从而实现快速跳跃。跳表的时间复杂度为 O(log n) 级别,适用于插入、删除和查找操作。

演示图只是为了方便,实际跳表并不一定是二分的,一个节点最初始在最底层(L0),他有75%的概率不增长层数有25%的概率往上增长一层,后续也是如此25%概率增长一层,我们的层数基本控制在log n。

cpp 复制代码
以下源码来自 redis7.0
​
#define ZSKIPLIST_MAXLEVEL 32 /* Should be enough for 2^64 elements */
#define ZSKIPLIST_P 0.25   /* Skiplist P = 1/4 */
​
/* Returns a random level for the new skiplist node we are going to create.
 * The return value of this function is between 1 and ZSKIPLIST_MAXLEVEL
 * (both inclusive), with a powerlaw-alike distribution where higher
 * levels are less likely to be returned. */
int zslRandomLevel(void) {
    static const int threshold = ZSKIPLIST_P*RAND_MAX;
    int level = 1;
    while (random() < threshold)
        level += 1;
    return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}

跳表的基本结构

跳表由以下几个部分组成:

  1. 表头(head):负责维护跳跃表的节点指针。

  2. 跳跃表节点:保存着元素值,以及多个层。

  3. :保存着指向其他元素的指针。高层的指针越过的元素数量大于等于低层的指针,为了提高查找的效率,程序总是从高层先开始访问,然后随着元素值范围的缩小,慢慢降低层次。

  4. 表尾:全部由 NULL 组成,表示跳跃表的末尾。

跳表的实现

在Redis中,跳表由 zskiplistNodezskiplist 两个结构定义。其中 zskiplistNode 结构用于表示跳跃表节点,而 zskiplist 结构则用于保存跳跃表节点的相关信息,比如节点的数量,以及指向表头节点和表尾节点的指针等等。

zskiplistNode 结构
  • 后退指针(backward):指向当前节点的前一个节点。

  • 分值(score):用于排序,跳跃表中的所有节点都按分值大小进行排序。

  • 成员对象(ele):即真正要往链表中存放的对象。

  • 层(level):每个节点都包含很多层,每一层指向的都是同一个对象。每一层包含一个前进指针和一个跨度(span),前进指针指向同一层的下一个节点,跨度表示两个节点之间的距离。

cpp 复制代码
typedef struct zskiplistNode {
    //Zset 对象的元素值
    sds ele;
    //元素权重值
    double score;
    //后退指针
    struct zskiplistNode *backward;
  
    //节点的level数组,保存每层上的前向指针和跨度
    struct zskiplistLevel {
        struct zskiplistNode *forward;
        unsigned long span;
    } level[];
} zskiplistNode;
zskiplist 结构
  • header:指向跳跃表的表头节点。

  • tail:指向跳跃表的表尾节点。

  • level:记录目前跳跃表内,层数最大的那个节点的层数(表头节点的层数不计算在内)。

  • length:记录跳跃表的长度,也即是,跳跃表目前包含节点的数量(表头节点不计算在内)。

跳表的操作

跳表支持以下基本操作:

  1. 插入(Insert):插入一个元素时,从最高层开始,查找到当前节点的目标插入位置,随机生成它的层数(25%概率增长一层),然后将元素插入到跳表中并更新指针。需要使用一个数组记录该节点在各层的上一节点,用于跟新指针。

  2. 删除(Delete):删除元素时,需要最高层级开始找到对应的节点,删除并更新相关层级指针。

  3. 查找(Search):查找元素时,从高层开始向下查找,直到找到元素或到达底层。

跳表是一种以空间换时间的方案,它通过增加索引层数来提高查找效率,但同时也会占用更多的内存。在Redis中,跳表主要用于实现有序集合(Sorted Set),它能够保证元素的快速查找和有序存储。

相关推荐
0~max~0几秒前
OpenGL材质系统和贴图纹理
c++·游戏引擎·图形渲染
不是只有你能在乱世中成为大家的救世主19 分钟前
学习第六十二行
c语言·c++·学习·gitee
CodeClimb40 分钟前
【华为OD-E卷 - 服务失效判断 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
CodeClimb42 分钟前
【华为OD-E卷 - 九宫格按键输入 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
14_1144 分钟前
Cherno C++学习笔记 P49 C++中使用静态库
c++·笔记·学习
Cedric_Anik1 小时前
数据结构——链表
数据结构·链表
pumpkin845141 小时前
C++移动语义
开发语言·c++
起名方面没有灵感1 小时前
力扣23.合并K个升序链表
java·算法
啊烨疯狂学java2 小时前
0105java字节面经
java·jvm·算法
九州~空城2 小时前
C++中map和set的封装
java·前端·c++