Skip to content

Commit 7c7e25d

Browse files
refactor 460
1 parent f92a373 commit 7c7e25d

File tree

2 files changed

+86
-74
lines changed

2 files changed

+86
-74
lines changed

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

Lines changed: 84 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.LinkedHashSet;
55

66
/**460. LFU Cache
7+
78
* Design and implement a data structure for Least Frequently Used (LFU) cache.
89
* It should support the following operations: get and put.
910
@@ -34,84 +35,95 @@ Could you do both operations in O(1) time complexity?
3435
*/
3536

3637
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<>());
8575
}
86-
countToLRUKeys.get(count + 1).add(key);
87-
88-
return keyToValue.get(key);
89-
}
9076

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);
94100
}
95101

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+
}
99122
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*/
110126
}
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*/
115127
}
116128
}
117129

src/test/java/com/fishercoder/_460Test.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77

88
public class _460Test {
99

10-
private static _460.LFUCache lfuCache;
10+
private static _460.Solution1.LFUCache lfuCache;
1111

1212
@Test
1313
public void test1() {
14-
lfuCache = new _460.LFUCache(2);
14+
lfuCache = new _460.Solution1.LFUCache(2);
1515
lfuCache.put(1, 1);
1616
lfuCache.put(2, 2);
1717
assertEquals(1, lfuCache.get(1));

0 commit comments

Comments
 (0)