LeetCode 460. LFU 缓存

原题链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

题目描述

请你为 最不经常使用(LFU)缓存算法设计并实现数据结构。

实现 LFUCache 类:

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

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

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

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

样例1:

输入

["LFUCache", "put", "put", "get", "put", "get", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [3], [4, 4], [1], [3], [4]]

输出

[null, null, null, 1, null, -1, 3, null, -1, 3, 4]

 // cnt(x) = 键 x 的使用计数
// cache=[] 将显示最后一次使用的顺序(最左边的元素是最近的)
LFUCache lfu = new LFUCache(2);
lfu.put(1, 1);   // cache=[1,_], cnt(1)=1
lfu.put(2, 2);   // cache=[2,1], cnt(2)=1, cnt(1)=1
lfu.get(1);      // 返回 1
                 // cache=[1,2], cnt(2)=1, cnt(1)=2
lfu.put(3, 3);   // 去除键 2 ,因为 cnt(2)=1 ,使用计数最小
                 // cache=[3,1], cnt(3)=1, cnt(1)=2
lfu.get(2);      // 返回 -1(未找到)
lfu.get(3);      // 返回 3
                 // cache=[3,1], cnt(3)=2, cnt(1)=2
lfu.put(4, 4);   // 去除键 1 ,1 和 3 的 cnt 相同,但 1 最久未使用
                 // cache=[4,3], cnt(4)=1, cnt(3)=2
lfu.get(1);      // 返回 -1(未找到)
lfu.get(3);      // 返回 3
                 // cache=[3,4], cnt(4)=1, cnt(3)=3
lfu.get(4);      // 返回 4
                 // cache=[3,4], cnt(4)=2, cnt(3)=3

Tag

双向链表 set 哈希映射

题目分析

这题是LeetCode 146. LRU 缓存-CSDN博客的升级版,加入了使用次数判断,如果只用一个双向链表的话无法满足需求,需要根据使用频率cnt来维护多个双向链表。

思路

数据结构:

1.链表节点

记录value值,有指向前节点和后节点的指针

2.双向链表

有链表头尾节点,借助头结点可以将新加入的节点O(1)复杂度加入队首,借助尾节点可以O(1)复杂度去除最久未使用的节点(即尾节点prev指向的节点)。

用哈希表unordered_map<int,node*> mp维护关键字到链表节点地址的映射,确保可以在O(1)复杂度时间根据关键字找到节点

3.LFUCache类

哈希表list_ptr维护多条双向链表,根据关键字使用频率找到所存储的链表位置,并进行相应操作

哈希表cnt记录关键字出现的次数

红黑树min_cnt记录当前存在的频率最低的关键字有哪些(即找到cnt最低的那条双向链表)

实现方法

1.get查询

找到了将关键字所在节点node从当前频率的链表中删掉,放到当前频率+1的链表首部,同时更新cnt修改关键字出现的次数,若当前频率+1还有链表需要申请并用list_ptr记录

2.put操作

修改频率和get一样,不同的是需要判断是否找过缓存,如果超过根据min_cnt先找到频率最低的那条链表,再删掉链表的尾节点,同时注意更新min_cnt、cnt和list_ptr

3.注意

删除节点后需要判断链表是否为空,若为空则需要删除这条链表,并且删掉min_cnt中的记录和list_ptr中记录当前链表的地址

C++代码

