跳跃表是一种随机化数据结构,基于并联的链表,其效率可比拟于二叉查找树(对于大多数操作需要O(log n)平均时间),并且对并发算法友好。

关于跳跃表的具体介绍可以参考MIT的公开课:跳跃表

跳跃表的应用

Skip list(跳表)是一种可以代替平衡树的数据结构,默认是按照Key值升序的。Skip list让已排序的数据分布在多层链表中,以0-1随机数决定一个数据的向上攀升与否,通过“空间来换取时间”的一个算法,在每个节点中增加了向前的指针,在插入、删除、查找时可以忽略一些不可能涉及到的结点,从而提高了效率。

在Java的API中已经有了实现:分别是

ConcurrentSkipListMap(在功能上对应HashTable、HashMap、TreeMap) ;

ConcurrentSkipListSet(在功能上对应HashSet). 

确切来说,SkipList更像Java中的TreeMap,TreeMap基于红黑树(一种自平衡二叉查找树)实现的,时间复杂度平均能达到O(log n)。

HashMap是基于散列表实现的,时间复杂度平均能达到O(1)。ConcurrentSkipListMap是基于跳表实现的,时间复杂度平均能达到O(log n)。

Skip list的性质

(1) 由很多层结构组成,level是通过一定的概率随机产生的。
(2) 每一层都是一个有序的链表,默认是升序
(3) 最底层(Level 1)的链表包含所有元素。
(4) 如果一个元素出现在Level i 的链表中,则它在Level i 之下的链表也都会出现。
(5) 每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素。 Ø ConcurrentSkipListMap具有Skip list的性质 ,并且适用于大规模数据的并发访问。多个线程可以安全地并发执行插入、移除、更新和访问操作。与其他有锁机制的数据结构在巨大的压力下相比有优势。 Ø TreeMap插入数据时平衡树采用严格的旋转(比如平衡二叉树有左旋右旋)来保证平衡,因此Skip list比较容易实现,而且相比平衡树有着较高的运行效率。 Java代码实现: 1. SkipListEntry.java , 这是跳跃表中存储的每个元素实体类,包含 上下左右 四个指针。
01
package skiplist;
02 03
public class SkipListEntry {
04
public String key;
05
public Integer value;
06 07
public int pos; //主要为了打印 链表用
08 09
public SkipListEntry up, down, left, right; // 上下左右 四个指针
10 11
public static String negInf = new String("-oo"); // 负无穷
12
public static String posInf = new String("+oo"); // 正无穷
13 14
public SkipListEntry(String k, Integer v) {
15
key = k;
16
value = v;
17 18
up = down = left = right = null;
19
}
20 21
public Integer getValue() {
22
return value;
23
}
24 25
public String getKey() {
26
return key;
27
}
28 29
public Integer setValue(Integer val) {
30
Integer oldValue = value;
31
value = val;
32
return oldValue;
33
}
34 35
public boolean equals(Object o) {
36
SkipListEntry ent;
37
try {
38
ent = (SkipListEntry) o; // 检测类型
39
} catch (ClassCastException ex) {
40
return false;
41
}
42
return (ent.getKey() == key) && (ent.getValue() == value);
43
}
44 45
public String toString() {
46
return "(" + key + "," + value + ")";
47
}
48
} 2. SkipList.java, 跳跃表类,包含算法的实现。 head 和 tail 分别是 顶层的头和尾。
001
package skiplist;
002 003
import java.util.*;
004 005
public class SkipList {
006
public SkipListEntry head; // 顶层的第一个元素
007
public SkipListEntry tail; // 顶层的最后一个元素
008 009
public int n; // 跳跃表中的元素个数
010 011
public int h; // 跳跃表的高度
012
public Random r; // 投掷硬币
013 014
public SkipList() // 默认构造函数...
015
{
016
SkipListEntry p1, p2;
017 018
p1 = new SkipListEntry(SkipListEntry.negInf, null);
019
p2 = new SkipListEntry(SkipListEntry.posInf, null);
020 021
head = p1;
022
tail = p2;
023 024
p1.right = p2;
025
p2.left = p1;
026 027
n = 0;
028
h = 0;
029
r = new Random();
030
}
031 032
/** 返回 包含的元素个数 */
033
public int size() {
034
return n;
035
}
036 037
/** 跳跃表是否为空 */
038
public boolean isEmpty() {
039
return (n == 0);
040
}
041 042
//在最下面一层,找到要插入的位置前面的那个key
043
public SkipListEntry findEntry(String k) {
044
SkipListEntry p;
045
p = head;
046 047
while (true) {
048
/**
049
* 一直向右找,例: k=34.
050
* 10 ---> 20 ---> 30 ---> 40 ^ | p 会在30处停止
051
* --------------------------------------------
052
***/
053
while (p.right.key != SkipListEntry.posInf
054
&& p.right.key.compareTo(k) <= 0) {
055
p = p.right;
056
// System.out.println(">>>> " + p.key);
057
}
058
// 如果还有下一层,就到下一层继续查找
059
if (p.down != null) {
060
p = p.down;
061
//System.out.println("vvvv " + p.key);
062
} else
063
break; // 到了最下面一层 就停止查找
064
}
065 066
return (p); // p.key <= k
067
}
068 069
/** 返回和key绑定的值 */
070
public Integer get(String k) {
071
SkipListEntry p;
072 073
p = findEntry(k);
074 075
if (k.equals(p.getKey()))
076
return (p.value);
077
else
078
return (null);
079
}
080 081
/** 放一个key-value到跳跃表中, 替换原有的并返回 */
082
public Integer put(String k, Integer v) {
083
SkipListEntry p, q;
084
int i;
085 086
p = findEntry(k);
087 088
if (k.equals(p.getKey())) {
089
Integer old = p.value;
090
p.value = v;
091
return (old);
092
}
093 094
q = new SkipListEntry(k, v);
095
q.left = p;
096
q.right = p.right;
097
p.right.left = q;
098
p.right = q;
099 100
i = 0; // 当前层 level = 0
101 102
while (r.nextDouble() < 0.5) {
103 104
//如果超出了高度,需要重新建一个顶层
105
if (i >= h) {
106
SkipListEntry p1, p2;
107 108
h = h + 1;
109
p1 = new SkipListEntry(SkipListEntry.negInf, null);
110
p2 = new SkipListEntry(SkipListEntry.posInf, null);
111 112
p1.right = p2;
113
p1.down = head;
114 115
p2.left = p1;
116
p2.down = tail;
117 118
head.up = p1;
119
tail.up = p2;
120 121
head = p1;
122
tail = p2;
123
}
124 125
while (p.up == null) {
126
p = p.left;
127
}
128
p = p.up;
129 130
SkipListEntry e;
131 132
e = new SkipListEntry(k, null);
133
e.left = p;
134
e.right = p.right;
135
e.down = q;
136 137
p.right.left = e;
138
p.right = e;
139
q.up = e;
140 141
q = e; // q 进行下一层迭代
142
i = i + 1; // 当前层 +1
143 144
}
145
n = n + 1;
146 147
return (null); // No old value
148
}
149 150
public Integer remove(String key) {
151
return (null);
152
}
153 154
public void printHorizontal() {
155
String s = "";
156
int i;
157
SkipListEntry p;
158 159
p = head;
160 161
while (p.down != null) {
162
p = p.down;
163
}
164 165
i = 0;
166
while (p != null) {
167
p.pos = i++;
168
p = p.right;
169
}
170 171
p = head;
172
while (p != null) {
173
s = getOneRow(p);
174
System.out.println(s);
175 176
p = p.down;
177
}
178
}
179 180
//用了打印测试
181
public String getOneRow(SkipListEntry p) {
182
String s;
183
int a, b, i;
184 185
a = 0;
186 187
s = "" + p.key;
188
p = p.right;
189 190
while (p != null) {
191
SkipListEntry q;
192 193
q = p;
194
while (q.down != null)
195
q = q.down;
196
b = q.pos;
197 198
s = s + " <-";
199 200
for (i = a + 1; i < b; i++)
201
s = s + "--------";
202 203
s = s + "> " + p.key;
204 205
a = b;
206 207
p = p.right;
208
}
209 210
return (s);
211
}
212 213
//用了打印测试
214
public void printVertical() {
215
String s = "";
216
SkipListEntry p;
217
p = head;
218
while (p.down != null)
219
p = p.down;
220 221
while (p != null) {
222
s = getOneColumn(p);
223
System.out.println(s);
224 225
p = p.right;
226
}
227
}
228
//用了打印测试
229
public String getOneColumn(SkipListEntry p) {
230
String s = "";
231
while (p != null) {
232
s = s + " " + p.key;
233
p = p.up;
234
}
235
return (s);
236
}
237
} 测试类,Test.java
01
package skiplist;
02 03
public class Test1 {
04
public static void main(String[] args) {
05
SkipList S = new SkipList();
06 07
S.printHorizontal();
08
System.out.println("------");
09
// S.printVertical();
10
// System.out.println("======");
11 12
S.put("ABC", 123);
13
S.printHorizontal();
14
System.out.println("------");
15
// S.printVertical();
16
//System.out.println("======");
17 18
S.put("DEF", 123);
19
S.printHorizontal();
20
System.out.println("------");
21
// S.printVertical();
22
// System.out.println("======");
23 24
S.put("KLM", 123);
25
S.printHorizontal();
26
System.out.println("------");
27
// S.printVertical();
28
// System.out.println("======");
29 30
S.put("HIJ", 123);
31
S.printHorizontal();
32
System.out.println("------");
33
// S.printVertical();
34
// System.out.println("======");
35 36
S.put("GHJ", 123);
37
S.printHorizontal();
38
System.out.println("------");
39
// S.printVertical();
40
// System.out.println("======");
41 42
S.put("AAA", 123);
43
S.printHorizontal();
44
System.out.println("------");
45
// S.printVertical();
46
// System.out.println("======");
47 48
}
49
} 输出结果:
01
-oo <-> +oo
02
------
03
-oo <-> ABC <-> +oo
04
-oo <-> ABC <-> +oo
05
------
06
-oo <-> ABC <---------> +oo
07
-oo <-> ABC <-> DEF <-> +oo
08
------
09
-oo <-> ABC <---------> KLM <-> +oo
10
-oo <-> ABC <-> DEF <-> KLM <-> +oo
11
------
12
-oo <-----------------> HIJ <---------> +oo
13
-oo <-> ABC <---------> HIJ <-> KLM <-> +oo
14
-oo <-> ABC <-> DEF <-> HIJ <-> KLM <-> +oo
15
------
16
-oo <-------------------------> HIJ <---------> +oo
17
-oo <-> ABC <-----------------> HIJ <-> KLM <-> +oo
18
-oo <-> ABC <-> DEF <-> GHJ <-> HIJ <-> KLM <-> +oo
19
------
20
-oo <---------------------------------> HIJ <---------> +oo
21
-oo <---------> ABC <-----------------> HIJ <-> KLM <-> +oo
22
-oo <-> AAA <-> ABC <-> DEF <-> GHJ <-> HIJ <-> KLM <-> +oo
23
------ 每次运行的结果是不一样的,这就是为什么说跳跃表是属于随机化数据结构。 代码参考:Implementing the skip list data structure

