简介
跳跃表支持插入、删除、搜索。是个动态(插删)搜索数据结构。同时每个操作复杂度都是对数级别的。
“是个随机化的、简单的数据结构。”
“几乎可以保证每一步操作都是logN的可能性为(1-1/n^α),n可以取很大。因此极小概率超过logN”
其他动态搜索数据结构:树堆(treaps)、红黑树(red-black trees)、B树(B-trees)。
结构
如果只是一个双向链表,则操作的复杂度是O(n)。
下图分别是wiki和课程里两种跳表数据结构的示意图。
其中,有多层“链表”组成,链表节点是有序的,最下层链表保存了所有数据。
搜索
- 再顶层链表上向右搜索,直到搜索过头,则返回。
- 向下走一层继续向右搜索。继续1。
- 直到搜索到该节点或者搜到头,或者底层中也没有该元素(至少确定了该插入的位置)。
复杂度分析
- 当只有两层时,考虑L2层的链表节点在L1层上均匀分布,L1层的个数为n,则复杂度为
∣ L 2 ∣ + n ∣ L 2 ∣ |L2| + \frac{n}{|L2|} ∣L2∣+∣L2∣n
因 此 当 ∣ L 2 ∣ = n 时 , 最 小 值 为 结 果 为 , 2 n 因此当|L2| = \sqrt{n} 时,最小值为结果为, 2\sqrt{n} 因此当∣L2∣=n时,最小值为结果为,2n - 当有n个节点时,设置log(n)层,每一层的节点个数是下一层的一半。因此每一层上最多只要走两步。最终复杂度为
2 ∗ l o g 2 n 2*log_{2}^{n} 2∗log2n
插入
- 通过搜索的步骤,找出待插入的位置。
- 判断该数据除了底层以外还需要插入到上层的哪些链表里。(随机,比如抛硬币),(1/2的概率往上升一层,如果往上升了,则接着1/2的概率往上升,即在每一层的概率为1,1/2,1/4,1/8…)、
注意:在一开始时要插入一个负无穷,让其升到最高(保持每个链表都有该元素),因为每次都从最上一个链表开始搜索。防止链表中第一个元素的高度小于后面的元素,则此情况无法在最高的链表上进行遍历。
删除
通过搜索找到该元素,再把每条链表上的该元素删除即可。
性质
with high probability
- 对于任意大于等于1的α,存在合适的常数,使事件E的概率至少为1-O(1/(n^α))
- 链表的最大层数的期望是log(N)。
证明复杂,略
代码实现
package com.wangqi.javase.datastruct;
import java.util.Random;
public class SkipList<T> {
private SkipListNode<T> head, tail;
private int nodes;//节点总数
private int listLevel;//层数
private Random random;// 用于投掷硬币
private static final double PROBABILITY = 0.5;//向上提升一个的概率
public SkipList() {
random = new Random();
clear();
}
/**
* 清空跳跃表
*/
public void clear() {
head = new SkipListNode<T>(SkipListNode.HEAD_KEY, null);
tail = new SkipListNode<T>(SkipListNode.TAIL_KEY, null);
horizontalLink(head, tail);
listLevel = 0;
nodes = 0;
}
public int size() {
return nodes;
}
/**
* 从最高层第一个(左上角即head开始找),一直找到最下面一层,找到要插入的位置前面的那个key
*/
private SkipListNode<T> findNode(int key) {
SkipListNode<T> p = head;
while (true) {
while (p.right.key != SkipListNode.TAIL_KEY && p.right.key <= key) {
p = p.right;
}
if (p.down != null) {
p = p.down;
} else {
break;
}
}
return p;
}
/**
* 查找是否存在key,存在则返回该节点,否则返回null
*/
public SkipListNode<T> search(int key) {
SkipListNode<T> p = findNode(key);
if (key == p.key) {
return p;
} else {
return null;
}
}
/**
* 向跳跃表中添加key-value
*/
public void put(int k, T v) {
SkipListNode<T> p = findNode(k);
//如果key值相同,替换原来的vaule即可结束
if (k == p.key) {
p.value = v;
return;
}
SkipListNode<T> q = new SkipListNode<T>(k, v);
//在p后面插入q
backLink(p, q);
int currentLevel = 0;//当前所在的层级是0
//抛硬币
while (random.nextDouble() < PROBABILITY) {
//如果超出了高度,需要重新建一个顶层
if (currentLevel >= listLevel) {
listLevel++;
SkipListNode<T> p1 = new SkipListNode<T>(SkipListNode.HEAD_KEY, null);
SkipListNode<T> p2 = new SkipListNode<T>(SkipListNode.TAIL_KEY, null);
horizontalLink(p1, p2);
vertiacallLink(p1, head);
vertiacallLink(p2, tail);
head = p1;
tail = p2;
}
//将p移动到上一层
while (p.up == null) {
p = p.left;
}
p = p.up;
SkipListNode<T> e = new SkipListNode<T>(k, null);//只保存key就ok
backLink(p, e);//将e插入到p的后面
vertiacallLink(e, q);//将e和q上下连接
q = e;
currentLevel++;
}
nodes++;//层数递增
}
/**
* 在跳跃表中删除k
*/
public boolean remove(int k) {
SkipListNode<T> p = findNode(k);
if (p.key != k) { //跳表中不存在k
return false;
}
// 从底层开始往上删
while (p != null) {
horizontalLink(p.left, p.right);
p = p.up;
}
nodes--;
// 判断链表的最高层是否受到影响
while (head.right == tail && listLevel != 1) {
head = head.down;
tail = tail.down;
listLevel--;
}
return true;
}
//node1后面插入node2
private void backLink(SkipListNode<T> node1, SkipListNode<T> node2) {
node2.left = node1;
node2.right = node1.right;
node1.right.left = node2;
node1.right = node2;
}
/**
* 水平双向连接
*/
private void horizontalLink(SkipListNode<T> node1, SkipListNode<T> node2) {
node1.right = node2;
node2.left = node1;
}
/**
* 垂直双向连接
*/
private void vertiacallLink(SkipListNode<T> node1, SkipListNode<T> node2) {
node1.down = node2;
node2.up = node1;
}
/**
* 打印出原始数据
*/
@Override
public String toString() {
if (size() == 0) {
return "跳跃表为空!";
}
StringBuilder builder = new StringBuilder();
SkipListNode<T> p = head;
while (p.down != null) {
p = p.down;
}
while (p.left != null) {
p = p.left;
}
if (p.right != null) {
p = p.right;
}
while (p.right != null) {
builder.append(p);
builder.append("\n");
p = p.right;
}
return builder.toString();
}
//节点
private static class SkipListNode<T> {
private int key;
private T value;
public SkipListNode<T> up, down, left, right; // 上下左右 四个指针
public static final int HEAD_KEY = Integer.MIN_VALUE; // 负无穷
public static final int TAIL_KEY = Integer.MAX_VALUE; // 正无穷
public SkipListNode(int k, T v) {
// TODO Auto-generated constructor stub
key = k;
value = v;
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null) {
return false;
}
if (!(o instanceof SkipListNode<?>)) {
return false;
}
SkipListNode<T> ent;
try {
ent = (SkipListNode<T>) o; // 检测类型
} catch (ClassCastException ex) {
return false;
}
return (ent.key == key) && (ent.value == value);
}
@Override
public String toString() {
return "key-value:" + key + "-" + value;
}
}
}
测试函数
package com.wangqi.javase.datastruct;
import java.util.HashMap;
import java.util.Map;
public class SkipListTest {
public static void main(String[] args) {
SkipList<String> skipList = new SkipList<>();
skipList.put(1, "tom");
//System.out.println(skipList.search(1));
//System.out.println(skipList.search(2));
skipList.put(2, "bob");
//System.out.println(skipList.search(2));
skipList.put(1, "jack");
//System.out.println(skipList.search(1));
System.out.println(skipList.remove(1));
System.out.println(skipList.remove(2));
System.out.println(skipList.size());
}
}