面试经典150题——LRU 缓存

1. 题目描述

2. 题目分析与解析

首先讲解一下LRU

LRU 是"Least Recently Used"的缩写,LRU 算法的基本思想是跟踪最近最少使用的数据,并在缓存已满且需要存储新数据时优先驱逐该数据。

LRU 算法通常的工作原理的简化解释:

  1. 当访问或使用一条数据时,将该数据移动到"最近使用"的列表的前面。

  2. 当缓存已满且需要存储新数据时,算法会查看列表的末尾,以找到最近最少使用的数据。

  3. 然后,最近最少使用的数据将被从缓存中驱逐,以腾出空间存储新数据。

这个过程确保了最近使用的数据保留在缓存中,而较旧、使用频率较低的数据在需要时被驱逐。这有助于通过最大限度地确保最相关的数据随时可用来提高缓存系统的效率。

2.1 思路一

注意题目中提示的 函数 getput 必须以 O(1) 的平均时间复杂度运行 ,根据这个信息可能在提示我们是不是会用hashMap(因为O(1))。但是如果使用hashMap我们怎么跟踪它最近是否被使用呢?这时我们可能也会想到使用一个队列,按照队列先进先出的性质,我们就可以实现在容量不够时移除最近最少使用的那个对象。但是如果使用队列也会面临一个问题,那就是如下图所示的情况:

这时如果我要put进去的value为3,那我就需要把队列更新为如下形式:

那就牵涉到了队列的重新更新,肯定无法实现O(1)的要求。

而此时我们想象一下如果对于一个新用到的元素,那必然要把它更新到这个LRU的链子的头部就像上图 3 放在0号位一样,能够在常数时间内进行这样移动的好像链表就能行。

现在我们假设使用链表,对于前面同样的情况:

换成链表后就是一个个单独的节点,那么移动只需要找到当前需要put的节点,找到它的位置及其前后节点,将当前节点放在链表头部,前后节点进行连接不就可以完成题目的要求。注意一下要想在O(1)的时间复杂度中找到这个节点,还是要借助hash。因此我们就可以提出以下思路:

解题思路:

整体思路------构建环形链表,每次插入新节点时,将尾部节点删除(也就是替换),将新节点插入到头部(也是替换)

  1. 定义一个HashMap存储key和Node

  2. 定义一个虚拟头节点指向环形链表的头部

  3. 构造函数:构建一个capacity长度的双向环形链表

  4. get方法:如果key不存在,返回-1;如果key存在,返回value,并将当前节点移动到头部

  5. put方法:如果key存在,更新value,并将当前节点移动到头部;如果key不存在,将新节点插入到头部并将尾部节点删除

  6. 移动到头部方法:如果当前节点是头部节点,直接返回;如果当前节点是中间节点,将当前节点的前一个节点和后一个节点连接,将当前节点插入到头部

  7. 时间复杂度:get和put方法的时间复杂度都是O(1)

  8. 空间复杂度:O(n)

3. 代码实现

4. 相关复杂度分析

这个LRUCache实现的时间复杂度主要集中在put和get方法上。

时间复杂度分析:

  1. get(int key):

    • 在HashMap中查找key的时间复杂度是O(1)。

    • 如果key存在,调用moveToHead方法,该方法的时间复杂度是O(1)。

    • 因此,get方法的总体时间复杂度是O(1)。

  2. put(int key, int value):

    • 如果key已经存在,需要更新其对应的value,这涉及到HashMap的查找和更新操作,时间复杂度均为O(1)。

    • 如果key不存在,需要将新节点插入到头部并将尾部节点删除,这包括HashMap的插入和删除操作,以及moveToHead方法的调用,总体时间复杂度也是O(1)。

    • 因此,put方法的总体时间复杂度是O(1)。

空间复杂度分析:

LRUCache类中使用了HashMap来存储键和节点的映射关系,其空间复杂度为O(capacity)。另外,还有capacity个节点,每个节点占用的空间也是常数级的。因此,LRUCache的总体空间复杂度为O(capacity)。

综上所述,该LRUCache实现的时间复杂度为O(1),空间复杂度为O(capacity)。

相关推荐
码匠许师傅36 分钟前
【C++ 面试真题】30. 聊聊 C++ 的互斥锁与读写锁
java·c++·面试
Heo3 小时前
怎么保证缓存和数据库一致性?
前端·后端·面试
晓晓_za8986683 小时前
Geo 优化服务 CI/CD 流水线搭建:源码自动构建、测试与灰度发布
运维·服务器·tcp/ip·spring·缓存·ci/cd
众人皆醒我独醉4 小时前
调和循环:kubectl apply 之后发生了什么
面试·llm·gpu
众人皆醒我独醉4 小时前
从 Spec 到资源:Predictor/Transformer/Explainer 如何变成 Deployment
面试·llm·gpu
面试鸭4 小时前
Codex 跑进法务部,周活居然暴涨了 108 倍???
后端·面试·求职
程序员爱钓鱼7 小时前
Rust const泛型详解:让常量也成为泛型参数
后端·面试·rust
nvd117 小时前
LiteLLM 与 Redis 响应缓存机制:从精确哈希匹配到模型底层 KV-Cache 边界
redis·缓存·哈希算法
黄敬峰7 小时前
一文搞懂 Next.js + AI 全栈开发:为什么它是 2026 年最强的 AI 全栈框架
面试
吴声子夜歌9 小时前
Java面试——数据结构(一)
java·数据结构·面试