Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit ffef544

Browse files
corvo-007realstealthninja
andauthored
feat: LRU (Least recently used) cache - different implementation (TheAlgorithms#2783)
* feat: add lru cache * test: add test to cover exception * test: add assert for exception message * review: change int to uint32_t * review: add header for std::uint32_t --------- Co-authored-by: realstealthninja <68815218+realstealthninja@users.noreply.github.com>
1 parent 37a9811 commit ffef544

File tree

1 file changed

+277
-0
lines changed

1 file changed

+277
-0
lines changed

‎others/lru_cache2.cpp‎

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
/**
2+
* @file
3+
* @brief Implementation for [LRU Cache]
4+
* (https://en.wikipedia.org/wiki/Cache_replacement_policies#:~:text=Least%20Recently%20Used%20(LRU))
5+
*
6+
* @details
7+
* LRU discards the least recently used value.
8+
* Data structures used - doubly linked list and unordered_map
9+
*
10+
* unordered_map maps the key to the address of the node of the linked list.
11+
* If the element is accessed, the element is moved to the beginning of the
12+
* linked list.
13+
*
14+
* When the cache is full, the last element in the linked list is popped.
15+
*
16+
* @author [Karan Sharma](https://github.com/deDSeC00720)
17+
*/
18+
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+
24+
/**
25+
* @namespace
26+
* @brief Other algorithms
27+
*/
28+
namespace others {
29+
30+
/**
31+
* @namespace
32+
* @brief Cache algorithm
33+
*/
34+
namespace Cache {
35+
36+
/**
37+
* @class
38+
* @brief Node for a doubly linked list with data, prev and next pointers
39+
* @tparam T type of the data of the node
40+
*/
41+
template <typename T>
42+
class D_Node {
43+
public:
44+
T data; ///< data of the node
45+
D_Node<T> *prev; ///< previous node in the doubly linked list
46+
D_Node<T> *next; ///< next node in the doubly linked list
47+
48+
explicit D_Node(T data) : data(data), prev(nullptr), next(nullptr) {}
49+
};
50+
51+
template <typename K, typename V>
52+
using CacheNode = D_Node<std::pair<K, V>>;
53+
54+
/**
55+
* @class
56+
* @brief LRUCache
57+
* @tparam K type of key in the LRU
58+
* @tparam V type of value in the LRU
59+
*/
60+
template <typename K, typename V>
61+
class LRUCache {
62+
CacheNode<K, V> *head; ///< head of the doubly linked list
63+
CacheNode<K, V> *tail; ///< tail of the doubly linked list
64+
std::uint32_t _capacity; ///< maximum capacity of the cache
65+
66+
std::unordered_map<K, CacheNode<K, V> *>
67+
node_map; ///< maps the key to the node address
68+
69+
public:
70+
/**
71+
* @brief Constructor, Initialize the head and tail pointers to nullptr and
72+
* initialize the _capacity of the cache
73+
* @param _capacity Total capacity of the cache
74+
*/
75+
explicit LRUCache(int _capacity)
76+
: head(nullptr), tail(nullptr), _capacity(_capacity) {}
77+
78+
private:
79+
/**
80+
* @brief push the node to the front of the linked list.
81+
* @param node_ptr the node to be pushed
82+
*/
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+
95+
/**
96+
* @brief move the existing node in the list to the beginning of the list.
97+
* @param node_ptr node to be moved to the beginning.
98+
*/
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+
119+
/**
120+
* @brief pop the last node in the linked list.
121+
*/
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:
140+
/**
141+
* @brief upsert a key-value pair
142+
* @param key key of the key-value pair
143+
* @param value value of the key-value pair
144+
*/
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+
166+
/**
167+
* @brief get the value of the key-value pair if exists
168+
* @param key key of the key-value pair
169+
* @return the value mapped to the given key
170+
* @exception exception is thrown if the key is not present in the cache
171+
*/
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+
183+
/**
184+
* @brief Returns the number of items present in the cache.
185+
* @return number of items in the cache
186+
*/
187+
int size() const { return node_map.size(); }
188+
189+
/**
190+
* @brief Returns the total capacity of the cache
191+
* @return Total capacity of the cache
192+
*/
193+
int capacity() const { return _capacity; }
194+
195+
/**
196+
* @brief returns whether the cache is empty or not
197+
* @return true if the cache is empty, false otherwise.
198+
*/
199+
bool empty() const { return node_map.empty(); }
200+
201+
/**
202+
* @brief destructs the cache, iterates on the map and deletes every node
203+
* present in the cache.
204+
*/
205+
~LRUCache() {
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+
216+
/**
217+
* @brief self test implementations
218+
* @return void
219+
*/
220+
static void test() {
221+
others::Cache::LRUCache<int, int> cache(5);
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+
270+
/**
271+
* @brief main function
272+
* @return 0 on exit
273+
*/
274+
int main() {
275+
test(); // run the self test implementation
276+
return 0;
277+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /