图解算法:跳表(Skip List)

目录

跳表概念和性质:

图解元素查找插入过程:

代码实现:


跳表(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);

	}

}
相关推荐
程序趣谈28 分钟前
算法随笔_12:最短无序子数组
算法
米饭「」1 小时前
数据结构-栈和队列
java·开发语言·数据结构
WXG10111 小时前
【matlab】matlab知识点及HTTP、TCP通信
算法
岸榕.2 小时前
树的连边II
算法·深度优先·图论
ExRoc2 小时前
蓝桥杯真题 - 异或和之差 - 题解
c++·算法·蓝桥杯
Python_enjoy2 小时前
洛谷题解 - P1003 [NOIP2011 提高组] 铺地毯
数据结构·c++·算法
理智的灰太狼2 小时前
求两个矩阵的乘积
线性代数·算法·矩阵
明月醉窗台2 小时前
C++ 之多线程相关总结
开发语言·c++·算法
多多*2 小时前
Java锁 从乐观锁和悲观锁开始讲 面试复盘
java·开发语言·前端·python·算法·面试·职场和发展
兑生3 小时前
力扣面试150 串联所有单词的子串 分组滑动窗口
算法·leetcode·面试