ZSkipList - 跳表
ZSkipList 在redis中的使用场景只有一个,那就是作为Zset的使用。ZSkipList的性能可以保证在查找,删除,添加等操作的时候在对数期望时间内完成,这个性能是可以和平衡树相比较的,而且在实现方面比平衡树要优雅。缺点是需要的存储空间较大,属于利用空间来换取时间的数据结构。
ZSkipList结构定义
/* ZSETs use a specialized version of Skiplists */
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;
- ele 持有数据
- score 节点数据对应的分数,节点之间凭分数升序排列
- backward 指针,指向节点的前一个紧邻节点
- level 用于记录所有节点(除头节点外);每个节点中最多持有32个zskiplistLevel结构,实际数量在节点创建时,按幂次定律随机生成,每个zskiplistLevel中有两个字段
- forward 指向比自己分数高的某个节点(不一定是紧邻的),并且,若当前zskiplistLevel 实例在 level[] 中的索引为x, 则其forward指向的节点,其level[] 字段的容量至少是x+1
- span 代表forward字段指向的节点,距离当前节点的距离,紧邻的两个节点之间的距离定义为1
ZSkipList 与平衡树、哈希表的比较
ZSkipList 和各种平衡树(如AVL,红黑树等)的元素是有序排列的,而哈希表不是有序的。因此哈希表不适合做范围查找。
在做范围查找时,平衡树比ZSkipList操作复杂。平衡树上,找到小值后,还需要以中序遍历的顺序继续寻找其他不超过大值的节点。如果不对平衡树进行一定的改造,这里的中序遍历并不容易实现。而ZSkipList只需要找到小值后,对第一层链表进行若干步的遍历就可以实现
平衡树的插入和删除可能引发子树的调整,逻辑复杂,而ZSkipList的插入和删除只需要修改相邻节点的指针,操作简单又快速
从内存占用来说,ZSkipList比平衡树更灵活
查找单个key,ZSkipList和平衡树的时间复杂度都为O(log n),大体相当
从算法实现难度来比较,ZSkipList比平衡树要简单
浙公网安备 33010602011771号