|
4 | 4 | import java.util.LinkedHashSet;
|
5 | 5 |
|
6 | 6 | /**460. LFU Cache
|
| 7 | +
|
7 | 8 | * Design and implement a data structure for Least Frequently Used (LFU) cache.
|
8 | 9 | * It should support the following operations: get and put.
|
9 | 10 |
|
@@ -34,84 +35,95 @@ Could you do both operations in O(1) time complexity?
|
34 | 35 | */
|
35 | 36 |
|
36 | 37 | public class _460 {
|
37 |
| - /** |
38 |
| - * Wikipedia: The simplest method to employ an LFU algorithm is to assign a counter to every |
39 |
| - * block that is loaded into the cache. Each time a reference is made to that block the counter |
40 |
| - * is increased by one. When the cache reaches capacity and has a new block waiting to be |
41 |
| - * inserted the system will search for the block with the lowest counter and remove it from the |
42 |
| - * cache. |
43 |
| - * |
44 |
| - * Policy to handle frequency ties: based on timestamp, the entries that get set into cache earlier will be evicted first. |
45 |
| - */ |
46 |
| - |
47 |
| - public static class LFUCache { |
48 |
| - /**credit: https://discuss.leetcode.com/topic/69737/java-o-1-very-easy-solution-using-3-hashmaps-and-linkedhashset/2*/ |
49 |
| - |
50 |
| - HashMap<Integer, Integer> keyToValue;/**key is the key, value is the value*/ |
51 |
| - HashMap<Integer, Integer> keyToCount;/**key is the key, value if the count of the key/value pair*/ |
52 |
| - HashMap<Integer, LinkedHashSet<Integer>> countToLRUKeys; |
53 |
| - /**key is count, value is a set of keys that have the same key, but keeps insertion order*/ |
54 |
| - int cap; |
55 |
| - int minimumCount; |
56 |
| - |
57 |
| - public LFUCache(int capacity) { |
58 |
| - this.minimumCount = -1; |
59 |
| - this.cap = capacity; |
60 |
| - this.keyToValue = new HashMap<>(); |
61 |
| - this.keyToCount = new HashMap<>(); |
62 |
| - this.countToLRUKeys = new HashMap<>(); |
63 |
| - this.countToLRUKeys.put(1, new LinkedHashSet<>()); |
64 |
| - } |
65 |
| - |
66 |
| - public int get(int key) { |
67 |
| - if (!keyToValue.containsKey(key)) { |
68 |
| - return -1; |
69 |
| - } |
70 |
| - int count = keyToCount.get(key); |
71 |
| - keyToCount.put(key, count + 1); |
72 |
| - countToLRUKeys.get(count).remove(key); |
73 |
| - |
74 |
| - if (count == minimumCount && countToLRUKeys.get(count).size() == 0) { |
75 |
| - /**This means this key's count equals to current minimumCount |
76 |
| - * AND |
77 |
| - * this count doesn't have any entries in the cache. |
78 |
| - * So, we'll increment minimumCount by 1 to get the next LFU cache entry |
79 |
| - * when we need to evict.*/ |
80 |
| - minimumCount++; |
81 |
| - } |
82 |
| - |
83 |
| - if (!countToLRUKeys.containsKey(count + 1)) { |
84 |
| - countToLRUKeys.put(count + 1, new LinkedHashSet<>()); |
| 38 | + public static class Solution1 { |
| 39 | + /** |
| 40 | + * Wikipedia: The simplest method to employ an LFU algorithm is to assign a counter to every |
| 41 | + * block that is loaded into the cache. Each time a reference is made to that block the counter |
| 42 | + * is increased by one. When the cache reaches capacity and has a new block waiting to be |
| 43 | + * inserted the system will search for the block with the lowest counter and remove it from the |
| 44 | + * cache. |
| 45 | + * Policy to handle frequency ties: based on timestamp, the entries that get set into cache earlier will be evicted first. |
| 46 | + */ |
| 47 | + |
| 48 | + public static class LFUCache { |
| 49 | + /** |
| 50 | + * credit: https://discuss.leetcode.com/topic/69737/java-o-1-very-easy-solution-using-3-hashmaps-and-linkedhashset/2 |
| 51 | + */ |
| 52 | + |
| 53 | + HashMap<Integer, Integer> keyToValue; |
| 54 | + /** |
| 55 | + * key is the key, value is the value |
| 56 | + */ |
| 57 | + HashMap<Integer, Integer> keyToCount; |
| 58 | + /** |
| 59 | + * key is the key, value if the count of the key/value pair |
| 60 | + */ |
| 61 | + HashMap<Integer, LinkedHashSet<Integer>> countToLRUKeys; |
| 62 | + /** |
| 63 | + * key is count, value is a set of keys that have the same key, but keeps insertion order |
| 64 | + */ |
| 65 | + int cap; |
| 66 | + int minimumCount; |
| 67 | + |
| 68 | + public LFUCache(int capacity) { |
| 69 | + this.minimumCount = -1; |
| 70 | + this.cap = capacity; |
| 71 | + this.keyToValue = new HashMap<>(); |
| 72 | + this.keyToCount = new HashMap<>(); |
| 73 | + this.countToLRUKeys = new HashMap<>(); |
| 74 | + this.countToLRUKeys.put(1, new LinkedHashSet<>()); |
85 | 75 | }
|
86 |
| - countToLRUKeys.get(count + 1).add(key); |
87 |
| - |
88 |
| - return keyToValue.get(key); |
89 |
| - } |
90 | 76 |
|
91 |
| - public void put(int key, int value) { |
92 |
| - if (cap <= 0) { |
93 |
| - return; |
| 77 | + public int get(int key) { |
| 78 | + if (!keyToValue.containsKey(key)) { |
| 79 | + return -1; |
| 80 | + } |
| 81 | + int count = keyToCount.get(key); |
| 82 | + keyToCount.put(key, count + 1); |
| 83 | + countToLRUKeys.get(count).remove(key); |
| 84 | + |
| 85 | + if (count == minimumCount && countToLRUKeys.get(count).size() == 0) { |
| 86 | + /**This means this key's count equals to current minimumCount |
| 87 | + * AND |
| 88 | + * this count doesn't have any entries in the cache. |
| 89 | + * So, we'll increment minimumCount by 1 to get the next LFU cache entry |
| 90 | + * when we need to evict.*/ |
| 91 | + minimumCount++; |
| 92 | + } |
| 93 | + |
| 94 | + if (!countToLRUKeys.containsKey(count + 1)) { |
| 95 | + countToLRUKeys.put(count + 1, new LinkedHashSet<>()); |
| 96 | + } |
| 97 | + countToLRUKeys.get(count + 1).add(key); |
| 98 | + |
| 99 | + return keyToValue.get(key); |
94 | 100 | }
|
95 | 101 |
|
96 |
| - if (keyToValue.containsKey(key)) { |
97 |
| - /**If the key is already in the cache, we can simply overwrite this entry; |
98 |
| - * then call get(key) which will do the update work.*/ |
| 102 | + public void put(int key, int value) { |
| 103 | + if (cap <= 0) { |
| 104 | + return; |
| 105 | + } |
| 106 | + |
| 107 | + if (keyToValue.containsKey(key)) { |
| 108 | + /**If the key is already in the cache, we can simply overwrite this entry; |
| 109 | + * then call get(key) which will do the update work.*/ |
| 110 | + keyToValue.put(key, value); |
| 111 | + get(key); |
| 112 | + return; |
| 113 | + } |
| 114 | + |
| 115 | + /**If the key is not in the cache, we'll check the size first, evict the LFU entry first, |
| 116 | + * then insert this one into cache.*/ |
| 117 | + if (keyToValue.size() >= cap) { |
| 118 | + int evit = countToLRUKeys.get(minimumCount).iterator().next(); |
| 119 | + countToLRUKeys.get(minimumCount).remove(evit); |
| 120 | + keyToValue.remove(evit); |
| 121 | + } |
99 | 122 | keyToValue.put(key, value);
|
100 |
| - get(key); |
101 |
| - return; |
102 |
| - } |
103 |
| - |
104 |
| - /**If the key is not in the cache, we'll check the size first, evict the LFU entry first, |
105 |
| - * then insert this one into cache.*/ |
106 |
| - if (keyToValue.size() >= cap) { |
107 |
| - int evit = countToLRUKeys.get(minimumCount).iterator().next(); |
108 |
| - countToLRUKeys.get(minimumCount).remove(evit); |
109 |
| - keyToValue.remove(evit); |
| 123 | + keyToCount.put(key, 1); |
| 124 | + countToLRUKeys.get(1).add(key);/**Because we put this key/value into cache for the first time, so its count is 1*/ |
| 125 | + minimumCount = 1;/**For the same above reason, minimumCount is of course 1*/ |
110 | 126 | }
|
111 |
| - keyToValue.put(key, value); |
112 |
| - keyToCount.put(key, 1); |
113 |
| - countToLRUKeys.get(1).add(key);/**Because we put this key/value into cache for the first time, so its count is 1*/ |
114 |
| - minimumCount = 1;/**For the same above reason, minimumCount is of course 1*/ |
115 | 127 | }
|
116 | 128 | }
|
117 | 129 |
|
|
0 commit comments