算法 —— LRU算法

算法 —— LRU算法

  • LRU
      • LRU算法的工作原理:
      • 实现方法:
      • 性能考虑:
  • 模拟过程
    • splice函数
      • 对于`std::list`和`std::forward_list`
        • 基本语法:
        • 功能描述:
      • 示例:
      • 注意事项:

如果大家已经学习过了Cache的替换算法和页面置换算法,大家一定对LRU(Least Recently Used,最近最少使用)不陌生,我们今天来研究下这个算法:

https://leetcode.cn/problems/lru-cache/description/

在这里插入图片描述这里有一个例子:
在这里插入图片描述

LRU

LRU(Least Recently Used,最近最少使用)算法是一种常用的缓存淘汰策略,用于在缓存空间有限的情况下决定哪些数据应该被保留,哪些数据应该被移除LRU算法的基本理念是:如果某数据在最近一段时间内没有被访问,那么在未来被访问的可能性也比较低。反之,如果某数据被频繁访问,那么它应当被保留在缓存中

LRU算法的工作原理:

  1. 缓存初始化:当缓存初始化时,它是空的。
  1. 数据访问
  • 如果请求的数据已经在缓存中,称为缓存命中(Hit),则更新该数据项的访问状态,表明它最近被使用过。
  • 如果请求的数据不在缓存中,称为缓存未命中(Miss),则需要从主存或其他存储中加载数据到缓存。
  1. 数据淘汰
  • 当缓存已满,而新的数据需要加入缓存时,LRU算法会选择最近最少使用的数据项进行淘汰,以便为新数据腾出空间。
  • “最近最少使用”的定义是:在当前时刻,从上次访问到现在时间间隔最长的数据。

实现方法:

LRU算法可以通过多种数据结构来实现,其中最常见的是使用双向链表和哈希表的组合:

  • 双向链表:用于维护数据项的访问顺序,最新访问的数据放在链表头部,最久未访问的数据放在链表尾部。
  • 哈希表:用于快速查找数据项在双向链表中的位置。

当数据被访问时,它从链表中的当前位置移动到链表头部。当缓存满时,链表尾部的数据项被移除。

性能考虑:

LRU算法虽然直观且有效,但在某些情况下可能会有性能开销,尤其是当数据集非常大时,维护链表的插入和删除操作可能会成为瓶颈。此外,如果数据访问模式中存在大量突发性的随机访问,LRU算法可能无法很好地预测哪些数据是真正需要保留在缓存中的。

尽管如此,LRU仍然是许多缓存系统中首选的淘汰策略,因为它在大多数情况下能够提供较好的命中率和性能。在软件和硬件缓存管理中,LRU算法都有广泛应用。例如,在Web服务器缓存、数据库查询缓存、CPU缓存和虚拟内存管理系统中都能见到它的身影。

模拟过程

我们这里用unordered_map和list来模拟:

#pragma once
#include<iostream>
#include<unordered_map>
#include<list>
using namespace std;class LRUCache {
public:LRUCache(int capacity) {}int get(int key) {}void put(int key, int value) {}private:size_t _capacity; //容量//查询unordered_map<int ,list<pair<int,int>>::iterator> _LRUMap;//插入删除list<pair<int, int>> _LRUList;
};

unordered_map可以帮助我们查询是O(1)的时间复杂度,list帮助我们模拟过程,这里我们unordered_map的第二个键值对是list的迭代器,这个方便我们直接修改顺序是O(1)的操作:

我们来看看:
在这里插入图片描述我们按照这个过程来模拟:

LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1);    // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2);    // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1);    // 返回 -1 (未找到)
lRUCache.get(3);    // 返回 3
lRUCache.get(4);    // 返回 4

在这里插入图片描述
在这里插入图片描述
这个时候执行了查询操作:

lRUCache.get(1);    // 返回 1

在这里插入图片描述接下来我们放入了3,3:

lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}

在这里插入图片描述
以此类推,我们可以得出代码:

