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
+ }
0 commit comments