跳表--数据结构与算法之美--CH17

1. 什么是跳表

  跳表是为了解决"二分查找无法应用到链表"的问题而产生的。
  跳表是基于有序的链表实现的,为了提高查找效率,提出对链表加多级“索引”的方式,从而形成跳表。因此,跳表是具有多级索引的链表。如下图所示:

  跳表体现了一种以空间换时间的思想,提高了链表的查找效率。

2. 跳表的复杂度分析

2.1 查找的时间复杂度

  首先,分析n个结点的链表,有多少级索引。每2个节点抽取抽出一个节点作为上一级索引的节点,那第1级索引的节点个数大约是 n / 2 n/2 n/2,第2级索引的节点个数大约是 n / 4 n/4 n/4,依次类推,第k级索引的节点个数就是 n / ( 2 k ) n/(2^k) n/(2k)。假设索引有h级别,最高级的索引有2个节点,则有 n / ( 2 h ) = 2 n/(2^h)=2 n/(2h)=2,得出 h = l o g 2 n − 1 h=log_2n-1 h=log2n1,包含原始链表这一层,整个跳表的高度就是 l o g 2 n log_2n log2n
  然后,对于查找需要看每层遍历多少个元素,假设每层遍历m个元素,则最终查找的时间复杂度为 O ( m ∗ l o g 2 n ) O(m*log_2n) O(mlog2n)。对于每2个结点抽取一个索引的跳表,m最多为3,如下图所示。因此可以查找的时间复杂度就为 O ( l o g 2 n ) O(log_2n) O(log2n)

2.2 插入、删除的时间复杂度

  跳表不仅支持高校查找,而且支持高效的插入删除。对于一个单链表而言,插入和删除数据的时间复杂度为 O ( 1 ) O(1) O(1),但是由于要找到插入的位置和删除的元素,因此需要 O ( n ) O(n) O(n)的查找来完成,所以对于链表而言,插入和删除的时间复杂度受限于查找。
  而对于跳表而言,插入、删除主要耗时在查找上,因此总时间复杂度等于查找时间复杂度 O l o g ( n ) + O ( 1 ) = O l o g ( n ) Olog(n)+O(1)=Olog(n) Olog(n)+O(1)=Olog(n)
  这里需要注意,删除的时候需要拿到待删除结点的前驱结点,如果对于单链表,需要一些特殊操作,对于双向链表则不需要考虑这个问题。

2.3 空间复杂度

  如果链表有n个节点,每2个节点抽取抽出一个节点作为上一级索引的节点,那每一级索引的节点数分别为:n/2,n/4,n/8,…,8,4,2,等比数列求和n-2,所以跳表的空间复杂度为O(n)。
  在实际软件开发中,不必太在意索引占用的额外空间。在讲数据结构和算法时,习惯性地把要处理的数据看成整数,但是在实际的软件开发中,原始链表中存储的有可能是很大的对象,而索引结点只需要存储关键值和几个指针,并不需要存储对象,所以当对象比索引结点大很多时,那索引占用的额外空间就可以忽略了。

2.4 空间复杂度优化

  可以考虑,每3个结点抽取一个索引,这样n/3+n/9+n/27+…+9+3+1=n/2,查找效率稍微低了一点 O ( 4 ∗ l o g ( n ) ) O(4*log(n)) O(4log(n)),但是空间占用少了将近一半。

3. 跳表的动态更新

  当不停地往跳表中插入数据时,如果不更新索引,就有可能出现某 2 个索引结点之间数据非常多的情况。极端情况下,跳表还会退化成单链表。如下图所示:

  为了避免这种情况,可以选择插入的同时将这个数据插入到部分索引层。
  如何选择这个索引层呢?可以通过随机函数来决定将这个节点插入到哪几级索引中,比如随机函数生成了值K,那就可以把这个节点添加到第1级到第K级索引中。

4. 跳表小结

  跳表使用空间换时间的设计思路,通过构建多级索引来提高查询的效率,实现了基于链表的“二分查找”。
  跳表是一种动态数据结构,支持快速的插入、删除、查找操作,时间复杂度都是 O(logn), 跳表的空间复杂度是 O(n)。不过,跳表的实现非常灵活,可以通过改变索引构建策略,有效平衡执行效率和内存消耗。
  虽然跳表的代码实现并不简单,但是作为一种动态数据结构,比起红黑树来说,实现要简单多了。所以很多时候,为了代码的简单、易读,比起红黑树,更倾向用跳表。

5. 问题思考

5.1 为什么Redias用跳表来实现有序集合而不是红黑树?

  严格讲,Redis是用跳表和散列表实现的。Redias支持有序集合的核心操作:

  1. 插入
  2. 删除
  3. 查找
  4. 按区间查找
  5. 迭代输出有序序列

Redis用跳表的原因:

  1. 按区间查找,跳表可以做到 O(logn) 的时间复杂度定位区间的起点,然后在原始链表中顺序往后遍历就可以了。红黑树效率没有跳表高,其余都一样。
  2. 跳表更容易代码实现。
  3. 跳表更加灵活,它可以通过改变索引构建策略,有效平衡执行效率和内存消耗。

跳表不能完全替代红黑树:

  1. 因为红黑树较早,很多语言Map就是基于其实现的。
  2. 红黑树有现成实现,而跳表没有,需要自己写。

5.2 如果3个节点抽取,5个几点抽取一个,则时间复杂度为多少?

  都是 O ( l o g n ) O(logn) O(logn),虽然都是 O ( l o g n ) O(logn) O(logn),但是深入分析,其中是有细微差别的,当然无非是时间和空间的相互妥协。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值