Redis数据结构之跳跃表

跳跃表是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。

跳跃表支持平均 0(logn)、最坏 O(n)的复杂度的节点查找,还可以通过顺序性操作来批量处理节点。

在大部分情况下,跳跃表的效率可以和平衡树相媲美,井且因为跳跃表的实现比平衡树要来得更为简单,所以有不少程序都使用跳跃表来代替平衡树。

一个有序集合包含的元素数量比较多,或者有序集合中元素的成员是比较长的字符串时,Redis 就会使用跳跃表来作为有序集合键的底层实现。

数据结构

typedef struct zskiplistNode {
    //成员对象
    sds ele;
    //分值,节点按各自所保存的分值从小到大排列
    double score;
    //后退指针在程序从表尾向表头遍历时使用
    struct zskiplistNode *backward;
    //层数组
    struct zskiplistLevel {
        //前进指针
        struct zskiplistNode *forward;
        //跨度
        unsigned long span;
    } level[];
} zskiplistNode;

typedef struct zskiplist {
    //head 指向跳跃表的表头节点,tail 指向跳跃表的表头节点
    struct zskiplistNode *header, *tail;
    //记录跳跃表的长度,即跳跃表目前包含节点的数量,不包含头结点
    unsigned long length;
    //记录目前跳跃表内,层数最大的那个节点的层数
    int level;
} zskiplist;

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA54ix5Zyo5YeM5pmo,size_20,color_FFFFFF,t_70,g_se,x_16

 跳跃表增加节点与删除节点

//随机生成层高度,越大的level概率越小
int zslRandomLevel(void) {
    int level = 1;
    while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
        level += 1;
    return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}

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--) {
        //寻找每一层中,分值小于score,或分值相等且节点的ele小于ele,最靠右的节点                
        //update[i] 记录第i层,最靠右节点
        //rand[i] 记录第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)))
        {
            rank[i] += x->level[i].span;
            x = x->level[i].forward;
        }
        update[i] = x;
    }
    //随机产生level
    level = zslRandomLevel();
    //产生的level比当前的最大的level大
    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++) {
        //将第i层小于等于要插入数据节点的前进指针 赋值给 新插入节点的前进指针
        x->level[i].forward = update[i]->level[i].forward;
        //将第i层小于等于要插入数据节点的前进指针 设置为 新插入节点
        update[i]->level[i].forward = x;
        //更新span
        x->level[i].span = update[i]->level[i].span - (rank[0] - rank[i]);        
        update[i]->level[i].span = (rank[0] - rank[i]) + 1;
    }
    //更新未达到节点span,此时update[i] 是 zsl->header, 添加了一个新节点,所以加1
    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;
}
//删除节点
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--;
}

//删除score,ele 对应的节点
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--) {
        //寻找每一层中,分值小于score,或分值相等且节点的ele小于ele,最靠右的节点        
        //update[i] 记录第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;
    }
        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 */
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值