前言
跳跃表(skiplist)是一种有序的数据结构,是在有序链表
的基础上进行了扩展,解决了有序链表查找某个值效率问题。跳跃表支持平均O(logn)
、最坏O(n)复杂度的节点查找,大部分情况效率可以和平衡树相媲美,且实现更简单。Redis使用跳跃表作为有序集合键
的底层实现之一。
基本介绍
跳跃表作为有序集合的底层实现之一,我们先来看有序集合的简单用法。
> zadd fruit 10 banana
(integer) 1
> zadd fruit 12 apple
(integer) 1
> zadd fruit 8 cherry
(integer) 1
> zrange fruit 0 -1 #按 score 有序取出
1) "cherry"
2) "banana"
3) "apple"
原理
有序链表的查找
如下面的有序链表
从该有序表中搜索元素 < 5, 23, 40 > ,需要比较的次数分别为 < 2, 4, 6 >,总共比较的次数为 2 + 4 + 6 = 12 次。
那有没有优化的算法呢?
考虑链表是有序的,但不能使用二分查找。类似二叉搜索树,我们把一些节点提取出来,作为索引。得到如下结构:
这里我们把 < 4, 8, 34, 45 > 提取出来作为一级索引,这样搜索的时候就可以减少比较次数了。
我们还可以再从一级索引提取一些元素出来,作为二级索引,变成如下结构:
由于类似二分查找,效率得到提高,查找平均复杂度可以达到O(logn)
跳跃表实现
结构图如下
位于图片最左边的是zskiplist结构,该结构包含以下属性:
1) header:指向跳跃表的表头节点
2) tail:指向跳跃表的表尾节点
3) level:记录目前跳跃表内,层数最大的那个节点的层数(表头节点的层数不计算在内)
4) length:记录跳跃表的长度,即跳跃表中包含的节点数量(表头节点不计算在内)
层(level):图中用L1、L2标记的是节点的层,每一层相当于一个有序链表。每个层都带有2个属性:前进指针和跨度。
前进指针:用于访问位于表尾方向的其他节点。
跨度:记录了前进指针指向的节点与当前节点的距离,即中间隔着多少个节点。
后退指针:节点中用BW标记的即为后退指针,它指向位于当前节点的前一个节点。用于在程序从表尾向表头遍历时使用。
分值(score):各节点中的1.0、2.0和3.0是节点所保存的分值。在跳跃表中节点按照分值从小到大排序。
成员对象(obj):各个节点中的o1、o2和o3是节点所保存的成员对象。
查找
如上图中
查找节点o3
从L5层经过一次查找就找到o3
查找节点o2
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。