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