转自:https://zhuanlan.zhihu.com/p/68516038
https://zhuanlan.zhihu.com/p/53975333?ivk_sa=1024320u
1.跳表
跳表全称为跳跃列表,它允许快速查询,插入和删除一个有序连续元素的数据链表。
- 平均查找和插入时间复杂度都是O(logn)。
- 通过维护一个多层次的链表,且每一层链表中的元素是前一层链表元素的子集。
- 算法在最稀疏的层次进行搜索,直至需要查找的元素在该层两个相邻的元素中间,则跳到下一个层次继续搜索。
在单链表中插入的复杂度为O(n),在原始链表的基础上,每两个结点提取一个结点建立索引,我们把抽取出来的结点叫做索引层或者索引,down 表示指向原始链表结点的指针。
比如上图在查找15的过程中,首先在第二级索引层比较,通过14的down指针跳到第一级索引层,与14比较,以及14的next 17比较,发现15在两者之间,那么就跳到14的down指针,到原始链表中查找。
通过对链表加多级索引的数据结构,就是跳表。
2.跳表复杂度
时间复杂度:
如果一个链表有 n 个结点,如果每两个结点抽取出一个结点建立索引的话,那么第一级索引的结点数大约就是 n/2,第二级索引的结点数大约为 n/4,以此类推第 m 级索引的节点数大约为 n/(2^m)。则m=log(n)-1,那么跳表高度为logn,在查询跳表的时候,如果每一层都需要遍历 k 个结点,那么最终的时间复杂度就为 O(k*log(n))。k是常数,可以忽略。
空间复杂度:
如果一个链表有 n 个结点,如果每两个结点抽取出一个结点建立索引的话,那么第一级索引的结点数大约就是 n/2,第二级索引的结点数大约为 n/4,以此类推第 m 级索引的节点数大约为 n/(2^m),等比数列求和,跳表的空间复杂度为 o(n)。
3.插入和删除
插入:
就可能会造成两个索引点之间的结点过多的情况,所以需要维护索引与原始链表的大小平衡,也就是结点增多了,索引也相应增加,避免出现两个索引之间结点过多的情况,查找效率降低。
跳表是通过一个随机函数来维护这个平衡的,当我们向跳表中插入数据的的时候,我们可以选择同时把这个数据插入到索引里,那我们插入到哪一级的索引呢,这就需要随机函数,来决定我们插入到哪一级的索引中。
因为删除和插入节点是不可预测的,很难预测,所以选择随机抛硬币的方式来选择。
插入流程:
- 新节点和各层索引节点逐一比较,确定原链表的插入位置。O(logN)
- 把索引插入到原链表。O(1)
- 利用抛硬币的随机方式,决定新节点是否提升为上一级索引。结果为“正”则提升并继续抛硬币,结果为“负”则停止。O(logN)
删除:
单向链表,删除时不仅要删除节点还要删除索引,需要获取其前驱节点;双向链表,删除时无需前驱。
删除流程:
- 自上而下,查找第一次出现节点的索引,并逐层找到每一层对应的节点。O(logN)
- 删除每一层查找到的节点,如果该层只剩下1个节点,删除整个一层(原链表除外)。O(logN)
4.应用
在进行排序时,可以维护,以空间换时间。