使用双向链表和哈希表实现LRU缓存

news2024/9/27 20:07:53

在日常开发中,缓存 是一个非常常见且重要的技术手段,能够显著提升系统性能。为了保证缓存的有效性,需要实现一种机制,在缓存空间不足时,能够自动淘汰最久未被使用的数据。这种机制就是**LRU(Least Recently Used,最近最少使用)**算法。

一、LRU缓存的原理

LRU是一种常用的缓存淘汰策略,基本思路是:当缓存已满时,淘汰最近最少使用的数据。为了实现这种策略,我们需要快速找到最久未使用的数据,同时在每次访问缓存时,都要将访问的数据移到最前面。

为了实现这一需求,我们可以通过双向链表哈希表的结合:

  • 双向链表:用于记录访问顺序,最新访问的数据在链表头部,最久未使用的数据在链表尾部。当缓存满时,删除链表尾部的数据。
  • 哈希表:通过哈希表实现O(1)的查找速度,快速判断某个数据是否在缓存中。

二、LRU缓存的设计

我们使用如下的数据结构来实现LRU缓存:

  1. 双向链表:用于维护缓存中的数据,链表的头部是最近访问的数据,尾部是最久未使用的数据。
  2. 哈希表:用于存储缓存中每个节点的地址,以便快速查找。
双向链表的节点结构

我们定义了一个双向链表的节点 ListNode,用于存储每个缓存项的键值对:

struct ListNode {
    int key;
    string val;
    struct ListNode* prev;
    struct ListNode* next;
    ListNode(int k, const string& v): key(k), val(v), prev(nullptr), next(nullptr) {}
};

这个结构体有四个成员:

  • key:缓存项的键
  • val:缓存项的值
  • prev:指向前一个节点
  • next:指向后一个节点
LRU类设计

接下来,我们实现LRU缓存类 LRU。该类包含以下成员:

  • headtail:指向链表的头节点和尾节点,便于快速插入和删除。
  • listSize:当前链表的长度。
  • Size:缓存的最大容量。
  • mp:一个哈希表,用于存储键与链表节点的映射。
class LRU {
private:
    struct ListNode* head;
    struct ListNode* tail;
    int listSize;
    int Size;
    unordered_map<int, struct ListNode*> mp;
public:
    LRU() {
        head = new ListNode(0, "");
        tail = new ListNode(0, "");
        head->next = tail;
        tail->prev = head;
        listSize = 0;
        Size = 5;  // 缓存容量设为5
    }

三、LRU缓存的实现

我们需要实现的功能有:

  1. 插入或更新缓存项:每次插入或访问某个缓存项时,将其移到链表的头部。
  2. 淘汰最久未使用的缓存项:当缓存容量超出时,删除链表尾部的节点。
1. 缓存插入或更新操作

每次插入缓存时,首先检查该键是否已经存在:

