//创建一个跳表节点 /* Create a skiplist node with the specified number of levels. * The SDS string 'ele' is referenced by the node after the call. */ zskiplistNode *zslCreateNode(int level, double score, sds ele){ //申请空间,zskiplistLevel这个server.h有声明主要是存的是跨度和后续指针 zskiplistNode *zn = zmalloc(sizeof(*zn)+level*sizeof(struct zskiplistLevel)); zn->score = score; //赋值分数 zn->ele = ele; //赋值元素值 return zn; //返回节点指针(引用) }
//创建一个节点,同时插入一个元素,返回节点信息。要插入的跳表是zsl /* 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){ // update 是插入节点所在位置的前一个节点。我们在学习链表插入的时候,需要找到插入 // 位置的前一个节点。因为在跳表中一个节点是有多个前驱指针的,所以这里需要保存的 // 是多个节点,而不是一个节点 zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x;//x要插入的值,update是插入值的后驱节点数组 unsignedint rank[ZSKIPLIST_MAXLEVEL]; //用于存储排序。后面计算跨度 int i, level;
serverAssert(!isnan(score)); //校验分数信息 x = zsl->header; //拿到头节点信息 // 遍历skiplist 中所有的层,找到数据将要插入的位置,并保存在update 中。遍历zsl for (i = zsl->level-1; i >= 0; i--) { /* store rank that is crossed to reach the */ //每次使用的都是后继值去比较 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;//记录了新数据项的前驱, } //解决重复插入问题,在表里相同分数和相同元素不会存在。下面是翻译 //自从允许重复分数存在,重新插入相同的元素也不会发生。我们假定元素是不存在跳表里,每次插入会调用zsInsert()方法去测试hash表里是否存在这个值 /* 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) {// random level 比原有的zsl->level 大,需要增加skiplist 的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);//根据获取的level创建插入节点 for (i = 0; i < level; i++) { // 新节点项插到update[i] 的后面 x->level[i].forward = update[i]->level[i].forward;//x前驱节点的后继指针,赋予x节点的后继指针 update[i]->level[i].forward = x;//x的前驱节点的后继指针赋值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]);//计算x节点的跨度 update[i]->level[i].span = (rank[0] - rank[i]) + 1;//x的前置节点的跨度+1,插入一个值 } //增加跨度,对于没有更新的几点,也就是影响节点的跨度。也是+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++; // 调整skiplist 的长度 return x; }
//删除一个元素的匹配(分数)节点元素,在跳表上,如果存在就返回要删除的元素节点,否则返回0。通过**node传递删除节点的信息。zsl跳表信息,score和ele是删除元素的信息 /* Delete an element with matching score/element from the skiplist. * The function returns 1 if the node was found and deleted, otherwise * 0 is returned. * * If 'node' is NULL the deleted node is freed by zslFreeNode(), otherwise * it is not freed (but just unlinked) and *node is set to the node pointer, * so that it is possible for the caller to reuse the node (including the * referenced SDS string at node->ele). */ intzslDelete(zskiplist *zsl, double score, sds ele, zskiplistNode **node){ zskiplistNode *update[ZSKIPLIST_MAXLEVEL], *x; //update删除的前驱节点数组,x删除的节点 int i;
x = zsl->header;//拿到头节点信息,开始遍历,根据元素获取前驱节点的信息。 for (i = zsl->level-1; i >= 0; i--) {//找到分数比他大,或者分数相同但是排序在x查找值的后面的节点。跳出循环最后一次的元素就是删除元素前驱节点 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); //删除后释放内存,毕竟没有内存回收,如果是null的话 else *node = x;//将删除的元素信息返回 return1; } return0; /* not found */ }
// x 是需要删除的节点 update 是每一个层x 的后驱数组 /* Internal function used by zslDelete, zslDeleteRangeByScore and * zslDeleteRangeByRank. */ voidzslDeleteNode(zskiplist *zsl, zskiplistNode *x, zskiplistNode **update){ int i; // 调整span 和forward 指针 for (i = 0; i < zsl->level; i++) { if (update[i]->level[i].forward == x) { //如果前驱的后继指针有x,改变指针 update[i]->level[i].span += x->level[i].span - 1;//跨度减一 update[i]->level[i].forward = x->level[i].forward;//将x的前驱后继指针,赋值x节点的后继指针。 } 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--;//长度减一 }
//生成一个随机的层次节点,对于一个新的元素节点。它返回额是1与最大层级的中间值、类似于幂律分布(幂律分布我也不太懂)。 /* Returns a random level for the new skiplist node we are going to create. * The return value of this function is between 1 and ZSKIPLIST_MAXLEVEL * (both inclusive), with a powerlaw-alike distribution where higher * levels are less likely to be returned. */ intzslRandomLevel(void){ int level = 1; while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF)) level += 1; return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL; }