目录
跳表(Skip List)是一种基于链表的数据结构,它主要用于实现高效的有序集合。
跳表概念和性质:
1.跳表由多个层次的链表组成,每一层都是一个有序的链表。
2.最底层的链表包含所有元素。
3.如果一个元素出现在Level i的链表中,则它在Level i之下的链表也都会出现。
4.每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素。
5.跳表的层次是随机函数产生的(抛硬币),数据越多层级多,但实际代码中会限制一个最大层级。
6.查找元素过程就是从顶层开始,指针不停地右移和下移。
7.跳表本质就是为了性能空间换时间。
图解元素查找插入过程:
元素插入需要先查找定位,然后再插入,实际上包含了查找过程。
约定: 抛硬币函数,返回1代表正面向上,需要向上层插入该元素,返回0则不需要将该元素插入到上一次。
插入过程:
1.假设跳表初始状态如下:level1 已经插入了30 40 50 60 70 90 这些元素, 且通过抛硬币函数一共产生了4个层级。
2.插入新元素 80
3.从level4 的链表(顶层链表)开始,比较level4链表的第一个元素80>30,指针尝试右移,发现到达链尾,指针下移到 level3.
4.指针在level3 上右移, 80>50 ,指针尝试右移,发现到达链尾,指针下移到level2
5.指针level2 上右移到70, 80>70需要继续右移,到达链尾,指针下移到level1
6.指针在Level1 右移, 80<90 , 直接将80插入到 70和90之间即可。
7.80插入完毕,开始执行抛硬币函数,正面向上,需要将80插入到上一层level2, 指针移动到level2 的 70 ,然后将80插入到70后面。
8.Level2 70后面插入80完毕,继续执行抛硬币函数,反面向上,不需要要将80插入到上一层level3 了, 插入结束。
代码实现:
java
import java.lang.reflect.Array;
import java.lang.IllegalStateException;
import java.util.AbstractList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Random;
/**
* Implements the List interface as a skiplist so that all the
* standard operations take O(log n) time
*
* TODO: currently, listIterator() return an iterator that takes O(log n)
* time per step
* @author morin
*
* @param <T>
*/
public class SkiplistList<T> extends AbstractList<T> {
class Node {
T x;
Node[] next;
int[] length;
@SuppressWarnings("unchecked")
public Node(T ix, int h) {
x = ix;
next = (Node[])Array.newInstance(Node.class, h+1);
length = new int[h+1];
}
public int height() {
return next.length - 1;
}
}
/**
* This node sits on the left side of the skiplist
*/
protected Node sentinel;
/**
* The maximum height of any element
*/
int h;
/**
* The number of elements stored in the skiplist
*/
int n;
/**
* A source of random numbers
*/
Random rand;
public SkiplistList() {
n = 0;
sentinel = new Node(null, 32);
h = 0;
rand = new Random(0);
}
/**
* Find the node that precedes list index i in the skiplist.
*
* @param x - the value to search for
* @return the predecessor of the node at index i or the final
* node if i exceeds size() - 1.
*/
protected Node findPred(int i) {
Node u = sentinel;
int r = h;
int j = -1; // index of the current node in list 0
while (r >= 0) {
while (u.next[r] != null && j + u.length[r] < i) {
j += u.length[r];
u = u.next[r];
}
r--;
}
return u;
}
public T get(int i) {
if (i < 0 || i > n-1) throw new IndexOutOfBoundsException();
return findPred(i).next[0].x;
}
public T set(int i, T x) {
if (i < 0 || i > n-1) throw new IndexOutOfBoundsException();
Node u = findPred(i).next[0];
T y = u.x;
u.x = x;
return y;
}
/**
* Insert a new node into the skiplist
* @param i the index of the new node
* @param w the node to insert
* @return the node u that precedes v in the skiplist
*/
protected Node add(int i, Node w) {
Node u = sentinel;
int k = w.height();
int r = h;
int j = -1; // index of u
while (r >= 0) {
while (u.next[r] != null && j+u.length[r] < i) {
j += u.length[r];
u = u.next[r];
}
u.length[r]++; // accounts for new node in list 0
if (r <= k) {
w.next[r] = u.next[r];
u.next[r] = w;
w.length[r] = u.length[r] - (i - j);
u.length[r] = i - j;
}
r--;
}
n++;
return u;
}
/**
* Simulate repeatedly tossing a coin until it comes up tails.
* Note, this code will never generate a height greater than 32
* @return the number of coin tosses - 1
*/
protected int pickHeight() {
int z = rand.nextInt();
int k = 0;
int m = 1;
while ((z & m) != 0) {
k++;
m <<= 1;
}
return k;
}
public void add(int i, T x) {
if (i < 0 || i > n) throw new IndexOutOfBoundsException();
Node w = new Node(x, pickHeight());
if (w.height() > h)
h = w.height();
add(i, w);
}
public T remove(int i) {
if (i < 0 || i > n-1) throw new IndexOutOfBoundsException();
T x = null;
Node u = sentinel;
int r = h;
int j = -1; // index of node u
while (r >= 0) {
while (u.next[r] != null && j+u.length[r] < i) {
j += u.length[r];
u = u.next[r];
}
u.length[r]--; // for the node we are removing
if (j + u.length[r] + 1 == i && u.next[r] != null) {
x = u.next[r].x;
u.length[r] += u.next[r].length[r];
u.next[r] = u.next[r].next[r];
if (u == sentinel && u.next[r] == null)
h--;
}
r--;
}
n--;
return x;
}
public Iterator<T> iterator() {
class SkiplistIterator implements Iterator<T> {
Node u;
int i;
boolean removable;
public SkiplistIterator() {
u = sentinel;
i = -1;
removable = false;
}
public boolean hasNext() {
return u.next[0] != null;
}
public T next() {
if (u.next[0] == null)
throw new NoSuchElementException();
u = u.next[0];
i++;
removable = true;
return u.x;
}
public void remove() {
if (!removable)
throw new IllegalStateException();
SkiplistList.this.remove(i);
i--;
removable = false;
}
}
return new SkiplistIterator();
}
public void clear() {
n = 0;
h = 0;
Arrays.fill(sentinel.length, 0);
Arrays.fill(sentinel.next, null);
}
public int size() {
return n;
}
public static void main(String[] args) {
int n = 20;
List<Integer> l = new SkiplistList<Integer>();
for (int i = 0; i < n; i++) {
l.add(i);
}
System.out.println(l);
for (int i = -1; i > -n; i--) {
l.add(0,i);
}
System.out.println(l);
for (int i = 0; i < 20; i++) {
l.add(n+i,1000+i);
}
System.out.println(l);
}
}