class node{
public:node *prev,*next;int key;int value;node(node *prev,node *next,int key,int value){this->key = key;this->value = value;this->prev = prev;this->next = next;}};class DoublyLinkedList{
public:node *head,*tail;unordered_map<int,node*> mp;//key:关键字 value:存储链表中某个节点地址DoublyLinkedList(){head = new node(nullptr,nullptr,0,0);tail = new node(head,nullptr,0,0);head->next = tail;}//将node移动到队首void adjust(node* temp) {temp->next = head->next;temp->prev = head;head->next = temp;temp->next->prev = temp;}//删除节点void remove(node *move){move->prev->next = move->next;move->next->prev = move->prev;}//删除尾节点int remove_tail(){node* del = tail->prev;int key = del->key;tail->prev = del->prev;del->prev->next = tail;mp.erase(key);return key;}
};class LFUCache {
public:int capacity;int now;unordered_map<int,DoublyLinkedList*> list_ptr;//key:使用次数 value:双向链表地址unordered_map<int,int>cnt;//key:关键字 value:出现次数set<int>min_cnt;//查找出现次数最少的LFUCache(int capacity) {this->capacity = capacity;now = 0;}int get(int key) {if(cnt.count(key)){int count = cnt[key];//关键字查询前出现次数cnt[key]++;//查询后访问次数+1DoublyLinkedList *list = list_ptr[count];//cout<<"size:"<<min_cnt.size()<<endl;int value = list->mp[key]->value;//查询到的值//从count中删除尾节点node* move = list->mp[key];list->remove(move);if(list->head->next == list->tail){//删空了list_ptr.erase(count);min_cnt.erase(count);}//加入count+1中的队首if(list_ptr.count(count+1)){DoublyLinkedList *newlist = list_ptr[count+1];newlist->adjust(move);newlist->mp[key] = move;}else{DoublyLinkedList* newlist = new DoublyLinkedList();list_ptr[count+1] = newlist;newlist->adjust(move);newlist->mp[key] = move;min_cnt.insert(count+1);}return value;}return -1;}void put(int key, int value) {if(cnt.count(key)){int count = cnt[key];//关键字查询前出现次数cnt[key]++;//put后访问次数+1DoublyLinkedList *list = list_ptr[count];list->mp[key]->value = value;//改值//从count中删除尾节点node* move = list->mp[key];list->remove(move);if(list->head->next == list->tail){//删空了list_ptr.erase(count);min_cnt.erase(count);}//加入count+1中的队首if(list_ptr.count(count+1)){DoublyLinkedList *list = list_ptr[count+1];list->adjust(move);list->mp[key] = move;}else{DoublyLinkedList* newlist = new DoublyLinkedList();list_ptr[count+1] = newlist;newlist->adjust(move);newlist->mp[key] = move;min_cnt.insert(count+1);}}else{//加入count = 1的队首if(now+1>capacity){//若超缓存则删除now--;int x = *min_cnt.begin();DoublyLinkedList *list = list_ptr[x];//出现次数最少的那条链表int toRemoveKey = list->remove_tail();cnt.erase(toRemoveKey);//key不在缓存中了,出现次数置0if(list->head->next == list->tail){min_cnt.erase(x);list_ptr.erase(x);}}node* newnode = new node(nullptr,nullptr,key,value);if(list_ptr.count(1)){DoublyLinkedList *list = list_ptr[1];list->adjust(newnode);list->mp[key] = newnode;}else{DoublyLinkedList* newlist = new DoublyLinkedList();newlist->adjust(newnode);newlist->mp[key] = newnode;list_ptr[1] = newlist;min_cnt.insert(1);}cnt[key] = 1;now++;}}
};

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

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

相关文章

面试记录_

1&#xff1a;面试杉岩数据&#xff08;python开发&#xff09; 1.1.1 选择题 for(int i0;i<n;i){for(int j0;j<n;jji) } }O(n) * (O(0) O(n/1) O(n/2) O(n/3) ... O(n/n)) 在最坏情况下&#xff0c;内部循环的迭代次数为 n/1 n/2 n/3 ... n/n&#xff0c;这是…

笔试强训Day8

链接&#xff1a;求最小公倍数__牛客网 T1:求最小公倍数 正整数A和正整数B 的最小公倍数是指 能被A和B整除的最小的正整数值&#xff0c;设计一个算法&#xff0c;求输入A和B的最小公倍数。 数据范围&#xff1a;1≤a,b≤100000 #include<iostream> using namespace…

【算法|贪心算法系列No.2】leetcode2208. 将数组和减半的最少操作次数

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 &#x1f354;本专栏旨在提高自己算法能力的同时&#xff0c;记录一下自己的学习过程&#xff0c;希望…

Unity把UGUI再World模式下显示到相机最前方

Unity把UGUI再World模式下显示到相机最前方 通过脚本修改Shader 再VR里有时候要把3D的UI显示到相机最前方&#xff0c;加个UI相机会坏事&#xff0c;可以通过修改unity_GUIZTestMode来解决。 测试用例 测试用例如下&#xff1a; 场景包含一个红色的盒子&#xff0c;一个UI…

MonkeyRunner自动化测试

一&#xff1a;简介 MonkeyRunner提供了一个API&#xff0c;使用此API写出的程序可以在Android代码之外控制Android设备和模拟器。通过monkeyrunner&#xff0c;您可以写出一个Python程序去安装一个Android应用程序或测试包&#xff0c;运行它&#xff0c;向它发送模拟击键&…

Linux C/C++下收集指定域名的子域名信息(类似dnsmap实现)

我们知道dnsmap是一个工具&#xff0c;主要用于收集指定域名的子域名信息。它对于渗透测试人员在基础结构安全评估的信息收集和枚举阶段非常有用&#xff0c;可以帮助他们发现目标公司的IP网络地址段、域名等信息。 dnsmap的操作原理 dnsmap&#xff08;DNS Mapping&#xff…

Xmake v2.8.3 发布,改进 Wasm 并支持 Xmake 源码调试

Xmake 是一个基于 Lua 的轻量级跨平台构建工具。 它非常的轻量&#xff0c;没有任何依赖&#xff0c;因为它内置了 Lua 运行时。 它使用 xmake.lua 维护项目构建&#xff0c;相比 makefile/CMakeLists.txt&#xff0c;配置语法更加简洁直观&#xff0c;对新手非常友好&#x…

学信息系统项目管理师第4版系列14_沟通管理

1. 与IT项目成功有关的最重要的四个因素 1.1. 主管层的支持 1.2. 用户参与 1.3. 有经验的项目经理 1.4. 清晰的业务目标 1.5. 依赖于项目经理和团队具有良好的沟通能力 2. 沟通的主旨 2.1. 互动双方建立彼此相互了解的关系 2.2. 相互回应 2.3. 期待能经由沟通的行为与…

