设计LRU缓存

LRU缓存

  • LRU缓存的实现思路
  • LRU缓存的操作
  • C++11 STL实现LRU缓存
  • 自行设计双向链表 + 哈希表

LRU(Least Recently Used,最近最少使用)缓存是一种常见的缓存淘汰算法,其基本思想是:当缓存空间已满时,移除最近最少使用的数据。LRU缓存通常通过链表(双向链表)和哈希表相结合来实现,利用哈希表快速查找,链表保持数据的使用顺序。

链接:leetcode 设计LRU缓存

LRU缓存的实现思路

实现思路:哈希表 + 双向链表

  • 为什么使用哈希表?
    哈希表:用来存储键值对,可以在常数时间内(O(1))进行查找、插入和删除操作。

  • 为什么使用双向带头尾链表?
    双向链表:用来维护数据的使用顺序。最近使用的元素放在链表的头部,最久未使用的元素放在链表的尾部。通过这种方式可以在O(1)的时间复杂度下实现删除最久未使用的元素。

LRU缓存的操作

  • Get(key): 如果键存在于缓存中,返回对应的值并将该键值对移到链表头部,表示最近被访问过。如果不存在,返回-1。
  • Put(key, value): 插入键值对。如果缓存已满,则删除最久未使用的元素,之后插入新的键值对,并将其移到链表头部。

C++11 STL实现LRU缓存

时间复杂度分析:
get(key):查找操作是O(1),然后通过 touch 函数将键移到链表头部,也是在O(1)时间内完成的。
put(key, value):插入或更新键值对的操作是O(1),如果缓存满了需要删除最久未使用的元素(evict),删除操作也是O(1)。因此,get 和 put 操作的时间复杂度都是 O(1)。

#include <iostream>
#include <unordered_map>
#include <list>class LRUCache {
public:LRUCache(int capacity) : capacity(capacity) {}// 获取缓存中的值int get(int key) {auto it = cache.find(key);if (it == cache.end()) {return -1;  // 未找到键,返回 -1}touch(it);  // 标记为最近使用return it->second.first;  // 返回对应的值}// 插入新的键值对void put(int key, int value) {auto it = cache.find(key);if (it != cache.end()) {touch(it);  // 标记为最近使用it->second.first = value;  // 更新值} else {if (cache.size() == capacity) {evict();  // 删除最久未使用的元素}// 插入新的键值对到链表头部order.push_front(key);cache[key] = {value, order.begin()};  // 存入哈希表,值和链表位置}}private:// 更新元素为最近使用void touch(std::unordered_map<int, std::pair<int, std::list<int>::iterator>>::iterator it) {int key = it->first;order.erase(it->second.second);  // 删除当前元素order.push_front(key);  // 将元素插入链表头部it->second.second = order.begin();  // 更新迭代器位置}// 淘汰最久未使用的元素void evict() {int key_to_evict = order.back();  // 获取尾部元素(最久未使用)order.pop_back();  // 从链表中移除cache.erase(key_to_evict);  // 从哈希表中删除}int capacity;  // 缓存容量std::list<int> order;  // 双向链表,维护键的访问顺序std::unordered_map<int, std::pair<int, std::list<int>::iterator>> cache;  // 哈希表,存储键值对和链表位置
};int main() {LRUCache cache(2);  // 设置缓存容量为2cache.put(1, 1);    // 缓存: {1=1}cache.put(2, 2);    // 缓存: {1=1, 2=2}std::cout << cache.get(1) << std::endl;  // 返回 1,缓存: {2=2, 1=1}cache.put(3, 3);    // 淘汰键 2,缓存: {1=1, 3=3}std::cout << cache.get(2) << std::endl;  // 返回 -1 (未找到)cache.put(4, 4);    // 淘汰键 1,缓存: {3=3, 4=4}std::cout << cache.get(1) << std::endl;  // 返回 -1 (未找到)std::cout << cache.get(3) << std::endl;  // 返回 3std::cout << cache.get(4) << std::endl;  // 返回 4return 0;
}

效果:代码简洁,但效率不高。
在这里插入图片描述

自行设计双向链表 + 哈希表

