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 HashMapvals; 3 HashMap counts; 4 HashMap > 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 */