一篇搞懂什么是LRU缓存|一篇搞懂LRU缓存的实现|LRUCache详解和实现

LRUCache

文章目录

  • LRUCache
    • 前言
    • 项目代码仓库
    • 什么时候会用到缓存(Cache)
    • 缓存满了,怎么办?
    • 什么是LRUCache
    • LRUCache的实现
    • LRUCache对应的OJ题实现
    • LRUCache对应的STL风格实现

前言

这里分享我的一些博客专栏,都是干货满满的。

  • 手撕数据结构专栏
  • 高质量博客专栏

项目代码仓库

  • CPlusPlus-review-main/tree/master/skip_list

什么时候会用到缓存(Cache)

缓存(Cache)通常用于两个速度不同的介质之间,以提高数据访问的速度和效率。这里有几个典型的应用场景:

处理器和内存之间: 处理器(CPU)的运算速度远快于从内存中读取数据的速度。因此,在CPU和内存之间会有多级缓存(L1、L2、甚至L3缓存),用来临时存储即将被CPU使用的数据和指令。这样做可以大幅减少CPU等待数据的时间,提高整体计算效率。

内存和硬盘之间: 内存的访问速度也远快于硬盘(无论是HDD还是SSD)。操作系统会使用一部分内存作为硬盘缓存(有时称为“磁盘缓存”或“缓冲区缓存”),用于临时存储最近访问过的数据和文件。当再次请求这些数据时,可以直接从内存中获得,而不是从较慢的硬盘中读取。

数据库系统中: 数据库管理系统(DBMS)也会使用缓存技术来提高查询速度和数据处理效率。缓存可以存储经常访问的查询结果、数据库索引等信息,从而加速后续相同或相似查询的处理速度。

网络请求: 在网络请求中,缓存也是提高数据访问速度的重要技术。例如,Web浏览器会缓存访问过的网页资源(如HTML文件、图片等),当再次访问这些资源时,可以直接从本地缓存读取,而不需要重新从网络下载。

缓存的关键在于它能够存储一份数据副本,在访问速度较慢的介质之前提供快速访问路径。这样,即使背后的存储介质响应较慢,系统性能也不会受到太大影响。然而,缓存管理(如缓存更新、缓存失效策略等)是实现高效缓存系统的一个挑战。正确和高效地使用缓存可以显著提高系统性能,减少数据处理和响应时间。

缓存满了,怎么办?

缓存空间满了之后,更新数据,我要进去,谁出去呢?

什么是LRUCache

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

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

LRUCache的实现

要设计一个LRUCache不难,要设计一个高效的LRUCache有难度,即:任意操作都是O(1)。

使用双向链表和哈希表的搭配是最高效和经典的。使用双向链表是因为双向链表可以实现任意位置0(1)的插入和删除,使用哈希表是因为哈希表的增删查改也是O(1)。

LRUCache对应的OJ题实现

我们借助一个oj题来讲解。

  • 146. LRU 缓存

class LRUCache {
public:LRUCache(int capacity) {}int get(int key) {}void put(int key, int value) {}
};/*** 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);*/

首先用一个哈希表,可以保证我们查找是O(1),那么如何保证LRU?

我们可以用一个链表,我们认为尾巴是最不常用的,如果数据被用了,就弄到头上去,所以尾巴就一定是要被淘汰的数据。

std::unordered_map<int, int> __hash_map;
std::list<std::pair<int, int>> __lru_list;

此时这种设计:

  1. get是O(1)
  2. 新增是O(1)
  3. 但是更新是O(n)

为什么?因为我们要更新数据,就要找到这个数据,就要遍历链表。

那怎么办?

哈希表里面存链表节点的指针就行了。

std::unordered_map<int, std::list<std::pair<int, int>>::iterator> __hash_map;
std::list<std::pair<int, int>> __lru_list;

题目的实现如下:

