Skip to content

Commit 9a10dad

Browse files
committed
lfu cache: 90% done
1 parent a718d3d commit 9a10dad

File tree

2 files changed

+136
-7
lines changed

2 files changed

+136
-7
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package com.leetcode.design;
2+
3+
import java.util.HashMap;
4+
import java.util.LinkedHashSet;
5+
import java.util.Map;
6+
7+
import static org.junit.jupiter.api.Assertions.assertEquals;
8+
9+
/**
10+
* Level: Hard
11+
* Link: https://leetcode.com/problems/lfu-cache/
12+
* Description:
13+
* Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the following
14+
* operations: get and put.
15+
*
16+
* get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
17+
* put(key, value) - Set or insert the value if the key is not already present. When the cache reaches its capacity, it
18+
* should invalidate the least frequently used item before inserting a new item. For the purpose of this problem, when
19+
* there is a tie (i.e., two or more keys that have the same frequency), the least recently used key would be evicted.
20+
*
21+
* Follow up:
22+
* Could you do both operations in O(1) time complexity?
23+
*
24+
* Note:
25+
* -
26+
*
27+
* @author rampatra
28+
* @since 2019-08-20
29+
*/
30+
public class LFUCache {
31+
32+
private int minCount = 0;
33+
private int capacity;
34+
private Map<Integer, Integer> keyValueMap;
35+
private Map<Integer, Integer> keyCountMap;
36+
private Map<Integer, LinkedHashSet<Integer>> countKeysMap;
37+
38+
39+
public LFUCache(int capacity) {
40+
this.capacity = capacity;
41+
keyValueMap = new HashMap<>();
42+
keyCountMap = new HashMap<>();
43+
countKeysMap = new HashMap<>();
44+
}
45+
46+
47+
public int get(int key) {
48+
Integer val = keyValueMap.get(key);
49+
if (val == null) {
50+
return -1;
51+
}
52+
53+
int prevCount = keyCountMap.get(key);
54+
countKeysMap.getOrDefault(prevCount, new LinkedHashSet<>()).remove(key);
55+
countKeysMap.putIfAbsent(prevCount + 1, new LinkedHashSet<>());
56+
countKeysMap.get(prevCount + 1).add(key);
57+
58+
if (prevCount == minCount && countKeysMap.get(prevCount).size() == 0) {
59+
minCount++;
60+
}
61+
62+
return val;
63+
}
64+
65+
66+
public void put(int key, int value) {
67+
if (capacity <= 0) {
68+
return;
69+
}
70+
71+
if (!keyValueMap.containsKey(key) && keyCountMap.size() == capacity) {
72+
int keyToEvict = countKeysMap.get(minCount).iterator().next();
73+
countKeysMap.get(minCount).remove(keyToEvict);
74+
keyValueMap.remove(keyToEvict);
75+
keyCountMap.remove(keyToEvict);
76+
}
77+
78+
keyValueMap.put(key, value);
79+
int prevCount = keyCountMap.getOrDefault(key, 0);
80+
keyCountMap.put(key, 1);
81+
82+
countKeysMap.getOrDefault(prevCount, new LinkedHashSet<>()).remove(key);
83+
countKeysMap.putIfAbsent(1, new LinkedHashSet<>());
84+
countKeysMap.get(1).add(key);
85+
86+
minCount = 1;
87+
}
88+
89+
90+
public static void main(String[] args) {
91+
LFUCache lfuCache = new LFUCache(2);
92+
lfuCache.put(2, 2);
93+
lfuCache.put(3, 3);
94+
lfuCache.put(4, 4);
95+
assertEquals(-1, lfuCache.get(2));
96+
assertEquals(3, lfuCache.get(3));
97+
lfuCache.put(5, 5);
98+
assertEquals(-1, lfuCache.get(4));
99+
100+
lfuCache = new LFUCache(2);
101+
lfuCache.put(3, 1);
102+
lfuCache.put(2, 1);
103+
lfuCache.put(2, 2);
104+
lfuCache.put(4, 4);
105+
assertEquals(-1, lfuCache.get(3));
106+
assertEquals(2, lfuCache.get(2));
107+
108+
lfuCache = new LFUCache(2);
109+
lfuCache.put(2, 1);
110+
lfuCache.put(2, 2);
111+
assertEquals(2, lfuCache.get(2));
112+
lfuCache.put(1, 1);
113+
lfuCache.put(4, 4);
114+
assertEquals(2, lfuCache.get(2));
115+
116+
lfuCache = new LFUCache(2);
117+
assertEquals(-1, lfuCache.get(2));
118+
lfuCache.put(2, 6);
119+
assertEquals(-1, lfuCache.get(1));
120+
lfuCache.put(1, 1);
121+
lfuCache.put(1, 2);
122+
assertEquals(2, lfuCache.get(1));
123+
assertEquals(6, lfuCache.get(2));
124+
125+
// todo fix some test cases https://leetcode.com/submissions/detail/253376947/
126+
}
127+
}

src/main/java/com/rampatra/linkedlists/LRUCache.java

+9-7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.util.Iterator;
44
import java.util.LinkedHashMap;
5+
import java.util.LinkedHashSet;
56
import java.util.Map;
67

78
/**
@@ -20,7 +21,7 @@
2021
*/
2122
public class LRUCache<E, V> {
2223

23-
LinkedHashMap<E, V> linkedHashMap;
24+
private LinkedHashMap<E, V> linkedHashMap;
2425

2526
// initialize cache
2627
LRUCache(final int size) {
@@ -37,7 +38,7 @@ V add(E key, V value) {
3738
}
3839

3940
V get(E key) {
40-
return linkedHashMap.get(key);
41+
return linkedHashMap.get(key);
4142
}
4243

4344
private void print() {
@@ -52,12 +53,13 @@ public static void main(String[] args) {
5253
cache.add(1, 1);
5354
cache.add(2, 2);
5455
cache.add(3, 3);
56+
cache.print(); // initial cache contents
57+
58+
cache.add(4, 4); // should remove 1 as it was accessed last
5559
cache.print();
56-
if (cache.get(4) == null) {
57-
cache.add(4, 4);
58-
}
59-
cache.print();
60-
cache.add(5, 5);
60+
61+
cache.get(2);
62+
cache.add(5, 5); // should remove 3 as 2 was recently accessed
6163
cache.print();
6264
}
6365
}

0 commit comments

Comments
 (0)