#pragma once
#include <iostream>
#include <unordered_map>
#include <list>
using namespace std;class LRUCache {
public:// 构造函数,初始化缓存容量LRUCache(int capacity) : _capacity(capacity) {}// 获取缓存中的值,如果存在则更新其位置至最近使用int get(int key) {// 查找键值对应的迭代器auto ret = _LRUMap.find(key);if (ret != _LRUMap.end()) { // 如果找到了键值list<pair<int, int>>::iterator it = ret->second;// 将找到的元素移动到列表的前端,表示最近被使用_LRUList.splice(_LRUList.begin(), _LRUList, it);// 返回值return it->second;} else {// 如果没有找到,返回-1return -1;}}// 插入或更新键值对void put(int key, int value) {// 查找键值对应的迭代器auto ret = _LRUMap.find(key);if (ret == _LRUMap.end()) { // 如果没找到,即键值不存在// 如果缓存已满if (_capacity == _LRUList.size()) {// 删除最旧的元素(列表的最后一个元素)_LRUMap.erase(_LRUList.back().first);_LRUList.pop_back();}// 插入新的键值对到列表前端_LRUList.push_front(make_pair(key, value));// 更新或添加键值对应的迭代器到哈希表_LRUMap[key] = _LRUList.begin();} else {// 如果键值已存在,更新值并移动到列表前端list<pair<int, int>>::iterator it = ret->second;it->second = value; // 更新值// 将元素移动到列表前端_LRUList.splice(_LRUList.begin(), _LRUList, it);}}// 打印缓存内容void Print() {for (auto e : _LRUList) {cout << "key值: " << e.first << " value值: "<< e.second << endl;}cout << endl;}private:size_t _capacity; // 缓存的最大容量// 用于快速查找键值对应的迭代器unordered_map<int, list<pair<int, int>>::iterator> _LRUMap;// 存储键值对的有序列表,用于维护最近使用的顺序list<pair<int, int>> _LRUList;
};

在这里插入图片描述在这里插入图片描述

splice函数

splice是C++标准模板库(STL)中容器(如std::list, std::forward_list, std::deque)的一个成员函数,用于在容器之间或容器内部移动元素。splice函数允许你将一个容器中的元素或一组连续的元素无缝地插入到另一个相同类型的容器的指定位置,而无需复制或构造元素。这对于需要高效地重新组织元素顺序的情况非常有用。

对于std::liststd::forward_list

对于std::liststd::forward_listsplice的用法如下:

基本语法:
void splice(position, x);
void splice(position, x, iterator i);
void splice(position, x, iterator first, iterator last);
  • position:在当前容器中插入元素的位置,对于std::list,可以是iteratorconst_reference;对于std::forward_list,总是before_begin()
  • x:源容器,必须与当前容器具有相同的类型。
  • i:源容器中的单个元素迭代器。
  • first, last:源容器中元素范围的迭代器。
功能描述:
  • splice(position, x);:将x容器中的所有元素移动到当前容器的position位置之前。
  • splice(position, x, i);:将x容器中由i指向的单个元素移动到当前容器的position位置之前。
  • splice(position, x, first, last);:将x容器中由[first, last)区间定义的元素序列移动到当前容器的position位置之前。

示例:

假设我们有两个std::list<int>容器,list1list2,我们想把list2中的元素5移动到list1的开始位置:

std::list<int> list1{1, 2, 3, 4};
std::list<int> list2{5, 6, 7, 8};auto it = list2.find(5);
list1.splice(list1.begin(), list2, it);

现在list1看起来应该是{5, 1, 2, 3, 4},而list2应该是{6, 7, 8}

注意事项:

  • 移动操作是常数时间复杂度的,因此splice非常高效。
  • 被移动的元素将从源容器中移除。
  • 如果两个容器共享同一个分配器(例如,它们是同一个容器的不同部分),splice操作不会抛出异常。

对于std::dequesplice的用法与上述略有不同,因为std::deque不允许在中间插入或删除元素,只能在两端进行。因此,std::dequesplice只接受before_begin()end()作为插入位置,而且只能从另一个std::deque中移动元素到当前std::deque的开头或结尾。

总之,splice是一个强大的工具,可以高效地重新组织容器中的元素,特别是在需要移动大量元素或避免不必要的元素复制时。

