一篇搞懂什么是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.mfbz.cn/a/444158.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

账号管理支持批量测试资产可连接性,资产管理支持通过标签方式选择资产,JumpServer堡垒机v3.10.4 LTS版本发布

2024年3月4日&#xff0c;JumpServer开源堡垒机正式发布v3.10.4 LTS版本。JumpServer开源项目组将对v3.10 LTS版本提供长期的支持和优化&#xff0c;并定期迭代发布小版本。欢迎广大社区用户升级至v3.10 LTS最新版本&#xff0c;以获得更佳的使用体验。 在v3.10.4 LTS版本中&a…

Chrome中如何导出和导入书签

导出书签 如下图所示&#xff1a; 右上角三点->书签和清单->书签管理器->右上角三点->导出书签 然后你选择保存地址即可。打开后如下&#xff1a; 导入书签 如下图所示&#xff1a; 右上角三点->书签和清单->导入书签和设置->选择以前导出的书签&…

基于Vue移动端电影票务服务APP设计与实现

目 录 摘 要 I Abstract II 引 言 1 1 相关技术 3 1.1 Vue框架 3 1.2 数据库MongoDB 3 1.3 Axios请求 3 1.4 H5、CSS3和JavaScript 4 1.5 本章小结 4 2 系统分析 5 2.1 功能需求 5 2.2 用例分析 5 2.3 用户功能 6 2.4本章小结 6 3 基于Vue电影票务服务APP设计 7 3.1 页面设计 …

DFS和BFS以及练习题目(未完待续)

DFS和BFS 温馨提示&#xff1a;学习dfs之前最好先了解一下递归的思想。 递归思想 斐波那契 题目分析 题目代码 import java.util.Scanner; public class Main{static long dp[]; public static void main(String[] args) {Scanner scanner new Scanner(System.in);int t…

手机号验证码重新发送

前文叙述 很久以前做的一个 demo &#xff0c;纯 HTML 、CSS、js 制作&#xff0c;一定时间段之后才可以重新发送验证码&#xff0c;如 60s 后再次发送验证码&#xff0c;在该时间段内发送验证码按钮为禁用状态&#xff0c;实战开发过程也亦是同理&#xff0c;因此记录一手。 一…

供应商评价与选择改进研究——21年数学建模国赛C题分析

题目描述 问题一分析&#xff08;基于APH、PCA和TOPSIS的供应商评价与选择&#xff09; 问题一需要我们对附件一中的402家供应商的数据进行处理并量化分析&#xff0c;并构建数学模型选择当中最重要的50家供应商。 附件一&#xff1a; 部分订货量 部分供货量 注意&#xff…

Android 9.0 Folder文件夹全屏后文件夹图标列表居中时拖拽app到桌面的优化

1.概述 在9.0的系统rom产品开发中,在Launcher3中在目前的产品需求开发中,对于Launcher3中的文件夹Folder的布局UI 进行了定制化的需求要求把Folder修改为全屏,然后在中间显示文件夹图标的列表,这时候如果Folder是全屏的话,如果拖拽文件夹列表中的app图标,只有拖拽 到屏幕…

csp复习题

最短路&#xff1a;最优灌溉&#xff08;201412-4&#xff09; 题目描述 问题描述 雷雷承包了很多片麦田&#xff0c;为了灌溉这些麦田&#xff0c;雷雷在第一个麦田挖了一口很深的水井&#xff0c;所有的麦田都从这口井来引水灌溉。   为了灌溉&#xff0c;雷雷需要建立一些…

【算法与数据结构】栈的实现详解

文章目录 &#x1f4dd;栈的概念及结构&#x1f309;栈的实现 &#x1f320;栈的接口&#x1f309;初始化栈&#x1f320;入栈&#x1f309;出栈&#x1f320;获取栈顶元素&#x1f309;获取栈中有效元素个数&#x1f309;检测栈是否为空&#x1f309;销毁栈&#x1f309;Stack…

别错过AI 大模型的奇妙世界!让你惊艳不已!

AI大模型的应用已经渐渐渗透到我们生活的方方面面&#xff0c;从语音识别到自然语言处理&#xff0c;从图像识别到智能推荐&#xff0c;无处不在的AI大模型正在改变着我们的生活。其背后隐藏的奇妙世界让人惊艳不已。 一方面&#xff0c;AI大模型在语音识别领域展现出了强大的…

