一、跳表简述
跳表可以看做是一个带有索引的链表,在介绍跳表之前先看一下一个普通的链表,假如当前维护了一个有序的链表:
现在要在这个链表中查找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
---------------------------------------------------------------------------
.