数据结构笔记之跳表(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(): ""& ...
随机推荐
- Mocha 单元测试框架简介
前言: mocha是JavaScript的一种单元测试框架,既可以在浏览器环境下运行,也可以在Node.js环境下运行. 使用mocha,我们就只需要专注于编写单元测试本身,然后,让mocha去自动运 ...
- [Latex] Travis-CI与Latex构建开源中文PDF
博主有一本开源书籍,用 latex 排版,托管在Github上.但用 latex 不像是 Markdown,当tex文本更新时,用于最终浏览的PDF文件很难得到及时的更新, 所以博主一直想找到一套工具 ...
- ElasticSearch 2 (37) - 信息聚合系列之内存与延时
ElasticSearch 2 (37) - 信息聚合系列之内存与延时 摘要 控制内存使用与延时 版本 elasticsearch版本: elasticsearch-2.x 内容 Fielddata ...
- Alpha冲刺第8天
Alpha第8天 1.团队成员 郑西坤 031602542 (队长) 陈俊杰 031602504 陈顺兴 031602505 张胜男 031602540 廖钰萍 031602323 雷光游 03160 ...
- PHP中Cookie的使用
1.什么是Cookie? Cookie保存在客户端浏览器中,cookie是Http头的一部分,通过浏览器请求页面时,它会被通过Http头的形式发送过去.被请求的页面,可以通过PHP来获取cookie的 ...
- Java之JDBC操作
下载jar包: mysql-connector-java-5.1.44.jar 导入包: import java.sql.*; 源码如下: /** * 使用JDBC底层实现查询 */ public s ...
- Pathwalks CodeForces - 960F(主席树 || 树状数组)
题意: 求树上最长上升路径 解析: 树状数组版: 998ms edge[u][w] 代表以u为一条路的终点的小于w的最长路径的路的条数 · 那么edge[v][w] = max(edge[u][w-1 ...
- Codeforces - 1020B Badge(找环)
题意: 以每个点为起点,找到第一个出现两次的点 解析: 我是先找出来所有的环 环上的点找出来的肯定是自己 bz[i] = i; 然后去遍历不在环上的点j 如果通过这个点找到一个已经标记的的点i ...
- Single VIP LLB and SLB config
Single VIP LLB and SLB config >>>>>>>>>>>>>>>>>&g ...
- 【agc002f】Leftmost Ball(动态规划)
[agc002f]Leftmost Ball(动态规划) 题面 atcoder 洛谷 题解 我们从前往后依次把每个颜色按顺序来放,那么如果当前放的是某种颜色的第一个球,那么放的就会变成\(0\)号颜色 ...