| 1. 跳跃表的用处有序集合(zset)的底层可以采用数组, 链表, 平衡树等结果来实现, 但是他们都有各自的缺点 . 数组方便查询, 但不便于插入和删除, 链表方便插入和删除, 但是不利于查找, 平衡树/红黑树效率高但是实现起来很复杂所以Redis自己实现了跳跃表来来当做有序集合(zset)的底层实现, 他的查询复杂度平均O(logN), 最坏O(N), 堪比红黑树, 但实现起来远比红黑树简单
 2. 跳跃表的具体示例我们先来看一下如果用有序链表来实现有序集合
  插入和删除为O(1), 但是查找的复杂度为O(N)
 但是我们看跳跃表的实现
 
  我们从有序链表中选取部分节点, 组成一个新的链表如图中的
 10 -> 30 -> 50 -> 70 -> null, 当做一级索引如果一级索引中节点还是很多, 我们可以再从一级索引中选取部分节点, 再组成一个新的链表, 并以此作为原始链表的二级索引
 10 -> 50 -> null 跳跃表的查找那么跳跃表是如何进行查找的呢 优先从高层查找若当前节点的值, 小于要查找的值, 并且next指针指向大于目标值的节点, 就要降一级寻找, 或者next指针指向了null, 那么也需要降一级查找
 跳跃表的具体实现跳跃表底层使用了zskiplist, 和zskiplistnode两个结构体来实现zskiplist
 typedef struct zskiplist {
    struct zskiplistNode *header, *tail;
    unsigned long length;
    int level;
} zskiplist;
 header 指向跳跃表表头节点, tail指向表尾节点length:记录跳跃表的长度,也即是,跳跃表目前包含节点的数量(表头节点不计算在内)level:记录目前跳跃表内,层数最大的那个节点的层数(表头节点的层数不计算在内),通过这个属性可以在O(1)的时间复杂度内获取层数最高的节点的层数。
 typedef struct zskiplistNode {
    sds ele;
    double score;
    
    struct zskiplistNode *backward;
    
    struct zskiplistLevel {
        
        struct zskiplistNode *forward;
        
        unsigned long span;
    } level[];
} zskiplistNode;
  backward 指针指向前一个节点, 用于方便寻找数据 sds动态字符串存储内容, score存储分数, 用来排序使用 level(每个节点的层数) :节点中用1、2、L3等字样标记节点的各个层,L1代表第一层,L代表第二层,以此类推.
 每个层都带有两个属性:前进指针和跨度。前进指针用于访问位于表尾方向的其他节点,而跨度则记录了前进指针所指向节点和当前节点的距离(跨度越大、距离越远)。在上图中,连线上带有数字的箭头就代表前进指针,而那个数字就是跨度。当程序从表头向表尾进行遍历时,访问会沿着层的前进指针进行。
 
 相当于这是三个节点第一个节点处于第0层, 第二个节点他是可以处于三层, 第三个节点可以处于5层 下图是根据具体一点的一个跳跃表实现
  header指向的是一个层高为32的伪头结点, 实际链表的开端是这个伪头结点的后一个节点, 根据这个伪头结点就可以快速的找到层数最高的, 因为其他节点的层数都是要比32小的
 本文重点跳跃表基于单链表加索引的方式实现跳跃表以空间换时间的方式提升了查找速度Redis有序集合在节点元素较大或者元素数量较多时使用跳跃表实现Redis的跳跃表实现由 zskiplist和 zskiplistnode两个结构组成,其中 zskiplist用于保存跳跃表信息(比如表头节点、表尾节点、长度),而zskiplistnode则用于表示跳跃表节点Redis每个跳跃表节点的层高都是1至32之间的随机数
 |