在接下来的系列文章中,我们将介绍一系列应用于存储以及IO子系统的数据结构。这些数据结构相互关联又有着巨大的区别,希望我们能够不辱使命的将他们分门别类的介绍清楚。本文为第一节,介绍一个简单而又有用的数据结构:跳表 (SkipList)


在对跳表进行讨论之前,我们首先描述一下跳表的核心思想。

跳表(Skip List)是有序线性链表的一种。通常对线性链表进行查找需要遍历,因而不能很好的使用二分查找这样快速的方法(想像一下在链表中定位中间元素的复杂度)。为了提高查找速率,我们可以将这些线性链表打散,组织成树结构,这样的树就叫做查找树。查找树中尤以平衡查找树的查找代价最小,因此平衡二叉查找树成为了在内存中进行查找的最佳的数据结构。红黑树作为平衡二叉查找树的一种实现,经常被我们用在这种场景之中。

然而,平衡树就一定需要平衡化。在对树进行一系列的插入删除之后,树不再平衡了,此时就需要调整平衡的算法。红黑树的复杂性就体现在这里,相信所有写过红黑树的童鞋都记忆犹新。

就在此时,晴天一个霹雳,跳表诞生了[1]。它支持快速检索且不需要复杂的平衡操作,由于它的简单性,使得它往往比红黑树还有更好的性能表现。

上图就是一个跳表的示意图,图中 (a) 是一个正常的有序列表,而 (b) 到 (e) 则是不同level的跳表。

定义:

对于一个节点,如果它有k个指针,那么就称之为一个 level k 节点。

一个跳表的最大 level 是当前在列表中最大的节点的 level。对于空列表, level为1.

推论:

如果每第 (2i) 个节点有一个指针指向向后数 (2i) 节点,而不是指向紧接着的那个节点,那么所有的节点基本上会满足这样一个分布:50%的节点位于level 1,25%的节点位于level 2,12.5%的节点位于level 3 等等。

如果某个节点的level是随机选择的,且随机所遵守的分布符合上个推论所描述,那么 level k 节点的第 i 个指针只需要指向下一个 level 大于等于 i 的节点就构造如上图所示的跳表,并不需要一定要严格指向第 ii-1 个节点。

算法:

1> 初始化

只创建第一个列表,该列表的leve值就是1。

2> 检索

a) 搜索从最大level的列表开始,找到比要检索的key小的最大的那个元素

b) 如果不能找到,减少一个level继续检索

c) 如果在level 1一层也不能找到,那就说明所要找的元素一定在当前停下的位置的下一个节点

Search(list, key)
{
x = list->header
for (i = list.level; i >= 1; i--)
{
while (x->forward[i]->key < key)
x = x->forward[i]
}
x = x->forward[1]
if (x->key == key) return x->vlaue
else return failure
}

3> 插入

a) 检索要插入的key是否存在,如果存在那么更新value即可

b) 如果不存在,则需要插入。需要注意的是,在检索的过程中,我们应当用一个数组 update[1...MaxLevel] 来保存每一个 level 中搜索停止的节点,这些节点用于后来指向新加入的节点

c) 产生一个新节点,随机的给定该节点的level。如果随机出来的level比当前最大的level还要大,那么扩大 update 数组到新的 MaxLevel 个元素,并且将新增的 update 元素设置为 list 的 header

d) 从 1 开始到 MaxLevel,将新节点的 forward 数据设置为 update[i...MaxLevel] 的值;而将 update[i...MaxLevel] 的 forward[i] 设置为 x.

 insert(list, key, value){
local update[...MaxLevel]
x = list->header
for i = list->level downto do
while x->forward[i]->key < key
x = x->forward[i]
update[i] = x
if x->key == key then x->value = value;
else
lvl = randomLevel()
if lvl > list->level then
for i = list->level + to lvl
update[i] = list->header
list->level = lvl
x = MakeNode(lvl, key, value)
for i = to level
x->forward[i] = update[i]->forward[i];
update[i]->forward[i] = x;
}

> 选择新节点的level

新节点的level完全由一个随机函数产生:

 randomLevel()
lvl =
while random() < p and lvl < MaxLevel do
lvl = lvl +
return lvl

复杂度分析

搜索复杂度的分析方法很简单,结论是非常确定的O(logn),那剩下的比较就是常数项了,如下图所示:

可以看出来,跳表还是有很大的常数项优势的。

-- Reference --

[1] William Pugh, 1990, Communications of the ACM 33.6, Skip Lists: A Probabilistic Alternative to Balanced Trees.

