zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    atitit.TokenService v3 qb1 token服务模块的设计 新特性.docx
    Atitit attilax在自然语言处理领域的成果
    Atitit 图像清晰度 模糊度 检测 识别 评价算法 原理
    Atitit (Sketch Filter)素描滤镜的实现  图像处理  attilax总结
    atitit。企业的价值观 员工第一 vs 客户第一.docx
    Atitit 实现java的linq 以及与stream api的比较
    Atitit dsl exer v3 qb3 新特性
    Atititi tesseract使用总结
    Atitit 修改密码的功能流程设计 attilax总结
    atitit.TokenService v3 qb1  token服务模块的设计 新特性.docx
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/6231677.html
Copyright © 2011-2022 走看看