当前位置: 首页 > news >正文

力扣146:LRU缓存

力扣146:LRU缓存

  • 题目
  • 思路
  • 代码

题目

请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:

  • LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
  • void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。

函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

思路

这是一个非常经典的面试题!!!
我们先看题目最后的要求,对get和put我们必须以O(1)的时间复杂度来完成,我们从题目中可以发现get和put我们都需要判断关键字是否存在所以对于查找这个过程我们就必须用O(1)来完成。很容易就能想到使用哈希表。那么关键是哈希表中的value我们使用什么类型呢?
我们再来看题目,get的操作很简单put前半段操作也很简单,主要是put的最后一句话:如果关键字数量超过capacity则应该逐出最久未使用的关键字。这个最久未使用的关键字要如何确认呢?什么又叫做最久未使用。这里我给出解释,我们使用get获得关键字也算是使用,我们使用put插入KV或者更改value这也叫做使用,所以这道题的关键是我们如何判断最久未使用的关键字是哪个。我们可以联想一下队列,队列是先进先出的,那么队列的队首元素不就一定程度上是最久未使用的元素吗?因为他第一个进入队列所以他第一个离开队列。那么我们是否也可以使用一个数据结构来演化出最新使用的在数据结构的这一端最久未使用的在数据结构的另一端。所以我们可以使用双向链表来完成这个功能,链表的节点中存储着kv。我们为链表增加一个哨兵头节点和哨兵尾节点用来方便后续的移动和删除。
在双向链表中链表头是最近使用的关键字节点链表尾是最久未使用的关键字节点,当我们使用get和put对一个关键字对应的value进行获得值,插入以及修改后我们将这个节点移动到链表头从而实时保证链表尾是最久未使用的关键字节点。
有了思路之后代码就好写了,我们需要先创造一个双向链表类,然后在LRU对象中增加哨兵头节点和哨兵尾节点以及容量和大小还有哈希表而哈希表的value就是链表的节点。

  1. 当我们使用get时先在哈希表中判断关键字是否存在,如果不存在我们就直接返回-1,存在我们就从哈希表中得到对应的节点之后将这个节点移到链表头最后返回节点的值。
  2. 当我们使用put时一样先在哈希表中判断关键字是否存在,如果不存在我们就使用kv直接创造一个新的节点之后判断链表的大小是否等于容量如果等于就要删除链表尾的节点,再将节点插入到链表头的位置以及将关键字添加到哈希表中,如果存在就修改对应节点中value的值再将节点移到链表头的位置上。

代码

// 双向链表
class LRUListNode {
public:int _key, _value;LRUListNode* _prev;LRUListNode* _next;LRUListNode() : _key(0), _value(0), _prev(NULL), _next(NULL) {};LRUListNode(int key, int value): _key(key), _value(value), _prev(NULL), _next(NULL) {};
};class LRUCache {
private:LRUListNode* _head;LRUListNode* _tail;unordered_map<int, LRUListNode*> um;int _capacity;int _size;public:LRUCache(int capacity) : _capacity(capacity), _size(0) {// 创造伪头部和伪尾部_head = new LRUListNode();_tail = new LRUListNode();_head->_next = _tail;_tail->_prev = _head;}int get(int key) {// 如果哈希表中没有key对应的节点if (!um.count(key)) {// 直接返回-1return -1;}// 如果有else {// 将该节点移到双向链表的头部LRUListNode* node = um[key];MovetoHead(node);// 返回对应的value值return node->_value;}}void put(int key, int value) {// 如果不存在对应节点if (!um.count(key)) {// 使用kv创造一个新节点LRUListNode* newnode = new LRUListNode(key, value);// 将新节点加入到哈希表中um[key] = newnode;// 如果双向链表的大小等于容量了if (_size == _capacity) {// 在双向链表中移除尾部节点LRUListNode* node = RemoveTail();// 在哈希表中移除尾部节点um.erase(node->_key);// 释放节点delete node;--_size;}// 将新节点插入到头部AddtoHead(newnode);_size++;}// 如果存在else {// 修改值LRUListNode* node = um[key];node->_value = value;// 将节点移到头部MovetoHead(node);}}// 删除节点void RemoveNode(LRUListNode* node) {node->_prev->_next = node->_next;node->_next->_prev = node->_prev;}// 删除尾部节点LRUListNode* RemoveTail() {LRUListNode* node = _tail->_prev;RemoveNode(node);return node;}// 在头部添加新节点void AddtoHead(LRUListNode* node) {node->_next = _head->_next;node->_prev = _head;_head->_next->_prev = node;_head->_next = node;}// 将节点移到头部void MovetoHead(LRUListNode* node) {// 先移除再插入RemoveNode(node);AddtoHead(node);}
};/*** Your LRUCache object will be instantiated and called as such:* LRUCache* obj = new LRUCache(capacity);* int param_1 = obj->get(key);* obj->put(key,value);*/
http://www.dtcms.com/a/293618.html

相关文章:

  • 使用阿里云 ESA 边缘函数转发代理 docker registry
  • 利用aruco标定板标定相机
  • 电商通用话术模板搭建指南:高效转化,服务升级
  • macOS配置maven及报错处理:zsh: permission denied: mvn
  • Transformer输入部分实现
  • 学习 Flutter(五):玩安卓项目实战 - 下
  • springcloud环境和工程搭建
  • 数组算法之【数组中第K个最大元素】
  • RK3568笔记九十:基于web显示RTSP流
  • 【第三章自定义检视面板_创建自定义编辑器_如何创建自定义PropertyDrawer(9/9)】
  • SQL 中 CASE WHEN 及 SELECT CASE WHEN 的用法
  • HF86611_VB1/HF86611Q_VB1:多通道USB HiFi音频解码器固件技术解析
  • CLI 与 IDE 编码代理比较:提升开发效率的两种路径
  • docker安装minio及配置禁止列出目录文件
  • 解决Node 17+版本与Metro、Webpack等兼容性问题(500)
  • 【计算机网络】正/反向代理服务器,有状态/无状态应用
  • 构建高性能推荐系统:MixerService架构解析与核心实现
  • Spring-IoCDI
  • VPS海外部署Linux分布式计算任务调度-跨国资源整合方案
  • Git 常用的提交类型
  • Object Sense (OSE):一款从编辑器脚本发展起来的编程语言
  • 【数学建模 | Matlab】二维绘图 和 三维绘图
  • 2025年7月一区SCI-投影迭代优化算法Projection Iterative Methods-附Matlab免费代码
  • kotlin基础【1】
  • MATLAB 2024b深度学习新特性全面解析与DeepSeek大模型集成开发技术
  • android studio(NewsApiDemo)100%kotlin
  • 如何在 npm 上发布 Element Plus 二次封装组件
  • Oracle 常用 SQL 命令集合
  • 将 `knife4j` 和 `springdoc-openapi` 集成到你的 Spring Boot 应用
  • 微软Fabric重塑数据管理:Forrester报告揭示高ROI