TheAlgorithms/C++ 1.0.0
All the algorithms implemented in C++
Loading...
Searching...
No Matches
lru_cache2.cpp
Go to the documentation of this file.
1
19#include <cassert> // for assert
20#include <cstdint> // for std::uint32_t
21#include <iostream> // for std::cout
22#include <unordered_map> // for std::unordered_map
23
28namespace others {
29
34namespace Cache {
35
41template <typename T>
42class D_Node {
43 public:
44 T data;
47
48 explicit D_Node(T data) : data(data), prev(nullptr), next(nullptr) {}
49};
50
51template <typename K, typename V>
53
60template <typename K, typename V>
61class LRUCache {
64 std::uint32_t _capacity;
65
66 std::unordered_map<K, CacheNode<K, V> *>
68
69 public:
75 explicit LRUCache(int _capacity)
76 : head(nullptr), tail(nullptr), _capacity(_capacity) {}
77
78 private:
83 void push_front(CacheNode<K, V> *node_ptr) {
84 if (!head) {
85 head = node_ptr;
86 tail = node_ptr;
87 return;
88 }
89
90 node_ptr->next = head;
91 head->prev = node_ptr;
92 head = node_ptr;
93 }
94
99 void make_recent(CacheNode<K, V> *node_ptr) {
100 if (head == node_ptr) {
101 return;
102 }
103
104 CacheNode<K, V> *prev = node_ptr->prev;
105 CacheNode<K, V> *next = node_ptr->next;
106
107 prev->next = next;
108 if (next) {
109 next->prev = prev;
110 } else {
111 tail = prev;
112 }
113
114 node_ptr->prev = nullptr;
115 node_ptr->next = nullptr;
116 push_front(node_ptr);
117 }
118
122 void pop_back() {
123 if (!head) {
124 return;
125 }
126 if (head == tail) {
127 delete head;
128 head = nullptr;
129 tail = nullptr;
130 return;
131 }
132
133 CacheNode<K, V> *temp = tail;
134 tail = tail->prev;
135 tail->next = nullptr;
136 delete temp;
137 }
138
139 public:
145 void put(K key, V value) {
146 // update the value if key already exists
147 if (node_map.count(key)) {
148 node_map[key]->data.second = value;
149 make_recent(node_map[key]);
150 return;
151 }
152
153 // if the cache is full
154 // remove the least recently used item
155 if (node_map.size() == _capacity) {
156 node_map.erase(tail->data.first);
157 pop_back();
158 }
159
160 CacheNode<K, V> *newNode = new CacheNode<K, V>({key, value});
161
162 node_map[key] = newNode;
163 push_front(newNode);
164 }
165
172 V get(K key) {
173 if (!node_map.count(key)) {
174 throw std::runtime_error("key is not present in the cache");
175 }
176
177 // move node to the beginning of the list
178 V value = node_map[key]->data.second;
179 make_recent(node_map[key]);
180 return value;
181 }
182
187 int size() const { return node_map.size(); }
188
193 int capacity() const { return _capacity; }
194
199 bool empty() const { return node_map.empty(); }
200
206 auto it = node_map.begin();
207 while (it != node_map.end()) {
208 delete it->second;
209 ++it;
210 }
211 }
212};
213} // namespace Cache
214} // namespace others
215
220static void test() {
222
223 // test the initial state of the cache
224 assert(cache.size() == 0);
225 assert(cache.capacity() == 5);
226 assert(cache.empty());
227
228 // test insertion in the cache
229 cache.put(1, 10);
230 cache.put(-2, 20);
231
232 // test the state of cache after inserting some items
233 assert(cache.size() == 2);
234 assert(cache.capacity() == 5);
235 assert(!cache.empty());
236
237 // test getting items from the cache
238 assert(cache.get(1) == 10);
239 assert(cache.get(-2) == 20);
240
241 cache.put(-3, -30);
242 cache.put(4, 40);
243 cache.put(5, -50);
244 cache.put(6, 60);
245
246 // test the state after inserting more items than the capacity
247 assert(cache.size() == 5);
248 assert(cache.capacity() == 5);
249 assert(!cache.empty());
250
251 // fetching 1 throws runtime_error
252 // as 1 was evicted being the least recently used
253 // when 6 was added
254 try {
255 cache.get(1);
256 } catch (const std::runtime_error &e) {
257 assert(std::string(e.what()) == "key is not present in the cache");
258 }
259
260 // test retrieval of all items in the cache
261 assert(cache.get(-2) == 20);
262 assert(cache.get(-3) == -30);
263 assert(cache.get(4) == 40);
264 assert(cache.get(5) == -50);
265 assert(cache.get(6) == 60);
266
267 std::cout << "test - passed\n";
268}
269
274int main() {
275 test(); // run the self test implementation
276 return 0;
277}
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
CacheNode< K, V > * head
head of the doubly linked list
int size() const
Returns the number of items present in the cache.
void push_front(CacheNode< K, V > *node_ptr)
push the node to the front of the linked list.
CacheNode< K, V > * tail
tail of the doubly linked list
void put(K key, V value)
upsert a key-value pair
~LRUCache()
destructs the cache, iterates on the map and deletes every node present in the cache.
LRUCache(int _capacity)
Constructor, Initialize the head and tail pointers to nullptr and initialize the _capacity of the cac...
std::unordered_map< K, CacheNode< K, V > * > node_map
maps the key to the node address
void pop_back()
pop the last node in the linked list.
bool empty() const
returns whether the cache is empty or not
V get(K key)
get the value of the key-value pair if exists
void make_recent(CacheNode< K, V > *node_ptr)
move the existing node in the list to the beginning of the list.
std::uint32_t _capacity
maximum capacity of the cache
int capacity() const
Returns the total capacity of the cache.
static void test()
self test implementations
int main()
main function
for vector