LeetCode Hard|【460. LFU 缓存】
力扣题目链接
LFU全称是最不经常使用算法(Least Frequently Used),LFU算法的基本思想和所有的缓存算法一样,一定时期内被访问次数最少的页,在将来被访问到的几率也是最小的。
相较于 LRU 算法,LFU 更加注重于使用的频率。 LRU 其实可以看作是频率为 1 的LFU。
从上图中我们可以看到,LFU是根据频率来进行排序,当我们插入的时候,会把新插入的放到链表的尾部,因为新插入的一定没有出现过的,所以频率都会是1,所以会放在最后。

整个算法流程如下:
- 如果A没有出现过,那么就会放在双向链表的最后,依次类推,就会是Z、Y。。C、B、A的顺序放到频率为1的链表中。
- 当我们新插入 A,B,C 那么A,B,C就会到频率为2的链表中
- 如果再次插入A,B那么A,B会在频率为3中。C依旧在2中
- 如果此时已经满了 ,新插入一个的话,我们会把最后一个D移除,并插入 6
整体算法如下:
文章目录
- 自定义类型
- 定义成员变量
- 定义私有成员函数
- 构造函数
- get方法
- put方法
- 总体代码如下
自定义类型
根据题目中的描述,我们除了维护一个键值对之外,还要为这个键值对维护一个计数器:
struct Node {int key, value, freq;Node() : key(0), value(0), freq(1) {}Node(int key, int value) : key(key), value(value), freq(1) {}
};
定义成员变量
很明显,除了 LFUCache 的容量之外,我们还要维护一个最小频率,到时候清除缓存是清除最小频率的最久未使用:
class LFUCache {
private:int capacity_, minFreq_;std::unordered_map<int, Node*> keyNode_; //从键到节点的映射std::unordered_map<int, std::list<Node*>> freqList_; //从频率到节点链表的映射std::unordered_map<Node*, std::list<Node*>::iterator> nodeIter_; //从节点到其在列表中位置的映射
};
定义私有成员函数
这里我们需要一个私有成员函数,也就是更新频率的函数,其实逻辑还是比较好理解的。
void updateFrequency(Node* node) {int freq = node->freq;auto& nodeList = freqList_[freq]; //获取该频率对应的节点链表nodeList.erase(nodeIter_[node]); // 从该链表中删除该节点,因为该节点的频率注定被改变了// 如果当前频率的节点链表为空,删除该频率并更新最小频率if (nodeList.empty()) {freqList_.erase(freq);if (minFreq_ == freq) minFreq_++; //当前删除的频率链表为最小频率时,更新最小频率}//增加节点的频率,并将其插入到新的频率-节点链表的最前面node->freq++;freqList_[node->freq].push_front(node);nodeIter_[node] = freqList_[node->freq].begin(); //记录每个节点在各自链表中的位置}
构造函数
class LFUCache {
private:...
public:LFUCache(int capacity) : capacity_(capacity), minFreq_(0) {}
};
get方法
// 获取指定键的值int get(int key) {if (keyNode_.find(key) == keyNode_.end()) {return -1;} else {Node* node = keyNode_[key];updateFrequency(node);return node->value;}}
put方法
//插入或更新键值对void put(int key, int value) {if (capacity_ == 0) return; //容量为0,直接返回// 更新值、更新频率if (keyNode_.find(key) != keyNode_.end()) {Node* node = keyNode_[key];node->value = value;updateFrequency(node);} else {// 容量已满if (keyNode_.size() == capacity_) {auto& nodeList = freqList_[minFreq_]; //找到最小频率的那个 nodelistNode* node = nodeList.back(); //最小频率的最久未使用节点keyNode_.erase(node->key); //从键到节点的映射中删除该节点nodeIter_.erase(node); // 从节点到其所属位置映射中删除该节点nodeList.pop_back(); //从获取到的频率链表中删除该节点if (nodeList.empty()) freqList_.erase(minFreq_); //如果频率列表为空,删除该频率链表delete node;}//如果缓存未满的情况下,插入新节点并更新相关映射Node* newNode = new Node(key, value);keyNode_[key] = newNode;freqList_[1].push_front(newNode);nodeIter_[newNode] = freqList_[1].begin();minFreq_ = 1; //重置最小频率}}
总体代码如下
struct Node {int key, value, freq;Node() : key(0), value(0), freq(1) {}Node(int key, int value) : key(key), value(value), freq(1) {}
};class LFUCache {
private:int capacity_, minFreq_;std::unordered_map<int, Node*> keyNode_; //从键到节点的映射std::unordered_map<int, std::list<Node*>> freqList_; //从频率到节点链表的映射std::unordered_map<Node*, std::list<Node*>::iterator> nodeIter_; //从节点到其在列表中位置的映射// 无论使用 get 还是 put 方法都会导致节点频率的增加void updateFrequency(Node* node) {int freq = node->freq;auto& nodeList = freqList_[freq]; //获取该频率对应的节点链表nodeList.erase(nodeIter_[node]); // 从该链表中删除该节点,因为该节点的频率注定被改变了// 如果当前频率的节点链表为空,删除该频率并更新最小频率if (nodeList.empty()) {freqList_.erase(freq);if (minFreq_ == freq) minFreq_++; //当前删除的频率链表为最小频率时,更新最小频率}//增加节点的频率,并将其插入到新的频率-节点链表的最前面node->freq++;freqList_[node->freq].push_front(node);nodeIter_[node] = freqList_[node->freq].begin(); //记录每个节点在各自链表中的位置}
public:LFUCache(int capacity) : capacity_(capacity), minFreq_(0) {}int get(int key) {if (keyNode_.find(key) == keyNode_.end()) {return -1;} else {Node* node = keyNode_[key];updateFrequency(node);return node->value;}}void put(int key, int value) {if (capacity_ == 0) return; //容量为0,直接返回// 更新值、更新频率if (keyNode_.find(key) != keyNode_.end()) {Node* node = keyNode_[key];node->value = value;updateFrequency(node);} else {// 容量已满if (keyNode_.size() == capacity_) {auto& nodeList = freqList_[minFreq_]; //找到最小频率的那个 nodelistNode* node = nodeList.back(); //最小频率的最久未使用节点keyNode_.erase(node->key); //从键到节点的映射中删除该节点nodeIter_.erase(node); // 从节点到其所属位置映射中删除该节点nodeList.pop_back(); //从获取到的频率链表中删除该节点if (nodeList.empty()) freqList_.erase(minFreq_); //如果频率列表为空,删除该频率链表delete node;}//如果缓存未满的情况下,插入新节点并更新相关映射Node* newNode = new Node(key, value);keyNode_[key] = newNode;freqList_[1].push_front(newNode);nodeIter_[newNode] = freqList_[1].begin();minFreq_ = 1; //重置最小频率}}
};
相关文章:

LeetCode Hard|【460. LFU 缓存】
力扣题目链接 LFU全称是最不经常使用算法(Least Frequently Used),LFU算法的基本思想和所有的缓存算法一样,一定时期内被访问次数最少的页,在将来被访问到的几率也是最小的。 相较于 LRU 算法,LFU 更加注重…...

积极参与全球能源科技前沿对话,海博思创推动绿色低碳发展
在能源转型与绿色低碳发展的全球浪潮中,国内领先的储能解决方案供应商海博思创以卓越的技术实力和前瞻性的战略眼光,站在了行业变革的前沿。公司不仅在国内外多个重要展会上大放异彩,更通过一系列技术创新与深度合作,为全球能源行…...
[工具]-ffmpeg-笔记
朋友有一个需求,将视频文件转化为音频文件、音频文件获取音频转化为文本文件。 思路:通过ffmpeg转化视频为音频,通过百度ai提供的voice_t_text接口提取语音文本,但是需要将音频分割成1分钟内的pcm编码 ,采样率16000的…...

Android Fragment:详解,结合真实开发场景Navigation
目录 1)Fragment是什么 2)Fragment的应用场景 3)为什么使用Fragment? 4)Fragment如何使用 5)Fragment的生命周期 6)Android开发,建议是多个activity,还是activity结合fragment&…...

