[LeetCode] 460. LFU Cache

Posted aaronliu1991

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[LeetCode] 460. LFU Cache相关的知识,希望对你有一定的参考价值。

LFU缓存。我就直接引用LC中文网的题干了。

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

put(key, value) - 如果键不存在,请设置或插入值。当缓存达到其容量时,则应该在插入新项之前,使最不经常使用的项无效。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最近 最少使用的键。
「项的使用次数」就是自插入该项以来对其调用 get 和 put 函数的次数之和。使用次数会在对应项被移除后置为 0 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lfu-cache
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

这个题有两个函数需要实现,分别是get和put,依然是需要在O(1)时间复杂度内完成。例子,

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

这个题没有什么算法,但是通过这个题我学到了原来Java里面有一个叫做LinkedHashSet的东西。这个题既然是找LFU,最不经常使用的缓存,所以在有capacity限制的情况下,使用次数最少,频率最低的元素会被淘汰。首先我给出一个图帮助理解。

技术图片

 

 

如下分别解释一下两个函数的一些细节。

put() - 首先看是否超过capacity,若没超过,则放入元素并get元素;如果超过了capacity,需要先找到出现次数最少的那个次数所对应的LinkedHashSet,并通过iterator删除这个LinkedHashSet中的第一个元素。这个元素就是那个在capacity临界点需要被删除的元素。如果这个元素从未被放入过,则需要把这个元素分别放入vals,counts两个hashmap和LinkedHashSet,并且标记最小出现次数为1。

get() - 如果要找的元素不在缓存,则直接return -1。如果在缓存里面,则做两件事,1是counts++,更新其出现次数;2是更新这个元素在LinkedHashSet的位置,如果这个行为同时导致了min值的改变,也需要更新min值。

时间O(1) - required

空间O(n)

Java实现

 1 class LFUCache {
 2     HashMap<Integer, Integer> vals;
 3     HashMap<Integer, Integer> counts;
 4     HashMap<Integer, LinkedHashSet<Integer>> list;
 5     int capacity;
 6     int min;
 7 
 8     public LFUCache(int capacity) {
 9         this.capacity = capacity;
10         vals = new HashMap<>();
11         counts = new HashMap<>();
12         list = new HashMap<>();
13         list.put(1, new LinkedHashSet<>());
14         min = -1;
15     }
16 
17     public int get(int key) {
18         // corner case
19         if (!vals.containsKey(key)) {
20             return -1;
21         }
22         // normal case
23         int count = counts.get(key);
24         counts.put(key, count + 1);
25         list.get(count).remove(key);
26         if (count == min && list.get(count).size() == 0) {
27             min++;
28         }
29         if (!list.containsKey(count + 1)) {
30             list.put(count + 1, new LinkedHashSet<>());
31         }
32         list.get(count + 1).add(key);
33         return vals.get(key);
34     }
35 
36     public void put(int key, int value) {
37         // corner case
38         if (capacity <= 0) {
39             return;
40         }
41         // normal case
42         if (vals.containsKey(key)) {
43             vals.put(key, value);
44             get(key);
45             return;
46         }
47         // if it‘s over the capacity
48         if (vals.size() >= capacity) {
49             int evit = list.get(min).iterator().next();
50             list.get(min).remove(evit);
51             vals.remove(evit);
52         }
53         // if this is never saved
54         vals.put(key, value);
55         counts.put(key, 1);
56         min = 1;
57         list.get(1).add(key);
58     }
59 }
60 
61 /**
62  * Your LFUCache object will be instantiated and called as such:
63  * LFUCache obj = new LFUCache(capacity);
64  * int param_1 = obj.get(key);
65  * obj.put(key,value);
66  */

 

以上是关于[LeetCode] 460. LFU Cache的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 460. LFU Cache

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

460. LFU缓存

460. LFU Cache

460. LFU Cache

leetcode 460 LFU缓存