Skip to content

Commit a37ce7b

Browse files
committed
docs: add comments and explanation to class LFUCache
1 parent 7fb3ca5 commit a37ce7b

File tree

1 file changed

+92
-5
lines changed

1 file changed

+92
-5
lines changed

others/lfu_cache.cpp

Lines changed: 92 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
/**
2+
* @file
3+
* @brief Implementation for [LFU Cache]
4+
* (https://en.wikipedia.org/wiki/Least_frequently_used)
5+
*
6+
* LFU discards the least frequently used value. if there are multiple items
7+
* with the same minimum frequency then, the least recently used among them is
8+
* discarded. Data structures used - doubly linked list and unordered_map(hash
9+
* map).
10+
*
11+
* Hashmap maps the key to the address of the node of the linked list and its
12+
* current usage frequency. If the element is accessed the element is removed
13+
* from the linked list of the current frequency and added to the linked list of
14+
* incremented frequency.
15+
*
16+
* When the cache is full, the last element in the minimum frequency linked list
17+
* is popped.
18+
*
19+
* @author [Karan Sharma](https://github.com/deDSeC00720)
20+
*/
21+
122
#include <iostream>
223
#include <unordered_map>
324

@@ -18,41 +39,65 @@ using CacheNode = D_Node<std::pair<K, V>>;
1839

1940
template <typename K, typename V>
2041
class LFUCache {
21-
std::unordered_map<K, std::pair<CacheNode<K, V> *, int>> node_map;
42+
std::unordered_map<K, std::pair<CacheNode<K, V> *, int>>
43+
node_map; // maps the key to the node address and frequency
2244
std::unordered_map<int, std::pair<CacheNode<K, V> *, CacheNode<K, V> *>>
23-
freq_map;
45+
freq_map; // maps the frequency to doubly linked list
2446

25-
int minFreq;
26-
int _capacity;
47+
int minFreq; // minimum frequency in the cache
48+
int _capacity; // maximum capacity of the cache
2749

2850
public:
29-
LFUCache(int _capacity) : minFreq(0), _capacity(_capacity) {}
51+
/**
52+
* @brief Constructor, Initialize with minFreq and _capacity.
53+
* @param _capacity Total capacity of the cache.
54+
*/
55+
explicit LFUCache(int _capacity) : minFreq(0), _capacity(_capacity) {}
3056

3157
private:
58+
/**
59+
* @brief push the node at first position in the linked list of given
60+
* frequency
61+
* @param freq the frequency mapping to the linked list where node should be
62+
* pushed.
63+
* @param node node to be pushed to the linked list.
64+
*/
3265
void push(int freq, CacheNode<K, V> *node) {
66+
// if freq is not present, then make a new list with node as the head as
67+
// well as tail.
3368
if (!freq_map.count(key)) {
3469
freq_map[freq] = {node, node};
3570
}
3671

3772
std::pair<CacheNode<K, V> *, CacheNode<K, V> *> &p = freq_map[freq];
3873

74+
// insert the node at the beginning of the linked list and update the
75+
// head.
3976
p.first->prev = node;
4077
node->next = p.first;
4178
p.first = node;
4279
}
4380

81+
/**
82+
* @brief increase the frequency of node and push it in the respective list.
83+
* @param p_node the node to be updated
84+
*/
4485
void inc_freq(std::pair<CacheNode<K, V> *, int> &p_node) {
4586
CacheNode<K, V> *node = p_node.first;
4687
int freq = p_node.second;
4788

4889
std::pair<CacheNode<K, V> *, CacheNode<K, V> *> &p = freq_map[freq];
4990

91+
// if the given node is the only node in the list,
92+
// then erase the frequency from map
93+
// and increase minFreq by 1.
5094
if (p.first == node && p.second == node) {
5195
freq_map.erase(freq);
5296
if (minFreq == freq) {
5397
minFreq = freq + 1;
5498
}
5599
} else {
100+
// remove the given node from current freq linked list
56101
CacheNode<K, V> *prev = node->prev;
57102
CacheNode<K, V> *next = node->next;
58103
node->prev = nullptr;
@@ -74,53 +119,95 @@ class LFUCache {
74119
p_node.second++;
75120
}
76121

122+
/**
123+
* @brief pop the last node in the least frequently used linked list
124+
*/
77125
void pop() {
78126
std::pair<CacheNode<K, V> *, CacheNode<K, V> *> &p = freq_map[minFreq];
127+
128+
// if there is only one node
129+
// remove the node and erase
130+
// the frequency from freq_map
79131
if (p.first == p.second) {
80132
delete p.first;
81133
freq_map.erase(minFreq);
82134
return;
83135
}
136+
137+
// remove the last node in the linked list
84138
CacheNode<K, V> *temp = p.second;
85139
p.second = temp->prev;
86140
p.second->next = nullptr;
87141
delete temp;
88142
}
89143

90144
public:
145+
/**
146+
* @brief upsert a key-value pair
147+
* @param key key of the key-value pair
148+
* @param value value of the key-value pair
149+
*/
91150
void put(K key, V value) {
151+
// update the value if key already exists
92152
if (node_map.count(key)) {
93153
node_map[key].first->data.second = value;
94154
inc_freq(node_map[key]);
95155
return;
96156
}
97157

158+
// if the cache is full
159+
// remove the least frequently used item
98160
if (node_map.size() == _capacity) {
99161
node_map.erase(freq_map[minFreq].second->data.first);
100162
pop();
101163
}
164+
165+
// insert the new node and set minFreq to 1
102166
CacheNode<K, V> *node = new CacheNode<K, V>({key, value});
103167
node_map[key] = {node, 1};
104168
minFreq = 1;
105169
push(1, node);
106170
}
107171

172+
/**
173+
* @brief get the value of the key-value pair if exists
174+
* @param key key of the key-value pair
175+
* @return the value mapped to the given key
176+
* @exception exception is thrown if the key is not present in the cache
177+
*/
108178
V get(K key) {
109179
if (!node_map.count(key)) {
110180
throw std::exception("key is not present in the cache");
111181
}
112182

183+
// increase the frequency and return the value
113184
V value = node_map[key].first->data.second;
114185
inc_freq(node_map[key]);
115186
return value;
116187
}
117188

189+
/**
190+
* @brief Returns the number of items present in the cache.
191+
* @return number of items in the cache
192+
*/
118193
int size() { return node_map.size(); }
119194

195+
/**
196+
* @brief Returns the total capacity of the cache
197+
* @return Total capacity of the cache
198+
*/
120199
int capacity() { return _capacity; }
121200

201+
/**
202+
* @brief returns true if the cache is empty, false otherwise.
203+
* @return true if the cache is empty, false otherwise.
204+
*/
122205
bool empty() { return node_map.size() == 0; }
123206

207+
/**
208+
* @brief destructs the cache, iterates on the map and deletes every node
209+
* present in the cache.
210+
*/
124211
~LFUCache() {
125212
auto it = node_map.begin();
126213
while (it != node_map.end()) {

0 commit comments

Comments
 (0)