JAVA SkipList 跳表 的原理和使用例子

跳跃表是一种随机化数据结构,基于并联的链表,其效率可比拟于二叉查找树(对于大多数操作需要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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值