数据结构--跳表(skip List)(二十一)

学习恋上数据结构与算法的记录,本篇主要内容是跳表

思考
一个有序链表搜索、添加、删除的平均时间复杂度是多少?O(n)
能否利用二分搜索优化有序链表,将搜索、添加、删除的平均时间复杂度降低至O(logn)?
链表没有像数组那样的高效随机访问(O(1)时间复杂度),所以不能像有序数组那样直接进行二分搜索优化
那有没有其他办法让有序链表搜索、添加、删除的平均时间复杂度降低至O(logn)?
使用跳表(SkipList)

跳表(SkipList)

又叫做跳跃表、跳跃列表,在有序链表的基础上增加了“跳跃”的功能

由William Pugh于1990年发布,设计的初衷是为了取代平衡树(比如红黑树)
Redis中的SortedSet、LevelDB 中的MemTable 都用到了跳表
Redis、LevelDB 都是著名的Key-Value 数据库

●对比平衡树:
跳表的实现和维护会更加简单
跳表的搜索、删除、添加的平均时间复杂度是O(logn)

使用跳表优化链表(图片来源于网络)
在这里插入图片描述
跳表的搜索
①从顶层链表的首元素开始,从左往右搜索,直至找到一个大于或等于目标的元素,或者到达当前层链表的尾部
②如果该元素等于目标元素,则表明该元素已被找到
③如果该元素大于目标元素或已到达链表的尾部,则退回到当前层的前一个元素,然后转入下一层进行搜索

跳表的添加、删除
在这里插入图片描述
●添加的细节
随机决定新添加元素的层数
●删除的细节
删除一个元素后,整个跳表的层数可能会降低

跳表的层数
跳表是按层构造的,底层是一个普通的有序链表,高层相当于是低层的“快速通道”
在第i 层中的元素按某个固定的概率p(通常为½ 或¼ )出现在第i + 1层中,产生越高的层数,概率越低
✓元素层数恰好等于1 的概率为1 –p
✓元素层数大于等于2 的概率为p,而元素层数恰好等于2 的概率为p * (1 –p)
✓元素层数大于等于3 的概率为p^2,而元素层数恰好等于3 的概率为p^2 * (1 –p)
✓元素层数大于等于4 的概率为p^3,而元素层数恰好等于4 的概率为p^3 * (1 –p)
✓…
✓一个元素的平均层数是1 / (1 –p
在这里插入图片描述
当p = ½ 时,每个元素所包含的平均指针数量是2
当p = ¼ 时,每个元素所包含的平均指针数量是1.33

跳表的复杂度分析
每一层的元素数量
第1 层链表固定有n 个元素
第2 层链表平均有n * p 个元素
第3 层链表平均有n * p^2 个元素
第k 层链表平均有n * p^k 个元素

另外
最高层的层数是log1/pn,平均有个1/p 元素
在搜索时,每一层链表的预期查找步数最多是1/p,所以总的查找步数是–(logpn/p),时间复杂度是O(logn)

java实现

public class SkipList<K, V> {
	private static final int MAX_LEVEL = 32;
	private static final double p = 0.25;
	private int size;
	private Comparator<K> comparator;
	/**
	 * 有效层数
	 */
	private int level;
	/**
	 * 头节点 不存放任何K-V
	 */
	private Node<K, V> first;

	public SkipList(Comparator<K> comparator) {
		this.comparator = comparator;
		first = new Node<>(null, null, MAX_LEVEL);
	}

	public SkipList() {
		this(null);
	}

	public int size() {
		return size;
	}

	public boolean isEmpty() {
		return size == 0;
	}

	public V get(K key) {
		KeyCheck(key);
		Node<K, V> node = first;
		for (int i = level - 1; i >= 0; i--) {
			int cmp = -1;
			while (node.nexts[i] != null && (cmp = compare(key, node.nexts[i].key)) > 0) {
				node = node.nexts[i];
			}
			if (cmp == 0)
				return node.nexts[i].value;
		}
		return null;
	}

	public V put(K key, V value) {
		KeyCheck(key);
		Node<K, V> node = first;
		Node<K, V>[] prevs = new Node[level];
		for (int i = level - 1; i >= 0; i--) {
			int cmp = -1;
			while (node.nexts[i] != null && (cmp = compare(key, node.nexts[i].key)) > 0) {
				node = node.nexts[i];
			}
			if (cmp == 0) {
				// 节点是存在的 覆盖
				V old = node.nexts[i].value;
				node.nexts[i].value = value;
				return old;
			}
			prevs[i] = node;
		}
		// 新节点的层数
		int newLevel = randomLevel();
		// 添加新节点
		Node<K, V> newNode = new Node<>(key, value, newLevel);
		// 设置前驱和后继
		for (int i = 0; i < newLevel; i++) {
			if (i >= level) {
				first.nexts[i] = newNode;
			} else {
				newNode.nexts[i] = prevs[i].nexts[i];
				prevs[i].nexts[i] = newNode;
			}
		}
		// 节点数量增加
		size++;
		// 计算跳表的最终层数
		level = Math.max(newLevel, level);
		return null;
	}

	public V remove(K key) {
		KeyCheck(key);
		Node<K, V> node = first;
		Node<K, V>[] prevs = new Node[level];
		boolean exits = false;
		for (int i = level - 1; i >= 0; i--) {
			int cmp = -1;
			while (node.nexts[i] != null && (cmp = compare(key, node.nexts[i].key)) > 0) {
				node = node.nexts[i];
			}
			prevs[i] = node;
			if (cmp == 0)
				exits = true;
		}
		if (!exits)
			return null;
		// 需要被删除的节点
		Node<K, V> removedNode = node.nexts[0];

		// 数量减少
		size--;
		
		// 设置后继
		for (int i = 0; i < removedNode.nexts.length; i++) {
			prevs[i].nexts[i] = removedNode.nexts[i];
		}
		
		// 更新跳表的层数
		int newLevel = level;
		while(--newLevel >=0 && first.nexts[newLevel] == null) {
			level = newLevel;
		}
		return removedNode.value;
	}

	private int randomLevel() {
		int level = 1;
		while (Math.random() < p && level < MAX_LEVEL) {
			level++;
		}
		return level;
	}

	private void KeyCheck(K key) {
		if (key == null) {
			throw new IllegalArgumentException("key must not be null.");
		}
	}

	private int compare(K k1, K k2) {
		return comparator != null ? comparator.compare(k1, k2) : ((Comparable<K>) k1).compareTo(k2);
	}

	private static class Node<K, V> {
		K key;
		V value;
		Node<K, V>[] nexts;

		public Node(K key, V value, int level) {
			this.key = key;
			this.value = value;
			nexts = new Node[level];
		}

		@Override
		public String toString() {
			return key + ":" + value + "_" + nexts.length;
		}
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("一共" + level + "层").append("\n");
		for (int i = level - 1; i >= 0; i--) {
			Node<K, V> node = first;
			while (node.nexts[i] != null) {
				sb.append(node.nexts[i]);
				sb.append(" ");
				node = node.nexts[i];
			}
			sb.append("\n");
		}
		return sb.toString();
	}
}

猜你喜欢

转载自blog.csdn.net/qq_44961149/article/details/105489380
今日推荐