LeetCode 460. LFU 缓存 -- 哈希查询+双向链表

news2025/1/15 22:35:37
  1. LFU 缓存
    困难
    634
    相关企业
    请你为 最不经常使用(LFU)缓存算法设计并实现数据结构。

实现 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) 的平均时间复杂度运行。

示例:

输入:
[“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

提示:

0 <= capacity <= 104
0 <= key <= 105
0 <= value <= 109
最多调用 2 * 105 次 get 和 put 方法

题解

这个题目是真的痛苦,和那个LRU算法思路基本一致,但是需要额外定义一个map用来储存所有被访问次数为count的头节点,这样更新的时候会快很多。
Debug了好久。。。。

AC代码

class LFUCache {
struct Node{
    int key, val, count;
    Node *next, *prev;
};
private:

//定义每个键值的指针位置
unordered_map<int, Node*>cache;
map<int, Node*>count_start;
Node * head = new Node();
Node * tail = new Node();

int capacity;

public:
    LFUCache(int capacity):capacity(capacity)
    {
        head->count = 1e9;
        tail->count = -1e9;
        head->next = tail;
        tail->prev = head;
        head->prev = NULL;
        tail->next = NULL;
        count_start[1e9] = head;
        count_start[-1e9] = tail;
    }
    void delete_count_start(Node *p)
    {
        //该节点是操作次数为count的首节点
        if(count_start[p->count]==p)
        {
            if(p->next->count==p->count)
            {
                count_start[p->count] = p->next;//更新下
            }   
            else
            {
                //直接删除键值为count
                count_start.erase(p->count);
            }
        }      
    }
    //假设当前键值使用次数为x,把节点p直接插到所有使用次数为x的第一个位置
    void update(Node * p)
    {
        //如果是刚插入的节点
        if(p->next==NULL)
        {
            Node * Prev = tail->prev;
            Prev->next = p;
            p->prev = Prev;
            p->next = tail;
            tail->prev = p;        
        }
        //切断原来的联系
        Node * Prev = p->prev;
        Node * Next = p->next;
        Prev->next = Next;
        Next->prev = Prev; 

        int count = p->count;
        auto iter = count_start.upper_bound(count);
        //iter--是为了找到count_start中第一个小于等于count的键值
        iter--;
        if(iter->first==-1e9)
        {
            //是使用次数最小的,直接接链表后面
            Node * Prev = tail->prev;
            Prev->next = p;
            p->prev = Prev;
            p->next = tail;
            tail->prev = p;
            count_start[count] = p;
        }
        else
        {
            Node * Prev = iter->second->prev;
            Node * Next = iter->second;
            //插入
            Prev->next = p;
            p->prev = Prev;
            p->next = Next;
            Next->prev = p;
            count_start[count] = p;
        }
    }
    int get(int key) 
    {
        if(cache.find(key)==cache.end())return -1;
        Node * p =cache[key];
        int val = p->val;
        //先判断更新下原来的被访问次数count
        delete_count_start(p);
        p->count += 1;
        update(p);
        return val;
    }
    void put(int key, int value) 
    {
        if(cache.find(key)==cache.end())
        {
            if(cache.size()>=capacity)
            {
                //删除最久并且使用次数最少的键值
                Node * p2 = tail->prev;
                Node * Prev = p2->prev;
                Prev->next = tail;
                tail->prev = Prev;
                //删除
                cache.erase(p2->key);
                //先判断更新下原来的被访问次数count
                delete_count_start(p2);
                delete p2;
            }
            Node * p = new Node();
            p->key = key;
            cache[key] = p;
            p->count = 1;
            p->val = value;
            update(p);
        }
        else
        {
            Node * p = cache[key];
            //先判断更新下原来的被访问次数count
            delete_count_start(p);
            p->count += 1;
            p->val = value;
            update(p);
        }
    }
};

/**
 * Your LFUCache object will be instantiated and called as such:
 * LFUCache* obj = new LFUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
 */

在这里插入图片描述

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

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

相关文章

IP网络广播对讲系统的先进性及系统功能

IP网络广播对讲系统是一套基于TCP/IP网络的纯数字IP网络对讲机系统。在物理结构上&#xff0c;IP网络广播系统与标准IP网络集成。它不仅真正实现了基于TCP/IP网络的数字音频广播、直播和点播&#xff0c;而且借助TCP/IP网络的优势&#xff0c;突破了传统模拟广播系统的局限性、…

用Python写个猜数字游戏,写游戏难道比玩游戏还好玩(12)

小朋友们好&#xff0c;大朋友们好&#xff01;我是猫妹&#xff0c;一名爱上Python编程的小学生。欢迎和猫妹一起&#xff0c;趣味学Python。今日主题开学差不多有1个月了吧&#xff1f;猫妹刚刚从寒假的玩玩玩模式切换到上学的学学学模式。你呢&#xff1f;这是猫妹的课程表&…

嵌入式中backtrace的使用

大家好&#xff0c;我是bug菌&#xff5e; backtrace主要用于调试程序时&#xff0c;能够打印出程序在运行过程中的函数调用栈&#xff0c;以帮助开发者快速定位程序出现异常或崩溃的原因。 通过backtrace的输出&#xff0c;开发者可以了解程序在哪个函数出现问题&#xff0c…

APM飞控使用动捕等外部定位

本文初次写于2023.03.03&#xff0c;pixhawk飞控应该是刷写了ArduPilot 4.1以上的版本。 机载计算机通过WIFI和vrpn_ros_client获取动捕系统&#xff08;vicon或者nokov&#xff09;的无人机定位数据&#xff08;x&#xff0c;y&#xff0c;z四元数&#xff09;&#xff0c;然…

HMM-维特比算法

HMM-维特比算法&#xff08;viterbi&#xff09;HMM回顾隐马科夫链解法&#xff1a;维特比算法&#xff08;Viterbi&#xff09;HMM回顾 最终的公式可以解释主要分为两个部分&#xff1a; P(xi|yi)&#xff0c;发射概率&#xff0c;字面意思是从一个词性中发射/生成出某一个单…

【Java】Spring Boot下的MVC

文章目录Spring MVC程序开发1. 什么是Spring MVC&#xff1f;1.1 MVC定义1.2 MVC 和 Spring MVC 的关系2. 为什么学习Spring MVC&#xff1f;3. 怎么学习Spring MVC&#xff1f;3.1 Spring MVC的创建和连接3.1.1 创建Spring MVC项目3.1.2 RequestMapping 注解介绍3.1.3 Request…

1 网关介绍

网关介绍 在微服务架构中&#xff0c;一个系统会被拆分为很多个微服务。那么作为客户端要如何去调用这么多的微服务呢&#xff1f;如果没有网关的存在&#xff0c;我们只能在客户端记录每个微服务的地址&#xff0c;然后分别去调用。这样的话会产生很多问题&#xff0c;例如&a…

Threejs 创建的透明球体与模型前后看起来重叠解决方案

创建了球体透明的位置在后面例如 x: -2.2, y: 6, z: 0&#xff0c; 加载的模型在前面x: 2, y: 1, z: 13 ,创建的发光精灵 let spriteMaterial new THREE.SpriteMaterial({ map: spriteTexture, color: object.color, transparent: true, depthWrite: false, //深度写入属性 d…

SpringMVC中JSON数据的设置、RestFul风格

Java知识点总结&#xff1a;想看的可以从这里进入 目录3.4、JSON数据3.4.1、前端使用3.4.2、后端使用1、Jackson2、fastjson3.5、RestFul风格3.5.1、简介3.5.2、使用3.4、JSON数据 3.4.1、前端使用 前端在JavaScript中有封装的JSON对象&#xff0c;可以直接用来操作JSON数据。…

JavaSE之集合篇

文章目录前言一、集合概述集合继承结构图二、Collection接口中常用方法2.1Collection中存放什么元素&#xff1f;2.2常用方法2.3迭代器三、List接口中常用的方法四、ArrayList初始化容量及扩容五、Vector六、Map接口常用方法七、Properties前言 由于在刷题过程中&#xff0c;经…

大型三甲医院云HIS系统源码 强大的电子病历+完整文档

医院HIS系统源码云HIS系统&#xff1a;SaaS运维平台多医院入驻强大的电子病历完整文档 有源码&#xff0c;有演示 一、系统概述 采用主流成熟技术&#xff0c;软件结构简洁、代码规范易阅读&#xff0c;SaaS应用&#xff0c;全浏览器访问前后端分离&#xff0c;多服务协同&am…

使用AppSmith(PagePlug )低代码平台快速构建小程序应用实践

文章目录一、入门&#xff08;一&#xff09;介绍&#xff08;二&#xff09;功能特性&#xff08;三&#xff09;体验一下&#xff08;四&#xff09;参考教程二、使用Appsmith构建商城微信小程序&#xff08;一&#xff09;说明&#xff08;二&#xff09;应用配置&#xff0…

安卓逆向_5 --- jeb 和 AndroidStudio 动态调试 smali

Jeb 工具的使用 &#xff1a;https://www.52pojie.cn/forum.php?modviewthread&tid742250&#xff1a;https://zhuanlan.zhihu.com/p/302856081动态调试 smali 有两种方法&#xff1a; Jeb 调试AndroidStudio smalidea 插件动态调试。1、Jeb 动态调试 smali ​JEB是一个…

LeetCode 236.二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也可以是它自己的祖…

Modbus协议初探(C#实现)

由于作者水平有限&#xff0c;如有写得不对得地方请指正 趁着今天休息&#xff0c;就折腾一下Modbus协议&#xff0c;之前零零散散的看过几篇博客&#xff0c;听说搞上位机开发的要会这个协议&#xff0c;虽然我不是搞上位机开发的&#xff0c;但个人对这个比较感兴趣。按照我个…

开发一个看番app[樱花动漫移动端app]

使用react-native开发&#xff0c;功能&#xff1a; 支持看番支持历史记录浏览支持追番 项目地址: https://github.com/HGGshiwo/Sakura 界面&#xff1a; 首页分类用户界面播放界面历史记录搜索界面全部动漫追番

OpenAI Whisper and ChatGPT 语音助手

OpenAI Whisper and ChatGPT ASR Gradio Web UI一 环境准备1.1 python1.2 windows二 导入所需要的包三 加载模型四 定义openai和whisper接口五 生成Gradio Web UI麦克风输入&#xff0c;展示三种结果输入ASR结果输出文本输出TTS结果 一 环境准备 1.1 python gradio3.19.1 gTT…

ubuntu 如何搭建git远程仓库

ubuntu 安装git sudo apt-get install git配置用户名和邮箱 git config --global user.name “xxx” git config --global user.email “邮箱地址”关于远程仓库 Git是分布式版本控制系统&#xff0c;同一个Git仓库&#xff0c;可以分布到不同的机器上。这就需要一台电脑充当…

打印名片-课后程序(Python程序开发案例教程-黑马程序员编著-第一章-课后作业)

实例2&#xff1a;打印名片 名片是标示姓名及其所属组织、公司单位和联系方法的纸片&#xff0c;也是新朋友互相认识、自我介绍的快速有效的方法。本实例要求编写程序&#xff0c;模拟输出效果如图1所示的名片。 图1 名片样式 实例目标 掌握print()函数的用法 实例分析 名片…

【办公类-19-01-02】办公中的思考——Python,统计教职工的姓名中那些字最多?

背景需求&#xff1a;上一篇计算了教职工的姓氏谁最多&#xff0c;col[0]]这一篇统计教职工的&#xff08;姓氏名字&#xff09;里面哪些字出现最多。材料准备&#xff1a;1、下载所有员工名单写代码。py 包含”姓氏名字“的重字率统计from pandas import DataFrame, Series im…