普通链表
如图所示,链表从小到大排序
缺点:如果想在链表中查询到44,需要从头开始遍历,时间复杂度是o(n)
优化,提取一层索引层出来
如图所示,每隔一个元素,提取一个索引
第一层索引数=N/2,搜索次数缩小一半
继续提取,第二层索引
第二层索引数=N/2^2 搜索次数再次缩小一半
增加索引后查找效果与平衡树相同,时间复杂度O(logN)
缺点:每次添加新的元素,需要全部重新提取索引
索引层数随机提取
redis实现时,不是隔一个提取索引,而是使用规律,每个节点都可能提取成索引,这样新增或修改节点,并不需要重新提取索引
如上图所示,哪一层提取成索引,随机的,每次都不相同
提取出来的标记上层级,L1、L2、L3…LN,如下图
redis最终跳表实现如下图
redis最高支持32层,默认有个header是32层,每层指向最近相同层数的索引层,越高层索引,生成概率越低
源码定义
typedef struct zskiplist {
struct zskiplistNode *header, *tail;
unsigned long length;
int level;
} zskiplist;
typedef struct zskiplistNode {
sds ele;
double score;
struct zskiplistNode *backward;
struct zskiplistLevel {
struct zskiplistNode *forward;
unsigned long span;
} level[];
} zskiplistNode;
字段含义
所属类 | 字段 | 含义 | |
zskiplist | header | 指针->指向header节点 | |
tail | 指针->指向最后一个节点 | ||
length | 跳表中节点总数 | ||
level | 跳表中,层级最高的层数(除了header) | ||
zskiplistNode | ele | 节点的值(member) | |
level[] (最高支持32长度) zskiplistLevel | forward | 前驱指针->指向下一个相同层级的节点 | |
span | 与下一个节点的跨度 | ||
score | 分值,节点按照分值排序 |
生成层数的源码
int zslRandomLevel(void) {
int level = 1;
while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
level += 1;
return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}