Skip to content

Commit

Permalink
手写 skip list,看看自己最近有多脑残
Browse files Browse the repository at this point in the history
  • Loading branch information
YunaiV committed Jun 8, 2019
1 parent 1137a9a commit 91a4218
Show file tree
Hide file tree
Showing 3 changed files with 302 additions and 0 deletions.
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;
}

}
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 +
'}';
}
}

}
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;
}
}

}

0 comments on commit 91a4218

Please sign in to comment.