Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 46e7636

Browse files
edit 460
1 parent 14ab484 commit 46e7636

File tree

3 files changed

+111
-33
lines changed

3 files changed

+111
-33
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ Your ideas/fixes/algorithms are more than welcome!
163163
|463|[Island Perimeter](https://leetcode.com/problems/island-perimeter/)|[Solution](../master/src/main/java/com/fishercoder/solutions/_463.java)| O(m*n)|O(1) | Easy|
164164
|462|[Minimum Moves to Equal Array Elements II](https://leetcode.com/problems/minimum-moves-to-equal-array-elements-ii/)|[Solution](../master/src/main/java/com/fishercoder/solutions/_462.java) | O(nlogn) |O(1) | Medium|
165165
|461|[Hamming Distance](https://leetcode.com/problems/hamming-distance/)|[Solution](../master/src/main/java/com/fishercoder/solutions/_461.java) | O(n) |O(1) | Easy|
166-
|460|[LFU Cache](https://leetcode.com/problems/lfu-cache/)|[Solution](../master/src/main/java/com/fishercoder/solutions/_460.java) | O(?) |O(?) | Hard| Design
166+
|460|[LFU Cache](https://leetcode.com/problems/lfu-cache/)|[Solution](../master/src/main/java/com/fishercoder/solutions/_460.java) | O(1) |O(n) | Hard| Design, LinkedHashMap, HashMap
167167
|459|[Repeated Substring Pattern](https://leetcode.com/problems/repeated-substring-pattern/)|[Solution](../master/src/main/java/com/fishercoder/solutions/_459.java)| O(n)|O(n) | Easy| KMP
168168
|458|[Poor Pigs](https://leetcode.com/problems/poor-pigs/)|[Solution](../master/src/main/java/com/fishercoder/solutions/_458.java) | O(1) |O(1) | Easy| Math
169169
|456|[132 Pattern](https://leetcode.com/problems/132-pattern/)|[Solution](../master/src/main/java/com/fishercoder/solutions/_456.java) | O(n) |O(n) | Medium| Stack

src/main/java/com/fishercoder/solutions/_460.java

Lines changed: 84 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,36 @@
33
import java.util.HashMap;
44
import java.util.LinkedHashSet;
55

6+
/**460. LFU Cache
7+
* Design and implement a data structure for Least Frequently Used (LFU) cache.
8+
* It should support the following operations: get and put.
9+
10+
get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
11+
put(key, value) - Set or insert the value if the key is not already present. When the cache reaches its capacity,
12+
it should invalidate the least frequently used item before inserting a new item.
13+
For the purpose of this problem, when there is a tie (i.e., two or more keys that have the same frequency),
14+
the least recently used key would be evicted.
15+
16+
Follow up:
17+
Could you do both operations in O(1) time complexity?
18+
19+
Example:
20+
21+
LFUCache cache = new LFUCache( 2 /* capacity );
22+
23+
cache.put(1, 1);
24+
cache.put(2, 2);
25+
cache.get(1); // returns 1
26+
cache.put(3, 3); // evicts key 2
27+
cache.get(2); // returns -1 (not found)
28+
cache.get(3); // returns 3.
29+
cache.put(4, 4); // evicts key 1.
30+
cache.get(1); // returns -1 (not found)
31+
cache.get(3); // returns 3
32+
cache.get(4); // returns 4
33+
34+
*/
35+
636
public class _460 {
737
/**
838
* Wikipedia: The simplest method to employ an LFU algorithm is to assign a counter to every
@@ -14,53 +44,75 @@ public class _460 {
1444
* Policy to handle frequency ties: based on timestamp, the entries that get set into cache earlier will be evicted first.
1545
*/
1646

17-
class LFUCache {
47+
public static class LFUCache {
1848
/**credit: https://discuss.leetcode.com/topic/69737/java-o-1-very-easy-solution-using-3-hashmaps-and-linkedhashset/2*/
19-
HashMap<Integer, Integer> vals;
20-
HashMap<Integer, Integer> counts;
21-
HashMap<Integer, LinkedHashSet<Integer>> lists;
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*/
2254
int cap;
23-
int min = -1;
55+
int minimumCount;
56+
2457
public LFUCache(int capacity) {
25-
cap = capacity;
26-
vals = new HashMap<>();
27-
counts = new HashMap<>();
28-
lists = new HashMap<>();
29-
lists.put(1, new LinkedHashSet<>());
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<>());
3064
}
3165

3266
public int get(int key) {
33-
if(!vals.containsKey(key))
67+
if (!keyToValue.containsKey(key)) {
3468
return -1;
35-
int count = counts.get(key);
36-
counts.put(key, count+1);
37-
lists.get(count).remove(key);
38-
if(count==min && lists.get(count).size()==0)
39-
min++;
40-
if(!lists.containsKey(count+1))
41-
lists.put(count+1, new LinkedHashSet<>());
42-
lists.get(count+1).add(key);
43-
return vals.get(key);
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<>());
85+
}
86+
countToLRUKeys.get(count + 1).add(key);
87+
88+
return keyToValue.get(key);
4489
}
4590

4691
public void put(int key, int value) {
47-
if(cap<=0)
92+
if (cap <= 0) {
4893
return;
49-
if(vals.containsKey(key)) {
50-
vals.put(key, value);
94+
}
95+
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.*/
99+
keyToValue.put(key, value);
51100
get(key);
52101
return;
53102
}
54-
if(vals.size() >= cap) {
55-
int evit = lists.get(min).iterator().next();
56-
lists.get(min).remove(evit);
57-
vals.remove(evit);
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);
58110
}
59-
vals.put(key, value);
60-
counts.put(key, 1);
61-
min = 1;
62-
lists.get(1).add(key);
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*/
63115
}
64116
}
65-
117+
66118
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.fishercoder;
2+
3+
import com.fishercoder.solutions._460;
4+
import org.junit.Test;
5+
6+
import static org.junit.Assert.assertEquals;
7+
8+
public class _460Test {
9+
10+
private static _460.LFUCache lfuCache;
11+
12+
@Test
13+
public void test1(){
14+
lfuCache = new _460.LFUCache(2);
15+
lfuCache.put(1, 1);
16+
lfuCache.put(2, 2);
17+
assertEquals(1, lfuCache.get(1));
18+
lfuCache.put(3, 3);
19+
assertEquals(-1, lfuCache.get(2));
20+
assertEquals(3, lfuCache.get(3));
21+
lfuCache.put(4, 4);
22+
assertEquals(-1, lfuCache.get(1));
23+
assertEquals(3, lfuCache.get(3));
24+
assertEquals(4, lfuCache.get(4));
25+
}
26+
}

0 commit comments

Comments
 (0)