跳表介绍
跳表是一种可以提高查找效率的数据结构,
如上图,跳表的精髓就是跳
,怎么跳的呢?答曰分层
。越高层元素数量越少,也就是跳
的就越远。
跳表里的节点是按照score
有序排列的。当查找一个元素的时候,步骤大致如下:
- 从顶层第一个元素开始往后跳
- 当前元素小于被查找元素,就往后跳一个,
- 当前元素大于被查找元素,就返回跳之前的节点,往下走一层
- 重复步骤2和3,直到当前节点的
score
等于被查找元素 可以看出,这种一层一层跳着搜索的速度明显快于逐个节点搜索
跳表知识点总结
讲redis跳表的文章很多,推荐看这篇,而且这些文章说的很详细了,在这里就对知识点做个总结好了
数据结构代码
typedef struct zskiplistNode {
sds ele;
double score; // 分值
struct zskiplistNode *backward; // 后退指针
// 层
struct zskiplistLevel {
struct zskiplistNode *forward; // 前进指针
unsigned long span; // 跨度
} level[]; // level[]数组的大小就叫做该节点层的`高度`.表明从下往上多少层中存在该节点
} zskiplistNode;
typedef struct zskiplist {
struct zskiplistNode *header, *tail;
unsigned long length; // 节点数
int level; // 最高层数
} zskiplist;
Q&A
为什么redis用跳表而不是红黑树
范围查找简单
: 在skiplist上进行范围查找简单,只需要在找到小值之后,对第1层链表进行若干步的遍历就可以实现。插入删除简单
: 平衡树的插入和删除操作可能引发子树的调整,逻辑复杂,而skiplist的插入和删除只需要修改相邻节点的指针,操作简单又快速。难度低
: 从算法实现难度上来比较,skiplist比平衡树要简单得多。
节点的成员span有什么作用?
- 层的跨度(
level[i].span
属性)用于记录两个节点之间的距离 - 跨度是用来
计算排位
(rank)的: 在查找某个节点的过程中, 将沿途访问过的所有层的跨度累计起来, 得到的结果就是目标节点在跳跃表中的排位。
节点的后退指针有什么用?
- 后退指针在程序从表尾向表头遍历时使用。
redis新插入节点时怎么确定有几层?
- 随机出一个层数(level) 。比如,一个节点随机出的层数是 3,那么就把它链入到第 1 层到第 3 层这三层链表中。
- 随机符合
幂律分布
,层数低的可能性高