目录
跳表(跳跃表, skiplist)
1 zskiplist结构
2 zskiplistNode结构
3 skiplist示意图
4 skiplist插入节点
5 skiplist删除节点
跳跃表(skiplist)是用于有序元素序列快速搜索查找的数据结构,跳表是一个随机化的数据结构,实质是一种可以进行二分查找的、具有层次结构的有序链表
跳表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找,平均期望的查找、插入、删除时间复杂度都是O(logn),同时支持范围查询
具有如下性质:
1) 每个节点由多层组成,排列顺序为由高层到底层
2) 每一层都是一个有序链表
3) 最底层的链表包含了所有的元素
4) 如果一个元素出现在某一层的链表中,那么在该层之下的链表也全都会出现
5) 每个节点都包含两个指针,一个指向同一层的下一个链表节点,另一个指向下一层的同一个链表节点
6) 分数(score)允许重复,即key是允许重复的
typedef struct zskiplist {
struct zskiplistNode *header, *tail;
unsigned long length;
int level;
} zskiplist;
其中:
header: 表头节点,没有存储实际的数据,而是一个空的,初始化层级数为ZSKIPLIST_MAXLEVEL值(默认32)的节点
tail: 表尾节点
length: 节点数量
level: 最大层级,表头节点层数不计
typedef struct zskiplistNode {
sds ele;
double score;
struct zskiplistNode *backward;
struct zskiplistLevel {
struct zskiplistNode *forward;
unsigned long span;
} level[];
} zskiplistNode;
其中:
ele:节点对象,即节点数据
score:当前节点值对应的分值,用于排序,按分值从小到大来排序,各个节点对象必须是唯一的, 但是多个节点保存的分值却可以是相同的, 分值相同的节点按照成员对象值从小到大排序
backward:当前节点的上一个节点
level:表示层数
forward:同一层的下一个节点
span:跨度,当前节点到forward指向的节点跨越了多少个节点
步骤如下:
1) 找到每一层新节点要插入的位置(update):从高层到低层遍历skiplist,找到每一层小于新节点score的最大的节点,如果节点score相等,则比较ele
2) 随机分配一个层数(level),如果层数比skiplist的层数大,增加skiplist的层数并修改新加层数的span
3) 插入新节点(x):新节点每一层(x->level[i]) 的forward修改为每一层插入位置(update[i]->level[i]) 的forward,每一层插入插入位置的forward修改为新节点(顺序不能反),更新新节点(x),插入位置节点(update[i]->level[i]) 的每一层的span
4) 修改更高的level的span
5) 修改新节点的backward(x->backward)
6) 修改新节点最低层后一个节点(x->level[0].forward)的backward:如果新节点的forward不为null,则新节点后一个节点的backward为新节点;否则新节点是skiplist的尾结点
7) 修改skiplist的节点数量
src/t_zset.c文件对应的源码如下:
/* Insert a new node in the skiplist. Assumes the element does not already
* exist (up to the caller to enforce that). The skiplist takes ownership
* of the passed SDS string 'ele'. */
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)))
{
rank[i] += x->level[i].span;
x = x->level[i].forward;
}
update[i] = x;
}
/* we assume the element is not already inside, since we allow duplicated
* scores, reinserting the same element should never happen since the
* caller of zslInsert() should test in the hash table if the element is
* already inside or not. */
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;
}
步骤如下:
1) 找到每一层要删除节点(x) 的前一个节点(update):从高层到低层遍历skiplist,每一层都找到小于新节点score的最大的节点,如果节点score相等,则比较ele
2) 删除节点,更新每一层要删除节点前一个节点(udpate[i]) 的span和forward
3) 修改要删除节点后一个节点最低层([x->level[0].forward->backward]) 的backward:如果删除节点最低层的forward不为null,则要删除节点后一个节点最低层的backward为要删除节点的backward,否则要删除节点后一个节点是skiplist的尾结点
4) 修改skiplist的层数level
5) 修改skiplist的节点个数
src/t_zset.c文件对应的源码如下:
/* Internal function used by zslDelete, zslDeleteRangeByScore and
* zslDeleteRangeByRank. */
void zslDeleteNode(zskiplist *zsl, zskiplistNode *x, zskiplistNode **update) {
int i;
for (i = 0; i < zsl->level; i++) {
if (update[i]->level[i].forward == x) {
update[i]->level[i].span += x->level[i].span - 1;
update[i]->level[i].forward = x->level[i].forward;
} else {
update[i]->level[i].span -= 1;
}
}
if (x->level[0].forward) {
x->level[0].forward->backward = x->backward;
} else {
zsl->tail = x->backward;
}
while(zsl->level > 1 && zsl->header->level[zsl->level-1].forward == NULL)
zsl->level--;
zsl->length--;
}