JAVA SkipList 跳表 的原理和使用例子的更多相关文章

  1. skiplist(跳表)的原理及JAVA实现

    前记 最近在看Redis,之间就尝试用sortedSet用在实现排行榜的项目,那么sortedSet底层是什么结构呢? "Redis sorted set的内部使用HashMap和跳跃表(S ...

  2. skiplist 跳表(2)-----细心学习

    快速了解skiplist请看:skiplist 跳表(1) http://blog.sina.com.cn/s/blog_693f08470101n2lv.html 本周我要介绍的数据结构,是我非常非 ...

  3. skiplist 跳表(1)

    最近学习中遇到一种新的数据结构,很实用,搬过来学习. 原文地址:skiplist 跳表   为什么选择跳表 目前经常使用的平衡数据结构有:B树,红黑树,AVL树,Splay Tree, Treep等. ...

  4. SkipList跳表基本原理

    为什么选择跳表 目前经常使用的平衡数据结构有:B树,红黑树,AVL树,Splay Tree, Treep等. 想象一下,给你一张草稿纸,一只笔,一个编辑器,你能立即实现一颗红黑树,或者AVL树 出来吗 ...

  5. SkipList跳表(一)基本原理

    一直听说跳表这个数据结构,说要学一下的,懒癌犯了,是该治治了 为什么选择跳表 目前经常使用的平衡数据结构有:B树.红黑树,AVL树,Splay Tree(这个树好像还没有听说过),Treep(也没有听 ...

  6. 【转】SkipList跳表基本原理

    增加了向前指针的链表叫作跳表.跳表全称叫做跳跃表,简称跳表.跳表是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表.跳表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找.跳表不仅 ...

  7. 利用skipList(跳表)来实现排序(待补充)

    用于排名的数据结构 一般排序为利用堆排序(二叉树)和利用skipList(跳表)的方式 redis中SortedSet利用skipList(跳表)来实现排序,复杂度为O(logn),利用空间换时间,类 ...

  8. skip-list(跳表)原理及C++代码实现

    跳表是一个很有意思的数据结构,它实现简单,但是性能又可以和平衡二叉搜索树差不多. 据MIT公开课上教授的讲解,它的想法和纽约地铁有异曲同工之妙,简而言之就是不断地增加“快线”,从而降低时间复杂度. 当 ...

  9. SkipList 跳表

    1.定义描述      跳跃列表(也称跳表)是一种随机化数据结构,基于并联的链表,其效率可比拟于二叉查找树(对于大多数操作需要O(log n)平均时间).      基本上,跳跃列表是对有序的链表增加 ...

随机推荐

  1. js---15,模拟数组的ecah方法

    原型的另外一个作用就是扩展对象中的属性和方法的 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http:// ...

  2. java中goto语句

    goto是java中一个保留字,但在语言中并未使用它. goto语句起源于汇编语言的程序控制,是源码级上的跳跃,这使其招致了不好的声誉,若一个程序总是从一个地方跳转到另一个地方, 还有什么办法能识别程 ...

  3. IK分词器插件elasticsearch-analysis-ik 6.1.1

    http://88250.b3log.org/full-text-search-elasticsearch#b3_solo_h3_0 IK分词器插件 (1)源码 https://github.com/ ...

  4. iTOP-4412开发板p2p视频

    整体框架: 一.发送端 1.摄像头通过V4L2接口得到YUV视频格式,可以在win7上用yuvplayer播放 2.使用4412硬件编码模块MFC提供的接口进行硬件编码,得到.264文件,可以在win ...

  5. LoadRunner IP欺骗使用

  6. 洛谷 P2628 冒险岛

    P2628 冒险岛 题目背景 冒险岛是费老师新开发的一种情景模拟电脑的游戏,通过掷骰子(1~6个数字之间),让一种人物(棋子)在棋纸上从左至右的行走,从而模拟冒险的故事…… 题目描述 棋纸上有一条从左 ...

  7. Scala入门到精通——第二十二节 高级类型 (一)

    作者:摇摆少年梦 视频地址:http://www.xuetuwuyou.com/course/12 本节主要内容 this.type使用 类型投影 结构类型 复合类型 1. this.type使用 c ...

  8. Android实现点击通知栏后,先启动应用再打开目标Activity

    情况简述 在开发Android app的过程中,遇到这样一个需求:app中启动一个Service,该Service在独立进程中运行,与服务器保持长连接,将服务器推送过来的消息在通知栏中显示,并设置点击 ...

  9. Python 极简教程(十二)逻辑控制语句 if else

    计算机软件之所以能够对不同的情况进行不同的处理,就是我们在编码的时候,通过逻辑控制语句,告诉软件在不同的情况下应该做什么处理. 比如我们在登录的时候,那么当你输入正确的账号密码和错误的账号密码,完全是 ...

  10. 【习题 6-4 UVA-439】Knight Moves

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] bfs模板题 [代码] /* 1.Shoud it use long long ? 2.Have you ever test sev ...