跳跃表
跳跃表(Skip List)是一种用于有序元素序列快速搜索的数据结构。它的效率和红黑树以及AVL(自平衡二叉查找树)相近,但是实现起来更加容易(相对于复杂的自平衡算法)。
大概结构如下:
在最原始的已排序的链表基础上通过添加多个层级,理想情况下每个层级节点数是上一个层级1/2,查找时通过最上层开始,利用需要查找的值与当前层级值对比缩小范围并通过下一层进一步缩小范围,最终找到查询值的方式(类似于二分查找方式)。
Redis的跳跃表
Redis使用跳跃表作为有序集合的底层实现之一,如果一个有序集合包含的元素比较多或者集合中的元素时比较长的字符串时,Redis会使用跳跃表作为有序集合键的底层实现。
Redis跳跃表大致结构如下:
分为zskiplistNode和zskiplist两个结构,代码如下:
redis.h/zskiplistNode:
typedef struct zskiplistNode {
// 成员对象
robj *obj;
// 分值
double score;
// 后退指针
struct zskiplistNode *backward;
// 层
struct zskiplistLevel {
// 前进指针
struct zskiplistNode *forward;
// 跨度
unsigned int span;
} level[];
} zskiplistNode;
level数组包含了当前元素在不用层高的下一个元素指针和跨度,Redis中程序会根据幂次定律(power law, 越大的数出现的概率越小)随机生成一个介于1~32之间的值作为level数组的大小,这个大小就是层的“高度”。
span表示跨度,由于底层的链表是有序的,所以从头部到当前节点跨度之和也就是元素的排位。用于Redis有序集合的ZRANK命令获取元素排行。
backward回退指针用于逆序遍历链表,对应Redis有序集合的ZREVRANGE命令。
redis.h/zskiplist:
typedef struct zskiplist {
// 表头节点和表尾节点
struct zskiplistNode *header, *tail;
// 表中节点的数量
unsigned long length;
// 表中层数最大的节点的层数
int level;
} zskiplist;