JavaWeb中的Servlet
本笔记基于【尚硅谷全新JavaWeb教程,企业主流javaweb技术栈】https://www.bilibili.com/video/BV1UN411x7xe?vd_sourcea91dafe0f846ad7bd19625e392cf76d8总结 Servlet Servlet简介 动态资源和静态资源 静态资源 无需在程序运行时通过代码运行生成的资源,在程序运…...

SpringBoot AOP 简单的权限校验
本篇文章的主要内容是通过AOP切面编程实现简单的权限校验。 书接上回登录与注册功能 我们的用户表里面不是有role(权限)这个字段吗 在JWT令牌的生成中,我们加入了role字段。 那么接下来,我们就可以通过这个字段来实现权限校验。 我这里就很简单&#x…...

Java生成Word->PDF->图片:基于poi-tl 进行word模板渲染
文章目录 引言I Java生成Word、PDF、图片文档获取标签渲染数据生成文档案例II 工具类封装2.1 word 渲染和word 转 pfd2.2 pdf转成一张图片III poi-tl(word模板渲染) 标签简介文本标签{{var}}图片标签表格标签引用标签IV poi-tl提供了类 Configure 来配置常用的设置标签类型前后…...
JVM内存模型笔记
1. 运行时数据区概述 JVM内存布局规定了Java运行过程中的内存申请、分配和管理策略。运行时数据区分为线程私有和线程共享两种。 2. 线程私有内存 程序计数器:存储当前线程执行的字节码指令地址。虚拟机栈:保存方法调用的局部变量和部分结果。本地方法…...
每日一练 - eSight 网管远程告警通知方式
01 真题题目 eSight 网管支持的远程告警通知方式包括:(多选) A.邮件 B.语音 C.短信 D.微信 02 真题答案 AC 03 答案解析 eSight 网管系统支持多种远程告警通知方式,包括邮件和短信。 这些通知方式可以帮助网络管理员及时了解网络设备的状态和告警信息࿰…...

