跳跃表 SkipList 原理及Java实现

介绍

在二分查找中,我们需要对数组进行随机访问,而在普通的单链表中,我们只能从头到尾遍历,即使单链表是有序的。
跳跃表是有序的数据结构,在每个节点维持了多个指向其他节点的指针,达到快速访问节点的目的。
跳跃表查找的时间复杂度最坏为O(N),平均O(logN),大部分情况可以和平衡二叉树媲美,而且实现更简单。
本文的实现和数据结构的定义参照了这篇论文《Skip Lists: A Probabilistic Alternative to Balanced Trees》

在这里插入图片描述
如上图:a就是最普通的单链表,在单链表的基础上进行修改,使得某些节点有指向后面其他节点的指针,跳过了单链表的下一个节点,直接指向了后面的节点,就形成的跳跃表。这样的好处是我们可以在高层更快地遍历节点。

数据结构的定义

public class SkipList {
   
	//节点
    class SkipListNode {
   
		//向前的指针
        SkipListNode[] forward;
		//键
        Comparable key;
		//数据
        Object value;

        SkipListNode(Comparable key, Object value) {
   
            this.forward = new SkipListNode[maxLevel];
            this.value = value;
            this.key = key;
        }
    }
	//头节点
    private SkipListNode header;
	//允许的最高层级
    private int maxLevel;
	//当前跳跃表的最高层级
    private int level;
	//有多少个元素
    private int size;
}

跳跃表的节点可以看成有若干层(实际上是同一个节点),最下面是第一层,每一层的指针指向其他节点的同一层,并且有高层则必须有底层。
我们可以这样来得到一个跳跃表:在单链表中,遍历一个元素,让他有1/2的几率得到第二层,再1/2的几率得到的三层。。。。。。层数越高,几率越低,并且不能超过允许的最高层级,再用该节点的每一层的指针指向下一个有该层的节点,没有就直接指向空。这样,层级越高,跨度越大,跳跃越快。头节点拥有每一层级的指针,并且大于当前跳跃表的最高层级节点的层级指针指向空。

算法设计

查找

在查询一个节点的过程中,我们首先访问跳跃表头节点的最高层级,我们看指向的元素是否小于我们要找的元素,如果是,那么就向右移动,直到不小于或者下一个为空为止。如果该层级的下一个元素不为空并且等于要找的元素,那么可以直接返回数据,否则,向下移动一个层级,再重复上述过程,直到找到,或者不能再向下移动了返回空。
在这里插入图片描述

例如:上面图的e,我们找17。

  • 我们在头节点的第四层,下一个是6,小于17,向右移动
  • 在6的第四层,右边是空,向下移动
  • 在6的第三层,右边是25,大于17,向下移动
  • 在6的第二层,右边是9, 小于17,向右移动
  • 在9的第二层,右边是17,返回17的结果

插入

插入的过程中,我们用类似查找的方法找到元素在第一层的应当放置的位置,即左边小,右边大,如果在过程中发现了该元素,那么将新的数据替换掉原来的数据。生成一个新的节点,我们先定下它的层级,拥有第二层的几率是1/2,拥有第三层的几率是1/4。。。。。。另外,插入一个元素我们可能需要改变左边节点的指针的指向(当生成的节点的层级高于该指针时),我们用update数组来记录需要被改变的节点,以及改变哪一层。每一层其实只有一个节点需要改变该层的指针,那么我们用update[i]存放可能要改变的节点,让它的第i+1层指针指向新的节点,如果生成的节点层级大于当前最高层级,我们还要把header放入对应的update位置中,如下图
在这里插入图片描述
如上图所示:我们需要在上面的结构中插入17的节点,我们在寻找的过程中,记录下了update = [12, 9, 6, 6],意思是6的第四层和第三层,9的第二层,和12的第一层可能需要改变指针指向,而我们生成的17只有两层,那么就只改9和12。在生成17节点后,我们将17节点每一层的指针指向update数组对应层的节点的原来的下一个节点,比如第二层update[1] = 节点9,我们就将节点17的第二层指向原来节点9的原本下一个节点L节点25,然后再改变节点9的指向,让它指向17节点。

删除

删除的过程和插入的过程相似,同样用查找的方法来遍历,直到找到或者最后找不到,并且过程中也记录update数组,让其在节点删除后能够指向被删除节点的下一个

代码实现

@SuppressWarnings("unchecked")
public class SkipList {
   

    class SkipListNode {
   

        SkipListNode[] forward;

        Comparable key;

        Object value;

        SkipListNode(Comparable key, Object value) {
   
            this.forward = new SkipListNode[maxLevel];
            this.value = value;
            this.key = key;
        }
    }

    private SkipListNode header;

    private int maxLevel;

    private int level
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值