• 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
  • 相关阅读:
    C#开发串口总结,并提炼串口辅助类到公用类库中
    sharepoint Lists Web service 用法
    .NET简谈策略模式
    细说 Form (表单)
    步步为营 SharePoint 开发学习笔记系列 一、简介
    Memcached进行缓存层设计
    各大主流.Net的IOC框架性能测试比较
    十年磨一剑,BloodyAngel!
    hosts
    新浪微薄的挂件
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/6231677.html
Copyright © 2020-2023  润新知