[matlab] 鲸鱼优化算法优化KNN分类器的特征选择
目录 引言 智能优化算法概述 智能优化算法在KNN特征选择中的应用 应用步骤 UCI数据集 鲸鱼优化算法 一、算法背景与原理 二、算法组成与步骤 三、算法特点与优势 四、应用与挑战 代码实现 鲸鱼优化算法 主程序 打印结果 引言 智能优化算法在优化KNN(…...
vscode ssh-remote 疑似内存泄漏问题
vscode ssh-remote疑似内存泄漏问题 系统信息与版本号 版本:1.88.1(通用) 日期:2024-04-10T17:42:52.765Z Electron: 28.2.8 ElectronBuildId: 27744544 Chromium:120.0.6099.291 Node.js:18.18.2 V8&…...

初识自然语言处理NLP
文章目录 1、简介2、自然语言处理的发展简史3、语言学理论句法学(Syntax)语义学(Semantics)语用学(Pragmatics)形态学(Morphology) 4、统计与机器学习方法n-gram 模型隐马尔可夫模型…...
分布式系统架构-微服务架构
一.什么是分布式系统架构 分布式系统架构是指将一个单一的应用程序或服务拆分成多个独立的部分,这些部分可以在不同的计算机、服务器或者地理位置上运行,并通过网络进行通信和协作。分布式系统的设计旨在提高系统的可靠性、可用性和扩展性,同…...
docker搭建内网穿透服务
docker搭建内网穿透服务 frpfrpsfrpc zerotier构建 moon构建 planet查询客户端配置moon方法 nps frp 参考文章:https://blog.csdn.net/weixin_43909881/article/details/126526059 frps docker pull snowdreamtech/frps docker run --restartalways --network ho…...

html+css+js网页设计 体育 金轮健身7个页面
htmlcssjs网页设计 体育 金轮健身7个页面 网页作品代码简单,可使用任意HTML编辑软件(如:Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作)。 获取源码 1&…...

BGP基础简介(一)
AS 是一组运行相同IGP协议的设备组成的网络 AS号: 16bit:64512~65535为私有AS32bit:4200000000~4294967294为私有AS其余都是共有AS,需要向IANA申请 EGP 外部网关协议,bgp的前身,缺点:只发布路由信息,不…...

力扣面试150 反转链表 II 三指针
Problem: 92. 反转链表 II 👨🏫 参考题解 特殊情况 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val…...

GPT-4.o mini
https://share.xuzhugpt.cloud/ GPT-4.o mini 目前免费使用 把上面[chatgpt4o-mini-xuzhu]复制到UserToken的文本框中 点击[个人账户] 测试一下哈,看看: GPT-4.o代码有时候还是有严重错误:好奇怎么来的 上面是我写得,下面是GPT写…...

【C++】优先级队列(容器适配器)
欢迎来到我的Blog,点击关注哦💕 前言 string vector list 这种线性结构是最基础的存储结构,C(STL)container很好的帮助我们数据存储的问题。 容器适配器 介绍 容器适配器是C标准模板库(STL)中…...
docker代理
Dockerd 代理 sudo mkdir -p /etc/systemd/system/docker.service.d sudo touch /etc/systemd/system/docker.service.d/proxy.confproxy.conf [Service] Environment"HTTP_PROXYproxy.example.com:8080/" Environment"HTTPS_PROXYproxy.example.com:8080/&qu…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...

听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...

网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...

Python异步编程:深入理解协程的原理与实践指南
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 持续学习,不断…...

HTML版英语学习系统
HTML版英语学习系统 这是一个完全免费、无需安装、功能完整的英语学习工具,使用HTML CSS JavaScript实现。 功能 文本朗读练习 - 输入英文文章,系统朗读帮助练习听力和发音,适合跟读练习,模仿学习;实时词典查询 - 双…...