跳表概念:
跳表是一个有序的数据结构,它通过内部节点维护的一个指向其他节点的指针,从而达到快速访问的目的。
跳表支持平均时间复杂度为o(logn)的增删查改,最坏的时间复杂度为o(N)
跳表和平衡二叉树的区别:
1. 跳表实现步骤相对平衡二叉树来说相对简单一些。
2.跳表和二叉树都是有序存储的数据结构,他们的查找时间复杂度都是logN。
3.平衡二叉树每次增删节点都需要检查是否需要reblance从而使树达到平衡,而跳表在增删的时候只需要实现局部节点的指针的变化,也就是只需要改变相邻节点的指针。
4. 跳表在区间查询上要好于平衡二叉树,只需要找到起始的区间节点然后向后遍历即可,而平衡二叉树则比跳跃链表操作复杂,如果没有parent节点的话要一个一个找到。
Redis 选择 跳表的原因:
1.区间查找redis 的操作要由于 平衡树。
2.跳表实现相对于二叉树比较简单
3.高并发情况下,红黑树的reblance可能涉及到整棵树,但是跳跃链表只有局部
edis.h/zskiplistNode
typedef struct zskiplistNode {
// 层
struct zskiplistLevel{
// 前进指针
struct zskiplistNode *forward;
// 跨度
unsigned int span;
} level[];
// 后退指针
struct zskiplistNode *backward;
// 分值
double score;
// 成员对象
robj *obj;
} zskiplistNode;
level:level 数组可以包含多个元素,每个元素都包含一个指向其他节点的指针。程序可以通过这些层来加速访问其他节点。
redis.h/zskiplist
typedef struct zskiplist {
// 表头节点和表尾节点
structz skiplistNode *header,*tail;
// 表中节点数量,不包括头结点
unsigned long length;
// 表中层数最大的节点的层数,不包括头结点
int level;
} zskiplist;
header, 指向跳跃表的头结点
tail, 指向跳跃表的尾节点
length, 记录跳跃表中节点的数量,不包含头节点
level, 记录跳跃表中节点的最高层数,不计算头结点层数
增删查改的代码实现着重依赖于找到即将插入节点的路径 这个路径保存在一个零时的update[]数组里面,它记录了即将要插入的节点的previous的指针节点, 数组大小就是level大小,也就是随机出来的当前插入节点最高多少层。 redis中跳表层数的大小随机规则是 最下面的层的分配概率是50% 每上升一层概率减少50%,也就是第二层25%,第三层12.5%...依次次类推。
跳表中还有个属性是 在每一个 leve 数据结构中(也就是包含指向下一个节点的数据结构)有一个属性是span 通过这个属性 可以获取到 当前节点的排名, span储存了从当前节点到下一个节点中间有多少节点,这样要计算排名的话把span进行累加就可以了。