LFU缓存结构算法

news2024/11/20 11:48:59

设计LFU缓存结构

LFU:最近最少频率使用
基本思想: 当缓存满时,加入新数据,淘汰缓存中使用次数最少的key,当使用次数最少的key有多个,删除最早调用的key。

定义节点的数据结构

class Node{
		//使用频率
        int freq;
        //key值,用于数据更新的key
        int key;
        //value值
        int val;
        public Node(int freq, int key, int val){
            this.freq = freq;
            this.key = key;
            this.val = val;
        }
    }

确定实现缓存的的数据结构

  1. 定义一个<频率,链表>的频率集合freq_map
  2. 定义一个<key, node>的节点集合map
    链表存储的是相同使用频率的节点

freq_map的图示
当执行完[1,1,1],[1,2,2],[1,3,2]操作后,freq_map此时的状况:
频率为 1 的双向链表内有三个节点,按照插入顺序,节点每次都是从头部插入,删除节点每次都是尾部开始,目的是维护节点的调用秩序, 当频率相同时,删除最先使用的节点,即链表尾部的节点。
在这里插入图片描述

具体操作

  1. update
    1. 先获取节点的使用频率;
    2. 删除freq_map中双向链表中的对应节点
    3. 执行2之后,freq_map对应freq的链表可能为空,此时需要删除这个freq节点,注意如果该freq为最小频率,需要更新min_freq, 应为freq已经删除了,min_freq+1.
 private void update(Node node ,int key, int val){
        //找到频率
        int freq = node.freq;
        //原频率中删除该节点
        freq_map.get(freq).remove(node);
        //哈希表中该频率已经无节点,直接删除
        if(freq_map.get(freq).isEmpty()){
            freq_map.remove(freq);
            //若当前频率为最小,最小频率加1
            if(min_freq == freq){
                min_freq++;
            }
        }
        if(!freq_map.containsKey(freq+1)){
            freq_map.put(freq+1, new LinkedList<Node>());
        }
        //插入频率加1 的双向链表表头,链表中对应: freq key value
        freq_map.get(freq+1).addFirst(new Node(freq+1, key,val));
        map.put(key,freq_map.get(freq+1).getFirst());
    }
  1. set
    插入节点,首先判map中是否存在key:
    - 存在,调用update函数,进行数据更新操作
    - 不存在,插入新数据,但是插入之前需要判断缓存容量:
    1. 剩余空间为0, 即缓存已满,执行淘汰策略
    2. 剩余空间不为0,剩余空间-1,先存入ferq_map中,再存入map中。
 private void set(int key, int val){
        if(map.containsKey(key)){
            update(map.get(key),key,val);
        }else{
            if(size == 0){
                int oldkey = freq_map.get(min_freq).getLast().key;
                freq_map.get(min_freq).removeLast();
                if(freq_map.get(min_freq).isEmpty()){
                    freq_map.remove(min_freq);
                }
                //链表哈希表中删除
                map.remove(oldkey);
            }else{
                size--;
            }
            min_freq = 1;
            if(!freq_map.containsKey(1)){
                freq_map.put(1,new LinkedList<Node>());
            }
            freq_map.get(1).addFirst(new Node(1,key,val));
            map.put(key, freq_map.get(1).getFirst());
        }
    }
  1. get
    get操作在获取元素后,需要对freq_map和map进行更新操作。
private int get(int key){
        int res = -1;
        if(map.containsKey(key)){
            Node node =  map.get(key);
            res = node.val;
            update(node,key,res);
        }
        return res;
    }

完整代码

public class Solution {
    /**
     * lfu design
     * @param operators int整型二维数组 ops
     * @param k int整型 the k
     * @return int整型一维数组
     */
    class Node{
        int freq;
        int key;
        int val;
        public Node(int freq, int key, int val){
            this.freq = freq;
            this.key = key;
            this.val = val;
        }
    }
    //频率和双向链表的哈希表
    private Map<Integer, LinkedList<Node>> freq_map = new HashMap<>();
    
    //key到节点的哈希表
    private Map<Integer, Node> map = new HashMap<>();
    //记录缓存剩余容量
    private int size = 0;
    //记录当前最小频率
    private int min_freq = 0;

    public int[] LFU (int[][] operators, int k) {
        // 构建初始化连接
        //链表剩余大小
        this.size = k;
        //获取操作数
        int len = (int)Arrays.stream(operators).filter(x->x[0]==2).count();
        int[] res = new int[len];
        //遍历所有操作
        for(int i=0, j=0; i<operators.length;i++){
            if(operators[i][0] == 1){
                set(operators[i][1],operators[i][2]);
            }else{
                res[j++] = get(operators[i][1]);
            }
        }
        return res;
    }

