SkipList

1、什么是SkipList

当使用链表存放有序数据的时候,查找某个数据或者添加某个数据的复杂度是O(n),每次查找都需要顺序遍历。加快搜索的方法有哪些呢?想想数据库的设计思路,对,使用索引,利用空间换取时间。跳表基本上采用的就是这个思路。


图1

在最底层的链表中随机抽取一定是数量的节点作为索引,形成另一个链表。然后继续从新生成的链表中随机抽取点作为新的索引,继续形成另一个链表。将这个步骤继续多次。
可以得到上面图中的结构。通过观察上面的图,可以发现越往上的链表越稀疏。如果对多叉树比较熟悉的话,可以将上面的结构立即为多叉树。因此可以猜测SkipList的时间复杂度是O(ogn)。

来看看SkipList如何工作的。


图2
由于上层的链表十分稀疏,所以上层链表遍历的时候可以忽略掉底层链表中许多的元素。比如上面的图中,插入的数据是80的时候,从30到50可以把40忽略掉。如果链表中的数据规模达到一定地步,上层链表的一次遍历可以跳过的元素是相当可观的。就像二叉树一样,没经过一个节点都可以跳过当前元素的一般。而由于SkipList每层链表都是按照一定概率生成的,虽然不是像二叉树那样经过一个节点可以忽略一半元素,但是效果也还是不错的。

2、SkipList中的概念

2.1、Node

每个节点有一个值,用于排序。并且有一个数组用于存放后面的节点。比如图1中,节点6的nexts数组大小是4, 数组的数据依次指向节点7,节点9,节点25,NIL节点。


2.2、SkipList

跳表中有一个head节点,同时设置一个level表示当前最大的层数。比如图2中,level = 4。

而且每个跳表都有一个常量的MAX_LEVEL,链表层数无论如何增加也不能超过这个值。

2.3、NIL

NIL值用于表示比任何值都大。一般存放在每个链表的结尾。表示当前链表搜索可以结束了。

3、SkipList的初始化

An element NIL is given a key greater than any legal key. All levels of all skip lists are terminated with NIL.A new list is initialized so that the levelof the list is equal to 1 and all forward pointers of the list’s header point to NIL.
(摘自论文  A Skip List Cookbook  Author: William Pugh)

4、SkipList的搜索

We search for an element by traversing forward pointers that do not overshoot the node containing the element being searched for (Figure 2). When no more progress can be made at the current level of forward pointers, the search moves down to the next level. When we can make no more progress at level 1, we must be immediately in front of the node that contains the desired element (if it is in the list).

Search(list, searchKey)
	x := list→header
	-- loop invariant: x→key < searchKey
	for i := list→level downto1 do
		whilex→forward[i]→key < searchKey do
			x := x→forward[i]
	-- x→key < searchKey ≤x→forward[1]→key
	x := x→forward[1]
	if x→key = searchKey then returnx→value
	else return failure

5、SkipList的插入和删除

To insert or delete a node, we simply search and splice.Figure 3 gives algorithms for insertion and deletion. A vector updateis maintained so that when the search is complete (and we are ready to perform the splice), update[i] contains a pointer to the rightmost node of level ior higher that is to the left of the location of the insertion/deletion. If an insertion generates a node with a level greater than the previous maximum level of the list, we update the maximum level of the list and initialize the appropriate portions of the update vector. After each deletion, we check if we have deleted the maximum element of the list and if so, decrease the maximum level of the list.

Insert(list, searchKey, newValue)
	localupdate[1..MaxLevel]
	x := list→header
	for i := list→level down to 1 do
		whilex→forward[i]→key < searchKey do
			x := x→forward[i]
		update[i] := x
	x := x→forward[1]
	if x→key = searchKey then x→value := newValue
	else
		newLevel := randomLevel()
		if newLevel > list→level then
			for i := list→level + 1 to newLevel do
				update[i] := list→header
			list→level := newLevel
		x := makeNode(newLevel , searchKey, value)
		for i := 1 tonewLevel do
			x→forward[i] := update[i]→forward[i]
			update[i]→forward[i] := x