存储系统的基本数据结构之一: 跳表 (SkipList)的更多相关文章

  1. 跳表SkipList

    原文:http://www.cnblogs.com/xuqiang/archive/2011/05/22/2053516.html 跳表SkipList   1.聊一聊跳表作者的其人其事 2. 言归正 ...

  2. 跳表 SkipList

    跳表是平衡树的一种替代的数据结构,和红黑树不同,跳表对树的平衡的实现是基于一种随机化的算法,这样就使得跳表的插入和删除的工作比较简单.     跳表是一种复杂的链表,在简单链表的节点信息之上又增加了额 ...

  3. 3.3.7 跳表 SkipList

    一.前言 concurrentHashMap与ConcurrentSkipListMap性能测试 在4线程1.6万数据的条件下,ConcurrentHashMap 存取速度是ConcurrentSki ...

  4. 跳表(SkipList)原理篇

    1.什么是跳表? 维基百科:跳表是一种数据结构.它使得包含n个元素的有序序列的查找和插入操作的平均时间复杂度都是 O(logn),优于数组的 O(n)复杂度.快速的查询效果是通过维护一个多层次的链表实 ...

  5. 跳表(SkipList)设计与实现(Java)

    微信搜一搜「bigsai」关注这个有趣的程序员 文章已收录在 我的Github bigsai-algorithm 欢迎star 前言 跳表是面试常问的一种数据结构,它在很多中间件和语言中得到应用,我们 ...

  6. redis的zset数据结构:跳表

    点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人. 文章不定期同步公众号,还有各种一线大厂面试原题.我的学习系列笔记. 广州这边封闭式管理好久了,今天终于周末可以出去溜溜了 什么是zset z ...

  7. [转载] 跳表SkipList

    原文: http://www.cnblogs.com/xuqiang/archive/2011/05/22/2053516.html leveldb中memtable的思想本质上是一个skiplist ...

  8. C语言跳表(skiplist)实现

    一.简介 跳表(skiplist)是一个非常优秀的数据结构,实现简单,插入.删除.查找的复杂度均为O(logN).LevelDB的核心数据结构是用跳表实现的,redis的sorted set数据结构也 ...

  9. 跳表(skiplist)Python实现

    # coding=utf-8 # 跳表的Python实现 import random # 最高层数设置为4 MAX_LEVEL = 4 def randomLevel(): ""& ...

随机推荐

  1. 【ZZ】各类程序开发语言概述 | 菜鸟教程

    http://www.runoob.com/w3cnote/programming-intro.html 各类程序开发语言概述, 点击查看大图:

  2. 面试总结之MISC(操作系统,网络,数学,软件开发,测试,工具,系统设计,算法)

    操作系统 解释堆和栈的区别. 分配在堆的内存与分配在堆栈的内存有什么不同 分配在堆的内存要手动去释放 线程与进程的区别 多线程中栈与堆是公有的还是私有的 在多线程环境下,每个线程拥有一个栈和一个程序计 ...

  3. Codeforces Round #474-E(树形dp)

    一.题目链接 http://codeforces.com/contest/960/problem/B 二.题意 给定一棵$N$个节点的树,每个节点的权值$V$.定义树中两点$u_1$和$u_m$的权值 ...

  4. CFGym 101505I 题解

    一.题目链接 http://codeforces.com/gym/101505 二.题意 这题其实主要就是题意,理解题意后,就是水题了.我想了下,主要原因就是这几点: 1.题意太过英文化,很多句子不能 ...

  5. Advanced Simulation Library(ASL)&& An adaptive and distributed-memory parallel implementation of the immersed boundary (IB) method (IBAMR)

    How to install asl and ibamr tools: ASL 和 IBAMR 都是有限元分析的工具,流体力学等,ASL可以使用GPU加速计算, 主机配置,i7 6代,1060, 32 ...

  6. VMware CentOS Device eth0 does not seem to be present

    在VMware里克隆出来的CentOS Linux.. ifconfig...没有看到eth0..然后重启网卡又报下面错误. 故障现象: service network restartShutting ...

  7. js字符转换为数字

    转换函数.强制类型转换.利用js变量弱类型转换. 1. 转换函数: js提供了parseInt()和parseFloat()两个转换函数.前者把值转换成整数,后者把值转换成浮点数.只有对String类 ...

  8. “MSDTC 事务的导入失败: Result Code = 0x8004d00e。

    今天搞数据库移植,也把分布式程序移植了一下,一直报上面的错误,差点要重装了  %>_<%,网上看了几篇文章偶然间用DPCPING工具测试了下连接,发现不行.想到host文件的配置,发现其中 ...

  9. 一些js知识点总结

    1. 函数声明与函数表达式 解析器在像执行环境中加载数据时,会先读取函数声明,并使其在执行任何代码之前都可以访问,对于函数表达式,必须等到解析器执行到它所在的代码行,才会真正被执行. 例: alert ...

  10. Liunx下如何使用kettle

    在windows下完成所有操作, 把xxx.ktr上传到liunx, Pan.sh xxx.ktr 就完成了