跳表(skiplist)的理解

听到跳表(skiplist)这个名字,既然是list,那么应该跟链表有关。 
跳表是有序链表,但是我们知道,即使对于排过序的链表,我们对于查找还是需要进行通过链表的指针进行遍历的,时间复杂度很高依然是O(n),这个显然是不能接受的。是否可以像数组那样,通过二分法进行查找呢,但是由于在内存中的存储的不确定性,不能这做。

但是我们可以结合二分法的思想,没错,跳表就是链表与二分法的结合。 
1.链表从头节点到尾节点都是有序的 
2.可以进行跳跃查找(形如二分法),降低时间复杂度

《跳表(skiplist)的理解》
一个有序的链表,我们选取它的一半的节点用来建索引,这样如果插入一个节点,我们比较的次数就减少了一半。这种做法,虽然增加了50%的空间,但是性能提高了一倍。如上图。

既然,我们已经提取了一层节点索引,那么,可以在第一层索引上再提取索引。如下图。 

《跳表(skiplist)的理解》
对于node5来说,它的next:

node5->next[2] = tailNode;
node5->next[1] = node7;
node5->next[0] = node6;
1
2
3
对于node7来说,它的next:

node7->next[1] = node9;
node7->next[0] = node8;
1
2
对于node3来说,它的next:

node3->next[0] = node4;
1
查找 

《跳表(skiplist)的理解》

如果我们要找node6节点, 
第一次比较headerNode->next[2]的值,也就是node5的值。显然node5小于node6(跳表的数据是有序的),所以,下一次应该从第2级的node5开始查询,也就是令targetNode = targetNode->next[2];

第二次应该比较node5->next[2]的值,也就是tailNode的值。tailNode的值是最大的。所以结果是大于,下一次应该从第1级的node5开始查询。这里从第2级跳到第1级。但是没有改变targetNode。

第三次我们应该比较node5->next[1]的值,也就是node7的值。因为node7大于node6,所以,下一次应该从第0级的node5开始查询。这里从第1级跳到第0级。也没有改变targetNode。

第四次应该比较node5->next[0]的值,也就是node6的值。这时终于相等,找到了,结束。 
如果小于,targetNode往后移,改变targetNode = targetNode->next[0],如果大于,则没找到,结束。因为这已经是第0级,没法再降了。

综上: 
当targetNode->next[i]的值 < 待查找的值时,令targetNode = targetNode->next[i],targetNode移到第i级的下一个结点; 
当targetNode->next[i]的值 > 待查找的值时,向下降级,i- – ,不改变targetNode; 
当targetNode->next[i]的值 = 待查找的值时,向下降级,i- – ,不改变targetNode。

最后,再次比较targetNode->next[0]和theElement,判断是否找到。 
所以整个运算下来,targetNode是要查找的节点前面那个节点。

插入 
当有2级索引时,新的节点先和2级索引比较,再和1级索引比较,最后和原链表比较,最终插到原链表中。当节点很多时,比较次数是原来的四分之一。

当然,当节点足够多的时候,我们还可以继续加索引,保证每一层索引数是低级索引的一半。当这一层只剩两个节点时,就没有必要再建索引了,因为一个节点没有比较的意义。

当很多节点插入时,上层索引节点已经不够用,我们需要在新节点中选取一部分节点提到上一层,跳表的设计者用“抛硬币”的方法选取节点是否提拔,也就是随机的方式,每个节点有50%概率会提拔。这样虽然不会让索引绝对均匀分布,但也会大体上是均匀的。

综上,插入的步骤:

新节点和各层索引节点逐一比较,确定原链表的插入位置。O(logN)
把索引插入到原链表。O(1)
利用抛硬币的随机方式,决定新节点是否提升为上一级索引。结果为“正”则提升并继续抛硬币,结果为“负”则停止。O(logN)
总体上,跳表插入操作的时间复杂度是O(logN),而这种数据结构所占空间是2N,既空间复杂度是 O(N)。

删除

自上而下,查找第一次出现节点的索引,并逐层找到每一层对应的节点。O(logN)
删除每一层查找到的节点,如果该层只剩下1个节点,删除整个一层(原链表除外)。O(logN)
总体上,跳表删除操作的时间复杂度是O(N)。

应用 
Redis当中的Sorted-set这种有序的集合,正是对于跳表的改进和应用。

相比于二叉查找树,跳表维持结构平衡的成本比较低,完全靠随机。而二叉查找树需要Rebalance来重新调整平衡的结构。 
:-)
——————— 
作者:DEMI0815 
来源:CSDN 
原文:https://blog.csdn.net/weixin_41462047/article/details/81253106 
版权声明:本文为博主原创文章,转载请附上博文链接!

点赞