介绍
在二分查找中,我们需要对数组进行随机访问,而在普通的单链表中,我们只能从头到尾遍历,即使单链表是有序的。
跳跃表是有序的数据结构,在每个节点维持了多个指向其他节点的指针,达到快速访问节点的目的。
跳跃表查找的时间复杂度最坏为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