SkipList的java实现

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

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

跳跃表的应用

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

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

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

ConcurrentSkipListSet(在功能上对应HashSet). 

确切来说,SkipList更像Java中的TreeMapTreeMap基于红黑树(一种自平衡二叉查找树)实现的,时间复杂度平均能达到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 ------

每次运行的结果是不一样的,这就是为什么说跳跃表是属于随机化数据结构。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值