手撕LFU缓存

手撕LRU缓存_右大臣的博客-CSDN博客

是LRU的升级,多了一个访问次数的维度

实现 LFUCache 类:

  • LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象
  • int get(int key) - 如果键 key 存在于缓存中,则获取键的值,否则返回 -1 。
  • void put(int key, int value) - 如果键 key 已存在,则变更其值;如果键不存在,请插入键值对。当缓存达到其容量 capacity 时,则应该在插入新项之前,移除最不经常使用的项。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最近最久未使用 的键。

为了确定最不常使用的键,可以为缓存中的每个键维护一个 使用计数器 。使用计数最小的键是最久未使用的键。

当一个键首次插入到缓存中时,它的使用计数器被设置为 1 (由于 put 操作)。对缓存中的键执行 get 或 put 操作,使用计数器的值将会递增。

函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

LRU的实现是一个哈希表加上一个双链表
而LFU则要复杂多了,需要用两个哈希表再加上N个双链表才能实现

LFU需要为每一个频率构造一个双向链表

460. LFU 缓存 - 力扣(LeetCode)  一个逻辑讲解

总的来说就是下图这样:

 所以针对LRU的模仿写法,我就直接用STL的list去做

因为LFU多了频率,所以需要重构结点,,就不像LRU那个一样用pair对组了

//因为多了频率,所以重构结点,,就不像LRU那个一样用pair对组了
struct Node
{int key;int value;int frequency;Node(int k,int v,int f=1):key(k),value(v),frequency(f){}
};
class LFUCache {
private:int cap;int minfre;//最小的频率unordered_map<int,list<Node>::iterator>mpkey;//用记录key -val 缓存unordered_map<int,list<Node>>mpfre;//记录频率访问次数的public:LFUCache(int capacity=10) {cap = capacity;minfre = 1;mpkey.clear();mpfre.clear();}int get(int key) {//没有这个结点if(mpkey.count(key)==0){return -1;}//有结点 ,保存结点信息auto it=mpkey[key];//找到结点位置int fre1=it->frequency;//找到对应的频率int val=it->value;//开始操作记录频率的表了mpfre[fre1].erase(it);//删除这个频率链表上的结点if(mpfre[fre1].size()==0){//删完如果他空了//就把这个链表给删了mpfre.erase(fre1);if(fre1==minfre){//如果这个频率他刚好是最小的minfre++;} }   //把这个结点加到高频率的链表头mpfre[fre1+1].push_front(Node(key,val,fre1+1));//更新结点缓存表的指向mpkey[key]=mpfre[fre1+1].begin();return mpkey[key]->value;}void put(int key, int value) {if(get(key)!=-1){//有这个结点//get已经帮忙更新过了,只需要把值改了就行mpkey[key]->value=value;}else{//没有这个结点,需要新添加//看结点缓存满不满if(mpkey.size()==cap){//根据LRU算法,找到最小频率的尾巴的结点,删除auto it=mpfre[minfre].back();mpkey.erase(it.key);mpfre[minfre].pop_back();//如果删完 最小频率这个链表他空了if(mpfre[minfre].size()==0){mpfre.erase(minfre);}}//添加 新添加的频率置为1int freque=1;mpfre[freque].push_front(Node(key,value,freque));mpkey[key]=mpfre[freque].begin(); //最小频率置为1minfre=1;}}
};

下面是我们自己实现的双向循环链表的写法,代替list

构建结点和链表

为了方便操作,给双向链表加上了虚拟的头结点和尾结点,并在初始化的时候首尾相接。

struct Node
{int key;int value;int frequency;Node*prev;Node*next;Node():key(-1),value(-1),frequency(0),prev(nullptr),next(nullptr){}Node(int k,int v):key(k),value(v),frequency(1),prev(nullptr),next(nullptr){}
};
struct List//双向链表
{int frequency;//虚拟 头 尾 结点Node*vhead;Node*vtail;List(int f):frequency(f),vhead(new Node()),vtail(new Node()){vhead->next=vtail;vtail->prev=vhead;}
};

有了基本结构就能实现LFU以及自己手撕链表的一些简单操作

这里需要用到的有,插入,删除结点,以及链表判空,和返回链表最后一个尾结点