#include <unordered_map>
#include <list>
#include <utility>class LRUCache
{
private:std::unordered_map<int, std::list<std::pair<int, int>>::iterator> __hash_map;std::list<std::pair<int, int>> __lru_list;size_t __capacity;public:LRUCache(int capacity): __capacity(capacity) {}int get(int key){auto res = __hash_map.find(key);if (res == __hash_map.end())return -1;// 更新链表节点位置auto it = res->second;/*方法一: erase + push_front注意记得erase之后更新迭代器,防止迭代器失效方法二:转移节点的接口,stl::list提供了*/__lru_list.splice(__lru_list.begin(), __lru_list, it);return it->second;}void put(int key, int value){// 1. 新增 2. 更新auto res = __hash_map.find(key);if (res == __hash_map.end()){// 新增,如果满了,先删除数据/*这里用哈希表求size比较细节,这里一定是O(1)但是list有些版本下入过没有维护size这个字段,求一次size()就是O(n)了*/if (__capacity == __hash_map.size()){std::pair<int, int> back = __lru_list.back();__hash_map.erase(back.first);__lru_list.pop_back();}// 加入数据__lru_list.push_front(std::make_pair(key, value));__hash_map[key] = __lru_list.begin();}else{// 更新auto it = res->second;it->second = value; // 更新__lru_list.splice(__lru_list.begin(), __lru_list, it);}}
};

LRUCache对应的STL风格实现

同时我也实现了一个stl模式的类模版,实现也非常简单,也欢迎大家补充。


#include <unordered_map>
#include <list>
#include <utility>template <class key_type, class value_type, size_t CAPACITY = 10>
class LRUCache
{
private:std::unordered_map<key_type, typename std::list<std::pair<key_type, value_type>>::iterator> __hash_map;std::list<std::pair<key_type, value_type>> __lru_list;size_t __capacity = CAPACITY;public:LRUCache() {}value_type get(key_type key){auto res = __hash_map.find(key);if (res == __hash_map.end())return -1;// 更新链表节点位置auto it = res->second;__lru_list.splice(__lru_list.begin(), __lru_list, it);return it->second;}void put(key_type key, value_type value){// 1. 新增 2. 更新auto res = __hash_map.find(key);if (res == __hash_map.end()){if (__capacity == __hash_map.size()){auto back = __lru_list.back();__hash_map.erase(back.first);__lru_list.pop_back();}// 加入数据__lru_list.push_front(std::make_pair(key, value));__hash_map[key] = __lru_list.begin();}else{// 更新auto it = res->second;it->second = value; // 更新__lru_list.splice(__lru_list.begin(), __lru_list, it);}}public:void clear(){__hash_map.clear();__lru_list.clear();}size_t size() { return __hash_map.size(); }bool empty() { return this->size() == 0; }
};

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

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

相关文章

【UE5】游戏框架GamePlay

项目资源文末百度网盘自取 游戏框架 游戏 由 游戏模式(GameMode) 和 游戏状态(GameState) 所组成 加入游戏的 人类玩家 与 玩家控制器(PlayerController) 相关联 玩家控制器允许玩家在游戏中拥有 HUD&#xff0c;这样他们就能在关卡中拥有物理代表 玩家控制器还向玩家提供 …

Spring boot 请求参数包含[]等特殊字符,导致无法接收问题

前言对字符进行转义修改tomcat 配置 前言 Spring boot 请求参数包含[]等特殊字符&#xff0c;导致无法接收问题 对字符进行转义 中括号[] 必须用%5B%5D转义&#xff0c;否则tomcat无法解析&#xff0c;回抛出不合法字符异常&#xff0c;不会进入控制器 修改tomcat 配置 p…

安信可IDE(AiThinker_IDE)编译ESP8266工程方法

0 工具准备 AiThinker_IDE.exe ESP8266工程源码 1 安信可IDE&#xff08;AiThinker_IDE&#xff09;编译ESP8266工程方法 1.1 解压ESP8266工程文件夹 我们这里使用的是NON-OS_SDK&#xff0c;将NON-OS_SDK中的1_UART文件夹解压到工作目录即可 我这里解压到了桌面&#xff0c…

模拟框图的表示

微分方程的建立 目的&#xff1a;为建立LTI系统的数学模型&#xff0c;需要列写微分方程式。 以RLC电路为例&#xff1a; 以Us为输入&#xff0c;Uc为输入&#xff0c;则可以得出以下微分方程式&#xff1a; 抽去物理意义后&#xff0c;得到一般的常微分线性方程&#xff1a;…

朗伯特球腔均匀光源积分球

均匀光源积分球&#xff0c;又称照度积分球或光度球、光通球&#xff0c;是光电测试中常用的一种工具。它是一个中空的球体&#xff0c;内壁涂有一层平整的漫反射材料&#xff0c;通常由金属或陶瓷制成。积分球的主要功能是收集光并将其作为散射光源或测量光源使用。 积分球的工…

【Python】Python Astar算法生成最短路径GPS轨迹

简介 最短路径问题是计算机科学中一个经典问题&#xff0c;它涉及找到图中两点之间距离最短的路徑。在实际应用中&#xff0c;最短路径算法用于解决广泛的问题&#xff0c;例如导航、物流和网络优化。 步骤 1&#xff1a;加载道路网络数据 要计算最短路径&#xff0c;我们需…

脾胃,胃肠中医笔记

目录 脾胃的功能思伤脾&#xff0c;脑力工作者过度思考会伤脾胃焦虑会导致脾胃受伤按摩肚子顺时针还是逆时针&#xff0c;顺时针促消化/逆时针促排便脾胃生病症状舌苔腹胀、滞气的原因为什么大便稀&#xff1f;湿气重的原因及解决方案自测湿气重的方法 治疗脾胃药物总结补中益气…

Windows C++ 实现远程虚拟打印机(远程共享打印机)

编译错误已经修改完后的工程修改后的下载地址 https://download.csdn.net/download/2403_83063732/88928550 1、下载clawpdf&#xff08;0.8.7版本&#xff09; https://github.com/clawsoftware/clawPDF 2、打开clawpdf工程开始编译C#工程&#xff0c;出现如下错误&#xf…

Hadoop生态选择(一)

一、项目框架 1.1技术选型 技术选型主要考虑因素:维护成本、总成本预算、数据量大小、业务需求、行业内经验、技术成熟度。 数据采集传输:Flume&#xff0c;Kafka&#xff0c;DataX&#xff0c;Maxwell&#xff0c;Sqoop&#xff0c;Logstash数据存储:MySQL&#xff0c;HDFS…

【linux】04 :linix实用操作

1.常用快捷键 ctrlc表示强制停止。linux某些程序的运行&#xff0c;如果想强制停止&#xff0c;可以使用&#xff1b;命令输入错误&#xff0c;也可以通过ctrlc,退出当前输入&#xff0c;重新输入。 ctrld表示退出登录&#xff0c;比如退出root以回到普通用户&#xff0c;或者…

Awesome-Backbones-main——alexnet模型分析

AlexNet作为骨干网络相对较老&#xff0c;可能在复杂数据集上的表现不如一些最新的深度网络结构&#xff0c;如ResNet、EfficientNet等&#xff0c;学习率调整策略中采用了阶梯式学习率更新器&#xff0c;可能并不总是适合所有数据集和模型&#xff0c;需要根据具体情况调整学习…

00在linux环境下搭建stm32开发环境

文章目录 前言一、环境搭建1.arm-none-eabi-gcc2.openocd 三、创建stm32标准库工程1.创建工程目录2.修改stm32_flash.ld文件3.写makefile文件4.修改core_cm3.c5.写main函数并下载到板子上 最后 前言 我在那天终于说服自己将系统换成了linux系统了&#xff0c;当换成了linux系统…