【算法】 LRU Cache

目录

一、什么是LRU Cache

二、LRU Cache的实现

三、 LRU算法的运用场景


一、什么是LRU Cache

LRU是Least Recently Used的缩写,意思是最近最少使用,它是一种Cache替换算法。 什么是 Cache?狭义的Cache指的是位于CPU和主存间的快速RAM, 通常它不像系统主存那样使用 DRAM技术,而使用昂贵但较快速的SRAM技术。 广义上的Cache指的是位于速度相差较大的两种硬件之间, 用于协调两者数据传输速度差异的结构。除了CPU与主存之间有Cache, 内存与硬盘之间也有Cache,乃至在硬盘与网络之间也有某种意义上的Cache——称为Internet临时文件夹或 网络内容缓存等。

Cache的容量有限,因此当Cache的容量用完后,而又有新的内容需要添加进来时, 就需要挑选并舍弃原有的部分内容,从而腾出空间来放新内容。LRU Cache 的替换原则就是将最近最少使用的内容替换掉。其实,LRU译成最久未使用会更形象, 因为该算法每次替换掉的就是一段时间内最久没有使用过的内容。

二、LRU Cache的实现

实现LRU Cache的方法和思路很多,但是要保持高效实现O(1)的put和get,那么使用双向链表和 哈希表的搭配是最高效和经典的。

使用双向链表是因为双向链表可以实现任意位置O(1)的插入和删除,使用哈希表是因为哈希表的增删查改也是O(1);但我们要注意的是哈希表val值存储的并不是对应的数据值,而是指向双向链表某一个节点的迭代器,这样子在我们查找数据在链表中某一个节点位置时,就可以直接通过哈希表来定位,并不用遍历链表了:

下面我们通过一道OJ题来实现一下该算法:

题目地址:146. LRU 缓存 - 力扣(LeetCode)

解题代码: 

