【数据结构与算法】->数据结构->链表->LRU缓存淘汰算法的实现

本文介绍了链表与数组的区别,强调了链表在插入和删除操作上的优势。文章详细讲解了单链表、循环链表和双向链表的结构和特性,并通过示例说明了它们的应用场景。此外,还探讨了链表在缓存中的应用,特别是LRU缓存淘汰算法的实现。最后,分享了编写链表代码的一些技巧,包括理解指针、避免内存泄漏和使用哨兵节点等。
摘要由CSDN通过智能技术生成

Ⅰ 链表与数组

相比数组,链表是一种稍微复杂一点的数据结构。这两个非常基础,非常常用的数据结构,我们常常会放到一起来比较。所以我们先来看看,这两个数据结构有什么区别。

从底层的存储结构上看,数组在我的上一篇文章里已经讲过,申请的空间是连续的,对内存的要求比较高。如果我们申请一个大小为 100MB 的数组,当内存的连续存储空间没有 100MB ,就会申请失败。

但是链表就很方便,它并不需要一块连续的内存空间,它通过“指针”将一组零散的内存块串联起来使用,所以如果我们申请 100MB 大小的链表,就不会出现问题。

我画两个图做以区分。

在这里插入图片描述

Ⅱ 链表结构

链表结构五花八门,这篇文章我重点介绍三种最常见的链表结构,单链表双向链表以及循环链表

① 单链表

首先,我们先看最简单、最常用的单链表。

前面我们知道了,链表通过指针将一组零散的内存块串联在一起。其中,我们把内存块称为链表的”结点“。为了将所有的结点串起来,每个链表的结点除了存储数据之外,还需要记录链上的下一个结点的地址。如下图所示👇
在这里插入图片描述
我们把这个记录下个结点地址的指针叫作后继指针 next

在这条单链表中,有两个结点比较特殊,分别是第一个结点和最后一个结点。我们习惯把第一个结点叫作头结点,最后一个结点叫尾结点。其中,头结点用来记录链表的基地址,有了它,我们就可以遍历得到整条链表。而尾结点特殊的是,指针不是指向下一个结点,而是指向一个空地址 NULL,表示这是链表上最后一个结点。

在进行数组的插入、删除操作时,为了保持内存数据的连续性,需要做大量的数据搬移,所以时间复杂度是 O(n)。而链表中插入或者删除一个数据,我们并不需要为了保持内存的连续性而搬移结点,因为链表的存储空间本身就不是连续的。所以,在链表中插入和删除一个数据是非常快速的。

我画图做一个简单的演示。
在这里插入图片描述
在这里插入图片描述
但是同样的,有利就有弊。当链表想要随机访问第 k 个元素,就没有数组那么高效了。因为链表中的数据并非连续存储,所以无法像数组那样,根据首地址和下标,通过寻址公式就能直接计算出对应的内存地址,而是需要根据指针一个结点一个结点地依次遍历,直到找到相应的结点。

因此,链表的随机访问就没有数组那么好,需要 O(n) 的时间复杂度。

② 循环链表

循环链表是一种特殊的单链表,其实也很简单,它和单链表唯一的区别就在尾结点。我们知道,单链表的尾结点指针指向空地址,表示这就是最后的结点了。而循环链表的尾结点指针是指向链表的头结点。
在这里插入图片描述
和单链表相比,循环链表的优点是从链尾到链头比较方便。当要处理的数据具有环形结构特点时,就特别适合采用循环链表。比如著名的 约瑟夫问题

据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值