19. 跳表(Skip List)

1 跳表
  1. 一个有序链表搜索、添加、删除的平均时间复杂度是多少?
    1. 数组查找的时间复杂度可以达到O(logn)是因为数组支持随机访问,对于有序的数组,可以通过二分查找,达到O(logn)的效率
      在这里插入图片描述
  2. 能否利用二分搜索优化有序链表,将搜索、添加、删除的平均时间复杂度降低至 O(logn)?
    1. 链表没有像数组那样的高效随机访问(O(1) 时间复杂度),所以不能像有序数组那样直接进行二分搜索优化
  3. 那有没有其他办法让有序链表搜索、添加、删除的平均时间复杂度降低至 O(logn)?
    1. 使用跳表(SkipList)
  4. 跳表,又叫做跳跃表、跳跃列表,在有序链表的基础上增加了“跳跃”的功能。设计的初衷是为了取代平衡树(比如红黑树),跳表类似TreeMap,都存放key-value
  5. Redis中 的 SortedSet、LevelDB 中的 MemTable 都用到了跳表
  6. 对比平衡树
    1. 跳表的实现和维护会更加简单
    2. 跳表的搜索、删除、添加的平均时间复杂度是 O(logn)
  7. 使用跳表优化链表
    在这里插入图片描述
  8. 跳表的搜索
    1. 从顶层链表的首元素开始,从左往右搜索,直至找到一个大于或等于目标的元素,或者到达当前层链表的尾部
    2. 如果该元素等于目标元素,则表明该元素已被找到
    3. 如果该元素大于目标元素或已到达链表的尾部,则退回到当前层的前一个元素,然后转入下一层进行搜索
  9. 跳表的添加、删除
    1. 本质就是在搜索的基础上,建立一个数组,存放要添加或删除的节点的所有层的前驱节点
    2. 新添加节点的层数,是随机以一定规则获得的
    3. 最后还需要考虑层数的更新
      在这里插入图片描述
  10. 跳表的层数
    1. 跳表是按层构造的,底层是一个普通的有序链表,高层相当于是低层的“快速通道”
    2. 在第 i 层中的元素按某个固定的概率 p(通常为 ½ 或 ¼ )出现在第 i + 1层中,产生越高的层数,概率越低
      1. 元素层数恰好等于 1 的概率为 1 – p
      2. 元素层数大于等于 2 的概率为 p,而元素层数恰好等于 2 的概率为 p * (1 – p)
      3. 元素层数大于等于 3 的概率为 p^2,而元素层数恰好等于 3 的概率为 (p ^2) * (1 – p)
      4. 将这些概率加和,除以层数,得到一个元素的平均层数是 1 / (1 – p)
        1. 当 p = ½ 时,每个元素所包含的平均指针数量是 2
        2. 当 p = ¼ 时,每个元素所包含的平均指针数量是 1.33
        3. 跳表中的指针数指的就是nexts数组中元素个数,而红黑树每个节点上指针至少是三个,next、parent、right,因此当p=1/4可以发现,其空间复杂度要好于红黑树
  11. 跳表的复杂度分析
    1. 每一层的元素数量
      1. 第 1 层链表固定有 n 个元素(最底层,一定是和总元素个数相同)
      2. 第 2 层链表平均有 n * p 个元素
      3. 第 3 层链表平均有 n * p^2 个元素
      4. 第 k 层链表平均有 n * p^k 个元素
    2. 跳表最高有 log (1/p) (n)层,搜索时,每一层链表的预期查找步数最多是 1/p
      1. 所以,如果p是1/4,那么总的查找步数是最高层数*每层最多步数,即log4(n)/4,
      2. 因此时间复杂度为O(logn)
  12. SkipList
package com.mj;

import java.util.Comparator;

@SuppressWarnings("unchecked")
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;
	//记录有效层数,方便搜索的时,从有效的最高层数开始搜索,例如从first.next[3]开始搜索,注意层数是从1开始的不是从0,所以循环时,都从first.nexts[level-1]开始
	private int level;
	/**
	 * 不存放任何K-V,如果存放就没法走不同的层了
	 */
	private Node<K, V> first;
	
	public SkipList(Comparator<K> comparator) {
   
		this.comparator = comparator;
		//设置其最高层为MAX_LEVEL,redis是32层,节点中最高就是32层
		//虽然有32层,但实际上可能最后用到的就几层,也就是可能只first.next[0]到first.next[3]有值,其他都为null
		first = new Node<>(null, null, MAX_LEVEL);
	}
	
	public SkipList() {
   
		this(null);
	}
	
	public int size() {
   
		return size;
	}
	
	public boolean isEmpty() {
   
		return size == 0
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值