#include <iostream>
#include <unordered_map>using namespace std;struct DLinkedNode {int key, value;DLinkedNode* prev;DLinkedNode* next;DLinkedNode() : key(0), value(0), prev(nullptr), next(nullptr) {}DLinkedNode(int _key, int _value) : key(_key), value(_value), prev(nullptr), next(nullptr) {}
};class LRUCache {
private:unordered_map<int, DLinkedNode*> cache;DLinkedNode* head;DLinkedNode* tail;int size;int capacity;public:LRUCache(int _capacity) : capacity(_capacity), size(0) {// 使用伪头部和伪尾部节点head = new DLinkedNode();tail = new DLinkedNode();head->next = tail;tail->prev = head;}int get(int key) {if (!cache.count(key)) {return -1;  // 如果找不到该键,返回 -1}DLinkedNode* node = cache[key];moveToHead(node);  // 移动到链表头部return node->value;  // 返回值}void put(int key, int value) {if (!cache.count(key)) {// 如果 key 不存在,创建新节点DLinkedNode* node = new DLinkedNode(key, value);cache[key] = node;addToHead(node);  // 添加到链表头部++size;if (size > capacity) {// 超过容量,删除尾部节点DLinkedNode* removed = removeTail();cache.erase(removed->key);  // 从哈希表中删除该键delete removed;  // 防止内存泄漏--size;}}else {// 如果 key 存在,更新值,并移到头部DLinkedNode* node = cache[key];node->value = value;moveToHead(node);}}void addToHead(DLinkedNode* node) {node->prev = head;node->next = head->next;head->next->prev = node;head->next = node;}void removeNode(DLinkedNode* node) {node->prev->next = node->next;node->next->prev = node->prev;}void moveToHead(DLinkedNode* node) {removeNode(node);  // 移除节点addToHead(node);   // 重新添加到头部}DLinkedNode* removeTail() {DLinkedNode* node = tail->prev;removeNode(node);return node;}
};int main() {LRUCache cache(2);  // 缓存容量为2cache.put(1, 1);    // 缓存: {1=1}cache.put(2, 2);    // 缓存: {1=1, 2=2}cout << cache.get(1) << endl;  // 返回 1,缓存: {2=2, 1=1}cache.put(3, 3);    // 淘汰键 2,缓存: {1=1, 3=3}cout << cache.get(2) << endl;  // 返回 -1,键 2 不存在cache.put(4, 4);    // 淘汰键 1,缓存: {3=3, 4=4}cout << cache.get(1) << endl;  // 返回 -1,键 1 不存在cout << cache.get(3) << endl;  // 返回 3cout << cache.get(4) << endl;  // 返回 4return 0;
}

代码转自力扣官方题解。
效果:时间复杂度明显降低, 效率提高。在这里插入图片描述

总结

上述实现利用了哈希表和双向链表的组合,保证了LRU缓存操作的高效性。哈希表提供了O(1)的查找和更新时间,而双向链表提供了O(1)的插入和删除操作,确保了缓存的高效管理。这个实现适用于高性能缓存系统,如数据库缓存、Web缓存等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/887012.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SAM-Med2D 训练完成后boxes_prompt没有生成mask的问题

之前对着这这篇文章去微调SAM_Med2D(windows环境),发现boxes_prompt空空如也。查找了好长时间问题SAM-Med2D 大模型学习笔记&#xff08;续&#xff09;&#xff1a;训练自己数据集_sam训练自己数据集-CSDN博客 今天在看label2image_test.json文件的时候发现了一些端倪: 官方…

39页PDF | 毕马威_数据资产运营白皮书(限免下载)

一、前言 《毕马威数据资产运营白皮书》探讨了数据作为新型生产要素在企业数智化转型中的重要性&#xff0c;提出了数据资产运营的“三要素”&#xff08;组织与意识、流程与规范、平台与工具&#xff09;和“四重奏”&#xff08;数据资产盘点、评估、治理、共享&#xff09;…

在 macOS 和 Linux 中,波浪号 `~`的区别

文章目录 1、在 macOS 和 Linux 中&#xff0c;波浪号 ~macOS示例 Linux示例 区别总结其他注意事项示例macOSLinux 结论 2、root 用户的主目录通常是 /root解释示例切换用户使用 su 命令使用 sudo 命令 验证当前用户总结 1、在 macOS 和 Linux 中&#xff0c;波浪号 ~ 在 macO…

【LeetCode每日一题】——485.最大连续 1 的个数

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时空频度】九【代码实现】十【提交结果】 一【题目类别】 数组 二【题目难度】 LeetCode 三【题目编号】 485.最大连续 1 的个数 四【题目描述】 给定…

windows 操作系统下载 Android源码教程

前言 开始我是装了hyber-v 虚拟机ubuntu 的&#xff0c;然而非常的卡顿且难用。因此我尝试在windows上使用repo&#xff0c;因此有了这篇文章 补充 第二天发现编译源码也需要linux命令因为源码中的很多脚本都是.sh的 因此最终通过安装WSL解决&#xff08;在window应用商店就…

单片机进阶硬件部分_day2_项目实践

设计要求 从绘制原理图到画PCB板&#xff0c;完成智能云衣柜项目 STM32 &#xff08;Modbus&#xff09;云IOT衣物云端管理 华为PCB布线规范 基于IoT的智享家主控系统 步骤分析 需求分析 器件选型绘制原理图&#xff08;器件连接&#xff09;PCB布局、布线泪滴、铺铜、添加丝印…

接口上传视频和oss直传视频到阿里云组件

接口视频上传 <template><div class"component-upload-video"><el-uploadclass"avatar-uploader":action"uploadImgUrl":on-progress"uploadVideoProcess":on-success"handleUploadSuccess":limit"lim…

Flutter:photo_view图片预览功能

导入SDK photo_view: ^0.15.0单张图片预览&#xff0c;支持放大缩小 import package:flutter/material.dart; import package:photo_view/photo_view.dart;... ...class _MyHomePageState extends State<MyHomePage>{overrideWidget build(BuildContext context) {return…

【Linux】详解僵尸进程与孤儿进程(Z僵死状态引发的内存泄漏与处理办法)

&#x1f308; 个人主页&#xff1a;谁在夜里看海. &#x1f525; 个人专栏&#xff1a;《C系列》《Linux系列》《算法系列》 ⛰️ 丢掉幻想&#xff0c;准备斗争 目录 引言 一、僵尸进程 1.子进程的创建与退出 2.进程表 3.僵尸状态产生 4.直观感受一下&#xff1a; 二、…

基于微信小程序的科学健身助手的设计与实现

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

【Pytorch】torch.utils.data模块

torch.utils.data模块主要用于进行数据集处理&#xff0c;是常用的一个包。在构建数据集的过程中经常会用到。要使用data函数必须先导入&#xff1a; from torch.utils import data 下面介绍几个经常使用到的类。 torch.utils.data.DataLoader DataLoader(dataset, batch_…

Qt在designer设计师界面使用QSplitter

QSplitter控件是解决软件不同分辨率自适应布局的好帮手之一&#xff0c;该控件无法在控件列表找到&#xff0c;使用条件较为隐蔽 环境 qt5.12.12 MSVC2017 1.新建一个qt项目&#xff0c;初始自带Mainwindow&#xff0c;拖两个qwidget进来&#xff0c;注意此时centralWidget处于…

北京申请中级职称流程(2024年)

想找个完整详细点的申请流程资料真不容易&#xff0c;做个分享送给需要的人吧。 不清楚为什么说文章过度宣传&#xff0c;把链接和页面去掉了&#xff0c;网上自己找一下。 最好用windows自带的EDGE浏览器打开申请网站&#xff0c;只有在开始申请的时间内才可以进行网上申报&…

测评部署和管理 WordPress 最方便的面板

新版宝塔面板快速搭建WordPress新手教程 - 倚栏听风-Morii - 博客园 初学者使用1Panel面板快速搭建WordPress网站 - 倚栏听风-Morii - 博客园 可以看到&#xff0c;无论是宝塔还是1Panel&#xff0c;部署和管理WordPress都有些繁琐&#xff0c;而且还需要额外去配置Nginx和M…

ubuntu:20.04安装协议逆向工具netzob

创建容器 docker run -d --name ubuntu_env ubuntu:20.04 /bin/bash -c "while true; do sleep 1; done" 63a8f5cf5431a930671ff0e7bb2b667adf001efb05fd7261da244879d2699bec 进入容器 PS E:\src> docker exec -it ubuntu_env /bin/bash 安装常用工具 apt upda…

SpringBoot实现WebSocket

参考链接&#xff1a;https://www.kancloud.cn/king_om/mic_03/2783864 一、环境搭建 1.创建SpringBoot项目&#xff0c;引入相关依赖 <dependencies><!-- Spring Boot核心启动器&#xff0c;引入常用依赖基础 --><dependency><groupId>org.springf…

uni-app快速入门(八)--常用内置组件(上)

uni-app提供了一套基础组件&#xff0c;类似HTML里的标签元素&#xff0c;不推荐在uni-app中使用使用div等HTML标签。在uni-app中&#xff0c;对应<div>的标签是view&#xff0c;对应<span>的是text&#xff0c;对应<a>的是navigator&#xff0c;常用uni-app…

早期超大规模语言模型的尝试——BLOOM模型论文解读,附使用MindSpore和MindNLP的模型和实验复现

背景 预训练语言模型已经成为了现代自然语言处理pipeline中的基石&#xff0c;因为其在少量的标注数据上产生更好的结果。随着ELMo、ULMFiT、GPT和BERT的开发&#xff0c;使用预训练模型在下游任务上微调的范式被广泛使用。随后发现预训练语言模型在没有任何额外训练的情况下任…

【团购核销】抖音生活服务商家应用快速接入②——商家授权

文章目录 一、前言二、授权流程三、授权Url3.1 Url参数表3.2 授权能力表3.3 源码示例 四、授权回调4.1 添加授权回调接口4.2 授权回调接口源码示例 五、实际操作演示六、参考 一、前言 目的&#xff1a;将抖音团购核销的功能集成到我们自己开发的App和小程序中 【团购核销】抖音…

解决 Android 单元测试 No tests found for given includes:

问题 报错&#xff1a; Execution failed for task :testDebugUnitTest. > No tests found for given includes: 解决方案 1、一开始以为是没有给测试类加public修饰 2、然后替换 Test 注解的包可以解决&#xff0c;将 org.junit.jupiter.api.Test 修改为 org.junit.Tes…