跳跃表
跳跃表(skiplist)是一种有序
的数据结构,它通过在每一个节点中维持多个
指向其他节点的指针,从而达到快速访问节点的目的
跳跃表查找节点的平均时间复杂度为O(lonN)、最坏为O(N),但需要维护几个指向其他节点的指针,空间复杂度为O(N),还可以通过顺序性操作来批量处理节点
在大部分情况下,跳跃表的效率可以和二叉平衡树相同,并且跳跃表的实现较为简单
Redis使用跳跃表作为有序集合Zset
的底层实现之一,当一个有序集合包含的元素数量较多或者一个元素的成员是比较长的字符串时,Redis就会使用跳跃表来作为有序集合的底层实现
Redis只在两个地方用到了跳跃表,一个是有序集合Zset
,另一个是在集群节点中用作内部数据结构
跳跃表的实现
Redis的跳跃表由redis.h/zskiplistNode
和redis.h/zskiplist
两个结构体来定义,zskiplistNode
表示跳跃表节点,zskiplist
表示跳跃表
- 跳跃表
typedef struct zskiplist {
//跳跃表的头节点和尾节点,头节点为一个标识辅助节点,指向跳跃表中第一个元素节点
structz zskiplistNode *header,*tail;
//跳跃表中节点的数量
unsigned long length;
//跳跃表中节点层级的最大值,即每个节点的Math.max(level.length)
int level;
}zskiplist;
- 跳跃表节点
typedef struct zskiplistNode {
//后退指针,指向当前节点的前一个节点
struct zskiplistNode *backward;
//当前节点元素的分值
double score;
//当前节点保存的对象(不同的类型,如数字,字符串)
robj *obj;
//当前节点长度层级数组
struct zskiplistLevel {
//跳跃到下一个节点的指针,只能是向尾节点方向
struct zskiplistNode *forward;
//跳跃的跨度
unsigned int span;
}level[];
}zskiplistNode;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PL0ESAvd-1632553280688)(images/skiplist.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zKQ5UZMZ-1632553280690)(images/zskiplist.png)]
- 跳跃表的层级都是1~32之间的随机数,一个跳跃表节点中的
level层数
采用抛硬币
的方式决定该节点是否需要向上增加层级 - 在同一个跳跃表中,多个节点可以包含相同的分值,但每个节点的成员对象必须是唯一的
- 跳跃表中的节点按照分值大小进行排序,当分值相同时,节点按照成员对象的大小进行排序