SkipList跳表
SkipList跳表本质上是双端链表,且按照升序排列。即是有序链表。
与传统链表相比:
-
元素按照升序排列
-
节点可能含有多个不同的指针,指针的跨度不同。
-
查询是从最高层级往下查。(可根据不同跨度的指针查询,查询效率和红黑树、二分查找、B+树的查询效率差不多。时间复杂度为:
log(n)
)
整体索引层级结构如下:
SkipList数据结构
typedef struct zskiplist {
// skiplist 头尾节点
struct zskiplistNode *header, *tail;
// 节点数量
unsigned long length;
// 最大的索引层级,默认为1。
//(在此设置skipList中的最大索引层级,即跨度。旨在优先从最大的索引层级查询)
int level;
} zskiplist;
typedef struct zskiplistNode {
// 节点内容,使用的是动态字符串sds
sds ele;
// 节点分数,用来排序
double score;
// 前一节点的指针,后序遍历索引层级固定为1,指向前一节点
struct zskiplistNode *backward;
// 索引数组:因为存在不同层级索引
struct zskiplistLevel {
struct zskiplistNode *forward; // 指向下一节点
unsigned long span; // 索引层级(跨度)
} level[];
} zskiplistNode;
有上述结构体可看出:
-
skipList 节点使用score来排序,所以是有序链表
-
skipList 节点存在前、后指针,所以是双端链表
-
skipList 节点存在索引数组,所以存在多个索引层级,优先从最高的层级查询,依次往下级索引查询。
typedef struct zset {
dict *dict;
zskiplist *zsl;
} zset;
由上述结构体,可得出:zset(sortedSet)数据类型底层是由dict
和skipList
数据结构组成
数据结构示意图如下:
总结
SkipList的特点
-
SkipList是一个双向链表,每个节点都包含
score和ele
值 -
节点按照score值排序,score值一样则按照 ele字典(类似ascii码)排序
-
每个节点都可以包含多层指针,层数是1到32之间的随机数
-
不同层指针到下一个节点的跨度不同,层级越高,跨度越大
-
增删改查效率与红黑树基本一致,实现却更简单