跳跃链表简称为跳表(SkipList),它维护了一个多层级的链表,且第i+1层链表中的节点是第i层链表中的节点的子集。跳表作为一种平衡数据结构,经常和平衡树进行比较,在大多数场景下,跳表都可以达到平衡树的效率(查询节点支持平均O(lgN),最坏O(N)的复杂度),但实现和维护起来却比平衡树简单很多。(跳跃列表由 William Pugh 发明。他在 Communications of the ACM发表了《Skip lists: a probabilistic alternative to balanced trees》,在其中详细描述了他的工作)
这里需要明确一下跨度的定义:从前驱节点到自身经历的节点数(包含前驱节点在内),如下图连接线上数字即为箭头指向节点的在对应层的跨度。
跳表,持有跳表的结构包括指向跳表表头和表尾的指针,以及整个跳表的长度(即第一层的节点数,但不包含头结点),还有整个跳表最高层的层数。这里需要指出的是,表头节点是一个包含了所有层的虚拟节点(不包含任何数据),每一层中表头节点的forward都指向该层的第一个真实节点。
最终,Redis中的一个长度为2,层高为2的跳表如下图所示
在一个长度为4,高度为5的跳表中插入score为20,值为字符串c的节点,首先由上至下遍历每层查找插入位置,同时维护每层的rank值和update节点,遍历完之后,rank和update数组如图中所示,我们将其中的4层(即i=3)拿出来分析一下。在插入节点之前,update[3]指向头结点,rank[3]=0,rank[0]=2,第三层左边节点到右边节点的跨度是4,那么从图中可以看出,新节点的跨度=左边节点(即插入节点的前驱)跨度-(rank[0]-rank[3]),这里的rank[0]-rank[3]可以进一步模型化为rank[0]-rank[i],从上文中可以看出来,实际上rank[0]-rank[i]就表示,插入节点与插入节点前驱节点之间的节点数(不包含前驱节点自身),所以在原跨度中将其减掉,就是插入节点的跨度了。
当节点插入后,如下图所示。可以通过图中公式计算出插入节点的前驱节点的跨度。
整个例子走下来之后,我们可以通过流程图来回顾一下:
代码如下:
原创声明,本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。