单链表删除所有值为x的元素_SAST Weekly | 跳跃表(Skiplist)算法简介

d769f586560b04c6413fcf458d1213c6.png 9ecaa84ae104771f3d5214b2cbf226f8.png 568f3f8329260cfd5c88917cb2e1e36f.png

SAST Weekly 是由电子工程系学生科协推出的科技系列推送,内容涵盖信息领域技术科普、研究前沿热点介绍、科技新闻跟进探索等多个方面,帮助同学们增长姿势,开拓眼界,每周更新,欢迎关注!欢迎愿意分享知识的同学投稿至 eesast@mail.tsinghua.edu.cn , 期待你的作品!

04d1460f307c70be165386cd2a3cbd35.png 3885f4e6c4d16f416e0ba700b95b7240.png

九字班的同学们刚刚才结束了数算课上线性表的学习,那么应该会知道,对于一个普通单链表而言,查询一个元素的时间复杂度为O(n)。即使这个链表是有序的,我们也不能通过二分的方式降低其时间复杂度,而必须从头开始遍历。

例如,对于下面这个有序链表,假如我们要查找元素9,我们需要从头结点开始一共遍历8个结点才能找到元素9。

1c5d9a81114c56ce5cf6797450a9e9e3.png

对于规模小的单链表,这样的耗时似乎还可以忍受,但如果链表很大,遍历一遍会花费大量的时间(导致一堆TLE,就像封面这样,心肺骤停.JPG)。那么,有没有办法快速查询有序单链表中的元素呢?那就是今天的主题——跳跃表。

举个栗子

由于元素有序,可以通过增加一些路径来加快查找速度。还是刚刚这个链表,如果我们添加一层:只需要遍历5次就可以找到元素9了(红色的线为查找路径)。

a6ed187c176f7907635ac7ebc2989a57.png

还能再加快查找速度吗?答案是肯定的,只需要再加一层。

7bb3b2a6b1eda09cd34448851615ed57.png

对于具有n个元素的链表,我们可以采取logn 层指针路径的形式,消耗2n的空间,实现在 O(logn) 的时间复杂度内,查找某个目标元素的功能。这种数据结构,我们也称之为跳跃表。理想跳跃表应该具备如下特点:包含有N个元素节点的跳跃表拥有log2N层,并且上层链表包含的节点数恰好等于下层链表节点数的1/2。是不是有二分查找的意思了?

可是,单链表毕竟不是顺序存储结构,也不能实现常规的二分查找。如果我们对跳跃表进行插入/删除结点的操作,那么跳跃表结点数就会改变,意味着跳跃表的层数也会动态改变。我们该选取哪些节点到上一层?新插入的结点又该跨越多少层?

为了解决这个问题,通常的做法是,每次向跳跃表中增加一个节点时进行一次判定,有50%的概率向上层链表增加一个跳跃节点,并再次进行50%概率的判断,直到结果为不增加为止。

 举个栗子 ·

我们要插入结点 3,4,通过概率判断知道3,4跨越的层数分别为0,2 (层数从0开始算),则插入的过程如下:

插入3,跨越0层。

ef8e42626a961fbfaa3264a024e491aa.png

插入4,跨越2层。

f629ff36160278a0927b9fd71b9f0e44.png

上帝掷不掷骰子不知道,反正跳跃表的实现是依赖“抛硬币”的

在数据规模比较小时,跳跃表形成后可能不是理想的跳跃表结构,但是当数据量增大,结构越接近理想的跳跃表结构。

对比插入,删除就比较简单了,例如我们要删除4,那我们直接把4及其所跨越的层数删除就行了。

ef8e42626a961fbfaa3264a024e491aa.png

文末附有查询、插入、删除的C++实现网址,感兴趣的同学可以前往进一步学习。

 性能分析 ·

时间复杂度:当表中有n个元素时,搜索、插入、删除操作的复杂性均为O(n+MaxLevel)。在最坏情况下,可能只有一个MaxLevel级元素,且余下的所有元素均在0级链上。i>0时,在i级链上花费的时间为(MaxLevel),而在0级链上花费的时间为O(n)。尽管最坏情况下的性能较差,但跳跃表仍不失为一种有价值的数据描述方法。其每种操作(搜索、插入、删除)的平均复杂性均为O(logn)。

空间复杂度:最坏情况下所有元素都可能是MaxLevel级,每个元素都需要MaxLevel+1个指针。因此,除了存储n个元素(也就是n*sizeof(element)),还需要存储链指针(所需空间为O(n*MaxLevel))。不过,一般情况下,只有n*p个元素在1级链上,n*p2个元素在2级链上,n*pi在i级链上。因此指针域的平均值(不包括头尾节点的指针)是n/(1-p)。因此虽然最坏情况下空间需求比较大,但平均的空间需求并不大。当p=0.5时,平均空间需求(加上n个节点中的指针)大约是2n个指针的空间。

 小结 ·

1.一个跳跃表应该有几个层(level)组成。

2.跳跃表的第一层包含所有的元素。

3.每一层都是一个有序的链表。

4.如果元素x出现在第i层,则所有比i小的层都包含x。

5.第i层的元素通过一个down指针指向下一层拥有相同值的元素。

6.Top指针指向最高层的第一个元素。

7.跳跃表时间复杂度为O(logn),空间复杂度为 O(n)。

 后记&附录 ·

跳跃表结构是拿空间换时间的一种结构,尽管空间占用不是很大。跳跃表不同于树结构,如红黑树等,它不需要花费过多的精力进行平衡算法,这也是跳跃表的性能优越的一个方面。

感谢大家的阅读!希望大家能在阅读的过程中感受到算法的魅力。祝大家学习顺利,生活愉快~

附:跳跃表查找、插入、删除C++实现:https://blog.csdn.net/yinlili2010/article/details/39503655

533ca12b9e024df497f018313d3b69fa.png

撰稿人:杨馨婷

审稿人:贺鲲鹏

498db8d54983af3cd9cb2997465e6e47.png
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值