题目:

Design and implement a data structure for Least Frequently Used (LFU)cache. It should support the following operations: get and put.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present. When the cache reaches its capacity, it should invalidate the least frequently used item before inserting a new item. For the purpose of this problem, when there is a tie (i.e., two or more keys that have the same frequency), the least recently used key would be evicted.

Note that the number of times an item is used is the number of calls to the get and put functions for that item since it was inserted. This number is set to zero when the item is removed.

Follow up:
Could you do both operations in O(1) time complexity?

Example:

LFUCache cache = new LFUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // returns 1
cache.put(3, 3); // evicts key 2
cache.get(2); // returns -1 (not found)
cache.get(3); // returns 3.
cache.put(4, 4); // evicts key 1.
cache.get(1); // returns -1 (not found)
cache.get(3); // returns 3
cache.get(4); // returns 4

分析:

设计并实现最不经常使用(LFU)缓存的数据结构。相比LRU,LFU增加了频率这一概念,在cache满了的时候,将会把最不经常使用的缓存清除掉,在本题则是以访问频率体现的。

利用hashmap来记录当前数据{key, value}和其出现次数之间的映射,我们可以把出现频率相同的key都放到一个list集合中,再使用一个hashmap建立频率和list之间的映射。为了能够在O(1)的时间内完成操作了,要快速的定位list中key的位置,我们再用一个hashmap来建立key和freq中key的位置之间的映射。最后当然我们还需要两个变量cap和minFreq,分别来保存cache的大小,和当前最小的频率。

我们假设容量为2,先put(1, 1)和put(2, 2),此时容器的存储情况如下:

此时调用get(1,1),我们需要做的操作如下:

1.如果m中不存在key1,那么返回-1

2. 从freq中频率为1(从m中获取到的key对应的频率值)的list中将1删除

3. 将m中1对应的频率值自增1

4. 将1保存到freq中频率为2(当前频率加+1)的list的末尾

5. 在l中保存1在freq中频率为2的list中的位置

6. 如果freq中频率为minFreq的list为空,minFreq自增1

7. 返回m中1对应的value值

经过这些步骤后,我们再来看下此时内部容器情况:

此时再put(3, 3),我们需要做的操作如下:

1. 先调用get(3)返回的结果不是-1,也就是说此时cache中已经存在3这块缓存了,我们可以直接将m中3对应的value更新为当前value,并返回。

2. 如果此时m的大小大于了cap,即超过了cache的容量,则:

  a)在m中移除minFreq对应的list的首元素的key。

  b)在l中清除3对应的纪录

  c)在freq中移除minFreq对应的list的首元素。

3. 在m中建立3的映射,即 3 -> {value 3, frequent1}

4. 在freq中频率为1的list末尾加上3

5. 在l中保存3在freq中频率为1的list中的位置

6. minFreq重置为1

此时内部容器情况:

虚线表示此步骤删去的元素。

程序:

C++

