跳表

为什么选择跳表
目前经常使用的平衡数据结构像B树,红黑树,AVL树等这些,想象一下,给你一张草稿纸,一只笔,一个编辑器,你能立即实现一颗红黑树,或者AVL树出来吗? 很难吧,这需要时间,要考虑很多细节,要参考一堆算法与数据结构之类的树,还要参考网上的代码,相当麻烦。
那么在这种情况下,我们就可以用跳表。


那么什么是跳表
跳表是一种随机化的数据结构,目前开源软件 Redis 和 LevelDB 都有用到它,它的效率和红黑树以及 AVL 树不相上下,但跳表的原理相当简单,只要你能熟练操作链表,就能轻松实现一SkipList。

有序表的搜索
考虑一个有序表:

从该有序表中搜索元素 < 23, 43, 59 > ,需要比较的次数分别为 < 2, 4, 6 >,总共比较的次数
为 2 + 4 + 6 = 12 次。因为链表不能用二分查找,二分查找是正对与数组的。那有没有优化的算法吗? 类似二叉搜索树,我们把一些节点提取出来,作为索引。得到如下结构:

这里我们把 < 14, 34, 50, 72 > 提取出来作为一级索引,这样搜索的时候就可以减少比较次数了。
我们还可以再从一级索引提取一些元素出来,作为二级索引,变成如下结构:

这里元素不多,体现不出优势,如果元素足够多,这种索引结构就能体现出优势来了。
这基本上就是跳表的核心思想,其实也是一种通过“空间来换取时间”的一个算法,通过在每个节点中增加了向前的指针,从而提升查找的效率。

下面的结构是就是跳表:
其中 -1 表示 INT_MIN, 链表的最小值,1 表示 INT_MAX,链表的最大值。

跳表具有如下性质:
(1) 由很多层结构组成
(2) 每一层都是一个有序的链表
(3) 最底层(Level 1)的链表包含所有元素
(4) 如果一个元素出现在 Level i 的链表中,则它在 Level i 之下的链表也都会出现。
(5) 每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素。
跳表的搜索

例如查找元素 117
(1) 比较 21, 比 21 大,往后面找
(2) 比较 37, 比 37大,比链表最大值小,从 37 的下面一层开始找
(3) 比较 71, 比 71 大,比链表最大值小,从 71 的下面一层开始找
(4) 比较 85, 比 85 大,从后面找
(5) 比较 117, 等于 117, 找到了节点。

 

java实现

import java.util.Random;

public class SkipList {
private static Node head; // 头结点
private static Node tail; // 尾结点
private static int level; // 层数
private static int size; // 节点个数
private static Random random; // 随机数来确定层数

public SkipList() {
head = new Node(Integer.MIN_VALUE); // 头结点为最小值
tail = new Node(Integer.MAX_VALUE); // 尾结点为最大值
// 连起来
head.setRight(tail);
tail.setLeft(head);
level = 0; // 初始化层数为0
size = 0; // 初始化节点个数为0
random = new Random(); // new出random对象
}

// 判断是否为空
public static boolean isEmpty() {
if (size == 0) {
return true;
}
return true;
}

// 查找到需要插入的前一个节点
public static Node findPrevNode(Integer data) {
Node node = head;
while (node != null) {
// node的头结点不为尾结点并且node的值不大于data的时候,就一直往右走,否则循环结束
while (node.getRight() != tail && node.getRight().getValue() <= data) {
node = node.getRight();
}
// 如果当前节点有下节点,就继续往下走
if (node.getDown() != null) {
node = node.getDown();
} else {
// 否则返回当前节点
return node;
}
}
return null;
}

// 查找节点
public static Node findNode(Integer data) {
Node node = findPrevNode(data);
if (node == null) {
return null;
}
if (node.getValue() == data) {
return node;
}
return null;
}

public static void add(Integer data) {
// 找到前一个节点
Node prev = findPrevNode(data);
if (prev == null) {
return;
}
if (data == prev.getValue()) {
System.out.println("插入重复");
return;
}
// 插入
Node addNode = new Node(data);
addNode.setLeft(prev);
addNode.setRight(prev.getRight());
prev.getRight().setLeft(addNode);
prev.setRight(addNode);
int high = 0;// 当前层数
while (random.nextDouble() < 0.5) { // 进行随机,是否需要往上层添加
if (high >= level) { // 如果当前层数超出了高度,就需要新建一层
Node node1 = new Node(Integer.MIN_VALUE);
Node node2 = new Node(Integer.MAX_VALUE);
node1.setRight(node2);
node1.setDown(head);
node2.setLeft(node1);
node2.setDown(tail);
head.setUp(node1);
tail.setUp(node2);
level++;
head = node1;
tail = node2;
}
// 找到最上一层
while (prev.getUp() == null) {
prev = prev.getLeft();
}
prev = prev.getUp();
Node node = new Node(data);
node.setLeft(prev);
node.setRight(prev.getRight());
node.setDown(addNode);
prev.getRight().setLeft(node);
prev.setRight(node);
addNode.setUp(node);
addNode = node;
high++; // 高度+1
}
size++; // 节点个数+1
}

// 删除节点
public static boolean remove(Integer data) {
Node node = findNode(data);
if (node == null) {
return false;
}
while (node != null) {
node.getLeft().setRight(node.getRight());
node.getRight().setLeft(node.getLeft());
node = node.getUp();
}
return true;
}

public static void print() {
Node node;
Node node1 = head;
while (node1 != null) {
int k = 0;
node = node1;
while (node != null) {
System.out.print(node.getValue() + "\t");
k++;
node = node.getRight();
}

System.out.print("\t");
System.out.print("(" + k + ")");
System.out.println();
node1 = node1.getDown();

}
}

public static void main(String[] args) {
SkipList sl = new SkipList();
for (int i = 0; i < 30; i++) { // 随机数字进行测试
add(i);
}
print();
System.out.println("----------");
if (findNode(22) != null) { // 查找
System.out.println("OK");
} else {// 找不到
System.out.println("false");

}
remove(0); // 删除
print();

}
}

// 定义节点
class Node {
private Integer value;
private Node up, down, left, right;

public Node(Integer value) {
this.value = value;
}

public Integer getValue() {
return value;
}

public void setValue(Integer value) {
this.value = value;
}

public Node getUp() {
return up;
}

public void setUp(Node up) {
this.up = up;
}

public Node getDown() {
return down;
}

public void setDown(Node down) {
this.down = down;
}

public Node getLeft() {
return left;
}

public void setLeft(Node left) {
this.left = left;
}

public Node getRight() {
return right;
}

public void setRight(Node right) {
this.right = right;
}

}
View Code

结果

 

posted @ 2019-07-30 14:31  橘子洲头。  阅读(210)  评论(0编辑  收藏  举报