题目地址:

https://leetcode.com/problems/lfu-cache/

实现一个Least Frequently Used (LFU) cache。它是一个类似HashMap的数据结构,存的是key-value pair,key唯一,并可以做三种操作:
1、初始化,它有一个最大容量;
2、int get(int key),得到key对应的value,如果key不存在则返回 − 1 -1 −1;
3、void put(int key, int value),如果key不存在,则添加该key-value pair,并且如果当前已经存了最大容量个元素了,就需要将cache里使用次数最少的元素删掉,如果有多个,则把最近使用时间最久的元素删掉,这里的“使用”的含义是这个元素被get过或put过,都算;如果key存在,则更新其value。

思路是HashMap + 双链表。开两个HashMap,一个存key和Node的对应关系,其中Node里存key对应的value以及该key被使用的次数,另一个存使用次数和该使用次数对应的所有Node所组成的链表,之所以要用链表来存,是因为要在使用次数相同的情况下删除最近使用时间最久的那个Node,可以借助链表来实现,链表头存最新的Node,链表尾存最旧的Node。并且,由于我们要找使用次数最小的那个链表,所以我们再维护一个变量minFreq专门存当前使用次数最少是多少次。

我们先考虑某个node被使用过一次之后,应该怎样处理它。我们开个函数叫update(Node node)专门做这件事。其实现如下:
因为其freq会增加,所以我们先找到其freq对应的链表,然后将其在链表中删除,接着看一下minFreq是否要被更新,其被更新当且仅当当前使用次数为minFreq的节点都不存在了,即这个链表空了,并且它刚好就是对应的minFreq。如果是这样,就将minFreq增加 1 1 1(因为被update的节点的使用次数会等于minFreq加 1 1 1),再接着将node的使用次数加 1 1 1,再加入这个使用次数对应的链表的头部。当然如果该使用次数的链表不存在,则还需要将其建出来。

三个操作分别如下实现:
1、初始化两个HashMap,并存一下最大容量值。最好先在frequency map里先存一个使用次数 1 1 1对应的链表,因为添加新值的时候这个链表是一定会被用到的;
2、get的时候,先从key找到对应的node(找不到则直接返回 − 1 -1 −1),然后将其更新(执行update操作),最后返回该node里存的value值即可;
3、put的时候,如果最大容量是 0 0 0那就什么也不用做了,直接返回;接着先看一下key是否存在,如果存在,则将其对应的Node找出来,更新其value,并执行update;如果不存在,说明需要增加新Node了,此时要看一下是否达到了容量上限,如果达到了,则要执行删除Least Frequently Used元素的操作(该操作这样执行,先找到minFreq对应的链表,然后删掉其表尾node,并且将该node在key-Node的HashMap里也删掉。如果该链表空了,则说明minFreq的元素不存在了,但是这里事实上不需要更新minFreq,因为执行了删除Least Frequently Used元素的操作一定是因为有新元素进来,它的freq是 1 1 1,所以minFreq一定会变成 1 1 1,这可以在后面的逻辑里写上),删除完之后,将新节点new出来,加入key-Node的HashMap中,并将其加入使用次数为 1 1 1的链表表头,并且重置minFreq为 1 1 1。

代码如下:

class LFUCache {private:struct Node {int key, val, freq;Node *prev, *next;Node(int key, int val): key(key), val(val), prev(nullptr), next(nullptr), freq(1) {}};struct LinkedList {Node *head, *tail;int sz;LinkedList() : sz(0) {head = new Node(0, 0);tail = new Node(0, 0);head->next = tail;tail->prev = head;}void addFirst(Node *node) {node->prev = head;node->next = head->next;head->next = node;node->next->prev = node;sz++;}void remove(Node *node) {if (!sz) return;node->prev->next = node->next;node->next->prev = node->prev;sz--;}Node *removeLast() {if (!sz) return nullptr;auto *res = tail->prev;remove(res);return res;}};unordered_map<int, Node *> nodemp;unordered_map<int, LinkedList *> freq;int cap, minFreq;void update(Node *node) {auto *list = freq[node->freq];list->remove(node);// 如果该node是minFreq对应的唯一节点,则需要更新minFreqif (node->freq == minFreq && !list->sz) minFreq++;node->freq++;if (!freq.count(node->freq)) freq[node->freq] = new LinkedList();freq[node->freq]->addFirst(node);}// 这个是为了删除链表尾,即最近使用时间最久的元素void removeOldest() {auto *list = freq[minFreq];auto *node = list->removeLast();nodemp.erase(node->key);}public:LFUCache(int capacity) {// 使用次数为1的链表一开始就添进去,因为这个链表是一定会被用到的(除非cap = 0)freq[1] = new LinkedList();cap = capacity;minFreq = 0;}int get(int key) {if (!nodemp.count(key)) return -1;auto *node = nodemp[key];update(node);return node->val;}void put(int key, int value) {// 尤其要注意特判这个,否则nodeMap为空的时候会触发removeOldest操作,// 会导致NullPointerExceptionif (!cap) return;if (nodemp.count(key)) {auto *node = nodemp[key];node->val = value;update(node);return;}if (nodemp.size() == cap) removeOldest();auto *node = new Node(key, value);nodemp[key] = node;freq[1]->addFirst(node);minFreq = 1;}
};

所有操作时间复杂度 O ( 1 ) O(1) O(1),空间 O ( n ) O(n) O(n), n n n是最大容量。

【Leetcode】460. LFU Cache相关推荐

