LFU缓存(Leetcode460)

2024-02-05 22:20
文章标签 缓存 lfu leetcode460

本文主要是介绍LFU缓存(Leetcode460),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

例题:

分析:

这道题可以用两个哈希表来实现,一个hash表(kvMap)用来存储节点,另一个hash表(freqMap)用来存储双向链表,链表的头节点代表最近使用的元素,离头节点越远的节点代表最近最少使用的节点。

注意:freqMap 的 key 为节点的使用频次。

下图是freqMap的结构:

这是kvMap: 它的key没有什么特殊含义,value是储存的节点

题目中调用get方法会增加元素的使用次数(freq),这时在freqMap中需要将该节点转移到对应的位置上(比如该节点使用了两次,那就把该节点转移到key为2的位置上去)

put方法分为两种情况:如果要添加的key不存在,说明是新元素,放到key为1的位置上,如果key存在,此时是修改操作,改变元素的value值,对应的频次+1,同样放到对应的位置上。

淘汰策略:要保证两个hash表中的节点数一致。

代码实现:
package leetcode;import java.util.HashMap;//设计LFU缓存
public class LFUCacheLeetcode460 {static class LFUCache {static class Node{Node prev;Node next;int key;int value;int freq = 1;public Node(){}public Node(int key, int value) {this.key = key;this.value = value;}}static class DoublyLinkedList{Node head;Node tail;int size;public DoublyLinkedList(){head = tail = new Node();head.next = tail;tail.prev = head;}//头部添加   head<->1<->2<->tail  添加3public void addFirst(Node node){Node oldFirst = head.next;oldFirst.prev = node;head.next = node;node.prev = head;node.next = oldFirst;size++;}//指定节点删除   head<->1<->2<->3<->tail  删除2public void remove(Node node){Node prev = node.prev;Node next = node.next;prev.next = next;next.prev = prev;size--;}//删除最后一个节点public Node removeLast(){Node last = tail.prev;remove(last);return last;}//判断双向链表是否为空public boolean isEmpty(){return size == 0;}}private HashMap<Integer,Node> kvMap = new HashMap<>();private HashMap<Integer,DoublyLinkedList> freqMap = new HashMap<>();private int capacity;private int minFreq = 1;public LFUCache(int capacity) {this.capacity = capacity;}/** 获取节点  不存在:返回 -1*           存在: 增加节点的使用频次,将其转移到频次+1的链表中*           判断旧节点所在的链表是否为空,更新最小频次minFreq* */public int get(int key) {if(!kvMap.containsKey(key)){return -1;}Node node = kvMap.get(key);//先删除旧节点DoublyLinkedList list = freqMap.get(node.freq);list.remove(node);//判断当前链表是否为空,为空可能需要更新最小频次if(list.isEmpty() && node.freq == minFreq){minFreq++;}node.freq++;//将新节点放入新位置,可能该频次对应的链表不存在,不存在需要创建freqMap.computeIfAbsent(node.freq, k -> new DoublyLinkedList()).addFirst(node);return node.value;}/** 更新*     将节点的value更新,增加节点的使用频次,将其转移到频次+1的链表中* 新增*     检查是否超过容量,若超过,淘汰minFreq链表的最后节点*     创建新节点,放入kvMap,并加入频次为 1 的双向链表* */public void put(int key, int value) {if(kvMap.containsKey(key)){ //更新Node node = kvMap.get(key);DoublyLinkedList list = freqMap.get(node.freq);list.remove(node);if(list.isEmpty() && node.freq == minFreq){minFreq++;}node.freq++;freqMap.computeIfAbsent(node.freq, k -> new DoublyLinkedList()).addFirst(node);node.value = value;}else{ //新增//先判断容量是否已满if(kvMap.size() == capacity){//执行淘汰策略Node node = freqMap.get(minFreq).removeLast();kvMap.remove(node.key);}Node node = new Node(key, value);kvMap.put(key, node);//加入频次为1的双向链表freqMap.computeIfAbsent(1, k -> new DoublyLinkedList()).addFirst(node);minFreq = 1;}}}public static void main(String[] args) {LFUCache cache = new LFUCache(2);cache.put(1, 1);cache.put(2, 2);System.out.println(cache.get(1)); // 1 freq=2cache.put(3, 3);System.out.println(cache.get(2)); // -1System.out.println(cache.get(3)); // 3 freq=2cache.put(4, 4);System.out.println(cache.get(1)); // -1System.out.println(cache.get(3)); // 3  freq=3System.out.println(cache.get(4)); // 4  freq=2}
}

这篇关于LFU缓存(Leetcode460)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/682334

相关文章

MyBatis延迟加载与多级缓存全解析

《MyBatis延迟加载与多级缓存全解析》文章介绍MyBatis的延迟加载与多级缓存机制,延迟加载按需加载关联数据提升性能,一级缓存会话级默认开启,二级缓存工厂级支持跨会话共享,增删改操作会清空对应缓... 目录MyBATis延迟加载策略一对多示例一对多示例MyBatis框架的缓存一级缓存二级缓存MyBat

前端缓存策略的自解方案全解析

《前端缓存策略的自解方案全解析》缓存从来都是前端的一个痛点,很多前端搞不清楚缓存到底是何物,:本文主要介绍前端缓存的自解方案,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、为什么“清缓存”成了技术圈的梗二、先给缓存“把个脉”:浏览器到底缓存了谁?三、设计思路:把“发版”做成“自愈”四、代码

Java 缓存框架 Caffeine 应用场景解析

《Java缓存框架Caffeine应用场景解析》文章介绍Caffeine作为高性能Java本地缓存框架,基于W-TinyLFU算法,支持异步加载、灵活过期策略、内存安全机制及统计监控,重点解析其... 目录一、Caffeine 简介1. 框架概述1.1 Caffeine的核心优势二、Caffeine 基础2

Redis高性能Key-Value存储与缓存利器常见解决方案

《Redis高性能Key-Value存储与缓存利器常见解决方案》Redis是高性能内存Key-Value存储系统,支持丰富数据类型与持久化方案(RDB/AOF),本文给大家介绍Redis高性能Key-... 目录Redis:高性能Key-Value存储与缓存利器什么是Redis?为什么选择Redis?Red

React 记忆缓存的三种方法实现

《React记忆缓存的三种方法实现》本文主要介绍了React记忆缓存的三种方法实现,包含React.memo、useMemo、useCallback,用于避免不必要的组件重渲染和计算,感兴趣的可以... 目录1. React.memo2. useMemo3. useCallback使用场景与注意事项在 Re

Docker多阶段镜像构建与缓存利用性能优化实践指南

《Docker多阶段镜像构建与缓存利用性能优化实践指南》这篇文章将从原理层面深入解析Docker多阶段构建与缓存机制,结合实际项目示例,说明如何有效利用构建缓存,组织镜像层次,最大化提升构建速度并减少... 目录一、技术背景与应用场景二、核心原理深入分析三、关键 dockerfile 解读3.1 Docke

使用Spring Cache本地缓存示例代码

《使用SpringCache本地缓存示例代码》缓存是提高应用程序性能的重要手段,通过将频繁访问的数据存储在内存中,可以减少数据库访问次数,从而加速数据读取,:本文主要介绍使用SpringCac... 目录一、Spring Cache简介核心特点:二、基础配置1. 添加依赖2. 启用缓存3. 缓存配置方案方案

Java实现本地缓存的四种方法实现与对比

《Java实现本地缓存的四种方法实现与对比》本地缓存的优点就是速度非常快,没有网络消耗,本地缓存比如caffine,guavacache这些都是比较常用的,下面我们来看看这四种缓存的具体实现吧... 目录1、HashMap2、Guava Cache3、Caffeine4、Encache本地缓存比如 caff

Android 缓存日志Logcat导出与分析最佳实践

《Android缓存日志Logcat导出与分析最佳实践》本文全面介绍AndroidLogcat缓存日志的导出与分析方法,涵盖按进程、缓冲区类型及日志级别过滤,自动化工具使用,常见问题解决方案和最佳实... 目录android 缓存日志(Logcat)导出与分析全攻略为什么要导出缓存日志?按需过滤导出1. 按

java如何实现高并发场景下三级缓存的数据一致性

《java如何实现高并发场景下三级缓存的数据一致性》这篇文章主要为大家详细介绍了java如何实现高并发场景下三级缓存的数据一致性,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 下面代码是一个使用Java和Redisson实现的三级缓存服务,主要功能包括:1.缓存结构:本地缓存:使