Delete(list, searchKey)
	local update[1..MaxLevel]
	x := list→header
	for i := list→level down to 1 do
		while x→forward[i]→key < searchKey do
			x := x→forward[i]
		update[i] := x
	x := x→forward[1]
	if x→key = searchKey then
		for i := 1 to list→level do
			if update[i]→forward[i] ≠ x then break
			update[i]→forward[i] := x→forward[i]
		free(x)
		while list→level > 1 and list→header→forward[list→level] = NIL do
			list→level := list→level – 1	

6、新插入节点随机层数算法

randomLevel()
	lvl := 1
	while random() < p and lvl < MaxLevel do
		lvl := lvl + 1
	return lvl

这里设置p为1/2。
如果random()函数设计的足够好,那么每次random()产生的数据都有50%的概率小于1/2。也就是说层数为1的概率为100%,层数为2的概率为50%,层数为3的概率为25%,层数为4的概率为12.5%,依次衰减。所以可以看出越往上的链表,其中的节点数也就越少。

7、java版本的实现

import java.util.ArrayList;
import java.util.List;


public class Node<T extends Comparable<T>> {
	private T value;
	private List<Node<T>> nexts;
	
	public Node(){
		this.value = null;
		this.nexts = new ArrayList<Node<T>>(0);
	}
	
	public Node(T value){
		this.value = value;
		this.nexts = new ArrayList<Node<T>>(0);
	}
	
	public Node(int size){
		this.value = null;
		this.nexts = new ArrayList<Node<T>>(size);
	}
	
	public void setValue(T value) {
		this.value = value;
	}
	public T getValue() {
		return value;
	}
	public void setNexts(List<Node<T>> nexts) {
		this.nexts = nexts;
	}
	public List<Node<T>> getNexts() {
		return nexts;
	}
}

注意这里的nexts数组就是伪代码中的forward数组。

public class SkipList<T extends Comparable<T>> {
	public static final int MAX_LEVEL = 100;
	// the head of the SkipList
	private Node<T> head = null;

	private int level = 0;
	// initial of the SkipList
	public SkipList() {
		// 初始化头部
		setHead(new Node<T>(MAX_LEVEL));

		// 初始化head的nexts数组
		for (int i = 0; i < MAX_LEVEL; i++) {
			head.getNexts().add(null);
		}
		
		// 设置深度
		setLevel(1);
	}

	@SuppressWarnings("unchecked")
	public void insert(T newValue) {
		if (newValue == null) {
			return;
		}
		// 更新数组
		Node<T> update[] = new Node[MAX_LEVEL];
		// 获得头部
		Node<T> p = this.head;

		int j = this.level - 1;
		while (j >= 0) {
			// 下一个节点不为NULL 并且  值小于newValue
			while ((Node<T>) p.getNexts().get(j) != null
					&& newValue.compareTo(((Node<T>) p.getNexts().get(j))
							.getValue()) >= 0) {
				p = (Node<T>) p.getNexts().get(j);
			}
			update[j] = p;
			j--;
		}

		int newLevel = randomLevel();

		if (newLevel > this.level) {
			// 当newLevel大于当前的level时,设置更新数组中level到newLevel为head
			for (int i = newLevel - 1; i > level - 1; i--) {
				update[i] = this.head;
			}

			// 当newLevel大于当前的level时,需要更新当前level
			this.level = newLevel;
		}

		Node<T> newNode = new Node<T>();
		newNode.setValue(newValue);

		for (int i = 0; i < newLevel; i++) {
			newNode.getNexts().add(update[i].getNexts().get(i));
			update[i].getNexts().set(i, newNode);
		}
		
	}
	
	//获取随机层数
	private int randomLevel() {
		int level = 1;
		while (Math.random() < 1.0 / 2.0 && level < MAX_LEVEL) {
			level++;
		}
		return level;
	}
	public void setHead(Node<T> head) {
		this.head = head;
	}
	public Node<T> getHead() {
		return head;
	}

	public void setLevel(int level) {
		this.level = level;
	}

	public int getLevel() {
		return level;
	}

	public static void main(String[] args) {
		SkipList<Integer> skip = new SkipList<Integer>();

		for (int i = 0; i < 10; i++) {
			skip.insert(new Integer(i));
		}
	}
}





  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值