前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅析skiplist(跳表)

浅析skiplist(跳表)

作者头像
山行AI
发布2019-06-28 14:34:21
2.4K0
发布2019-06-28 14:34:21
举报
文章被收录于专栏:山行AI山行AI

skiplist(跳表)的应用场景

1. 几种常用的数据结构

在学习跳表之前,我们先来比较下数组、链表、二叉查找树、平衡二叉树的一些比较:

  • 数组:支持随机访问,在有序情况下可以采用二分查找(binarySearch)来快速查找。缺点也很明显:在有序数组中插入和删除数据时,为了保持元素的有序性,需要进行大量的数据移动操作。
  • 链表:维护前后节点的指针,有序情况下,插入和删除数据操作比较简单。缺点就是没有办法快速查找,二分查找并不适用。
  • 二叉查找树:支持高效的二分查找,也能快速地进行插入和删除操作。缺点是在某些极端情况下,二叉查找树可能变成一个线性链表。
  • 平衡二叉树:对二叉树的缺点进行了改进,引进了平衡的概念。根据平衡算法的不同,具体实现有AVL树/B树(B-Tree)/B+树(B+Tree)/红黑树等。但是平衡二叉树实现起来比较复杂,较难理解。

2. 跳表

1. 结构

跳表由William Pugh 1989年发明。他在论文《Skip lists: a probabilistic alternative to balanced trees》中详细介绍了跳表的数据结构和插入删除等操作。论文是这么介绍跳表的:

代码语言:javascript
复制
Skip lists are a data structure that can be used in place of balanced trees.Skip lists use probabilistic balancing rather than strictly enforced balancing and as a result the algorithms for insertion and deletion in skip lists are much simpler and significantly faster than equivalent algorithms for balanced trees.

引用自:https://en.wikipedia.org/wiki/Skip_list

  • 跳表是一个“概率型”的数据结构,在很多应用场景中可以替代平衡树。与平衡树相比,有相似的渐进期望时间边界,但是它更快,更简单也更省空间。
  • 是一个分层结构的多级链表,最下层链表包括所有数据,每个层级都是下一层级的索引,是一个用空间换时间的方案:

2. 复杂度

  • 红黑树:插入、查找为O(logn),但常数项较小;无锁实现的复杂性很高,一般需要加锁;数据天然有序。
  • SkipList:插入、查找为O(logn),但常数项比红黑树要大;底层结构为链表,可无锁实现;数据天然有序。

skiplist性能:

场景

平均值

最坏情况

查找

O(logn)

O(n)

插入

O(logn)

O(n)

删除

O(logn)

O(n)

目前redis的zset和levelDB存储结构采用的就是skiplist。

3. 跳表特性

一个普通的有序链表:

如果从上面的列表中查找23需要遍历4次,查找59时需要遍历6次。而对这个链表,我们没法使用二分查找。

于是我们对数据节点加上一级索引如下图:

这样通过一级索引去找就相对来说快上很多。

还可以加上二级索引,如下图:

跳表结构:

  • 跳跃表的层数,我们称之为维度,从上到下,我们称之为降维,它由很多个维度维成。
  • 每一层都是一个有序的链表。
  • 每一层中相同的元素,我们称为“同位素”。
  • Skip List主要思想是将链表与二分查找相结合,它维护了一个多层级的链表结构(用空间换取时间),可以把Skip List看作一个含有多个行的链表集合,每一行就是一条链表,这样的一行链表被称为一层,每一层都是下一层的"快速通道",即如果x层和y层都含有元素a,那么x层的a会与y层的a相互连接(垂直)。最底层的链表是含有所有节点的普通序列,而越接近顶层的链表,含有的节点则越少。
  • 最底层的链表,即包含了所有元素节点的链表是L1层,或称基础层。除此以外的所有链表层都称为跳跃层。
  • 基础层包括所有的元素。
  • 对一个目标元素的搜索会从顶层链表的头部元素开始,然后遍历该链表,直到找到元素大于或等于目标元素的节点,如果当前元素正好等于目标,那么就直接返回它。如果当前元素小于目标元素,那么就垂直下降到下一层继续搜索,如果当前元素大于目标或到达链表尾部,则移动到前一个节点的位置,然后垂直下降到下一层。
  • Skip List还有一个明显的特征,即它是一个不准确的概率性结构,这是因为Skip List在决定是否将节点冗余复制到上一层的时候(而在到达或超过顶层时,需要构建新的顶层)依赖于一个概率函数,举个栗子,我们使用一个最简单的概率函数:丢硬币,即概率P为0.5,那么依赖于该概率函数实现的Skip List会不断地"丢硬币",如果硬币为正面就将节点复制到上一层,直到硬币为反。
  • 相比于二叉查找树,跳表维持结构平衡的成本比较低,完全靠随机。而二叉查找树需要Rebalance来重新调整平衡的结构。
  • 删除时自上而下,查找第一次出现节点的索引,并逐层找到每一层对应的节点,删除每一层查找到的节点,如果该层只剩下1个节点,删除整个一层(原链表除外)。

4. java实现

存储结构一般为:

jdk中提供的实现主要有:ConcurrentSkipListMap与ConcurrentSkipListSet,其中后者是基于前者实现的。关于这两个的源码分析,请关注之后的文章。

5. 参考

https://en.wikipedia.org/wiki/Skip_list

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-06-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开发架构二三事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • skiplist(跳表)的应用场景
    • 1. 几种常用的数据结构
      • 2. 跳表
        • 1. 结构
        • 2. 复杂度
      • 3. 跳表特性
        • 4. java实现
          • 5. 参考
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档