class LRUCache {
public:
public:LRUCache(int capacity) { _capacity = capacity; }int get(int key) {// 如果key对应的值存在,则listit取出,这里就可以看出hashmap的value存的是list的iterator的好处:找到key也就找到key存的值在list中的iterator,也就直接删除,再进行头插,实现O(1)的数据挪动。auto hashit = _hashmap.find(key);if (hashit != _hashmap.end()) {auto listit = hashit->second;pair<int, int> kv = *listit;_list.erase(listit);_list.push_front(kv);_hashmap[key] = _list.begin();return kv.second;} else {return -1;}}void put(int key, int value) {// 1.如果没有数据则进行插入数据// 2.如果有数据则进行数据更新auto hashit = _hashmap.find(key);if (hashit == _hashmap.end()) {// 插入数据时,如果数据已经达到上限,则删除链表头的数据和hashmap中的数据,两个删除操作都是O(1)if (_list.size() >= _capacity) {_hashmap.erase(_list.back().first);_list.pop_back();}_list.push_front(make_pair(key, value));_hashmap[key] = _list.begin();} else {// 再次put,将数据挪动list前面auto listit = hashit->second;pair<int, int> kv = *listit;kv.second = value;_list.erase(listit);_list.push_front(kv);_hashmap[key] = _list.begin();}}private:list<pair<int, int>> _list; // 将最近用过的往链表的投上移动,保持LRUsize_t _capacity; // 容量大小,超过容量则换出,保持LRUunordered_map<int, list<pair<int, int>>::iterator> _hashmap;// 使用unordered_map,让搜索效率达到O(1)// 需要注意:这里最巧的设计就是将unordered_map的value// type放成list<pair<int,int// >>::iterator,因为这样,当get一个已有的值以后,就可以直接找到key在list中对应的iterator,然后将这个值移动到链表的头部,保持LRU。
};

三、 LRU算法的运用场景

LRU算法也可以用于一些实际的应用中,如你要做一个浏览器,或类似于淘宝客户端的应用的就要用到这个原理。大家都知道浏览器在浏览网页的时候会把下载的图片临时保存在本机的一个文件夹里,下次再访问时就会直接从本机临时文件夹里读取。但保存图片的临时文件夹是有一定容量限制的,如果你浏览的网页太多,就会一些你最不常使用的图像删除掉,只保留最近最久使用的一些图片。这时就可以用到LRU算法 了,这时上面算法里的这个特殊的栈就不是保存页面的序号了,而是每个图片的序号或大小;所以上面这个栈的元素都用Object类来表示,这样的话这个栈就可以保存的对像了。

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

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

相关文章

购买腾讯云服务器需要多少钱?价格表查询

腾讯云服务器多少钱一年&#xff1f;61元一年起。2024年最新腾讯云服务器优惠价格表&#xff0c;腾讯云轻量2核2G3M服务器61元一年、2核2G4M服务器99元一年可买三年、2核4G5M服务器165元一年、3年756元、轻量4核8M12M服务器646元15个月、4核16G10M配置32元1个月、312元一年、8核…

【每周赠书活动第1期】Python编程 从入门到实践 第3版(图灵出品)

编辑推荐 适读人群 &#xff1a;本书适合对Python感兴趣的所有读者阅读。 编程入门就选蟒蛇书&#xff01; 【经典】Python入门经典&#xff0c;常居Amazon等编程类图书TOP榜 【畅销】热销全球&#xff0c;以12个语种发行&#xff0c;影响超过 250 万读者 【口碑】好评如潮…

突发!Stability AI的CEO,跑路了

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了人工智能中文站https://ai.weoknow.com 每天给大家更新可用的国内可用chatGPT资源​ 发布在https://it.weoknow.com 更多资源欢迎关注 ​ 2024 年才第一季度&#xff0c;生成式 AI 明星公司就倒了俩&#xf…

Fantasy RPG Spell Pack 2

介绍奇幻角色扮演游戏魔法包VFX,这是为您的Unity奇幻角色扮演游戏提供的终极视觉效果解决方案!这个包包含30个独特的VFX,将为您的法术和能力带来生命,让您的玩家沉浸在魔法和奇迹的世界中。 从令人惊叹的彩虹盾和闪电到旋转门户和召唤圈,这个包有你需要的一切来创造一个真…

【Linux】线程互斥{线程间的互斥相关背景概念/锁的相关问题/锁的原理/可重入VS线程安全}

文章目录 0.计算机如何完成y a * b c &#xff1f;1.线程间的互斥相关背景概念2.pthread_mutex_t3.pthread_mutex_lock()4.time() or gettimeofday5.锁的相关问题6.锁的原理7.可重入VS线程安全8.完善后的代码 0.计算机如何完成y a * b c &#xff1f; 来源&#xff1a; 王道…

Docker容器初始

华子目录 docker简介虚拟化技术硬件级虚拟化硬件级虚拟化历史操作系统虚拟化历史基于服务的云计算模式 什么是dockerDocker和传统虚拟化方式的不同之处为什么要使用docker&#xff1f;Docker 在如下几个方面具有较大的优势 对比传统虚拟机总结docker应用场景docker改变了什么 基…

【Unity】Unity的生命周期(事件函数的执行顺序)

我们可以从官网上找到这样一张图&#xff1a; 其实这张图上就很清楚的表示各个事件函数的执行顺序了。 我们接下来挑几个非常重要的函数主要来描述一下&#xff1a; Awake Awake为场景加载时会调用的函数。其始终会在任何Start函数之前并在实例化Prefab之后调用Awake函数。…

【热门话题】ECMAScript vs JavaScript:理解两者间的联系与区别

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 ECMAScript vs JavaScript&#xff1a;理解两者间的联系与区别1. ECMAScript&am…

Day 15 Servlet(一)

Servlet 1、简介2、快速入手2.1servlet jar包导入2.2 Content-type2.3 Servlet-url 写法2.4 注解方式配置servlet2.5 servlet 生命周期 1、简介 资源包括静态资源和动态资源。 对于服务器响应&#xff0c;有时候我们需要根据客户的不同请求返回不同的数据和页面。此时就需要一…

研发安全(二)——后端开发必须掌握的SQL注入漏洞与防护

1 引言 SQL 注入对于我们研发人员来说&#xff0c;应该基本都听说过&#xff0c;甚至还在开发功能过程中不知不觉引入了SQL注入漏洞&#xff0c;只是没被发现而已。 所以&#xff0c;本文会系统的介绍下SQL注入漏洞的危害以及我们开发过程中如何进行防护。 2 SQL注入的成因 SQ…

【Linux】文件属性信息、文件目录权限修改

Linux文件属性信息 在 Linux 中&#xff0c;ls命令用于列出目录内容&#xff0c;并提供了许多参数以定制输出和显示不同类型的信息。以下是一些常用的ls命令参数 -a显示所有文件和目录&#xff0c;包括以.开头的隐藏文件。-l使用长格式列出文件和目录的详细信息&#xff0c;包…

深度学习模型部署(十一)TensorRT写Plugin

什么是plugin & 有什么用&#xff1f; TensorRT的一种机制&#xff0c;以.so的形式插入到网络中实现某些算子。 作用&#xff1a; 实现TensorRT不支持的层替换性能不好的层手动进行图优化算子融合 写Plugin就是自己写算子的CUDA kernel实现。 Plugin与其他layer之间无法…