    //调用函数时更新频率或者val值
    private void update(Node node ,int key, int val){
        //找到频率
        int freq = node.freq;
        //原频率中删除该节点
        freq_map.get(freq).remove(node);
        //哈希表中该频率已经无节点,直接删除
        if(freq_map.get(freq).isEmpty()){
            freq_map.remove(freq);
            //若当前频率为最小,最小频率加1
            if(min_freq == freq){
                min_freq++;
            }
        }
        if(!freq_map.containsKey(freq+1)){
            freq_map.put(freq+1, new LinkedList<Node>());
        }
        //插入频率加1 的双向链表表头,链表中对应: freq key value
        freq_map.get(freq+1).addFirst(new Node(freq+1, key,val));
        map.put(key,freq_map.get(freq+1).getFirst());
    }

    private void set(int key, int val){
        if(map.containsKey(key)){
            update(map.get(key),key,val);
        }else{
            if(size == 0){
                int oldkey = freq_map.get(min_freq).getLast().key;
                freq_map.get(min_freq).removeLast();
                if(freq_map.get(min_freq).isEmpty()){
                    freq_map.remove(min_freq);
                }
                //链表哈希表中删除
                map.remove(oldkey);
            }else{
                size--;
            }
            min_freq = 1;
            if(!freq_map.containsKey(1)){
                freq_map.put(1,new LinkedList<Node>());
            }
            freq_map.get(1).addFirst(new Node(1,key,val));
            map.put(key, freq_map.get(1).getFirst());
        }
    }
    private int get(int key){
        int res = -1;
        if(map.containsKey(key)){
            Node node =  map.get(key);
            res = node.val;
            update(node,key,res);
        }
        return res;
    }
}

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

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

相关文章

从零开始学习Linux运维,成为IT领域翘楚(八)

文章目录 &#x1f525;Linux进程管理&#x1f525;ps&#x1f525;top&#x1f525;htop &#x1f525;Linux进程管理 &#x1f525;ps 查看系统中所有进程 语法&#xff1a; ps [options] [--help]参数&#xff1a; &#x1f41f; -a 显示所有进程&#xff08;包括其他用…

Windows Server 安装docker

在windows 10 或windows 11 上使用docker&#xff0c;可以直接在docker 官网下载docker desktop安装即可。 但在windows server上则无法支持docker desktop&#xff0c;此时可通过如下方式安装&#xff1a; 以 管理员权限运行Power Shell&#xff0c;然后执行&#xff1a; 安装…

微软骚操作恶心Win10用户,上网得先看广告

IE 浏览器在几个月前被彻底禁用&#xff0c;预装了快30年的老古董也确实到了退役的时候。 而微软也早有准备&#xff0c;2015年随着 Win10 发布推出了 Microsoft Edge 浏览器。 2020年迁移到 Chromium 内核让其成为了主流浏览器之一。 和 Chromium 系其他浏览器一样支持扩展插…

【计算机视觉 | 自然语言处理】BLIP:统一视觉—语言理解和生成任务(论文讲解)

文章目录 一、前言二、试玩效果三、研究背景四、模型结构五、Pre-training objectives六、CapFilt架构七、Experiment八、结论 一、前言 今天我们要介绍的论文是 BLIP&#xff0c;论文全名为 Bootstrapping Language-Image Pre-training for Unified Vision-Language Understa…

Node.js 的安装

node.js 通用的安装方式&#xff08;单版本&#xff09; Node.js 可以用不同的方式进行安装。 第一种&#xff0c;可以在官网中&#xff0c;根据自己的操作系统&#xff0c;选择对应的安装包。 打开官网网址&#xff08;Download | Node.js&#xff09; 第二种&#xff0c;就…

php+vue网盘系统的设计与实现

该网盘系统的开发和设计根据用户的实际情况出发&#xff0c;对系统的需求进行了详细的分析&#xff0c;然后进行系统的整体设计&#xff0c;最后通过测试使得系统设计的更加完整&#xff0c;可以实现系统中所有的功能&#xff0c;在开始编写论文之前亲自到图书馆借阅php书籍&am…

吉时利Keithley6430/6485/6487皮安表测试软件NS-SourceMeter

软件概述 NS-SourceMeter皮安表上位机软件用于实现吉时利皮安表的上位机控制功能&#xff0c;通过在软件上的相应操作&#xff0c;控制皮安表进行配置或者测量&#xff0c;同时可以对测量的数据和图形进行保存。NS-SourceMeter皮安表软件由计算机和皮安表组成&#xff0c;通过计…

026 - C++ 可见性

本期我们讨论 C 的可见性。 可见性是一个属于面向对象编程的概念&#xff0c;它指的是类的某些成员或方法有多可见。 我说的可见性是指&#xff0c;谁能看见它们&#xff0c;谁能调用它们&#xff0c;谁能使用它们等这些内容。 可见性是对程序实际运行方式完全没有影响的东西…

