链表是什么(循环链表、双向链表)

链表

链表不需要一块连续的内存空间,它通过指针将一组零散的内存块串联起来使用。其中每个内存块叫做链表的结点,记录下个结点地址的指针叫做后继指针。

一 单链表

链表的插入和删除是非常快速的,因为不需要做数据搬移,但是链表想要随机访问第k个元素就没有数组那么高效了,因为链表的数据不是连续的,没办法将首地址和下标带入寻址公式直接计算出对应的内存地址,而是需要根据指针一个结点一个结点的一次遍历,直到找到相应的结点,需要O(n)的时间复杂度。

二 循环链表

在单链表基础上,尾结点的指针指向链表的头结点

三 双向链表

每个结点不止有一个后继指针next指向后面的结点,还有一个前驱指针prev指向前面的结点。

  • 删除和访问操作
    当删除结点中“值等于某个给定值”的结点,无论单链表还是双向链表,为了查找到匹配的结点,都需要从头指针一个一个一次遍历,然后将其删除。尽管删除操作时间复杂度是O(1),但是遍历查找的时间复杂度是O(n)。

当删除给定指针指向的结点,我们已经找到了要删除的结点,需要找到他的前驱结点,单链表不支持获取前驱结点,所以,为了找到前驱结点,还是要从头开始获取前驱结点。但是对于双向链表来说,有前驱指针,所以只需要O(1)的时间复杂度就可以。

除此之外,对于一个有序链表,双向链表在按值查询时,可以记录上次查找的位置,每次查询的时候,根据查找的值判断大小,然后决定往前找还是往后找,平均只需要查找一半的数据。

  • 空间换时间设计思想
    在实际的开发中,尽管双向链表比较耗费内存,也比单链表应用更加广泛,比如java中的linkedHashMap,这里体现一种用空间换时间的思想,当我们内存空间充足的时候,如果我们更加追求代码的执行速度,就可以选用空间复杂度相对较高,但时间复杂度相对较低的算法或者数据结构。反过来也类似。
四 数组和链表的缺点
  • 数组的缺点是大小固定,且需要占用整块连续的内存空间,如果声明的数组过大,系统可能没有足够的连续内存空间分配给它,导致out of memory,如果声明的过小,又容易产生动态扩容发现数据搬移。

  • 链表的缺点是需要耗费额外的存储空间去存储指针,而且对链表的频繁插入、删除操作,还会导致频繁的内存申请和释放,容易造成内存碎片,如果是Java语言,就有可能频繁的GC。

五 如何实现一个LRU缓存淘汰算法?

链表使用的经典场景,LRU(least recently userd),最少最近使用策略。

我的思路是这样的,维护一个按时间排序的有序单链表,越靠近尾部的结点是最早被访问的,当有一个新的数据被访问时,分以下两种情况:

  • 如果数据已经在链表中,先遍历找到这个数据对应的结点,将原来数据删除,然后把数据插入到链表的头部。
  • 如果数据不在链表中,要判断链表是否已经满了,如果未满,将数据插入到链表头部,如果已满,将链表尾结点删除,再将新的数据插入链表头部。

在这里插入图片描述
时间复杂度分析的话,不管缓存满不满,我们都需要遍历一遍链表,所以是O(n)的时间复杂度。实际上可以引入散列表来记录每个数据的位置,将缓存访问的时间复杂度降低到O(1),在后续散列表会详细说明。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值