  • 如果存在,将该节点移到链表的头部。
  • 如果不存在,创建一个新的节点并插入到链表头部。同时,当链表长度超过容量时,删除尾部节点。
void insert(int k, const string& v) {
    // 缓存命中
    if (mp.find(k) != mp.end()) {
        struct ListNode* t = mp[k];
        struct ListNode* p = mp[k]->prev;
        struct ListNode* n = mp[k]->next;

        // 将该节点从原位置移除
        p->next = n;
        n->prev = p;

        // 移动到链表头部
        p = head->next;
        head->next = t;
        t->next = p;
        p->prev = t;
        t->prev = head;
    }
    // 缓存不命中
    else {
        struct ListNode* t = new ListNode(k, v);
        mp[k] = t;
        struct ListNode* p = head->next;

        // 插入到链表头部
        head->next = t;
        t->next = p;
        p->prev = t;
        t->prev = head;
        listSize++;

        // 数量满了,需要删除最后的元素
        if (listSize == Size + 1) {
            t = tail->prev;
            t->prev->next = tail;
            tail->prev = t->prev;
            listSize--;

            mp.erase(t->key);
            delete t;
        }
    }
}
2. 缓存打印操作

我们还实现了一个简单的 print 函数,用于输出当前缓存的内容,帮助调试和验证程序的正确性:

void print() {
    struct ListNode* p = head->next;
    while (p != tail) {
        cout << "{" << p->key << "," << p->val << "}" << ' ';
        p = p->next;
    }
    cout << endl;
}

四、测试与输出

我们可以通过 main 函数测试这个LRU缓存:

int main() {
    LRU lru;
    lru.insert(1, "A");
    lru.insert(2, "B");
    lru.insert(3, "C");
    lru.insert(4, "D");
    lru.insert(5, "E");
    lru.insert(6, "F");
    lru.insert(7, "G");
    lru.print();
}

输出结果为:

{7,G} {6,F} {5,E}

请添加图片描述

这个输出说明,最新插入的键值对 {7, G} 在链表头部,而最早的 {1, A} 已经被淘汰。

五、总结

通过以上的实现,我们可以看到 LRU 缓存可以通过双向链表和哈希表的结合高效实现。双向链表用于维护缓存项的顺序,哈希表用于快速查找缓存项。每次访问或插入时,都将对应项移动到链表的头部,而当缓存超出容量时,淘汰链表尾部的最久未使用数据。

这种设计使得 LRU 缓存的查找插入删除操作都能在 O(1) 时间内完成,非常适合在高频率数据访问场景下使用。

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

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

相关文章

CSS文本格式化

通过 CSS 中的文本属性您可以像操作 Word 文档那样定义网页中文本的字符间距、对齐方式、缩进等等&#xff0c;CSS 中常用的文本属性如下所示&#xff1a; text-align&#xff1a;设置文本的水平对齐方式&#xff1b;text-decoration&#xff1a;设置文本的装饰&#xff1b;te…

面试题-部分

目录 1. 从输入url到渲染页面&#xff0c;中间经历了什么&#xff1f; 2. vue中的v-if和v-show有什么区别 3. 什么是Css中的回流&#xff08;重排&#xff09;与重绘 4. 介绍一下let、const、var的区别 5. 箭头函数和普通函数有什么区别 6. Css中常用的水平垂直居中解决方…

传输大咖49 | 科学研究院跨网文件交换高效、安全解决方案

在科学研究领域&#xff0c;数据的价值堪比黄金。科学研究所的日常运作依赖于大量的数据交换&#xff0c;高效的文件交换系统离不开内部合作和与外部合作伙伴的交流。然而&#xff0c;随着数据量的激增和网络环境的复杂性&#xff0c;传统的文件交换方法很难满足需求。本文将讨…

RK3568 android11 适配鼎桥MT5710-CN 5G模块

一,概述 鼎桥MT571X设备和Android系统主要通过USB接口进行数据通信,Android系统上的Linux内核需要根据鼎桥模块设备上报的USB设备接口加载USB驱动,USB驱动正确加载后,鼎桥模块才能正常工作。 Android系统中支持鼎桥模块设备相关的Linux内核驱动架构,如下图所示: 在Lin…

js删除emoji表情问题

emoji标签占位两个 &#xff0c;直接删除后一位会出现乱码符&#xff1b; 判断是否是emoji function isEmoji(char) {let code char.charCodeAt(0);return code>55296&&code<57343 } // 使用方法&#xff0c;传入单字符 console.log(isEmoji(1)); // false con…

Kubernetes 配置管理

一、什么是 ConfigMap&#xff1f; 在传统架构中&#xff0c;配置文件往往被保存在宿主机上&#xff0c;程序启动是可以指定某个配置文件&#xff0c;但是使用容器部署时&#xff0c;容器所在的节点并不固定&#xff0c;所以不能使用这种方式&#xff0c;此处在构建镜像时&…

【Redis】主从复制(下)--主从复制原理和流程

文章目录 主从复制原理主从节点建立复制流程图数据同步 psyncpsync的语法格式 psync运行流程全量复制全量复制的流程全量复制的缺陷有磁盘复制 vs 无磁盘复制 部分复制部分复制的流程复制积压缓冲区 实时复制 主从复制原理 主从节点建立复制流程图 保存主节点的信息从节点(sla…

感悟:糟糠之妻不下堂和现在女性觉醒的关系

古人说“糟糠之妻不下堂”真是害惨了中国女性&#xff0c;古代之所以有这一说法&#xff0c;大概是因为男子可以三妻四妾&#xff0c;妻子永远是正妻&#xff0c;也不需要讲究什么从一而终&#xff0c;更不会讲什么男德&#xff0c;只会要求女性学习女德、女训之类&#xff0c;…

性能测试:性能测试报告

性能测试报告是性能测试的产出物之一&#xff0c;它是对系统性能测试结果和数据的总结和分析&#xff0c;记录了系统在不同负载和场景下的性能表现和性能问题。性能测试报告提供了有关系统性能的详细信息&#xff0c;供项目团队、开发人员和其他相关利益相关者参考。 性能测试…

原生app云打包,更换图标,和名称。PDA的安装正式包

原生app云打包 复制下载即可&#xff0c;是正式版

Android下MVP和MVVM模式的实践

转载注明出处&#xff1a;https://blog.csdn.net/skysukai 1、前言 MVP和MVVM诞生已经好些年头了&#xff0c;记得刚毕业才参加工作的时候&#xff0c;第一次见到了有上万行的Activity&#xff0c;这种巨无霸的Activity维护起来简直就是噩梦。这时候&#xff0c;就需要进行代…

商标价值如何评估与增值?

商标是企业的标志&#xff0c;代表着企业的产品或服务质量、信誉和形象。一个具有高知名度和美誉度的商标&#xff0c;能够为企业带来巨大的商业价值。它不仅可以帮助企业在市场中脱颖而出&#xff0c;吸引消费者的关注和购买&#xff0c;还可以作为企业的重要资产进行融资、并…

无人便利店无人超市云值守收银系统源码

随着人力成本越来越高&#xff0c;很多门店越来想做无人值守模式&#xff0c;尤其是晚上休息时间等想让云值守客服来看店。自然要求收银系统需要可以在【有收银员值守】和【无收银员值守】两种模式灵活切换。 1. 有收银员值守模式 白天有收银员在店&#xff0c;收银员可以协助…

Tomcat服务与运用

案例准备 1.规划节点 IP 主机名 节点 192.168.20.20 tomcat Tomcat 2.基础准备 使用VMWare Workstation软件安装CentOS 7.2操作系统&#xff0c;镜像使用提供的CentOS-7-x86_64-DVD-1804.iso&#xff0c;最小化安装CentOS 7.2系统 案例实施 1.基础环境配置 1.1修改…

微信小程序的 button 标签的边框如何去除?

目录 问题描述&#xff1a; 问题原因&#xff1a; 解决办法&#xff1a; 方案一 方案二 问题描述&#xff1a; 实际开发中会发现这个 button 自带有样式&#xff0c;当背景颜色设置为白色的时候还有一个黑色的边框&#xff0c;刚开始那个边框怎么都去不掉 无法去除的边框…

IRR 之 24期免息等于免费?钱买保险还是余额宝?

一、IRR 是什么&#xff1f; 英文全称是 Internal Rate of Return 具体公式可以参考公众号 随园经济56&#xff5c;内部收益率 简单理解可以为&#xff1a; IRR是投资项目在整个生命周期中所能获得的平均年化收益率 更简单理解为&#xff0c;余额宝展现给你的年化收益 二、…

WLS2连接本地USB设备的方法

WLS2连接本地USB设备的方法 说明windows端1.下载usbipd-win并安装2.启动WSL3.以管理员身份运行Windows PowerShell”4.WSL中查看USB设备 说明 WLS2连接本地USB设备的方法 windows端 1.下载usbipd-win并安装 可下载**.msi文件&#xff0c;双击即可安装 2.启动WSL 3.以管理…

如何将Excel表格嵌入Web网页在线预览、编辑并保存到自己服务器上?

猿大师办公助手作为一款专业级的网页编辑Office方案&#xff0c;不仅可以把微软Office、金山WPS和永中Office的Word文档内嵌到浏览器网页中实现在线预览、编辑保存等操作&#xff0c;还可以把微软Office、金山WPS和永中Office的Excel表格实现网页中在线预览、编辑并保存到服务器…

如何在Windows、Mac和Linux系统上安装和更新Stable Diffusion WebUI

在图像生成领域&#xff0c;Stable Diffusion与Automatic1111表现出色&#xff0c;给MidJourney、OpenAI的DALL-E和Bing图像生成器带来了激烈的竞争。在本文中&#xff0c;我们将帮助您在本地安装Automatic1111 WebUI&#xff0c;以便您可以从文本提示创建出色的图像。 要生成…

原生代理IP是什么?

代理IP的各个类型称呼有很多&#xff0c;且它们在网络使用和隐私保护方面扮演着不同的角色。今天将探讨什么是原生IP以及原生IP和住宅IP之间的区别&#xff0c;帮助大家更好地理解这两者的概念和实际应用&#xff0c;并选择适合自己的IP类型。 一、什么是原生IP&#xff1f; 原…