软件过程的介绍

软件过程概述 软件的诞生和生命周期是一个过程&#xff0c;我们总体上称这个过程为软件过程。软件过程是为了开发出软件产品&#xff0c;或者是为了完成软件工程项目而需要完成的有关软件工程的活动&#xff0c;每一项活动又可以分为一系列的工程任务。任何一个软件开发组织&a…

ESP32官方MPU6050组件介绍

前言 &#xff08;1&#xff09;因为我需要使用MPU6050的组件&#xff0c;但是又需要在这条I2C总线上挂载多个设备&#xff0c;所以我本人打算自己对官方的MPU6050的组件进行微调。建立一个I2C总线&#xff0c;设备依赖于这个总线挂载。 &#xff08;2&#xff09;既然要做移植…

【AI视野·今日Robot 机器人论文速览 第四十四期】Fri, 29 Sep 2023

AI视野今日CS.Robotics 机器人学论文速览 Fri, 29 Sep 2023 Totally 38 papers &#x1f449;上期速览✈更多精彩请移步主页 Interesting: &#x1f4da;NCF,基于Neural Contact Fields神经接触场的方法实现有效的外部接触估计和插入操作。 (from FAIR ) 操作插入处理结果&am…

凉鞋的 Godot 笔记 101. Hello Godot!

101. Hello Godot 学习任何一门技术&#xff0c;第一件事就是先完成 Hello World&#xff01;的输出 所以我们也来先完成 Godot 的 Hello World。 我们所使用的 Godot 版本是 4.x 版本。 安装的过程就不给大家展示了&#xff0c;笔者更推荐初学者用 Steam 版本的 Godot&…

第一次作业题解

第一次作业题解 P5717 【深基3.习8】三角形分类 思路 考的是if()的使用,还要给三条边判断大小 判断优先级&#xff1a; 三角形&#xff1f;直角、钝角、锐角等腰等边 判断按题给顺序来 代码 #include <stdio.h> int main() {int a 0, b 0, c 0, x 0, y 0, z 0…

紫光同创FPGA图像视频采集系统,基于OV7725实现,提供工程源码和技术支持

目录 1、前言免责声明 2、设计思路框架视频源选择OV7725摄像头配置及采集动态彩条HDMA图像缓存输入输出视频HDMA缓冲FIFOHDMA控制模块HDMI输出 3、PDS工程详解4、上板调试验证并演示准备工作静态演示动态演示 5、福利&#xff1a;工程源码获取 紫光同创FPGA图像视频采集系统&am…

[每周一更]-(第64期):Dockerfile构造php定制化镜像

利用php官网镜像php:7.3-fpm&#xff0c;会存在部分插件缺失的情况&#xff0c;自行搭建可适用业务的镜像&#xff0c;才是真理 Dockerhub 上 PHP 官方基础镜像主要分为三个分支&#xff1a; cli: 没有开启 CGI 也就是说不能运行fpm。只可以运行命令行。fpm: 开启了CGI&#x…

Docker从认识到实践再到底层原理(九)|Docker Compose 容器编排

前言 那么这里博主先安利一些干货满满的专栏了&#xff01; 首先是博主的高质量博客的汇总&#xff0c;这个专栏里面的博客&#xff0c;都是博主最最用心写的一部分&#xff0c;干货满满&#xff0c;希望对大家有帮助。 高质量博客汇总 然后就是博主最近最花时间的一个专栏…

libtorch之tensor的使用

1. tensor的创建 tensor的创建有三种常用的形式&#xff0c;如下所示 ones创建一个指定维度&#xff0c;数据全为1的tensor. 例子中的维度是2维&#xff0c;5行3列。 torch::Tensor t torch::ones({5,3}); zeros创建一个指定维度&#xff0c;数据全为0的tensor&#xff0c;例子…

Java 基于 SpringBoot 的简历招聘系统

文章目录 1、效果演示2、 前言介绍3、主要技术4 **系统设计**4.1 系统体系结构4.2开发流程设计4.3 数据库设计原则 5 **系统详细设计**5.1管理员功能模块5.2用户功能模块5.3前台首页功能模块 6 源码咨询 1、效果演示 大家好&#xff0c;今天为大家带来的是基于 SpringBoot简历…

【AI视野·今日Robot 机器人论文速览 第四十一期】Tue, 26 Sep 2023

AI视野今日CS.Robotics 机器人学论文速览 Tue, 26 Sep 2023 Totally 73 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Robotics Papers Extreme Parkour with Legged Robots Authors Xuxin Cheng, Kexin Shi, Ananye Agarwal, Deepak Pathak人类可以通过以高度动态…

初识Java 11-2 函数式编程

目录 高阶函数 闭包 函数组合 柯里化和部分求值 本笔记参考自&#xff1a; 《On Java 中文版》 高阶函数 ||| 高阶函数的定义&#xff1a;一个能接受函数作为参数或能把函数当返回值的函数。 把函数当返回值的情况&#xff1a; import java.util.function.Function;inter…