具体更多的可以查看官网:

https://legacy.cplusplus.com/

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

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

相关文章

ElementUIV12相关使用方法

今日内容 零、 复习昨日 零、 复习昨日 一、Element UI Element&#xff0c;一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库 官网&#xff1a; https://element.eleme.cn/#/zh-CN Element Plus,基于 Vue 3&#xff0c;面向设计师和开发者的组件库 官网: htt…

C语言--递归

曾经有一个段子&#xff1a;上大学时&#xff0c;我们的c语言老师说&#xff1a;学c时&#xff0c;如果有50%的同学死在了循环上面&#xff0c;那么就有90%的同学死在了递归上面。接下来&#xff0c;就来看看递归是怎么个事&#xff1f; 一.递归的介绍 递归是指一个函数直接或…

Spring中的@Transactional什么时候会失效?

在Spring中&#xff0c;Transactional注解用于声明式事务管理&#xff0c;它可以使方法在事务上下文中执行。然而&#xff0c;Transactional注解有时会失效&#xff0c;这通常是由于以下几种情况&#xff1a; 1. 非public方法&#xff1a; - Transactional注解默认只能应用…

跨平台WPF音乐商店应用程序

目录 一 简介 二 设计思路 三 源码 一 简介 支持在线检索音乐&#xff0c;支持实时浏览当前收藏的音乐及音乐数据的持久化。 二 设计思路 采用MVVM架构&#xff0c;前后端分离&#xff0c;子界面弹出始终位于主界面的中心。 三 源码 视窗引导启动源码&#xff1a; namesp…

MySQL(8)事务

目录 1.事务; 1.事务: 1.1 如果CURD不加限制会这么样子? 可能造成数据同时被修改, 数据修改的结果是未知的.(可以想一下之前的抢票线程问题) 1.2 事务概念: 事务就是一组DML语句组成&#xff0c;这些语句在逻辑上存在相关性&#xff0c;这一组DML语句要么全部成功&#xff0…

基于python旅游景点满意度分析设计与实现

1.1研究背景与意义 1.1.1研究背景 随着旅游业的快速发展&#xff0c;满意度分析成为评估旅游景点质量和提升游客体验的重要手段。海口市作为中国的旅游城市之一&#xff0c;其旅游景点吸引了大量游客。然而&#xff0c;如何科学评估和提升海口市旅游景点的满意度&#xff0c;…

中电金信-杭州工商银行|面试真题|2024年

中电金信-杭州工商银行 JAva集合用过哪些? ArrayList、LinkedList、HashSet、TreeSet、HashMap、LinkedHashMap、ConcurrentHashMap Arraylist和linkbist区别 ArrayList底层是数据&#xff0c;查询快&#xff0c;增删慢&#xff0c;线程不安全&#xff0c;效率高LikedList 底…

【概率论三】参数估计:点估计(矩估计、极大似然法)、区间估计

文章目录 一. 点估计1. 矩估计法2. 极大似然法2.1. 似然函数2.2. 极大似然估计法 3. 评价估计量的标准3.1. 无偏性3.2. 有效性3.3. 一致性 二. 区间估计1. 区间估计的概念2. 正态总体参数的区间估计 参数估计讲什么 由样本来确定未知参数参数估计分为点估计与区间估计 一. 点估…

算法:二叉树相关

目录 题目一&#xff1a;单值二叉树 题目二&#xff1a;二叉树的最大深度 题目三&#xff1a;相同的树 题目四&#xff1a;对称二叉树 题目五&#xff1a;另一棵树的子树 题目六&#xff1a;二叉树的前序遍历 题目七&#xff1a;二叉树遍历 题目八&#xff1a;根据二叉…

linux搭建mysql主从复制(一主一从)

目录 0、环境部署 1、主服务器配置 1.1 修改mysql配置文件 1.2 重启mysql 1.3 为从服务器授权 1.4 查看二进制日志坐标 2、从服务器配置 2.1 修改mysql配置文件 2.2 重启mysql 2.3 配置主从同步 2.4 开启主从复制 3、验证主从复制 3.1 主服务器上创建test…

