leetcode【困难】460、LFU 缓存
请你为 最不经常使用(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) 的平均时间复杂度运行。
思路:
哈希+双向链表,链表里的node有三个属性(key, val, fre频率)
- Map<Integer, Node> cache
- Map<Integer, LinkedHashSet>,每个频次对应一个双向链表,为了相同频率时取出最久未使用的
- 每次get 、put 时,重新梳理链表,把这个key从原频率的链表移除,加入到新频率
- 维护当前最小频率min,缓存满了,从频率为min的链表里删除
直接使用LinkedHashSet
class LFUCache {Map<Integer, Node> cache; //缓存的内容Map<Integer, LinkedHashSet<Node>> freqMap; //每个频次对应的双向链表int size;int capacity;int min; // 存储当前最小频次public LFUCache(int capacity) {cache = new HashMap<> (capacity);freqMap = new HashMap<>();this.capacity = capacity;}public int get(int key) {Node node = cache.get(key);if (node == null) {return -1;}freqInc(node);return node.value;}public void put(int key, int value) {if (capacity == 0) {return;}Node node = cache.get(key);if (node != null) {node.value = value;freqInc(node);} else {if (size == capacity) {Node deadNode = removeNode();cache.remove(deadNode.key);size--;}Node newNode = new Node(key, value);cache.put(key, newNode);addNode(newNode);size++; }}void freqInc(Node node) {// 从原freq对应的链表里移除, 并更新minint freq = node.freq;LinkedHashSet<Node> set = freqMap.get(freq);set.remove(node);if (freq == min && set.size() == 0) { min = freq + 1;}// 加入新freq对应的链表node.freq++;LinkedHashSet<Node> newSet = freqMap.get(freq + 1);if (newSet == null) {newSet = new LinkedHashSet<>();freqMap.put(freq + 1, newSet);}newSet.add(node);}void addNode(Node node) {LinkedHashSet<Node> set = freqMap.get(1);if (set == null) {set = new LinkedHashSet<>();freqMap.put(1, set);} set.add(node); min = 1;}Node removeNode() {LinkedHashSet<Node> set = freqMap.get(min);Node deadNode = set.iterator().next();set.remove(deadNode);return deadNode;}
}class Node {int key;int value;int freq = 1;public Node() {}public Node(int key, int value) {this.key = key;this.value = value;}
}
自定义双向链表
class LFUCache {Map<Integer, Node> cache; // 存储缓存的内容Map<Integer, DoublyLinkedList> freqMap; // 存储每个频次对应的双向链表int size;int capacity;int min; // 存储当前最小频次public LFUCache(int capacity) {cache = new HashMap<> (capacity);freqMap = new HashMap<>();this.capacity = capacity;}public int get(int key) {Node node = cache.get(key);if (node == null) {return -1;}freqInc(node);return node.value;}public void put(int key, int value) {if (capacity == 0) {return;}Node node = cache.get(key);if (node != null) {node.value = value;freqInc(node);} else {if (size == capacity) {DoublyLinkedList minFreqLinkedList = freqMap.get(min);cache.remove(minFreqLinkedList.tail.pre.key);minFreqLinkedList.removeNode(minFreqLinkedList.tail.pre); // 这里不需要维护min, 因为下面add了newNode后min肯定是1.size--;}Node newNode = new Node(key, value);cache.put(key, newNode);DoublyLinkedList linkedList = freqMap.get(1);if (linkedList == null) {linkedList = new DoublyLinkedList();freqMap.put(1, linkedList);}linkedList.addNode(newNode);size++; min = 1; }}void freqInc(Node node) {// 从原freq对应的链表里移除, 并更新minint freq = node.freq;DoublyLinkedList linkedList = freqMap.get(freq);linkedList.removeNode(node);if (freq == min && linkedList.head.post == linkedList.tail) { min = freq + 1;}// 加入新freq对应的链表node.freq++;linkedList = freqMap.get(freq + 1);if (linkedList == null) {linkedList = new DoublyLinkedList();freqMap.put(freq + 1, linkedList);}linkedList.addNode(node);}
}class Node {int key;int value;int freq = 1;Node pre;Node post;public Node() {}public Node(int key, int value) {this.key = key;this.value = value;}
}class DoublyLinkedList {Node head;Node tail;public DoublyLinkedList() {head = new Node();tail = new Node();head.post = tail;tail.pre = head;}void removeNode(Node node) {node.pre.post = node.post;node.post.pre = node.pre;}void addNode(Node node) {node.post = head.post;head.post.pre = node;head.post = node;node.pre = head;}
}
leetcode【困难】460、LFU 缓存相关推荐
- leetcode:460. LFU最不常用缓存
题目来源 leetcode:460. LFU 缓存 题目描述 class LFUCache {public:LFUCache(int capacity) {}int get(int key) {}vo ...
- leetcode 460. LFU 缓存 hard
leetcode 460. LFU 缓存 hard 题目描述: 请你为 最不经常使用(LFU)缓存算法设计并实现数据结构. 实现 LFUCache 类: LFUCache(int capacity) ...
- 学习笔记 | LeetCode 460. LFU缓存
LeetCode 460. LFU缓存 请你为 最不经常使用(LFU)缓存算法设计并实现数据结构.它应该支持以下操作:get 和 put. get(key)- 如果键存在于缓存中,则获取键的值(总是正 ...
- lfu算法c语言,LeetCode算法系列 460. LFU 缓存机制
力扣原题 460. LFU 缓存机制 请你为 最不经常使用(LFU)缓存算法设计并实现数据结构. 实现 LFUCache 类:LFUCache(int capacity) - 用数据结构的容量 cap ...
- 460. LFU 缓存
460. LFU 缓存 请你为 最不经常使用(LFU)缓存算法设计并实现数据结构. 实现 LFUCache 类: LFUCache(int capacity) - 用数据结构的容量 capacity ...
- LeetCode 460. LFU 缓存 -- 哈希查询+双向链表
LFU 缓存 困难 634 相关企业 请你为 最不经常使用(LFU)缓存算法设计并实现数据结构. 实现 LFUCache 类: LFUCache(int capacity) - 用数据结构的容量 ca ...
- LeetCode 460. LFU缓存
文章目录 题目描述 思路 实现 解法二 扩展 题目描述 实现一个LFU缓存(Least Frequently Used). 在需要移除元素时,移除最近访问频率最低的.可以对每个元素增加一个计数器,访问 ...
- 力扣 460. LFU 缓存
题目来源:https://leetcode.cn/problems/lfu-cache/ 大致题意: 设计一个 LFU 缓存类: LFUCache(int capacity) - 用数据结构的容量 c ...
- lfu实现 java_LFU算法实现(460. LFU缓存)
今天字节客户端三面问了这道题,没做出来.第一,之前没见过lfu,第二,要求O(1)时间,条件苛刻一点.只能说无缘字节. 言归正传,LFU算法:least frequently used,最近最不经常使 ...
- LeetCode 460. LFU缓存(哈希双链表)
1. 题目 设计并实现最不经常使用(LFU)缓存的数据结构.它应该支持以下操作:get 和 put. get(key) - 如果键存在于缓存中,则获取键的值(总是正数),否则返回 -1. put(ke ...
最新文章
- Java对象序列化详解6,Java对象的序列化与反序列化详解
- php 怎么获取meta标签,php – 通过jQuery获取META描述
- 3DSlicer31:结构的实例分析IGSReader
- java字符串字节_Java中字符串与byte数组之间的相互转换
- 5岁儿童自学python编程-为什么外国5岁孩子就要学编程?原因你一定想知道
- 12v电量显示制作方法_12V电池电量指示电路
- 【DIY】自动鱼缸控制系统——【一】
- WinRM(Windows远程管理)介绍,Python远程操作Windows
- 什么是TF-IDF?
- matlab画gds图,Matlab GDS流程.doc
- 疯狂创客圈 JAVA 高并发 总目录
- touchjs中,touch的使用
- 如何给照片加边框?图片加边框的3个实用方法
- 油烟净化器如何判断质量好坏?
- 如何解决Maven依赖冲突
- 串口公头(九针)母头(九孔)对应接口(转)
- 【并发编程:线程池】深入简出的带你精通java线程
- Invoking “make -j12 -l12“ failed
- 【KAWAKO】audiotsm-使用python对音频进行变速不变调处理
- tk6071iq怎么恢复出厂设置_TK6071IQ触摸屏怎么用U盘下载程序