struct Node
{int key;int value;int frequency;Node*prev;Node*next;Node():key(-1),value(-1),frequency(0),prev(nullptr),next(nullptr){}Node(int k,int v):key(k),value(v),frequency(1),prev(nullptr),next(nullptr){}
};
struct List//双向链表
{int frequency;//虚拟 头 尾 结点Node*vhead;Node*vtail;List(int f):frequency(f),vhead(new Node()),vtail(new Node()){vhead->next=vtail;vtail->prev=vhead;}
};
class LFUCache {
private:int cap;int minfre;unordered_map<int,Node*>keymp;unordered_map<int,List*>fremp;
public:LFUCache(int capacity=10):cap(capacity),minfre(1) {}bool empty(List *ls){return ls->vhead->next==ls->vtail?true:false;}void destroy(Node*node){node->prev->next=node->next;node->next->prev=node->prev;}void addNode(Node*node){int fre=node->frequency;if(!fremp.count(fre)){ //如果结点不在fremp[fre]=new List(fre); //创建一个新链表}List*ls=fremp[fre];//开始插入结点node->next=ls->vhead->next;ls->vhead->next->prev=node; node->prev=ls->vhead;ls->vhead->next=node;}void popTail(){Node*tmp=fremp[minfre]->vtail->prev;destroy(tmp);keymp.erase(tmp->key);}int get(int key) {if(keymp.count(key)){//存在Node*cur=keymp[key];int val=cur->value;int fre=cur->frequency;destroy(cur);//删完之后如果 链表空了,那就删链表if(empty(fremp[fre])){fremp.erase(fre);if(fre==minfre){minfre++;}}//加结点cur->frequency+=1;addNode(cur);return val;}return -1;}void put(int key, int value) {if(get(key)!=-1){keymp[key]->value=value;}else{//满了没if(keymp.size()==cap){popTail();}Node*cur=new Node(key,value);keymp[key]=cur;minfre=1;addNode(cur);}    }
};

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

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

相关文章

C# Lamda到底是什么?!

lamda作为匿名函数&#xff0c;现在已经能够出现子啊C#程序的任何可能位置&#xff0c;它可能作为参数为委托或其他函数复制&#xff0c;或者单独作为表达式&#xff0c;或者承担一些类似C中内联函数的一些作用承担一些简单计算。熟练的使用Lamda表达式能够让减少代码的冗余&am…

Django图书商城系统实战开发-总结经验之后端开发

Django图书商城系统实战开发-总结经验之后端开发 简介 在这篇博客中&#xff0c;我将总结经验分享后端开发Django图书商城系统的过程。在开发过程中&#xff0c;我遇到了各种挑战和问题&#xff0c;并且通过实践获得了宝贵的经验和教训。通过本文&#xff0c;我希望能帮助读者…

vue3+vite配置vantUI主题

❓在项目中统一配置UI主题色&#xff0c;各个组件配色统一修改 vantUI按需安装 参考vantUI文档 创建vantVar.less文件夹进行样式编写 vantVar.less :root:root{//导航--van-nav-bar-height: 44px;//按钮--van-button-primary-color: #ffffff;--van-button-primary-backgr…

linux——mysql的高可用MHA

目录 一、概述 一、概念 二、组成 三、特点 四、工作原理 二、案例 三、构建MHA 一、基础环境 二、ssh免密登录 三、主从复制 master slave1 四、MHA安装 一、环境 二、安装node 三、安装manager 一、概述 一、概念 MHA&#xff08;MasterHigh Availability&a…

力扣 198. 打家劫舍

题目来源&#xff1a;https://leetcode.cn/problems/house-robber/description/ C题解&#xff1a;因为是间接偷窃&#xff0c;所以偷nums[i]家前&#xff0c;一定偷过第i-2或者i-3家&#xff0c;因为i-1不能偷。 例如12345共5家&#xff0c;先偷第1家&#xff0c;那么2不能偷…

(三)Unity开发Vision Pro——入门

3.入门 1.入门 本节涵盖了几个重要主题&#xff0c;可帮助您加快visionOS 平台开发速度。在这里&#xff0c;您将找到构建第一个 Unity PolySpatial XR 应用程序的分步指南的链接&#xff0c;以及 PolySpatial XR 开发时的一些开发最佳实践。 2.开发与迭代 有关先决条件、开…

显卡nvidia-smi后 提示Faild 解决过程,包含卸载重装NVIDIA驱动步骤

显卡异常: 显卡nvidia-smi后 提示Faild 解决过程&#xff0c;卸载重装nvidia驱动步骤 文章目录 显卡异常: 显卡nvidia-smi后 提示Faild 解决过程&#xff0c;卸载重装nvidia驱动步骤 [toc]1 缘由2 解决过程3 过程所需命令4 解决4.1 把该显卡重新拔插一下卸载NVIDIA驱动的方法&a…

单元测试优化:为什么要对程序进行测试?测试有什么好处?

单元测试&#xff08;Unit Testing&#xff09;又称为模块测试, 是针对程序模块&#xff08;软件设计的最小单位&#xff09;来进行正确性检验的测试工作。 程序单元是应用的最小可测试部件。简单来说&#xff0c;就是测试数据的稳定性是否达到程序的预期。 我们日常开发时可能…

19、SQL注入之SQLMAP绕过WAF

目录 逻辑层1、逻辑问题2、性能问题 白名单方式一&#xff1a;IP白名单方式二&#xff1a;静态资源方式三&#xff1a;url白名单方式四: 爬虫白名单 sqlmap在测试漏洞的时候&#xff0c;选择了no&#xff0c;它就不会去测试其它的了&#xff0c;我们一般选择yes&#xff0c;为了…

Deep Learning With Pytorch - 最基本的感知机、贯序模型/分类、拟合

文章目录 如何利用pytorch创建一个简单的网络模型&#xff1f;Step1. 感知机&#xff0c;多层感知机&#xff08;MLP&#xff09;的基本结构Step2. 超平面 ω T ⋅ x b 0 \omega^{T}xb0 ωT⋅xb0 or ω T ⋅ x b \omega^{T}xb ωT⋅xb感知机函数 Step3. 利用感知机进行决策…

SpringBoot整合Minio

SpringBoot整合Minio 在企业开发中&#xff0c;我们经常会使用到文件存储的业务&#xff0c;Minio就是一个不错的文件存储工具&#xff0c;下面我们来看看如何在SpringBoot中整合Minio POM pom文件指定SpringBoot项目所依赖的软件工具包 <?xml version"1.0" …

Ubuntu上安装RabbitMQ

在Ubuntu上安装RabbitMQ并设置管理员用户为"admin"&#xff0c;密码为"123456"&#xff0c;并开启开机自启 更新系统软件包列表。在终端中执行以下命令&#xff1a; sudo apt update安装RabbitMQ服务器软件包。运行以下命令&#xff1a; sudo apt insta…

DaVinci Resolve Studio 18 for Mac 达芬奇调色

DaVinci Resolve Studio 18是一款专业的视频编辑和调色软件&#xff0c;适用于电影、电视节目、广告等各种视觉媒体的制作。它具有完整的后期制作功能&#xff0c;包括剪辑、调色、特效、音频处理等。 以下是DaVinci Resolve Studio 18的主要特点&#xff1a; - 提供了全面的视…

Linux 设置 ssh 内网穿透

背景&#xff1a;有三台机器A、B、C&#xff0c;机器 A 位于某局域网内&#xff0c;能够连接到互联网。机器 B 有公网 IP&#xff0c;能被 A 访问到。机器 C 位于另外一个局域网内&#xff0c;能够连接到互联网&#xff0c;能够访问 B。 目标&#xff1a;以 B 为中介&#xff…

Jmeter-压测时接口按照顺序执行-临界部分控制器

文章目录 临界部分控制器存在问题 临界部分控制器 在进行压力测试时&#xff0c;需要按照顺序进行压测&#xff0c;比如按照接口1、接口2、接口3、接口4 进行执行 查询结果是很混乱的&#xff0c;如果请求次数少&#xff0c;可能会按照顺序执行&#xff0c;但是随着次数增加&a…

Python-OpenCV中的图像处理-模板匹配

Python-OpenCV中的图像处理-模板匹配 模板匹配单对象的模板匹配多对象的模板匹配 模板匹配 使用模板匹配可以在一幅图像中查找目标函数&#xff1a; cv2.matchTemplate()&#xff0c; cv2.minMaxLoc()模板匹配是用来在一副大图中搜寻查找模版图像位置的方法。 OpenCV 为我们提…

无线充电底座

<项目>无线充电器 前言 个人DIY的无线充电底座&#xff08;带磁吸&#xff09;&#xff0c;基于IP6829方案。 Drawn By:67373 硬件部分 3D模型 资料开源链接 https://github.com/linggan17/WirelessCharge

面试热题(每日温度)

请根据每日 气温 列表 temperatures &#xff0c;重新生成一个列表&#xff0c;要求其对应位置的输出为&#xff1a;要想观测到更高的气温&#xff0c;至少需要等待的天数。如果气温在这之后都不会升高&#xff0c;请在该位置用 0 来代替。 输入: temperatures [73,74,75,71,69…

SpringBoot + Mybatis多数据源

一、配置文件 spring: # datasource: # username: root # password: 123456 # url: jdbc:mysql://127.0.0.1:3306/jun01?characterEncodingutf-8&serverTimezoneUTC # driver-class-name: com.mysql.cj.jdbc.Driverdatasource:# 数据源1onedata:jdbc-url: j…

SCF金融公链新加坡启动会 链结创新驱动未来

新加坡迎来一场引人瞩目的金融科技盛会&#xff0c;SCF金融公链启动会于2023年8月13日盛大举行。这一受瞩目的活动将为金融科技领域注入新的活力&#xff0c;并为广大投资者、合作伙伴以及关注区块链发展的人士提供一个难得的交流平台。 在SCF金融公链启动会上&#xff0c; Wil…