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