Linux驱动开发:platform总线驱动

目录 1、为什么需要platform总线 2、设备端&#xff1a;platform_device 2.1 platform_device结构体 2.2 注册 2.3 注销 3、驱动端&#xff1a;platform_driver 3.1 platform_driver结构体 3.2 注册 3.3 注销 4、总线 4.1 bus_type 4.2 platform_bus_type 5、匹配…

2023第二届中国汽车碳中和国际峰会

会议背景 随着世界越来越认识到气候变化的破坏性影响&#xff0c;政府、组织和个人正在采取行动减少导致全球变暖的温室气体排放。随着电动化和互联技术的发展&#xff0c;汽车产业价值链正在经历变革。 汽车价值链的转型还为汽车行业创造了许多脱碳和更具可持续性的新机会。 …

vue3-admin-template页面

vue3-admin-template 本人学习视频网址为&#xff1a;视频地址源码:github 网页采用技术框架 本管理模板采用vue3开发&#xff0c;使用vue-router来作为路由跳转&#xff0c;将登录成功后产生的菜单&#xff0c;token放入到vuex中存储&#xff0c;通过axios来进行交互&#x…

深入理解 spring-boot-starter-parent

目录 一、前言二、Maven继承三、分析spring-boot-starter-parent四、Maven单继承问题五、不继承spring-boot-starter-parent需要注意的 一、前言 在idea当中创建springboot项目的时候都会继承一个spring-boot-starter-parent作为父类&#xff0c;假如不继承我们的项目就不能使…

Hudi的介绍与安装编译

Hudi的介绍 安装Maven 编译Hudi 执行编译 Hudi的介绍 Hudi简介 Hudi&#xff08;Hadoop Upserts Delete and Incremental&#xff09;是下一代流数据湖平台。Apache Hudi将核心仓库和数据库功能直接引入数据湖。Hudi提供了表、事务、高效的upserts/delete、高级索引、流摄取…

CentOS 7(2009) 升级 GCC 版本

1. 前言 CentOS 7 默认安装的 gcc 版本为 4.8&#xff0c;但是很多时候都会需要用到更高版本的 gcc 来编译源码&#xff0c;那么本文将会介绍如何在线升级 CentOS 的 gcc 版本。 2. 升级 GCC (1). 安装 centos-release-scl&#xff1b; [imaginemiraclecentos7 ~]$ sudo yum…

docker-compose搭建skywalking

SkyWalking 架构图 架构组成 SkyWalking Agent &#xff1a;负责从应用中&#xff0c;收集链路信息&#xff0c;发送给 SkyWalking OAP 服务器。目前支持 SkyWalking、Zikpin、Jaeger 等提供的 Tracing 数据信息。而我们目前采用的是&#xff0c;SkyWalking Agent 收集 SkyWalk…

测试知识总结

1.影响ui自动化稳定性 异常弹出对话框 --异常场景库 页面控件元素属性的细微变化--模糊匹配 延迟 --- retry 数据 -- 数据已被使用 2. 移动端应用细分为三大类&#xff1a;Web App、Native App&#xff08;原生应用&#xff09; 和 Hybrid App&#xff08;混合应用&…

Yjs + quill:快速实现支持协同编辑的富文本编辑器

大家好&#xff0c;我是前端西瓜哥&#xff0c;这次来看看 Yjs 如何帮助我们实现协同编辑能力的。 Y.js 是一个支持 协同编辑 的开源库。只要我们将自己的数据转换为 Y.js 提供的 Y.Array、Y.Map 类型&#xff0c;Y.js 就会自动帮我们做数据的一致性处理和同步。 一致性问题 …

Cookie和Session的API、登录页面

目录 一、Cookie 和 Session 1、HttpServletRequest 类中的相关方法 2、HttpServletResponse 类中的相关方法 3、HttpSession 类中的相关方法 4、Cookie 类中的相关方法 二、网页登录 1、约定前后端交互接口 2、编写一个简单的登录页面 3、编写一个Servlet 来处理这个…

Springboot +Flowable,任务认领和回退(二)

一.简介 有的时候&#xff0c;一个任务节点会存在多个候选人&#xff0c;例如&#xff1a;张三提交一个任务&#xff0c;这个任务即可以由李四处理&#xff0c;又可以由王五处理&#xff0c;那么针对这种多个任务候选人的情况&#xff0c;该如何处理&#xff1f; 二.绘制流程…

SuperMap GIS基础产品组件GIS FAQ集锦(2)

SuperMap GIS基础产品组件GIS FAQ集锦&#xff08;2&#xff09; 【iObjects for Spark】读取GDB参数该如何填写&#xff1f; 【解决办法】可参考以下示例&#xff1a; val GDB_params new util.HashMapString, java.io.Serializable GDB_params.put(FeatureRDDProviderParam…