class LFUCache {
public:
LFUCache(int capacity) {
cap = capacity;
} int get(int key) {
if(m.count(key) == 0)
return -1;
freq[m[key].second].erase(l[key]);
m[key].second++;
freq[m[key].second].push_back(key);
l[key] = --freq[m[key].second].end();
if(freq[minFreq].size() == 0)
minFreq++;
return m[key].first;
} void put(int key, int value) {
if(cap <= 0)
return;
if(get(key) != -1){
m[key].first = value;
return;
}
if(m.size() >= cap){
m.erase(freq[minFreq].front());
l.erase(freq[minFreq].front());
freq[minFreq].pop_front();
}
m[key] = make_pair(value, 1);
freq[1].push_back(key);
l[key] = --freq[1].end();
minFreq = 1;
} private:
int cap, minFreq;
unordered_map<int, pair<int, int>> m;
unordered_map<int, list<int>> freq;
unordered_map<int, list<int>::iterator> l;
}; /**
* Your LFUCache object will be instantiated and called as such:
* LFUCache* obj = new LFUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/

Java

public class LFUCache {

    public LFUCache(int capacity) {
cap = capacity;
map = new HashMap<>();
freq = new HashMap<>();
list = new HashMap<>();
minFreq = 1;
}
public int get(int key) {
if(!map.containsKey(key))
return -1;
int count = freq.get(key);
list.get(count).remove(key);
freq.put(key, count + 1);
if(list.get(minFreq).size() == 0)
minFreq++;
if(!list.containsKey(count + 1)){
LinkedHashSet<Integer> set = new LinkedHashSet<Integer>();
set.add(key);
list.put(count+1, set);
}else{
list.get(count+1).add(key);
}
return map.get(key);
}
public void put(int key, int value) {
if(cap <= 0)
return;
if(get(key) != -1){
map.put(key, value);
return;
}
if(map.size() >= cap){
Integer removeKey = list.get(minFreq).iterator().next();
map.remove(removeKey);
list.get(minFreq).remove(removeKey);
freq.remove(removeKey);
}
//System.out.println(map.toString());
map.put(key, value);
freq.put(key, 1);
if(!list.containsKey(1)){
LinkedHashSet<Integer> set = new LinkedHashSet<Integer>();
set.add(key);
list.put(1, set);
}else{
list.get(1).add(key);
}
minFreq = 1;
} private int cap;
private int minFreq;
private HashMap<Integer, Integer> map;
private HashMap<Integer, Integer> freq;
private HashMap<Integer, LinkedHashSet<Integer>> list;
} /**
* Your LFUCache object will be instantiated and called as such:
* LFUCache obj = new LFUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/

LeetCode 460. LFU Cache LFU缓存 (C++/Java)的更多相关文章

  1. LeetCode:146_LRU cache | LRU缓存设计 | Hard

    题目:LRU cache Design and implement a data structure for Least Recently Used (LRU) cache. It should su ...

  2. [LeetCode] 460. LFU Cache 最近最不常用页面置换缓存器

    Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the f ...

  3. [LeetCode] LFU Cache 最近最不常用页面置换缓存器

    Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the f ...

  4. leetcode 146. LRU Cache 、460. LFU Cache

    LRU算法是首先淘汰最长时间未被使用的页面,而LFU是先淘汰一定时间内被访问次数最少的页面,如果存在使用频度相同的多个项目,则移除最近最少使用(Least Recently Used)的项目. LFU ...

  5. Leetcode: LFU Cache && Summary of various Sets: HashSet, TreeSet, LinkedHashSet

    Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the f ...

  6. LeetCode LFU Cache

    原题链接在这里:https://leetcode.com/problems/lfu-cache/?tab=Description 题目: Design and implement a data str ...

  7. LeetCode之LRU Cache 最近最少使用算法 缓存设计

    设计并实现最近最久未使用(Least Recently Used)缓存. 题目描述: Design and implement a data structure for Least Recently ...

  8. [LeetCode] 146. LRU Cache 最近最少使用页面置换缓存器

    Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...

  9. LFU Cache

    2018-11-06 20:06:04 LFU(Least Frequently Used)算法根据数据的历史访问频率来淘汰数据,其核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”. ...

  10. LRU缓存实现(Java)

    LRU Cache的LinkedHashMap实现 LRU Cache的链表+HashMap实现 LinkedHashMap的FIFO实现 调用示例 LRU是Least Recently Used 的 ...

随机推荐

  1. docker安装mysql8.0.20并远程连接

    前言 今天docker安装mysql8.0.20捯饬了半天,主要是挂载问题和连接问题,索性记录一下.网上很多千篇一律,还有很多就是过时了,那还是我自己上场吧.大家看的时候,请睁大眼睛,按步骤来. Do ...

  2. 如何使用XSSFWorkbook读取文本薄?

    [版权声明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) https://www.cnblogs.com/cnb-yuchen/p/18146625 出自[进步*于辰的博客] 1.文件兼容类 ...

  3. 使用C# 创建、填写、删除PDF表单域

    通常情况下,PDF文件是不可编辑的,但PDF表单提供了一些可编辑区域,允许用户填写和提交信息.PDF表单通常用于收集信息.反馈或进行在线申请,是许多行业中数据收集和交换的重要工具. PDF表单可以包含 ...

  4. 【SQL】IN和EXISTS谁的效率更高

    [SQL]IN和EXISTS谁的效率更高 总结: 索引设置好的情况下 子查询数据量大的,用exists 子查询数据量小的,用in 原文连接:https://zhuanlan.zhihu.com/p/4 ...

  5. stmp 501 5.1.3 Invalid Address 无效的邮件地址

    stmp 501 5.1.3 Invalid Address 无效的邮件地址 一般来说就是要确认邮箱地址是不是对的 还有一种可能的情况是使用的邮件服务器仅支持对内邮件,没有对外邮件的发送权限

  6. 初接触:从创建工程到导出gerber(学习Altium Designer)

    学习Altium Designer Altium Designer的工程文件后缀为.PrjPcb,主要包含Source Documents和Libraries.Source Documents里面有S ...

  7. HL7标准的版本

    HL7V2 HL7v2是用于在系统之间交换临床和患者信息的最广泛使用的医疗保健消息传递标准.HL7v2的目标是使用代表临床事件信息的标准化消息(例如患者管理活动.人口统计.医疗订单.结果和财务信息)在 ...

  8. 力扣605(java&python)-种花问题(简单)

    题目: 假设有一个很长的花坛,一部分地块种植了花,另一部分却没有.可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去. 给你一个整数数组  flowerbed 表示花坛,由若干 0 和 1 ...

  9. 牛客网-SQL专项训练6

    ①要将employee 的表名更改为 employee_info,下面MySQL语句正确的是(A) 解析: RENAME用于表的重命名:RENAME  <NAME>(修改表名或索引名) 或 ...

  10. 读书笔记 dotnet 的字符串在内存是如何存放

    本文是读伟民哥翻译的 .NET内存管理宝典 这本书的笔记,我认为读书的过程也需要实践,这样对一知半解的知识也有较为清晰的了解.在阅读到 string 在内存的布局时,我看到 RuntimeHelper ...