跳表,是redis中的一中链表数据结构,能够提高链表的查询效率。使用跳表,可以避免遍历一些不必要的节点,从而提高了查询,删除,插入的效率。
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;//跳表
2,跳表基本操作
2.1,创建跳表
//创建跳表, 主要创建一个结构体对象并赋初始值
zskiplist *zslCreate(void) {
int j;
zskiplist *zsl;
zsl = zmalloc(sizeof(*zsl));
zsl->level = 1;
zsl->length = 0;
zsl->header = zslCreateNode(ZSKIPLIST_MAXLEVEL,0,NULL);
for (j = 0; j < ZSKIPLIST_MAXLEVEL; j++) {
zsl->header->level[j].forward = NULL;
zsl->header->level[j].span = 0;
}
zsl->header->backward = NULL;
zsl->tail = NULL;
return zsl;
}
//创建节点,创建节点对象,并赋值
zskiplistNode *zslCreateNode(int level, double score, sds ele) {
zskiplistNode *zn =
zmalloc(sizeof(*zn)+level*sizeof(struct zskiplistLevel));
zn->score = score;
zn->ele = ele;
return zn;
}
2.2,跳表释放和节点释放
//释放跳表的节点
void zslFreeNode(zskiplistNode *node) {
sdsfree(node->ele);
zfree(node);
}
/* 释放整个跳表 */
void zslFree(zskiplist *zsl) {
zskiplistNode *node = zsl->header->level[0].forward, *next;
zfree(zsl->header);
while(node) {
next = node->level[0].forward;
zslFreeNode(node);
node = next;
}
zfree(zsl);
}
2.3,插入节点
//插入一个节点
zskiplistNode *zslInsert(zskiplist *zsl, double score, sds ele) {
zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;//update数组会记录每一层最靠近新插入节点的后继节点
unsigned int rank[ZSKIPLIST_MAXLEVEL];//rank数组 最靠近新插入节点的后继节点索引。
int i, level;
serverAssert(!isnan(score));
x = zsl->header;
// 遍历查找插入的位置,查找每一层插入的位置
for (i = zsl->level-1; i >= 0; i--) {
/* */
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)))
{
//如果判定x节点可以前进到前一个节点,则把当前节点的跨度加到索引值上,
//并将x对应层级指向下一个节点。
rank[i] += x->level[i].span;//加每一层的跳跃范围
x = x->level[i].forward;//
}
update[i] = x;
}
/* 随机生成一个跳表层级,插入之前我们应该检测下当前节点是否在hash表中存在,防止重复插入*/
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;
}
/* 新加的层高,不需要插入,只需要修改跨度值 */
for (i = level; i < zsl->level; i++) {
update[i]->level[i].span++;
}
//第0层特化处理前向指针
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;
}
2.4,层高随机生成算法
/* Skiplist P = 1/4
* while 循环中
* 循环为真的概率为 P(1/4)
* 循环为假的概率为 (1 - P)
*
* 层高为1 的概率为 (1 -P)
* 层高为2 的概率为 P *(1 -P)
* 层高为3 的概率为 P^2 *(1 -P)
* 层高为4 的概率为 P^3 *(1 -P)
* 层高为n 的概率为 P^(n-1) *(1 -P)
*
* 因此平均层高为
* E = 1*( 1-P) + 2*P( 1-P) + 3*(P^2) ( 1-P) + ...
* = (1-P) ∑ +∞ iP^(i-1)
* i=1
*
* =1/(1-P)
*
* */
int zslRandomLevel(void) {
int level = 1;
while ((random() & 0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
level += 1;
return (level < ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}
2.5,删除节点
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;
}
}
//第0层特化处理
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--;
}
2.6,删除跳表中的某个元素
int zslDelete(zskiplist *zsl, double score, sds ele, zskiplistNode **node) {
zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
int i;
x = zsl->header;
for (i = zsl->level-1; i >= 0; i--) {//找到删除的位置
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)))
{
x = x->level[i].forward;
}
update[i] = x;
}
/* We may have multiple elements with the same score, what we need
* is to find the element with both the right score and object. */
x = x->level[0].forward;
if (x && score == x->score && sdscmp(x->ele,ele) == 0) {
zslDeleteNode(zsl, x, update);//删除节点
if (!node)
zslFreeNode(x);
else
*node = x;
return 1;
}
return 0; /* not found */
}
2.7,释放链表指针
//释放节点
void zslFreeNode(zskiplistNode *node) {
sdsfree(node->ele);
zfree(node);
}
/* 释放整个链表 */
void zslFree(zskiplist *zsl) {
zskiplistNode *node = zsl->header->level[0].forward, *next;
zfree(zsl->header);
while(node) {
next = node->level[0].forward;
zslFreeNode(node);
node = next;
}
zfree(zsl);
}
2.8,修改score
涉及因为修改score导致的节点删除和节点新增
zskiplistNode *zslUpdateScore(zskiplist *zsl, double curscore, sds ele, double newscore) {
zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;
int i;
/* We need to seek to element to update to start: this is useful anyway,
* we'll have to update or remove it. */
//查找到需要更新的元素
x = zsl->header;
for (i = zsl->level-1; i >= 0; i--) {
while (x->level[i].forward &&
(x->level[i].forward->score < curscore ||
(x->level[i].forward->score == curscore &&
sdscmp(x->level[i].forward->ele,ele) < 0)))
{
x = x->level[i].forward;
}
update[i] = x;//找到每层需要更新的元素X
}
/* Jump to our element: note that this function assumes that the
* element with the matching score exists. */
x = x->level[0].forward;
serverAssert(x && curscore == x->score && sdscmp(x->ele,ele) == 0);
/* If the node, after the score update, would be still exactly
* at the same position, we can just update the score without
* actually removing and re-inserting the element in the skiplist. */
//如果修改的元素,位置不变,我们就不需要进行删除和插入操作,直接更新值即可
if ((x->backward == NULL || x->backward->score < newscore) &&
(x->level[0].forward == NULL || x->level[0].forward->score > newscore))
{
x->score = newscore;
return x;
}
/* No way to reuse the old node: we need to remove and insert a new
* one at a different place. */
zslDeleteNode(zsl, x, update); //删除就节点
zskiplistNode *newnode = zslInsert(zsl,newscore,x->ele);//插入新节点
/* We reused the old node x->ele SDS string, free the node now
* since zslInsert created a new one. */
x->ele = NULL;
zslFreeNode(x);
return newnode;
}