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

面试题 16.25. LRU 缓存

题目链接: 面试题 16.25. LRU 缓存
📙题目描述:

在这里插入图片描述

✏️题目分析:
 我们通过阅读题目可知题目中出现键值对,我们遇到键值对就要想到哈希表。题目中让我们构建一个“最近最少使用”,并且支持插入和删除数据,对于有出入顺序的问题,我们就要想到栈,队列或者是链表。这道题我们可以用双向链表。
 通过示例我们可以发现这道题在访问数据的时候是随机的,由于哈希表支持能够在时间复杂度为O(1)内查找元素,如果哈希表中的值包含链表的结点信息,就能实现在O(1)内定位到链表中的某个结点。所以我们将来可以定义哈希表的结构为:

unordered_map<键值,链表的结点>

这样就能实现通过键值来定位到链表中的结点。
由于哈希表只负责定位查找功能,所以我们的双向链表结点中就应该包含keyvalue,所以双向链表的结点我们可以定义成:

struct DLinkedNode
{int key,value;DLinkedNode* prev;//双向链表中的上一个结点DLinkedNode* next;//双向链表中的下一个结点
};

🌵对于get操作:

  • 如果密钥key不存在
    返回-1
  • 如果密钥key存在,则获取密钥的值。并且当前密钥对应的结点为最近最新使用的结点。我们把最近最少使用的结点放在链表的尾部,最近最新使用的结点放在链表的头部。

🌿如何把最近最新使用的结点放在双向链表的头部呢?
我们可以先将此结点移出双向链表,再将此结点添加到链表的头部,所以这里就需要实现两种方法removeNodeaddToHead

removeNode

void removeNode(DLinkedNode* node)
{node->prev->next->node->next;//node结点的上一个结点的next指向node结点的下一个结点node->next->prev->node->prev;//node结点的下一个结点的prev指向node结点的上一个结点
}

在这里插入图片描述
addToHead

void addToHead(DLinkedNode* node)
{node->prev = head;node->next = head->next;head->next->prev = node;head->next = node;
}

🌵对于put操作:

  • 如果密钥key不存在
    keyvalue创建一个新的结点,并用key将此结点添加到哈希表中,调用addToHead将此结点放到双向链表的头部,判断结点的数量是否超出了容量,如果超出容量,删除掉最近最少使用的那个结点,即尾部结点,并删除哈希表中对应的项。

  • 如果密钥key存在
    get操作类似,先通过key使用哈希表找到此结点在双向链表中的位置,更新value,用moveToHead将此结点移动到链表的头部
    如果链表的数量大于链表的容量,我们就需要删除最近最少使用的结点。双向链表的尾部结点即为最近最少使用的,我们要删除这个结点就要先找到这个结点。我们使用removeTail()找到这个结点并删除,并且删除哈希表中对应的项,delete掉这个结点防止内存泄漏。

moveToHeaad

void moveToHead(DLinkedNode* node)
{removeNode(node);addToHead(node);
}

removeTail

DLinkedNode* removeTail()
{DLinkedNode* node = tail->prev;removeNode(node);return node;
}

🔖细节问题:我们可以定义一个伪头节点和一个伪尾结点,这样我们在增加或者删除结点的时候就不需要判断相邻的结点是否存在。

🐾 代码实现:

//定义双向链表结点
struct DLinkedNode
{int key, value;DLinkedNode* prev; //双向链表结点的上一个结点DLinkedNode* next;//双向链表结点的下一个结点DLinkedNode(int _key = 0, int _value = 0):key(_key),value(_value),prev(nullptr),next(nullptr)
{
}};
class LRUCache
{
public://构造函数LRUCache(int _capacity):capacity(_capacity), size(0){//使用伪头结点和伪尾结点head = new DLinkedNode();tail = new DLinkedNode();head->next = tail;tail->prev = head;}int get(int key){//首先判断key是否存在if (!cache.count(key)){//说明key不存在return -1;}else{//key存在DLinkedNode* node = cache[key];//通过哈希表定位到key所对应的结点//将key对应的结点移动到双向链表的头部moveToHead(node);return node->value;//最后返回该结点的值}}void put(int key, int value){//首先判断key是否存在if (!cache.count(key)){//key不存在DLinkedNode* node = new DLinkedNode(key, value);//使用key和value创建一个新的结点addToHead(node);//将新结点添加到双向链表的头部cache[key] = node;//将新结点添加到哈希表中++size;if (size > capacity){//如果节点数超出双向链表的容量DLinkedNode* removed = removeTail();//删除尾部的结点cache.erase(removed->key);//删除哈希表中对应的项delete removed;//防止内存泄漏--size;}}else{//key存在DLinkedNode* node = cache[key];//通过哈希表定位key对应的node结点node->value = value;//修改node对应的value值moveToHead(node);//将该节点移动到双向链表的头部}}void moveToHead(DLinkedNode* node){removeNode(node);addToHead(node);}void removeNode(DLinkedNode* node){node->prev->next = node->next;node->next->prev = node->prev;}void addToHead(DLinkedNode* node){node->prev = head;node->next = head->next;head->next->prev = node;head->next = node;}DLinkedNode* removeTail(){DLinkedNode* node = tail->prev;removeNode(node);return node;}
private:unordered_map<int, DLinkedNode*> cache;//通过哈希表实现O(1)的链表结点查找DLinkedNode* head;//伪头节点DLinkedNode* tail;//伪尾结点//有伪节点的存在,就不需要查找相邻结点是否存在int size;  //有效元素个数 int capacity;//空间大小
};

写完发现的问题:如果新添加的结点为0,0,和伪结点一样呢?
不影响,因为哈希表中并没有把伪结点的键值key添加到哈希表中。

http://www.dtcms.com/a/589143.html

相关文章:

  • st表详解
  • 企业网站优化甲薇g71679做同等效果下拉词做网站白云
  • 9、webgl 基本概念 + 复合变换 + 平面内容复习
  • gRPC C++库架构与异步编程实践
  • 做网站如何收益发送wordpress
  • 人工智能备考——4部分总结
  • Vite.js 快速入门指南 (React + JavaScript 版)
  • 如何建微信商城网站wordpress手机模板
  • 基于springboot纺织品企业财务管理系统【带源码和文档】
  • CHAR、VARCHAR、TEXT 的差别与存储方式
  • QtMainWindow C++详解:构建桌面应用的核心框架
  • 红帽虚拟机,NG搭建网站练习
  • EntryAbility继承FlutterAbility应用入口深度解析
  • (3)项目启航:Qt实战项目之创建项目
  • 补充说明:Windows 完全可以开发 Qt 鸿蒙应用!(附专属适配方案)
  • Apache 工具包(commons-io commons-lang3 )保姆介绍
  • 大小鼠跑步机 小动物跑台 动物跑步机 大鼠实验跑台
  • 哪里网站建设联系方式ppt模板下载网
  • PHP Mail:高效邮件发送的解决方案详解
  • 分布式专题——48 ElasticSearch聚合操作详解
  • 免费品牌网站制作给娃娃做衣服卖的网站
  • 【AI大模型技术】1.NLP
  • Linux应用开发-18- select、poll、epoll
  • 进程3:进程切换
  • PHP中各种超全局变量使用
  • 深入了解iOS内存管理
  • 介质电磁特性参数
  • 网站建设行业广告语建网站找那家企业好
  • Python中使用sqlite3模块和panel完成SQLite数据库中PDF的写入和读取
  • 佛山网站建设网络公司上海网站seo诊断