Skip to content

Commit e316c46

Browse files
committed
feat: Add C++ implementation of LRU Cache
1 parent b97919a commit e316c46

File tree

1 file changed

+111
-0
lines changed

1 file changed

+111
-0
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/**
2+
* @file lru_cache.cpp
3+
* @brief Implementation of the Least Recently Used (LRU) Cache.
4+
*
5+
* Description:
6+
* An LRU Cache is a fixed-size cache that evicts the least recently used item
7+
* when it reaches its capacity. This implementation uses a combination of a
8+
* hash map (std::unordered_map) and a doubly-linked list (std::list) to
9+
* achieve O(1) average time complexity for both `get` and `put` operations.
10+
*
11+
* - The `list` stores pairs of key-value to maintain the order of usage.
12+
* The most recently used items are moved to the front of the list.
13+
* - The `std::unordered_map` stores the key and an iterator to its corresponding
14+
* node in the list, allowing for O(1) access to any item.
15+
*
16+
* Complexity Analysis:
17+
* - Time Complexity: O(1) for both `get` and `put` operations.
18+
* - `get`: Hash map lookup is O(1) on average. Moving the element to the front of the list is also O(1).
19+
* - `put`: Hash map lookup and insertion are O(1) on average. List operations (push_front, pop_back, erase) are O(1).
20+
* - Space Complexity: O(capacity), as the cache stores up to 'capacity' elements.
21+
*/
22+
#include <bits/stdc++.h>
23+
using namespace std;
24+
25+
class LRUCache {
26+
private:
27+
int capacity;
28+
// Doubly-linked list to store {key, value} pairs.
29+
// The front of the list is the most recently used.
30+
list<pair<int, int>> items_list;
31+
// Hash map to store key -> list iterator for O(1) lookup.
32+
unordered_map<int, list<pair<int, int>>::iterator> items_map;
33+
34+
public:
35+
// Constructor to initialize the cache with a given capacity.
36+
// Capacity must be a positive integer.
37+
LRUCache(int cap) : capacity(cap) {}
38+
39+
int get(int key) {
40+
// Check if the key exists in the map.
41+
if (items_map.find(key) == items_map.end()) {
42+
return -1; // Key not found
43+
}
44+
45+
// Key found, get the iterator from the map.
46+
auto it = items_map[key];
47+
// The value is the second element of the pair in the list.
48+
int value = it->second;
49+
50+
// Move the accessed item to the front of the list to mark it as most recently used.
51+
// splice() efficiently moves the node without creating/destroying elements.
52+
items_list.splice(items_list.begin(), items_list, it);
53+
54+
return value;
55+
}
56+
57+
// put key-value pair into the cache
58+
// If the key already exists, update its value and move it to the front.
59+
// If the cache is at capacity, evict the least recently used item.
60+
void put(int key, int value) {
61+
// If the key already exists, update its value and move it to the front.
62+
if (items_map.find(key) != items_map.end()) {
63+
auto it = items_map[key];
64+
it->second = value; // Update value
65+
items_list.splice(items_list.begin(), items_list, it); // Move to front
66+
return;
67+
}
68+
69+
// If the cache is at full capacity, evict the least recently used item.
70+
if (items_map.size() == capacity) {
71+
// The least recently used item is at the back of the list.
72+
pair<int, int> last_item = items_list.back();
73+
items_map.erase(last_item.first); // Remove from map
74+
items_list.pop_back(); // Remove from list
75+
}
76+
77+
// Insert the new key-value pair at the front of the list.
78+
items_list.push_front({key, value});
79+
// Store the iterator to the new item in the map.
80+
items_map[key] = items_list.begin();
81+
}
82+
};
83+
84+
// Example usage and test cases
85+
int main() {
86+
// Create a cache with capacity 2
87+
LRUCache cache(2);
88+
89+
cout << "Putting (1, 10)" << endl;
90+
cache.put(1, 10);
91+
cout << "Putting (2, 20)" << endl;
92+
cache.put(2, 20);
93+
94+
// Test basic get
95+
cout << "Getting key 1: " << cache.get(1) << endl; // returns 10
96+
97+
// Test eviction: putting 3 should evict key 2 (least recently used)
98+
cout << "Putting (3, 30)" << endl;
99+
cache.put(3, 30);
100+
cout << "Getting key 2: " << cache.get(2) << endl; // returns -1 (not found)
101+
102+
// Test that key 1 is still present and now is the LRU
103+
cout << "Getting key 1: " << cache.get(1) << endl; // returns 10
104+
105+
// Test updating an existing key
106+
cout << "Putting (1, 100) to update" << endl;
107+
cache.put(1, 100);
108+
cout << "Getting key 1: " << cache.get(1) << endl; // returns 100
109+
110+
return 0;
111+
}

0 commit comments

Comments
 (0)