数据结构笔记之跳表(SkipList)
一、跳表简述
跳表可以看做是一个带有索引的链表,在介绍跳表之前先看一下一个普通的链表,假如当前维护了一个有序的链表:

现在要在这个链表中查找128,因为事先不知道链表中数值的分布情况,我们只能从前到后依次遍历,那么有没有什么取巧的方法呢?
如果我们能够存储下最中间节点的引用,比如存储节点29的引用,然后每次要查找节点的时候就先跟29比较一下,如果比29小就还是从前往后找(因为不会超过中间节点,从前往后和从中间往前区别不大),如果比29大就直接跳到29开始往后找,这种思想就是为链表添加了一个简单的索引,一下可以节省掉一半的查找时间。跳表的设计思想类似,通过为链表的每个节点设置高度来添加索引,不同的高度称为不同的层,每一层的next只能指向当前层的下一个节点,下面是一个跳表的示例:

最左面一列null为head节点,用于保存每一层的第一个节点,在查找的时候先从最高层开始查找,比如要在上面的跳表中查找128:

首先取最高层第六层的next,是47,比128小,直接跳到47,然后47的第六层的next是null,说明到了最后一个了,层数减少,47的第五层的next是255,比128大,也不行,继续减少层数,47的第四层的next是255,依然比128大,继续减少层数,47的第三层的next是128,get it!
看上面的过程有没有意识到一个问题,就是如果跳表的层数过多的话”比较-降层“这个过程就会有不少的浪费,比如上面的在47这个节点进行了4次比较才找到了有效的next节点,如果这个值再大一点的话就会退化得很严重。那么这个层是如何来的呢,跳表的高度是通过一个随机数决定的,初始时默认为一层,然后一直产生随机数只要小于0.5就将层数加一层继续随机,但是随机这种东西毕竟是不可控的,为了避免出现过高的层数最好还是设置一个最大层数以免某些节点的层数过大。
二、跳表的实现
跳表的简单Java实现:
SkipList.java:
package alg.link; import org.apache.commons.lang3.StringUtils; /**
* 跳表结构的简单Java实现
*
* @author CC11001100
*/
public class SkipList<T extends Comparable<T>> { private static final double DEFAULT_CONTINUE_INCREMENT_LEVEL_THRESHOLD = 0.75;
private static final int DEFAULT_MAX_LEVEL = 10; // 用于控制层数,决定层数时当随机数小于此值时会一直升层直到最大,取值范围[0, 1],值越大越可能得到高层数,此值设置不当很有可能退化成链表
private double continueIncrementLevelThreshold;
// 为了防止随机数出现异常情况过高,对最大层数做一个限制
private int maxLevel;
// 链表的头结点,每一层的头都保存在这里
private Node<T> head; public SkipList() {
this(DEFAULT_CONTINUE_INCREMENT_LEVEL_THRESHOLD, DEFAULT_MAX_LEVEL);
} public SkipList(double threshold) {
this(threshold, DEFAULT_MAX_LEVEL);
} public SkipList(double threshold, int maxLevel) {
this.continueIncrementLevelThreshold = threshold;
this.maxLevel = maxLevel;
this.head = new Node<>(null, 0);
// 初始化时就将其扩充到最大避免后面扩容增加复杂性,只有head会浪费一些空间其它节点都是有几层申请多长的数组
this.head.next = new Node[maxLevel];
} public void add(T value) {
Node[] previous = findPrevious(value);
Node<T> node = new Node<>(value, randomLevel());
for (int i = 0; i < previous.length && i < node.level; i++) {
node.next[i] = previous[i].next[i];
previous[i].next[i] = node;
}
if (node.level > head.level) {
for (int i = node.level - 1; i >= 0 && head.next[i] == null; i--) {
head.next[i] = node;
}
head.level = node.level;
}
} public void remove(T value) {
Node[] previous = findPrevious(value);
Node node = previous[0].next[0];
if (node.value.compareTo(value) != 0) {
return;
}
for (int i = 0; i < previous.length && i < node.level; i++) {
previous[i].next[i] = node.next[i];
}
} public boolean contains(T value) {
Node[] previous = findPrevious(value);
Node node = previous[0].next[0];
return node.value.compareTo(value) == 0;
} private int randomLevel() {
int level = 1;
while (level < maxLevel && Math.random() < continueIncrementLevelThreshold) {
level++;
}
return level;
} private Node[] findPrevious(T value) {
Node[] previous = new Node[head.level];
Node<T> node = head;
for (int i = head.level - 1; i >= 0; i--) {
while (node.next[i] != null && node.next[i].value.compareTo(value) < 0) {
node = node.next[i];
}
previous[i] = node;
}
return previous;
} /**
* head_next level_next # 注释,实际不会打印标题
* 67 67
* 67 67
* 67 67
* 67 67
* 19 19 67
* 19 19 67
* 6 6 19 67
* 6 6 19 67 82
* 6 6 19 46 67 82
*/
public void show() {
StringBuilder sb = new StringBuilder();
for (int i = maxLevel; i > 0; i--) {
// 这一层级的头指针指向谁
Node levelHead = head.next[i - 1];
if (levelHead != null) {
sb.append(String.format("%-10s", levelHead.value.toString())).append("\t");
} else {
sb.append(StringUtils.repeat(" ", 10)).append("\t");
}
// 然后是每一层按层数是否出现打印value或空白填充
Node node = head.next[0];
while (node != null) {
String s = node.value.toString();
if (node.level >= i) {
sb.append(s).append("\t");
} else {
sb.append(StringUtils.repeat(" ", s.length())).append("\t");
}
node = node.next[0];
}
if (i != 1) {
sb.append("\n");
}
}
System.out.println(sb.toString());
} private static class Node<T extends Comparable<T>> {
// 当前节点的值
T value;
// 当前节点的层数
int level;
// 记录当前节点在每一层的next节点
Node<T>[] next; private Node(T value, int level) {
this.value = value;
this.level = level;
this.next = new Node[level];
}
} }
SkipListTest.java:
package alg.link; import java.util.ArrayList;
import java.util.List;
import java.util.Random; /**
* @author CC11001100
*/
public class SkipListTest { public static void main(String[] args) { SkipList<Integer> skipList = new SkipList<>();
Random random = new Random();
List<Integer> numList = new ArrayList<>();
for (int i = 0; i < 30; i++) {
int n = random.nextInt(100);
System.out.println("now insert " + n);
skipList.add(n);
skipList.show();
numList.add(n);
System.out.println("---------------------------------------------------------------------------"); if (Math.random() < 0.5 && !numList.isEmpty()) {
int removeNum = numList.remove(random.nextInt(numList.size()));
System.out.println("now remove " + removeNum);
skipList.remove(removeNum);
skipList.show();
System.out.println("---------------------------------------------------------------------------");
}
} } }
测试效果:
now insert 35 35 35
35 35
35 35
---------------------------------------------------------------------------
now insert 56
56 56
56 56
56 56
56 56
56 56
56 56
56 56
35 35 56
35 35 56
35 35 56
---------------------------------------------------------------------------
now remove 35
56 56
56 56
56 56
56 56
56 56
56 56
56 56
56 56
56 56
56 56
---------------------------------------------------------------------------
now insert 45
56 56
56 56
56 56
56 56
56 56
45 45 56
45 45 56
45 45 56
45 45 56
45 45 56
---------------------------------------------------------------------------
now remove 56 45 45
45 45
45 45
45 45
45 45
---------------------------------------------------------------------------
now insert 7 45 45
45 45
45 45
45 45
7 7 45
---------------------------------------------------------------------------
now insert 57 45 45
45 45
45 45
45 45 57
7 7 45 57
---------------------------------------------------------------------------
now remove 7 45 45
45 45
45 45
45 45 57
45 45 57
---------------------------------------------------------------------------
now insert 75 45 45
45 45
45 45
45 45 57
45 45 57 75
---------------------------------------------------------------------------
now remove 75 45 45
45 45
45 45
45 45 57
45 45 57
---------------------------------------------------------------------------
now insert 96 45 45
45 45 96
45 45 96
45 45 57 96
45 45 57 96
---------------------------------------------------------------------------
now remove 96 45 45
45 45
45 45
45 45 57
45 45 57
---------------------------------------------------------------------------
now insert 40
40 40
40 40
40 40
40 40
40 40
40 40 45
40 40 45
40 40 45
40 40 45 57
40 40 45 57
---------------------------------------------------------------------------
now remove 40 45 45
45 45
45 45
45 45 57
45 45 57
---------------------------------------------------------------------------
now insert 69 45 45
45 45
45 45 69
45 45 57 69
45 45 57 69
---------------------------------------------------------------------------
now insert 11 45 45
11 11 45
11 11 45 69
11 11 45 57 69
11 11 45 57 69
---------------------------------------------------------------------------
now insert 19 45 45
11 11 45
11 11 45 69
11 11 19 45 57 69
11 11 19 45 57 69
---------------------------------------------------------------------------
now insert 55 45 45
11 11 45
11 11 45 55 69
11 11 19 45 55 57 69
11 11 19 45 55 57 69
---------------------------------------------------------------------------
now remove 55 45 45
11 11 45
11 11 45 69
11 11 19 45 57 69
11 11 19 45 57 69
---------------------------------------------------------------------------
now insert 43 45 45
11 11 45
11 11 45 69
11 11 19 45 57 69
11 11 19 43 45 57 69
---------------------------------------------------------------------------
now remove 43 45 45
11 11 45
11 11 45 69
11 11 19 45 57 69
11 11 19 45 57 69
---------------------------------------------------------------------------
now insert 5 45 45
11 11 45
11 11 45 69
11 11 19 45 57 69
5 5 11 19 45 57 69
---------------------------------------------------------------------------
now remove 5 45 45
11 11 45
11 11 45 69
11 11 19 45 57 69
11 11 19 45 57 69
---------------------------------------------------------------------------
now insert 92 45 45
11 11 45
11 11 45 69
11 11 19 45 57 69 92
11 11 19 45 57 69 92
---------------------------------------------------------------------------
now remove 45 11 11
11 11 69
11 11 19 57 69 92
11 11 19 57 69 92
---------------------------------------------------------------------------
now insert 56 11 11
11 11 69
11 11 19 57 69 92
11 11 19 56 57 69 92
---------------------------------------------------------------------------
now insert 31 11 11
11 11 69
11 11 19 31 57 69 92
11 11 19 31 56 57 69 92
---------------------------------------------------------------------------
now insert 29 11 11
11 11 29 69
11 11 19 29 31 57 69 92
11 11 19 29 31 56 57 69 92
---------------------------------------------------------------------------
now insert 96 11 11 96
11 11 29 69 96
11 11 19 29 31 57 69 92 96
11 11 19 29 31 56 57 69 92 96
---------------------------------------------------------------------------
now insert 0 11 11 96
0 0 11 29 69 96
0 0 11 19 29 31 57 69 92 96
0 0 11 19 29 31 56 57 69 92 96
---------------------------------------------------------------------------
now remove 19 11 11 96
0 0 11 29 69 96
0 0 11 29 31 57 69 92 96
0 0 11 29 31 56 57 69 92 96
---------------------------------------------------------------------------
now insert 54 11 11 54 96
0 0 11 29 54 69 96
0 0 11 29 31 54 57 69 92 96
0 0 11 29 31 54 56 57 69 92 96
---------------------------------------------------------------------------
now insert 8 11 11 54 96
0 0 11 29 54 69 96
0 0 11 29 31 54 57 69 92 96
0 0 8 11 29 31 54 56 57 69 92 96
---------------------------------------------------------------------------
now remove 56 11 11 54 96
0 0 11 29 54 69 96
0 0 11 29 31 54 57 69 92 96
0 0 8 11 29 31 54 57 69 92 96
---------------------------------------------------------------------------
now insert 60 11 11 54 96
0 0 11 29 54 69 96
0 0 11 29 31 54 57 69 92 96
0 0 8 11 29 31 54 57 60 69 92 96
---------------------------------------------------------------------------
now insert 41 41 41
11 11 41 54 96
0 0 11 29 41 54 69 96
0 0 11 29 31 41 54 57 69 92 96
0 0 8 11 29 31 41 54 57 60 69 92 96
---------------------------------------------------------------------------
now remove 92 41 41
11 11 41 54 96
0 0 11 29 41 54 69 96
0 0 11 29 31 41 54 57 69 96
0 0 8 11 29 31 41 54 57 60 69 96
---------------------------------------------------------------------------
now insert 20 41 41
11 11 20 41 54 96
0 0 11 20 29 41 54 69 96
0 0 11 20 29 31 41 54 57 69 96
0 0 8 11 20 29 31 41 54 57 60 69 96
---------------------------------------------------------------------------
now remove 41 11 11 20 54 96
0 0 11 20 29 54 69 96
0 0 11 20 29 31 54 57 69 96
0 0 8 11 20 29 31 54 57 60 69 96
---------------------------------------------------------------------------
now insert 58 11 11 20 54 96
0 0 11 20 29 54 58 69 96
0 0 11 20 29 31 54 57 58 69 96
0 0 8 11 20 29 31 54 57 58 60 69 96
---------------------------------------------------------------------------
now remove 31 11 11 20 54 96
0 0 11 20 29 54 58 69 96
0 0 11 20 29 54 57 58 69 96
0 0 8 11 20 29 54 57 58 60 69 96
---------------------------------------------------------------------------
now insert 79 11 11 20 54 96
0 0 11 20 29 54 58 69 96
0 0 11 20 29 54 57 58 69 79 96
0 0 8 11 20 29 54 57 58 60 69 79 96
---------------------------------------------------------------------------
now insert 38 11 11 20 54 96
0 0 11 20 29 54 58 69 96
0 0 11 20 29 38 54 57 58 69 79 96
0 0 8 11 20 29 38 54 57 58 60 69 79 96
---------------------------------------------------------------------------
now insert 31 11 11 20 54 96
0 0 11 20 29 54 58 69 96
0 0 11 20 29 31 38 54 57 58 69 79 96
0 0 8 11 20 29 31 38 54 57 58 60 69 79 96
---------------------------------------------------------------------------
now remove 79 11 11 20 54 96
0 0 11 20 29 54 58 69 96
0 0 11 20 29 31 38 54 57 58 69 96
0 0 8 11 20 29 31 38 54 57 58 60 69 96
---------------------------------------------------------------------------
now insert 78 11 11 20 54 78 96
0 0 11 20 29 54 58 69 78 96
0 0 11 20 29 31 38 54 57 58 69 78 96
0 0 8 11 20 29 31 38 54 57 58 60 69 78 96
---------------------------------------------------------------------------
now remove 0 11 11 20 54 78 96
11 11 20 29 54 58 69 78 96
11 11 20 29 31 38 54 57 58 69 78 96
8 8 11 20 29 31 38 54 57 58 60 69 78 96
---------------------------------------------------------------------------
.
数据结构笔记之跳表(SkipList)的更多相关文章
- 存储系统的基本数据结构之一: 跳表 (SkipList)
在接下来的系列文章中,我们将介绍一系列应用于存储以及IO子系统的数据结构.这些数据结构相互关联又有着巨大的区别,希望我们能够不辱使命的将他们分门别类的介绍清楚.本文为第一节,介绍一个简单而又有用的数据 ...
- 跳表SkipList
原文:http://www.cnblogs.com/xuqiang/archive/2011/05/22/2053516.html 跳表SkipList 1.聊一聊跳表作者的其人其事 2. 言归正 ...
- C语言跳表(skiplist)实现
一.简介 跳表(skiplist)是一个非常优秀的数据结构,实现简单,插入.删除.查找的复杂度均为O(logN).LevelDB的核心数据结构是用跳表实现的,redis的sorted set数据结构也 ...
- 跳表 SkipList
跳表是平衡树的一种替代的数据结构,和红黑树不同,跳表对树的平衡的实现是基于一种随机化的算法,这样就使得跳表的插入和删除的工作比较简单. 跳表是一种复杂的链表,在简单链表的节点信息之上又增加了额 ...
- 3.3.7 跳表 SkipList
一.前言 concurrentHashMap与ConcurrentSkipListMap性能测试 在4线程1.6万数据的条件下,ConcurrentHashMap 存取速度是ConcurrentSki ...
- 跳表(SkipList)设计与实现(Java)
微信搜一搜「bigsai」关注这个有趣的程序员 文章已收录在 我的Github bigsai-algorithm 欢迎star 前言 跳表是面试常问的一种数据结构,它在很多中间件和语言中得到应用,我们 ...
- [转载] 跳表SkipList
原文: http://www.cnblogs.com/xuqiang/archive/2011/05/22/2053516.html leveldb中memtable的思想本质上是一个skiplist ...
- 跳表(SkipList)原理篇
1.什么是跳表? 维基百科:跳表是一种数据结构.它使得包含n个元素的有序序列的查找和插入操作的平均时间复杂度都是 O(logn),优于数组的 O(n)复杂度.快速的查询效果是通过维护一个多层次的链表实 ...
- 跳表(skiplist)Python实现
# coding=utf-8 # 跳表的Python实现 import random # 最高层数设置为4 MAX_LEVEL = 4 def randomLevel(): ""& ...
随机推荐
- B. Lost Array
链接 [http://codeforces.com/contest/1043/problem/B] 题意 自己点开链接看 分析 1到n枚举某个值,判断是否满足并统计 判断方法:假设x序列成立,那么后面 ...
- Final互评------《飞词》---- 拉格朗日2018
一.基于NABCD评论作品,及改进建议 1.根据(不限于)NABCD评论作品的选题; N(Need,需求):拉格朗日2018团队对需求分析的做法是通过问卷调查的形式,通过问卷调查分析出目前的大学生群体 ...
- Android实现Service永久驻留
说实话,这是一种流氓行为.但有些时候又是不得不需要的.比如微信的NotifyReceiver.现在抛开这些伦理的东西不讲,我们只是来看看技术上怎么实现.在后台运行的service有几个途径可以将其停止 ...
- iOS 内存管理-copy、 retain、 assign 、readonly 、 readwrite、nonatomic、@property、@synthesize、@dynamic、IB_DESIGNABLE 、 IBInspectable、IBOutletCollection
浅谈iOS内存管理机制 alloc,retain,copy,release,autorelease 1)使用@property配合@synthesize可以让编译器自动实现getter/setter方 ...
- 查看django版本的方法
在cmd输入: python -m django --version django-admin --version
- [知乎]关于WindowsXPx64SP2系统的说明
自己简单安装了下发现 winxpsp3x86的系统版本为: 然后windowsXPx64sp2的版本为: 作者:qpi667链接:https://www.zhihu.com/question/29 ...
- Mobile Phone Network CodeForces - 1023F(并查集lca+修改环)
题意: 就是有几个点,你掌控了几条路,你的商业对手也掌控了几条路,然后你想让游客都把你的所有路都走完,那么你就有钱了,但你又想挣的钱最多,真是的过分..哈哈 游客肯定要对比一下你的对手的路 看看那个便 ...
- CodeForces - 707C
C. Pythagorean Triples time limit per test 1 second memory limit per test 256 megabytes input standa ...
- Day24-KindEditor基本使用和文件操作2
KindEditor是一套开源的HTML可视化编辑器,主要用于让用户在网站上获得所见即所得编辑效果,兼容IE.Firefox.Chrome.Safari.Opera等主流浏览器. 1. 准备 2. 写 ...
- SSH & Git
SSH基本用法 SSH服务详解 work with git branch some tips for git setup and git config git and github ssh servi ...