🌷 zSkipList
2022年10月10日
- db
🌷 zSkipList
1. 数据结构
// 跳表节点类
typedef struct zskiplistNode {
sds ele; // 值
double score; // 权重
struct zskiplistNode *backward; // 后置节点指针
struct zskiplistLevel { // 当前层
struct zskiplistNode *forward; // 前置节点指针
unsigned long span; // 跨度
} level[];
} zskiplistNode;
// 跳表
typedef struct zskiplist {
struct zskiplistNode *header, *tail; // 头、尾节点
unsigned long length; // 节点数
int level; // 最大层数
} zskiplist;
// 有序集合 = dict + zskiplist
typedef struct zset {
dict *dict;
zskiplist *zsl;
} zset;
2. zskiplist
- 跳表特点:
- 查找、增加、删除 复杂度 O(logN),无维护平衡操作,对跳表的修改只发生在 局部
- 实现比 红黑树 要简单一些
- 最底层存放所有有序节点,可快速 范围查询
- Redis 的 zskiplist 还额外保存了
span
遍历,可快速得到某个 节点的排名
1) 随机层数
// 获取节点所处的层数
// ZSKIPLIST_MAXLEVEL = 32
//
// 所有节点肯定会在 第一层 出现
// 第 2 层的概率 = 0.5;第 3 层的概率为 0.25;...;第 32 层的概率为 1/2^31
int zslRandomLevel(void) {
static const int threshold = ZSKIPLIST_P*RAND_MAX;
int level = 1;
while (random() < threshold) // 随机数 处于 概率范围内,可以进入上一层
level += 1;
return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}
2) 创建跳表
zskiplist *zslCreate(void) {
int j;
zskiplist *zsl;
zsl = zmalloc(sizeof(*zsl)); // 申请内存空间
zsl->level = 1; // 初始层数为 1
zsl->length = 0; // 节点数为 0
zsl->header = zslCreateNode(ZSKIPLIST_MAXLEVEL,0,NULL); // 创建一个 层数为 32,权重为 0,值为 null 的头节点
// 头节点初始化 - 每层节点进行初始化
for (j = 0; j < ZSKIPLIST_MAXLEVEL; j++) {
zsl->header->level[j].forward = NULL; // 前置节点指针为 null
zsl->header->level[j].span = 0; // 跨度为 0
}
zsl->header->backward = NULL;
zsl->tail = NULL;
return zsl;
}
3) 插入节点
zskiplistNode *zslInsert(zskiplist *zsl, double score, sds ele) {
zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x; // 节点路径
unsigned long rank[ZSKIPLIST_MAXLEVEL]; // 经过的节点跨度
int i, level;
serverAssert(!isnan(score));
x = zsl->header; // 头节点
// 搜索插入节点
for (i = zsl->level-1; i >= 0; i--) {
/* store rank that is crossed to reach the insert position */
rank[i] = i == (zsl->level-1) ? 0 : rank[i+1];
// 往右下角查找
while (x->level[i].forward &&
(x->level[i].forward->score < score ||
(x->level[i].forward->score == score &&
sdscmp(x->level[i].forward->ele,ele) < 0))) // 这里还比较了 值 的大小,因此不会出现 权重相等 时 O(N) 的复杂度
{
rank[i] += x->level[i].span;
x = x->level[i].forward;
}
update[i] = x;
}
level = zslRandomLevel(); // 生成随机层数
// 新生成的节点层数 > 目前最大层数,需要将当前节点更新
if (level > zsl->level) {
for (i = zsl->level; i < level; i++) {
rank[i] = 0;
update[i] = zsl->header;
update[i]->level[i].span = zsl->length;
}
zsl->level = level;
}
x = zslCreateNode(level,score,ele); // 创建要插入的新节点
// 下层节点插入
for (i = 0; i < level; i++) {
x->level[i].forward = update[i]->level[i].forward;
update[i]->level[i].forward = x;
/* update span covered by update[i] as x is inserted here */
x->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]);
update[i]->level[i].span = (rank[0] - rank[i]) + 1;
}
/* increment span for untouched levels */
for (i = level; i < zsl->level; i++) {
update[i]->level[i].span++;
}
x->backward = (update[0] == zsl->header) ? NULL : update[0];
if (x->level[0].forward)
x->level[0].forward->backward = x;
else
zsl->tail = x;
zsl->length++;
return x; // 返回
}