redis基础数据结构——跳表

357 阅读2分钟

image.png

跳表介绍

跳表是一种可以提高查找效率的数据结构, 如上图,跳表的精髓就是,怎么跳的呢?答曰分层。越高层元素数量越少,也就是的就越远。 跳表里的节点是按照score有序排列的。当查找一个元素的时候,步骤大致如下:

  1. 从顶层第一个元素开始往后跳
  2. 当前元素小于被查找元素,就往后跳一个,
  3. 当前元素大于被查找元素,就返回跳之前的节点,往下走一层
  4. 重复步骤2和3,直到当前节点的score等于被查找元素 可以看出,这种一层一层跳着搜索的速度明显快于逐个节点搜索

跳表知识点总结

讲redis跳表的文章很多,推荐看这篇,而且这些文章说的很详细了,在这里就对知识点做个总结好了

数据结构代码

typedef struct zskiplistNode {
    sds ele;
    double score; // 分值
    struct zskiplistNode *backward; // 后退指针
    // 层
    struct zskiplistLevel { 
        struct zskiplistNode *forward; // 前进指针
        unsigned long span; // 跨度
    } level[]; // level[]数组的大小就叫做该节点层的`高度`.表明从下往上多少层中存在该节点
} zskiplistNode;

typedef struct zskiplist {
    struct zskiplistNode *header, *tail;
    unsigned long length; // 节点数
    int level; // 最高层数
} zskiplist;

Q&A

为什么redis用跳表而不是红黑树

  1. 范围查找简单: 在skiplist上进行范围查找简单,只需要在找到小值之后,对第1层链表进行若干步的遍历就可以实现。
  2. 插入删除简单: 平衡树的插入和删除操作可能引发子树的调整,逻辑复杂,而skiplist的插入和删除只需要修改相邻节点的指针,操作简单又快速。
  3. 难度低: 从算法实现难度上来比较,skiplist比平衡树要简单得多。

节点的成员span有什么作用?

  • 层的跨度(level[i].span 属性)用于记录两个节点之间的距离
  • 跨度是用来计算排位(rank)的: 在查找某个节点的过程中, 将沿途访问过的所有层的跨度累计起来, 得到的结果就是目标节点在跳跃表中的排位。

节点的后退指针有什么用?

  • 后退指针在程序从表尾向表头遍历时使用。

redis新插入节点时怎么确定有几层?

  • 随机出一个层数(level) 。比如,一个节点随机出的层数是 3,那么就把它链入到第 1 层到第 3 层这三层链表中。
  • 随机符合幂律分布,层数低的可能性高

image.png