请你为 最不经常使用(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频率)

  1. Map<Integer, Node> cache
  2. Map<Integer, LinkedHashSet>,每个频次对应一个双向链表,为了相同频率时取出最久未使用的
  3. 每次get 、put 时,重新梳理链表,把这个key从原频率的链表移除,加入到新频率
  4. 维护当前最小频率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 缓存相关推荐

  1. leetcode:460. LFU最不常用缓存

    题目来源 leetcode:460. LFU 缓存 题目描述 class LFUCache {public:LFUCache(int capacity) {}int get(int key) {}vo ...

  2. ​ leetcode 460. LFU 缓存 hard​

    leetcode 460. LFU 缓存  hard 题目描述: 请你为 最不经常使用(LFU)缓存算法设计并实现数据结构. 实现 LFUCache 类: LFUCache(int capacity) ...

  3. 学习笔记 | LeetCode 460. LFU缓存

    LeetCode 460. LFU缓存 请你为 最不经常使用(LFU)缓存算法设计并实现数据结构.它应该支持以下操作:get 和 put. get(key)- 如果键存在于缓存中,则获取键的值(总是正 ...

  4. lfu算法c语言,LeetCode算法系列 460. LFU 缓存机制

    力扣原题 460. LFU 缓存机制 请你为 最不经常使用(LFU)缓存算法设计并实现数据结构. 实现 LFUCache 类:LFUCache(int capacity) - 用数据结构的容量 cap ...

  5. 460. LFU 缓存

    460. LFU 缓存 请你为 最不经常使用(LFU)缓存算法设计并实现数据结构. 实现 LFUCache 类: LFUCache(int capacity) - 用数据结构的容量 capacity ...

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

    LFU 缓存 困难 634 相关企业 请你为 最不经常使用(LFU)缓存算法设计并实现数据结构. 实现 LFUCache 类: LFUCache(int capacity) - 用数据结构的容量 ca ...

  7. LeetCode 460. LFU缓存

    文章目录 题目描述 思路 实现 解法二 扩展 题目描述 实现一个LFU缓存(Least Frequently Used). 在需要移除元素时,移除最近访问频率最低的.可以对每个元素增加一个计数器,访问 ...

  8. 力扣 460. LFU 缓存

    题目来源:https://leetcode.cn/problems/lfu-cache/ 大致题意: 设计一个 LFU 缓存类: LFUCache(int capacity) - 用数据结构的容量 c ...

  9. lfu实现 java_LFU算法实现(460. LFU缓存)

    今天字节客户端三面问了这道题,没做出来.第一,之前没见过lfu,第二,要求O(1)时间,条件苛刻一点.只能说无缘字节. 言归正传,LFU算法:least frequently used,最近最不经常使 ...

  10. LeetCode 460. LFU缓存(哈希双链表)

    1. 题目 设计并实现最不经常使用(LFU)缓存的数据结构.它应该支持以下操作:get 和 put. get(key) - 如果键存在于缓存中,则获取键的值(总是正数),否则返回 -1. put(ke ...

最新文章

  1. Java对象序列化详解6,Java对象的序列化与反序列化详解
  2. php 怎么获取meta标签,php – 通过jQuery获取META描述
  3. 3DSlicer31:结构的实例分析IGSReader
  4. java字符串字节_Java中字符串与byte数组之间的相互转换
  5. 5岁儿童自学python编程-为什么外国5岁孩子就要学编程?原因你一定想知道
  6. 12v电量显示制作方法_12V电池电量指示电路
  7. 【DIY】自动鱼缸控制系统——【一】
  8. WinRM(Windows远程管理)介绍,Python远程操作Windows
  9. 什么是TF-IDF?
  10. matlab画gds图,Matlab GDS流程.doc
  11. 疯狂创客圈 JAVA 高并发 总目录
  12. touchjs中,touch的使用
  13. 如何给照片加边框?图片加边框的3个实用方法
  14. 油烟净化器如何判断质量好坏?
  15. 如何解决Maven依赖冲突
  16. 串口公头(九针)母头(九孔)对应接口(转)
  17. 【并发编程:线程池】深入简出的带你精通java线程
  18. Invoking “make -j12 -l12“ failed
  19. 【KAWAKO】audiotsm-使用python对音频进行变速不变调处理
  20. tk6071iq怎么恢复出厂设置_TK6071IQ触摸屏怎么用U盘下载程序

热门文章

  1. Java有序数组——原地去重——不使用额外空间
  2. 如果你恨一个人...
  3. 手把手教python打包exe,打包一个简易的小程序。tkinter,python初学者。编程初学者作业:用*填充出自己的名字
  4. Java写泡泡堂网络游戏
  5. 【情人节表白神器:送她一个HTML动态表白网站 带源码】
  6. 基于AM5728的OpenCL例程开发分享
  7. WPF中的MVVM模式
  8. 你知道国内有哪些网站提供免费的云服务器吗
  9. <<2020云南省青少年创意编程与智能设计大赛>>参赛作品之变身魔药实验编程设计说明
  10. 记一次Android第三方日历控件CalendarView的使用