LFU Cache
Problem
Design and implement a data structure for a Least Frequently Used (LFU) cache.
Implement the LFUCache
class:
LFUCache(int capacity)
Initializes the object with thecapacity
of the data structure.int get(int key)
Gets the value of thekey
if thekey
exists in the cache. Otherwise, returns-1
.void put(int key, int value)
Update the value of thekey
if present, or inserts thekey
if not already present. When the cache reaches itscapacity
, it should invalidate and remove the least frequently used key before inserting a new item. For this problem, when there is a tie (i.e., two or more keys with the same frequency), the least recently usedkey
would be invalidated.
To determine the least frequently used key, a use counter is maintained for each key in the cache. The key with the smallest use counter is the least frequently used key.
When a key is first inserted into the cache, its use counter is set to 1
(due to the put
operation). The use counter for a key in the cache is incremented either a get
or put
operation is called on it.
The functions get
and put
must each run in O(1)
average time complexity.
Solution Approach
Expected Time complexity:
Click - to see solution code
- C++
struct Node {
int key, value, freq;
Node *next;
Node *prev;
Node(int k, int v, int f) {
key = k, value = v, freq = f;
next = prev = NULL;
}
};
struct dlist {
Node *head, *tail;
int size;
dlist() {
head = new Node(-1, -1, 0);
tail = new Node(-1, -1, 0);
head->next = tail;
tail->prev = head;
size = 0;
}
Node *addNode(int k, int v, int f) {
size++;
Node *newNode = new Node(k, v, f);
newNode->next = head->next;
head->next->prev = newNode;
newNode->prev = head;
head->next = newNode;
return newNode;
}
void delNode(Node *&node) {
size--;
node->prev->next = node->next;
node->next->prev = node->prev;
}
int empty() { return size == 0; }
};
class LFUCache {
int cap, cnt, mf;
map<int, Node *> mp;
map<int, dlist *> fmap;
public:
LFUCache(int capacity) {
cap = capacity;
cnt = 0;
mf = 0;
}
int get(int key) {
if (mp.find(key) == mp.end()) return -1;
Node *ptr = mp[key];
fmap[ptr->freq]->delNode(ptr);
cnt--;
if (fmap[ptr->freq]->empty()) {
if (mf == ptr->freq) mf = ptr->freq + 1;
fmap.erase(ptr->freq);
}
if (fmap.find(ptr->freq + 1) != fmap.end()) {
Node *ptrr =
fmap[ptr->freq + 1]->addNode(key, ptr->value, ptr->freq + 1);
mp[key] = ptrr;
cnt++;
} else {
cnt++;
dlist *ptrr = new dlist();
fmap[ptr->freq + 1] = ptrr;
Node *node =
fmap[ptr->freq + 1]->addNode(key, ptr->value, ptr->freq + 1);
mp[key] = node;
}
return ptr->value;
}
void put(int key, int value) {
if (cap == 0) return;
if (mp.find(key) != mp.end()) {
Node *ptr = mp[key];
fmap[ptr->freq]->delNode(ptr);
if (fmap[ptr->freq]->empty()) {
if (mf == ptr->freq) mf = ptr->freq + 1;
fmap.erase(ptr->freq);
}
if (fmap.find(ptr->freq + 1) != fmap.end()) {
Node *node =
fmap[ptr->freq + 1]->addNode(key, value, ptr->freq + 1);
mp[key] = node;
} else {
dlist *ptrr = new dlist();
fmap[ptr->freq + 1] = ptrr;
Node *node =
fmap[ptr->freq + 1]->addNode(key, value, ptr->freq + 1);
mp[key] = node;
}
return;
}
if (cnt == cap) {
Node *ptr = fmap[mf]->tail->prev;
fmap[mf]->delNode(ptr);
cnt--;
if (fmap[mf]->empty()) {
fmap.erase(mf);
}
mp.erase(ptr->key);
}
mf = 0;
if (fmap.find(mf) != fmap.end()) {
Node *ptr = fmap[mf]->addNode(key, value, mf);
mp[key] = ptr;
cnt++;
return;
}
cnt++;
dlist *ptr = new dlist();
fmap[mf] = ptr;
Node *node = fmap[mf]->addNode(key, value, mf);
mp[key] = node;
}
};