跳跃表(Skip List)是一种 基于链表的数据结构,用于加速查找、插入、删除等操作。它通过 多级链表 来实现快速的元素查找,具备类似于平衡树的特性,但实现方式更简单。跳跃表常用于数据库、搜索引擎(如 Elasticsearch、Lucene 等)中的倒排索引,以加速数据访问。
1. 跳跃表的基本结构
跳跃表可以看作是在普通 有序链表 上附加了 多级索引层。普通的有序链表查找某个元素时,需要从头遍历,时间复杂度为 O(n),而跳跃表通过增加索引层,可以使查找操作的时间复杂度接近 O(logn)。
跳跃表结构的组成:
- 底层链表(Level 0):这是一个普通的 升序链表,包含了所有元素。跳跃表的其他层是为这条链表加速的辅助层。
- 上层索引链表(Level 1, Level 2, …):上层链表由部分元素组成,每一层的元素是下一层链表的 子集。上层链表的长度通常比下一层链表短,通过在上层进行大步跳跃,可以加速到目标元素的查找。
举个例子:
假设我们有一个跳跃表,它包含以下元素:3、6、7、9、12、17、19、21、25、26、30、35。
跳跃表的层级可以是:
Level 2: 3 12 25
Level 1: 3 7 12 19 25 30
Level 0: 3 6 7 9 12 17 19 21 25 26 30 35
- Level 0:是完整的链表,包含所有元素,查找复杂度为 O(n)O(n)O(n)。
- Level 1:每隔几个元素选取一个节点,用于快速跳跃。
- Level 2:包含更少的节点,以更大步长进行跳跃。
2. 跳跃表的查找过程
跳跃表的查找过程类似于在层次结构中“跳跃”查找,具体步骤如下:
- 从最高层(Level 2)开始:跳跃表从最高层开始查找。因为最高层的元素较少,查找可以大步向前跳过多个元素。
- 向前跳跃,直到到达不大于目标的最大值:例如要查找
19
,从 Level 2 的3 -> 12
继续向前跳跃,直到25
,发现25
大于19
,则退回到12
。 - 逐层向下,缩小范围:在
12
之后,切换到下一层(Level 1),从12 -> 19
继续前进,找到了目标元素。 - 如果需要更精细的跳跃,继续到最低层:如果在上一层没有找到目标,切换到最底层(Level 0),从当前位置继续向前,最终找到目标元素。
查找示例:
- 查找目标
19
:
- 在 Level 2,从
3 -> 12
,再到25
,发现19
在12
和25
之间,因此向下切换到 Level 1。 - 在 Level 1,从
12 -> 19
,找到目标元素。
这种跳跃查找的过程将单层链表的线性查找时间从 O(n) 优化到 对数级别 O(logn),因为每一层的链表长度按指数级缩短。
3. 跳跃表的构建和插入
- 构建:跳跃表的构建是通过从底层链表开始,按一定概率(如 1/2 或 1/4)随机将元素提升到上层索引中。通常的做法是每个元素以 50% 的概率出现在上一层,从而形成多级跳跃表。
- 插入:插入一个新元素时,跳跃表会决定该元素需要出现在的层数,并按照该层数将其插入各层的对应位置。插入时也从上层往下查找合适的位置,并维护每层链表的有序性。
插入示例:
假设要在上面示例的跳跃表中插入元素 8
。
- 从最高层 Level 2 开始,
3 -> 12
,然后切换到 Level 1。 - 在 Level 1,
3 -> 7 -> 12
,在7
和12
之间找到合适的插入位置,切换到 Level 0。 - 在 Level 0,
7 -> 9
,最终在7
和9
之间插入8
。 - 然后按概率决定是否在上层插入
8
。
4. 跳跃表的删除
删除操作与查找类似:
- 从上到下查找要删除的元素:先从最高层开始查找目标元素的位置,一旦找到元素,删除它,并向下层进行检查,直到底层链表,确保在所有层级都删除目标元素。
- 删除后维护链表结构:确保每一层依旧是有序链表。
5. 跳跃表的性能分析
- 时间复杂度:跳跃表的查询、插入、删除操作的平均时间复杂度为 O(logn),因为每一层索引链表的长度是指数级减少的,从而减少了需要遍历的元素个数。最坏情况下时间复杂度为 O(n)(如果只有一层,退化为普通链表)。
- 空间复杂度:跳跃表的空间复杂度为 O(n),虽然有多层链表,但每一层的元素个数是底层链表的一部分,因此总体空间占用是线性增长的。
6. 跳跃表与其他数据结构的比较
- 跳跃表 vs. 平衡二叉树(如 AVL 树、红黑树):
- 跳跃表和平衡二叉树的查找、插入、删除的时间复杂度都是 O(logn)。
- 跳跃表的实现更简单:相较于平衡树复杂的旋转操作,跳跃表通过随机层级的方式实现平衡。
- 空间消耗较高:跳跃表占用了额外的空间存储多层链表,而平衡树只需存储树节点。
- 跳跃表的随机性:跳跃表基于随机数来决定元素所在的层级,而平衡树有严格的平衡条件,需要频繁的旋转调整。
7. 跳跃表在 Elasticsearch 和 Lucene 中的应用
在 Elasticsearch 和 Lucene 中,跳跃表被用于 加速倒排索引的查询,特别是在面对长倒排列表时。每个词对应的文档列表可能非常长,为了避免逐个文档 ID 进行遍历,Lucene 会为每个倒排列表构建跳跃表。
- 快速跳跃:当执行词项查询时,Lucene 通过跳跃表跳过无关的文档 ID,从而快速定位相关文档。
- 效率提升:跳跃表帮助 Lucene 在处理长倒排列表时依然保持高效,尤其是在处理大规模数据时,跳跃表能够大幅减少查询的计算量。
总结
跳跃表是一种高效的、基于链表的数据结构,通过多级索引层实现了快速查找操作。它以简单的随机算法代替了复杂的平衡树结构,提供了接近 O(logn)的查找性能,并且在数据库、全文搜索等场景中有广泛应用。特别是在倒排索引中,跳跃表的跳跃查找机制可以加速大规模数据的查询操作。