forked from yudaocode/SpringBoot-Labs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
YunaiV
committed
Jun 8, 2019
1 parent
1137a9a
commit 91a4218
Showing
3 changed files
with
302 additions
and
0 deletions.
There are no files selected for viewing
114 changes: 114 additions & 0 deletions
114
lab-09/src/main/java/cn/iocoder/springboot/labs/lab09/search/SkipList.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package cn.iocoder.springboot.labs.lab09.search; | ||
|
||
import java.util.Random; | ||
|
||
/** | ||
* 跳表的一种实现方法。 | ||
* 跳表中存储的是正整数,并且存储的是不重复的。 | ||
* | ||
* From https://github.com/wangzheng0822/algo/blob/master/java/17_skiplist/SkipList.java | ||
* | ||
* Author:ZHENG | ||
*/ | ||
@SuppressWarnings("Duplicates") | ||
public class SkipList { | ||
|
||
private static final int MAX_LEVEL = 16; | ||
|
||
private int levelCount = 1; | ||
|
||
private Node head = new Node(); // 带头链表 | ||
|
||
private Random r = new Random(); | ||
|
||
public Node find(int value) { | ||
Node p = head; | ||
for (int i = levelCount - 1; i >= 0; --i) { | ||
while (p.forwards[i] != null && p.forwards[i].data < value) { | ||
p = p.forwards[i]; | ||
} | ||
} | ||
|
||
if (p.forwards[0] != null && p.forwards[0].data == value) { | ||
return p.forwards[0]; | ||
} else { | ||
return null; | ||
} | ||
} | ||
|
||
public void insert(int value) { | ||
int level = randomLevel(); | ||
Node newNode = new Node(); | ||
newNode.data = value; | ||
newNode.maxLevel = level; | ||
Node[] update = new Node[level]; | ||
for (int i = 0; i < level; ++i) { | ||
update[i] = head; | ||
} | ||
|
||
// record every level largest value which smaller than insert value in update[] | ||
Node p = head; | ||
for (int i = level - 1; i >= 0; --i) { | ||
while (p.forwards[i] != null && p.forwards[i].data < value) { | ||
p = p.forwards[i]; | ||
} | ||
update[i] = p;// use update save node in search path | ||
} | ||
|
||
// in search path node next node become new node forwords(next) | ||
for (int i = 0; i < level; ++i) { | ||
newNode.forwards[i] = update[i].forwards[i]; | ||
update[i].forwards[i] = newNode; | ||
} | ||
|
||
// update node hight | ||
if (levelCount < level) levelCount = level; | ||
} | ||
|
||
public void delete(int value) { | ||
Node[] update = new Node[levelCount]; | ||
Node p = head; | ||
for (int i = levelCount - 1; i >= 0; --i) { | ||
while (p.forwards[i] != null && p.forwards[i].data < value) { | ||
p = p.forwards[i]; | ||
} | ||
update[i] = p; | ||
} | ||
|
||
if (p.forwards[0] != null && p.forwards[0].data == value) { | ||
for (int i = levelCount - 1; i >= 0; --i) { | ||
if (update[i].forwards[i] != null && update[i].forwards[i].data == value) { | ||
update[i].forwards[i] = update[i].forwards[i].forwards[i]; | ||
} | ||
} | ||
} | ||
} | ||
|
||
// 随机 level 次,如果是奇数层数 +1,防止伪随机 | ||
private int randomLevel() { | ||
int level = 1; | ||
for (int i = 1; i < MAX_LEVEL; ++i) { | ||
if (r.nextInt() % 2 == 1) { | ||
level++; | ||
} | ||
} | ||
|
||
return level; | ||
} | ||
|
||
public void printAll() { | ||
Node p = head; | ||
while (p.forwards[0] != null) { | ||
System.out.print(p.forwards[0] + " "); | ||
p = p.forwards[0]; | ||
} | ||
System.out.println(); | ||
} | ||
|
||
public class Node { | ||
private int data = -1; | ||
private Node forwards[] = new Node[MAX_LEVEL]; | ||
private int maxLevel = 0; | ||
} | ||
|
||
} |
146 changes: 146 additions & 0 deletions
146
lab-09/src/main/java/cn/iocoder/springboot/labs/lab09/search/SkipList2.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package cn.iocoder.springboot.labs.lab09.search; | ||
|
||
import java.util.Random; | ||
|
||
/** | ||
* 参考 {@link SkipList} ,自己实现一遍跳表 | ||
* | ||
* @author yunai | ||
*/ | ||
@SuppressWarnings("Duplicates") | ||
public class SkipList2 { | ||
|
||
private static final int MAX_LEVEL = 16; | ||
|
||
private Random random = new Random(); | ||
|
||
/** | ||
* 总层级数 | ||
*/ | ||
private int levelCount = 1; | ||
|
||
private Node head = new Node(null, MAX_LEVEL); | ||
|
||
public Node find(int value) { | ||
// 自最上层索引,开始往下查询 | ||
Node p = head; | ||
for (int i = levelCount - 1; i >= 0; i--) { | ||
while (p.forwards[i] != null && p.forwards[i].data < value) { // 这里看的会有点绕,第一次 for ,跳转到对应 i 层,后续的,就是第 i 层的不断向下指向 | ||
p = p.forwards[i]; | ||
} | ||
} | ||
|
||
// 判断是否相等 | ||
if (p.forwards[0] != null && p.forwards[0].data == value) { | ||
return p.forwards[0]; | ||
} | ||
return null; | ||
} | ||
|
||
public void insert(int value) { | ||
// 创建 Node | ||
int maxLevel = randomLevel(); | ||
Node node = new Node(value, maxLevel); | ||
|
||
// 寻找每一层的指向 | ||
Node[] update = new Node[maxLevel]; | ||
// for (int i = 0; i < maxLevel; i++) { // 初始化 node 在一层的指向 。不需要,因为下面的 for 循环,一定会给 update 数组赋值。 | ||
// update[i] = head; | ||
// } | ||
Node p = head; | ||
for (int i = maxLevel - 1; i >= 0; i--) { | ||
while (p.forwards[i] != null && p.forwards[i].data < value) { // 这里看的会有点绕,第一次 for ,跳转到对应 i 层,后续的,就是第 i 层的不断向下指向 | ||
p = p.forwards[i]; | ||
} | ||
update[i] = p; | ||
} | ||
|
||
// 设置指向 | ||
for (int i = 0; i < maxLevel; i++) { | ||
// 设置 node 在第 i 层,指向 update[i].forwards[i] | ||
node.forwards[i] = update[i].forwards[i]; | ||
// 将 update[i].forwards[i] 赋值成 node 。 | ||
// 这样,就形成了 update[i].forwards[i] = node ,并且 node..forwards[i] = 原 update[i].forwards[i] 。可能有点绕,可以调试下 | ||
update[i].forwards[i] = node; | ||
} | ||
|
||
// 设置新的最高高度 | ||
if (maxLevel > levelCount) { | ||
levelCount = maxLevel; | ||
} | ||
} | ||
|
||
public void delete(int value) { | ||
// 寻找一层对 value 应该 Node 的指向 | ||
Node[] update = new Node[levelCount]; | ||
Node p = head; | ||
for (int i = levelCount - 1; i >= 0; i--) { | ||
while (p.forwards[i] != null && p.forwards[i].data < value) { // 这里看的会有点绕,第一次 for ,跳转到对应 i 层,后续的,就是第 i 层的不断向下指向 | ||
p = p.forwards[i]; | ||
} | ||
update[i] = p; | ||
} | ||
|
||
// 如果找到指定节点 | ||
if (p.forwards[0] != null && p.forwards[0].data == value) { | ||
for (int i = levelCount - 1; i >= 0; i--) { | ||
// 指定层,有符合 value 应该 Node 的指向,进行删除 | ||
if (update[i].forwards[i] != null && update[i].forwards[i].data == value) { | ||
update[i].forwards[i] = update[i].forwards[i].forwards[i]; | ||
} | ||
} | ||
} | ||
} | ||
|
||
private int randomLevel() { | ||
return random.nextInt(MAX_LEVEL - 1) + 1; // 一定建立索引,避免直接添加到第 0 层。不然可能会丢失。 | ||
// return random.nextInt(MAX_LEVEL); // 因为我们是从 1 层开始计数,可以看 levelCount 参数 | ||
} | ||
|
||
// // 随机 level 次,如果是奇数层数 +1,防止伪随机 | ||
// private int randomLevel() { | ||
// int level = 1; | ||
// for (int i = 1; i < MAX_LEVEL; ++i) { | ||
// if (random.nextInt() % 2 == 1) { | ||
// level++; | ||
// } | ||
// } | ||
// | ||
// return level; | ||
// } | ||
|
||
public class Node { | ||
|
||
/** | ||
* 数值 | ||
*/ | ||
private Integer data; | ||
|
||
// TODO 芋艿,如果想实现类似 HashMap 的功能,可以把 data 改成 key + value 。 | ||
|
||
/** | ||
* 最高层级 | ||
*/ | ||
private int maxLevel; | ||
/** | ||
* 对指定层级的指向 | ||
*/ | ||
private Node[] forwards; | ||
|
||
public Node(Integer data, int maxLevel) { | ||
this.data = data; | ||
this.maxLevel = maxLevel; | ||
this.forwards = new Node[maxLevel]; | ||
// this.forwards = new Node[MAX_LEVEL]; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "Node{" + | ||
"data=" + data + | ||
", maxLevel=" + maxLevel + | ||
'}'; | ||
} | ||
} | ||
|
||
} |
42 changes: 42 additions & 0 deletions
42
lab-09/src/main/java/cn/iocoder/springboot/labs/lab09/search/SkipListTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package cn.iocoder.springboot.labs.lab09.search; | ||
|
||
public class SkipListTest { | ||
|
||
public static void main2(String[] args) { | ||
SkipList skipList = new SkipList(); | ||
skipList.insert(1); | ||
skipList.insert(2); | ||
skipList.insert(3); | ||
skipList.insert(1); | ||
skipList.insert(1); | ||
skipList.insert(1); | ||
skipList.delete(1); | ||
skipList.printAll(); | ||
} | ||
|
||
public static void main(String[] args) { | ||
for (int i = 0; i < 10000; i++) { | ||
SkipList2 skipList = new SkipList2(); | ||
// SkipList skipList = new SkipList(); | ||
// 测试添加 | ||
skipList.insert(2); | ||
skipList.insert(1); | ||
|
||
// 测试查询 | ||
System.out.println(skipList.find(1) != null); | ||
assert skipList.find(1) != null; | ||
System.out.println(skipList.find(2) != null); | ||
assert skipList.find(2) != null; | ||
System.out.println(skipList.find(3) == null); // null | ||
assert skipList.find(3) == null; | ||
|
||
// 测试删除 | ||
skipList.delete(1); | ||
System.out.println(skipList.find(1) == null); // null | ||
assert skipList.find(1) == null; | ||
System.out.println(skipList.find(2) != null); | ||
assert skipList.find(2) != null; | ||
} | ||
} | ||
|
||
} |