  1. 【LeetCode】460 and 1132(LFU缓存机制)

    LRU 算法的淘汰策略是 Least Recently Used,也就是每次淘汰那些最久没被使⽤的数据: ⽽ LFU 算法的淘汰策略是 Least Frequently Used,也就是每次淘汰那些使 ...

  2. 【Leetcode】100. 相同的树

    题目 给定两个二叉树,编写一个函数来检验它们是否相同. 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的. 示例 1: 输入: 1 1/ \ / \2 3 2 3[1,2,3], [1 ...

  3. 【leetcode】85. Maximal Rectangle 0/1矩阵的最大全1子矩阵

    1. 题目 Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing only 1 ...

  4. 【leetcode】486. Predict the Winner

    题目如下: Given an array of scores that are non-negative integers. Player 1 picks one of the numbers fro ...

  5. 【leetcode】132. Palindrome Partitioning II

    题目如下: 解题思路:本题是[leetcode]131. Palindrome Partitioning的升级版,要求的是求出最小cuts,如果用[leetcode]131. Palindrome P ...

  6. 【leetcode】86. Partition List

    题目如下: Given a linked list and a value x, partition it such that all nodes less than x come before no ...

  7. 【Leetcode】103. 二叉树的锯齿形层次遍历

    题目 给定一个二叉树,返回其节点值的锯齿形层次遍历.(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行). 例如: 给定二叉树 [3,9,20,null,null,15,7], 3 ...

  8. 【Leetcode】79.单词搜索

    题目 给定一个二维网格和一个单词,找出该单词是否存在于网格中. 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中"相邻"单元格是那些水平相邻或垂直相邻的单元格.同一个单元格 ...

  9. 【leetcode】 算法题1 两数之和

    [leetcode] 算法题1 两数之和 问题   给定一个整数数组和一个目标值,找出数组中和为目标值的两个数. 你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用. 示例: 给定 nums ...

最新文章

  1. 【高并发】又一个朋友面试栽在了Thread类的stop()方法和interrupt()方法上!
  2. linux spi测试程序,spidev测试方法 /how to test spidev
  3. 华为弹性云服务器ECS使用学习0
  4. 为什么要用枚举实现单例模式(避免反射、序列化问题)
  5. 设计模式(十三): 命令模式
  6. Java相关资源下载路径
  7. Linux内核分析——Linux内核学习总结
  8. ubuntu安装cuda11.2
  9. 只要学会这个PDF压缩方法,压缩PDF不再是难题
  10. GB50311-2007综合布线工程设计规范
  11. python的def什么意思_「Python基础」def是什么?如何自定义函数def
  12. 使用iptables-persistent永久保存iptables规则
  13. python汉字简繁体转换方法
  14. 【Android】获取当前的安卓版本号的代码
  15. 荣耀4a android art,荣耀4A黑科技大揭秘,真是给工程师给跪了!!
  16. Java各种数据类型互转
  17. oppo k7x和oppo k7哪个好 oppo k7x和oppo k7参数对比
  18. LTE细说-03-PCFICH-PHICH
  19. 动环监控系统的IO服务器,机房动环监控系统配套多路开关量IO
  20. BCGControlBar Professional v.28.0 Full Source 带安装序列号,带源代码,完全版。

热门文章

  1. 告别“浮云” 享用云计算服务优势
  2. Win11电脑名如何更改?Win11更改电脑名的方法
  3. CAD填充无法修剪的解决办法
  4. 【Swing】JTree:树组件
  5. 做全新的自己,向未来冲刺
  6. 春款童装产品做短视频营销应该怎么做?短视频营销策略分析
  7. java捕鱼增值版游戏下载_捕鱼游戏 java
  8. 利用PHP开发的一款万能、表白墙系统
  9. 最新的Vivado安装、使用教程(2022/12/31)
  10. 我自己制作的导航页网站,源码免费分享~