C语言学习笔记,学懂C语言,看这篇就够了!(上)

说明&#xff1a;这是本人在学习C语言的时候整理的笔记&#xff0c;因文字限制&#xff0c;所以分为三篇文章&#xff0c;即上中下来分享这份笔记。 看完这三部分&#xff0c;C语言基础、计算机C语言二级(关于C语言的部分)、期末考试。考研数据结构(如考408的话&#xff0c;数…

蓝桥杯倒计时 36天-DFS练习

文章目录 飞机降落仙境诅咒小怂爱水洼串变换 飞机降落 思路&#xff1a;贪心暴搜。 #include<bits/stdc.h>using namespace std; const int N 10; int t,n; //这题 N 比较小&#xff0c;可以用暴力搜搜复杂度是 TN*N! struct plane{int t,d,l; }p[N]; bool vis[N];//用…

【C语言】文件操作篇-----程序文件和数据文件,文件的打开和关闭,二进制文件和文本文件,fopen,fclose【图文详解】

欢迎来CILMY23的博客喔&#xff0c;本篇为【C语言】文件操作篇-----程序文件和数据文件&#xff0c;文件的打开和关闭&#xff0c;二进制文件和文本文件【图文详解】&#xff0c;感谢观看&#xff0c;支持的可以给个一键三连&#xff0c;点赞关注收藏。 前言 在了解完动态内存管…

Visual Basic6.0零基础教学(1)—vb的介绍和布局及其小案例

Visual Basic6.0零基础教学(1) 文章目录 Visual Basic6.0零基础教学(1)前言一、vb6.0介绍二、vb的起源一、起源&#xff1a;Basic二、版本三、 Visual Basic6.0 三种版本&#xff1a;四、vb的特点 1.vb的布局介绍创建应用程序的步骤总结 前言 大家好,从今天开始我也会开始更新…

视频可回溯系统技术方案vue3+ts+tegg+mysql+redis+oss

一、 项目背景 保险、基金、银行等众多行业在做技术平台时都会需要一种能够准确了解用户操作行为的方式方法。诸如通过埋点、平台监控、视频可回溯等&#xff0c;通过技术手段&#xff0c;保存用户操作轨迹&#xff0c;以此规范安全销售、平台健康检查、出现纠纷时可追溯、问题…

python的scripts文件夹作用

Windows系统&#xff1a; Scripts文件夹通常位于Python的安装目录下&#xff0c;如C:\Python\Scripts。该文件夹内包含了各种有用的工具&#xff0c;例如pip、virtualenv等&#xff0c;这些工具有助于管理和配置Python环境和依赖包。 Linux系统&#xff1a; 在Linux系统中&…

vivado管理实施、

管理实施 Vivado设计套件包括各种设计流程&#xff0c;并支持一系列设计来源。为了生成可以下载到AMD设备上的比特流&#xff0c;设计必须通过实施。实现是采取逻辑网表并将其映射到物理网表的一系列步骤目标AMD设备的阵列。实施包括&#xff1a; •逻辑优化 •逻辑单元的放…

Django添加app

Django添加App python manage.py startapp [app_name]快速上手 注册app&#xff0c;setting.py 编写url和视图的对应关系 添加视图函数 命令行启动 python manage.py runserver页面模板

Windows下安装pip

一、下载pip 官网地址&#xff1a;https://pypi.org/project/pip/#files 1.1、pip工具查找方法 单击官网首页“PyPi”选项 在弹出来的搜索框中输入“pip” 选择最新的pip版本&#xff0c;点进去 下载pip安装包包 二、安装pip 解压“pip-24.0.tar.gz”&#xff0c;进…

AI绘画提示词案例(宠物

目录 1. 雪地猫猫&#xff1a;1.1 提示词&#xff1a;1.2 效果&#xff1a; 2. 趴地猫猫&#xff1a;2.1 提示词&#xff1a;2.2 效果&#xff1a; 3. 长城萨摩耶&#xff1a;3.1 提示词&#xff1a;3.2 效果&#xff1a; 4. 沙发猫猫&#xff1a;4.1 提示词&#xff1a;4.2 效…