redis之跳跃表
来源:互联网 发布:怎么用vs2013写c语言 编辑:程序博客网 时间:2024/04/28 09:57
昨天在阅读redis 设计与实现时,看到了跳跃表这种数据结构,所以今天就把redis源码关于跳跃表实现有序集合的部分读了一遍.
这里是关于有序集合的实现
https://github.com/antirez/redis/blob/unstable/src/t_zset.c
结构体定义在
https://github.com/antirez/redis/blob/unstable/src/server.h
跳跃表可以用来对链式结构进行查找,当可以顺序遍历.他的时间复杂度最低O(2lg(n)),最大O(n),所以,我决定抛弃avi树(偷笑
跳跃表的核心是概率,但是一开始,我看到跳跃表的一张图,我的第一直觉告诉我,该用二分思想,每一层是下一层的1/2,那么他就跟普通的二分非常类似,而且实现也不难,但是发现我错了,因为我想到的是静态的,如果是静态的数据,那么,使用二分当然是可以的,但是如果要支持动态的插入和删除,就要GG了.
要掌握跳跃表,只需要知道他的插入是什么原理的—-概率,实现相比红黑树等要简单得多
这里放上一张盗来的跳跃表的图:
跳跃表有层次性,因为概率的原因,越往上越小.redis里有一个跨度的概念,就是某一层两个相连节点间间隔距离.越上层,跨度就越大(一般而言)
typedef struct zskiplistNode { sds ele; double score; struct zskiplistNode *backward; struct zskiplistLevel { struct zskiplistNode *forward; unsigned int span;//这个就是跨度,主要用来计算这个元素排第几位 } level[];} zskiplistNode;typedef struct zskiplist { struct zskiplistNode *header, *tail; unsigned long length; int level;} zskiplist;
它的结构设计也很有意思,我自刚写的时候,是把所有点分开的,然后就比较麻烦,但是他做了一层封装
,把一列压缩成一个点,既节约了空间,又做到了简化操作.
int zslRandomLevel(void) { int level = 1; while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF)) level += 1; return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;}
这个是他生成列高度的函数,就是概率,证明我没去证过,但是,很显然,越往上越稀疏,这样的结构肯定能优化查找的效率,按照论文说的话,他的时间复杂度是很不错的,当数据越大,随机化的效果越明显,越接近那个值.最重要的一点….实现简单啊
源码中,有两个中间数组起到了很好地作用,一个是update[]数组,一个是rank[]数组,
update记录的是 经过路径(每一层)的最后一个节点的前驱点
rank记录的是经过路径(每一层)的最后一个节点是第几个
这么说不好理解,就上代码
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))) { rank[i] += x->level[i].span; x = x->level[i].forward; } update[i] = x; }
有了这两个数组,插入和删除就简单多了,因为插入删除在链表里不是只和前驱和后继有关吗,问题就解决了,该更新前驱后继的更新前驱后继,该跟新span的就跟新span呗
来一段删除的源码
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--;}
我的博客:flyily.com
- redis之跳跃表
- Redis之跳跃表
- Redis之跳跃表SkipList
- redis之五跳跃表
- redis源码学习之跳跃表
- Redis底层数据结构之跳跃表
- Redis中的跳跃表
- redis 跳跃表
- Redis跳跃表实现
- Redis 跳跃表
- redis(五)跳跃表
- Redis内部数据结构详解之跳跃表(skiplist)
- Redis内部数据结构详解之跳跃表(skiplist)
- Redis 跳跃表的实现
- Redis 的跳跃表实现
- Redis 学习 ---- 5.跳跃表
- Redis-数据结构-4-跳跃表
- redis:跳跃表的实现
- myeclipse的破解问题,呵呵
- 序列号保护
- websocket基础
- 利用 REAgentC 实现快速的系统恢复
- HMM 模型
- redis之跳跃表
- c++
- javaBean
- [C++]using语义使用说明
- 【连载】关系型数据库是如何工作的?(19) - 查询管理器之贪婪算法
- GridView+下拉刷新+上拉加载
- [随笔]示波器"触发",爱它无法自拔
- 第八周项目四(1)-游戏中角色类的增强版
- bzoj 1119: [POI2009]SLO(置换)