Skip to content

Commit f69ec0e

Browse files
committed
feat: Implement LRU Cache algorithm
1 parent 788d95b commit f69ec0e

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

data_structures/lru_cache.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
"""
2+
An implementation of a Least Recently Used (LRU) Cache.
3+
4+
The LRU caching scheme is used to remove items from the cache to make
5+
room for new ones. It discards the least recently used items first.
6+
This implementation uses a hash map (dictionary) for O(1) lookups and a
7+
doubly linked list to maintain the order of usage, allowing for O(1)
8+
updates (insertion and deletion).
9+
10+
For more information on the algorithm, see:
11+
https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)
12+
"""
13+
14+
class Node:
15+
"""A node in the doubly linked list."""
16+
def __init__(self, key: int, val: int) -> None:
17+
self.key, self.val = key, val
18+
self.prev = self.next = None
19+
20+
class LRUCache:
21+
"""
22+
A Least Recently Used (LRU) Cache data structure.
23+
>>> cache = LRUCache(2)
24+
>>> cache.put(1, 1)
25+
>>> cache.put(2, 2)
26+
>>> cache.get(1)
27+
1
28+
>>> cache.put(3, 3) # evicts key 2
29+
>>> cache.get(2)
30+
-1
31+
>>> cache.put(4, 4) # evicts key 1
32+
>>> cache.get(1)
33+
-1
34+
>>> cache.get(3)
35+
3
36+
>>> cache.get(4)
37+
4
38+
"""
39+
def __init__(self, capacity: int) -> None:
40+
if capacity <= 0:
41+
raise ValueError("Capacity must be a positive integer.")
42+
self.capacity = capacity
43+
self.cache = {} # Maps key to node
44+
45+
self.head = Node(-1, -1)
46+
self.tail = Node(-1, -1)
47+
self.head.next = self.tail
48+
self.tail.prev = self.head
49+
50+
def _remove(self, node: Node) -> None:
51+
prev, nxt = node.prev, node.next
52+
prev.next = nxt
53+
nxt.prev = prev
54+
55+
def _add(self, node: Node) -> None:
56+
node.prev = self.head
57+
node.next = self.head.next
58+
self.head.next.prev = node
59+
self.head.next = node
60+
61+
def get(self, key: int) -> int:
62+
if key in self.cache:
63+
node = self.cache[key]
64+
self._remove(node)
65+
self._add(node)
66+
return node.val
67+
return -1
68+
69+
def put(self, key: int, value: int) -> None:
70+
if key in self.cache:
71+
node = self.cache[key]
72+
node.val = value
73+
self._remove(node)
74+
self._add(node)
75+
else:
76+
new_node = Node(key, value)
77+
self.cache[key] = new_node
78+
self._add(new_node)
79+
80+
if len(self.cache) > self.capacity:
81+
lru = self.tail.prev
82+
self._remove(lru)
83+
del self.cache[lru.key]
84+
85+
if __name__ == "__main__":
86+
import doctest
87+
doctest.testmod()

0 commit comments

Comments
 (0)