微服务拆分流程 (黑马商城拆分商品服务)

1. 创建新module - maven模块&#xff0c;并引入依赖&#xff08;可以复制 把不需要的依赖删掉 &#xff09; 2. 新建包com.hmall.xx&#xff08;业务名&#xff09;&#xff0c;添加和修改启动类&#xff0c;新建mapper包、domain包 - service包 - controller包 3. 拷贝并修…

4款良心软件,免费又实用,内存满了都舍不得卸载

以下几款高质量软件&#xff0c;若是不曾体验&#xff0c;实在是遗憾可惜。 PDF Guru 这是一款开源免费的PDF编辑软件&#xff0c;打开之后功能一目了然。 可以拆分、合并PDF&#xff0c;也可以给PDF添加水印和密码&#xff0c;同时也可以去除别人PDF里的水印或密码&#xff0…

HouseCrafter:平面草稿至3D室内场景的革新之旅

在室内设计、房地产展示和影视布景设计等领域,将平面草稿图快速转换为立体的3D场景一直是一个迫切的需求。HouseCrafter,一个创新的AI室内设计方案,正致力于解决这一挑战。本文将探索HouseCrafter如何将这一过程自动化并提升至新的高度。 一、定位:AI室内设计的革新者 Ho…

Scala之OOP讲解

Scala OOP 前序 Scala 为纯粹OOP1、不支持基本类型&#xff1a;一切皆为对象 Byte,Int,...2、不支持静态关键字&#xff1a;static 3、支持类型推断【通过判断泛型的父子关系来确定泛型类的父子关系>协变&#xff0c;逆变&#xff0c;不变】和类型预定&#xff0c; 动静…

【iOS】类对象的结构分析

目录 对象的分类object_getClass和class方法isa流程和继承链分析isa流程实例验证类的继承链实例验证 类的结构cache_t结构bits分析实例验证属性properties方法methods协议protocolsro类方法 类结构流程图解 对象的分类 OC中的对象主要可以分为3种&#xff1a;实例对象&#xf…

【React】JSX基础

一、简介 JSX是JavaScript XML的缩写&#xff0c;它是一种在JavaScript代码中编写类似HTML模板的结构的方法。JSX是React框架中构建用户界面&#xff08;UI&#xff09;的核心方式之一。 1.什么是JSX JSX允许开发者使用类似HTML的声明式模板来构建组件。它结合了HTML的直观性…

TDesign组件库日常应用的一些注意事项

【前言】Element&#xff08;饿了么开源组件库&#xff09;在国内使用的普及率和覆盖率高于TDesign-vue&#xff08;腾讯开源组件库&#xff09;&#xff0c;这也导致日常开发遇到组件使用上的疑惑时&#xff0c;网上几乎搜索不到其文章解决方案&#xff0c;只能深挖官方文档或…

2024.7.17 ABAP面试题目总结

2024.7.17 用的SAP什么平台&#xff0c;S4/HANA吗&#xff0c;有用过ECC吗 S4/HANA&#xff0c;没用过ECC 会不会CDS VIEW 不会 会不会FIORI 不会 银企直连里面的逻辑了解不 不了解&#xff0c;做过&#xff0c;但是只能算很简单的修改 SAP做增强&#xff0c;如何创建…

网络安全-网络安全及其防护措施7

31.防病毒和恶意软件保护 防病毒和恶意软件防护的定义和作用 防病毒和恶意软件防护是一种保护计算机和网络免受病毒、木马、间谍软件等恶意软件侵害的安全措施。通过防护措施&#xff0c;可以检测、阻止和清除恶意软件&#xff0c;确保系统和数据的安全。其主要作用包括&…

C++右值引用和移动语义

目录 概念&#xff1a; 左值引用和右值引用 概念&#xff1a; 注意&#xff1a; 左值引用的意义 作函数参数 函数引用返回 右值引用的意义 诞生背景 移动构造 移动赋值 其他应用 万能引用和完美转发 默认的移动构造和移动赋值 概念&#xff1a; 左值&#xff1a;顾…