TheAlgorithms/C++ 1.0.0
All the algorithms implemented in C++
Loading...
Searching...
No Matches
lfu_cache.cpp
Go to the documentation of this file.
1
23#include <cassert> // for assert
24#include <iostream> // for std::cout
25#include <unordered_map> // for std::unordered_map
26
31namespace others {
32
37namespace Cache {
38
44template <typename T>
45class D_Node {
46 public:
47 T data;
50
51 explicit D_Node(T data) : data(data), prev(nullptr), next(nullptr) {}
52};
53
54template <typename K, typename V>
55using CacheNode = D_Node<std::pair<K, V>>;
56
63template <typename K, typename V>
64class LFUCache {
65 std::unordered_map<K, std::pair<CacheNode<K, V> *, int>>
67 std::unordered_map<int, std::pair<CacheNode<K, V> *, CacheNode<K, V> *>>
69
70 int minFreq;
72
73 public:
79
80 private:
88 void push(int freq, CacheNode<K, V> *node) {
89 // if freq is not present, then make a new list with node as the head as
90 // well as tail.
91 if (!freq_map.count(freq)) {
92 freq_map[freq] = {node, node};
93 return;
94 }
95
96 std::pair<CacheNode<K, V> *, CacheNode<K, V> *> &p = freq_map[freq];
97
98 // insert the node at the beginning of the linked list and update the
99 // head.
100 p.first->prev = node;
101 node->next = p.first;
102 p.first = node;
103 }
104
109 void increase_frequency(std::pair<CacheNode<K, V> *, int> &p_node) {
110 CacheNode<K, V> *node = p_node.first;
111 int freq = p_node.second;
112
113 std::pair<CacheNode<K, V> *, CacheNode<K, V> *> &p = freq_map[freq];
114
115 // if the given node is the only node in the list,
116 // then erase the frequency from map
117 // and increase minFreq by 1.
118 if (p.first == node && p.second == node) {
119 freq_map.erase(freq);
120 if (minFreq == freq) {
121 minFreq = freq + 1;
122 }
123 } else {
124 // remove the given node from current freq linked list
125 CacheNode<K, V> *prev = node->prev;
126 CacheNode<K, V> *next = node->next;
127 node->prev = nullptr;
128 node->next = nullptr;
129
130 if (prev) {
131 prev->next = next;
132 } else {
133 p.first = next;
134 }
135
136 if (next) {
137 next->prev = prev;
138 } else {
139 p.second = prev;
140 }
141 }
142 push(freq + 1, node);
143 ++p_node.second;
144 }
145
149 void pop() {
150 std::pair<CacheNode<K, V> *, CacheNode<K, V> *> &p = freq_map[minFreq];
151
152 // if there is only one node
153 // remove the node and erase
154 // the frequency from freq_map
155 if (p.first == p.second) {
156 delete p.first;
157 freq_map.erase(minFreq);
158 return;
159 }
160
161 // remove the last node in the linked list
162 CacheNode<K, V> *temp = p.second;
163 p.second = temp->prev;
164 p.second->next = nullptr;
165 delete temp;
166 }
167
168 public:
174 void put(K key, V value) {
175 // update the value if key already exists
176 if (node_map.count(key)) {
177 node_map[key].first->data.second = value;
179 return;
180 }
181
182 // if the cache is full
183 // remove the least frequently used item
184 if (node_map.size() == _capacity) {
185 node_map.erase(freq_map[minFreq].second->data.first);
186 pop();
187 }
188
189 // insert the new node and set minFreq to 1
190 CacheNode<K, V> *node = new CacheNode<K, V>({key, value});
191 node_map[key] = {node, 1};
192 minFreq = 1;
193 push(1, node);
194 }
195
202 V get(K key) {
203 if (!node_map.count(key)) {
204 throw std::runtime_error("key is not present in the cache");
205 }
206
207 // increase the frequency and return the value
208 V value = node_map[key].first->data.second;
210 return value;
211 }
212
217 int size() const { return node_map.size(); }
218
223 int capacity() const { return _capacity; }
224
229 bool empty() const { return node_map.empty(); }
230
236 auto it = node_map.begin();
237 while (it != node_map.end()) {
238 delete it->second.first;
239 ++it;
240 }
241 }
242};
243} // namespace Cache
244} // namespace others
245
250static void test() {
252
253 // test the initial state of the cache
254 assert(cache.size() == 0);
255 assert(cache.capacity() == 5);
256 assert(cache.empty());
257
258 // test insertion in the cache
259 cache.put(1, 10);
260 cache.put(-2, 20);
261
262 // test the state of cache after inserting some items
263 assert(cache.size() == 2);
264 assert(cache.capacity() == 5);
265 assert(!cache.empty());
266
267 // test getting items from the cache
268 assert(cache.get(1) == 10);
269 assert(cache.get(-2) == 20);
270
271 cache.put(-3, -30);
272 cache.put(4, 40);
273 cache.put(5, -50);
274 cache.put(6, 60);
275
276 // test the state after inserting more items than the capacity
277 assert(cache.size() == 5);
278 assert(cache.capacity() == 5);
279 assert(!cache.empty());
280
281 // test retrieval of all items in the cache
282 assert(cache.get(1) == 10);
283 assert(cache.get(-2) == 20);
284
285 // fetching -3 throws runtime_error
286 // as -3 was evicted being the least frequently used
287 // when 6 was added
288 // assert(cache.get(-3) == -30);
289
290 assert(cache.get(4) == 40);
291 assert(cache.get(5) == -50);
292 assert(cache.get(6) == 60);
293
294 std::cout << "test - passed\n";
295}
296
301int main() {
302 test(); // run the self test implementation
303 return 0;
304}
struct node { int data; int height; struct node *left; struct node *right;} node
for std::queue
Definition avltree.cpp:13
Node for a doubly linked list with data, prev and next pointers.
D_Node< T > * next
next node in the doubly linked list
Definition lfu_cache.cpp:49
D_Node< T > * prev
previous node in the doubly linked list
Definition lfu_cache.cpp:48
T data
data of the node
Definition lfu_cache.cpp:47
void increase_frequency(std::pair< CacheNode< K, V > *, int > &p_node)
increase the frequency of node and push it in the respective list.
int _capacity
maximum capacity of the cache
Definition lfu_cache.cpp:71
V get(K key)
get the value of the key-value pair if exists
bool empty() const
returns true if the cache is empty, false otherwise.
int minFreq
minimum frequency in the cache
Definition lfu_cache.cpp:70
~LFUCache()
destructs the cache, iterates on the map and deletes every node present in the cache.
void pop()
pop the last node in the least frequently used linked list
std::unordered_map< int, std::pair< CacheNode< K, V > *, CacheNode< K, V > * > > freq_map
maps the frequency to doubly linked list
Definition lfu_cache.cpp:68
int capacity() const
Returns the total capacity of the cache.
std::unordered_map< K, std::pair< CacheNode< K, V > *, int > > node_map
maps the key to the node address and frequency
Definition lfu_cache.cpp:66
void push(int freq, CacheNode< K, V > *node)
push the node at first position in the linked list of given frequency
Definition lfu_cache.cpp:88
int size() const
Returns the number of items present in the cache.
void put(K key, V value)
upsert a key-value pair
LFUCache(int _capacity)
Constructor, Initialize with minFreq and _capacity.
Definition lfu_cache.cpp:78
static void test()
self test implementation
int main()
main function
for vector