From 0d76e2d70876a74688e2f2df8b506b23b3125e2f Mon Sep 17 00:00:00 2001 From: mervereis Date: Tue, 13 Oct 2026 21:19:10 +0100 Subject: [PATCH 1/2] Implement LRU Cache with Node class and basic functionality --- Sprint-2/implement_lru_cache/lru_cache.py | 61 +++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/Sprint-2/implement_lru_cache/lru_cache.py b/Sprint-2/implement_lru_cache/lru_cache.py index e69de29b..43ea187e 100644 --- a/Sprint-2/implement_lru_cache/lru_cache.py +++ b/Sprint-2/implement_lru_cache/lru_cache.py @@ -0,0 +1,61 @@ + +class Node: + def __init__(self, key, value): + self.key = key + self.value = value + self.prev = None + self.next = None + + +class LruCache: + def __init__(self, limit): + if limit <= 0: + raise ValueError("Limit must be greater than 0") + + self.limit = limit + self.cache = {} + + self.head = Node(None, None) + self.tail = Node(None, None) + + self.head.next = self.tail + self.tail.prev = self.head + + def _remove(self, node): + node.prev.next = node.next + node.next.prev = node.prev + + def _add_to_head(self, node): + node.next = self.head.next + node.prev = self.head + + self.head.next.prev = node + self.head.next = node + + def get(self, key): + if key not in self.cache: + return None + + node = self.cache[key] + + self._remove(node) + self._add_to_head(node) + + return node.value + + def set(self, key, value): + if key in self.cache: + node = self.cache[key] + node.value = value + + self._remove(node) + self._add_to_head(node) + else: + node = Node(key, value) + self.cache[key] = node + self._add_to_head(node) + + if len(self.cache) > self.limit: + lru_node = self.tail.prev + self._remove(lru_node) + del self.cache[lru_node.key] From e7e784fb5f85f28e0a63066dbe97f68865531992 Mon Sep 17 00:00:00 2001 From: mervereis Date: Sat, 17 Oct 2026 22:11:42 +0100 Subject: [PATCH 2/2] Implement LinkedList class and update LruCache to use it for managing entries --- Sprint-2/implement_lru_cache/linked_list.py | 45 +++++++++++++ Sprint-2/implement_lru_cache/lru_cache.py | 64 +++++++------------ .../implement_lru_cache/lru_cache_test.py | 12 ++++ 3 files changed, 79 insertions(+), 42 deletions(-) create mode 100644 Sprint-2/implement_lru_cache/linked_list.py diff --git a/Sprint-2/implement_lru_cache/linked_list.py b/Sprint-2/implement_lru_cache/linked_list.py new file mode 100644 index 00000000..e25d0cf9 --- /dev/null +++ b/Sprint-2/implement_lru_cache/linked_list.py @@ -0,0 +1,45 @@ +class Node: + def __init__(self, value): + self.value = value + self.previous = None + self.next = None + + +class LinkedList: + def __init__(self): + self.head = None + self.tail = None + + def push_head(self, value): + node = Node(value) + node.next = self.head + + if self.head is None: + self.tail = node + else: + self.head.previous = node + + self.head = node + return node + + def pop_tail(self): + if self.tail is None: + return None + + node = self.tail + self.remove(node) + return node.value + + def remove(self, node): + if node.previous is None: + self.head = node.next + else: + node.previous.next = node.next + + if node.next is None: + self.tail = node.previous + else: + node.next.previous = node.previous + + node.previous = None + node.next = None diff --git a/Sprint-2/implement_lru_cache/lru_cache.py b/Sprint-2/implement_lru_cache/lru_cache.py index 43ea187e..089f55b4 100644 --- a/Sprint-2/implement_lru_cache/lru_cache.py +++ b/Sprint-2/implement_lru_cache/lru_cache.py @@ -1,10 +1,10 @@ +from linked_list import LinkedList -class Node: + +class _CacheEntry: def __init__(self, key, value): self.key = key self.value = value - self.prev = None - self.next = None class LruCache: @@ -14,48 +14,28 @@ def __init__(self, limit): self.limit = limit self.cache = {} - - self.head = Node(None, None) - self.tail = Node(None, None) - - self.head.next = self.tail - self.tail.prev = self.head - - def _remove(self, node): - node.prev.next = node.next - node.next.prev = node.prev - - def _add_to_head(self, node): - node.next = self.head.next - node.prev = self.head - - self.head.next.prev = node - self.head.next = node + self.order = LinkedList() def get(self, key): - if key not in self.cache: + node = self.cache.get(key) + if node is None: return None - node = self.cache[key] - - self._remove(node) - self._add_to_head(node) - - return node.value + self.order.remove(node) + self.cache[key] = self.order.push_head(node.value) + return node.value.value def set(self, key, value): - if key in self.cache: - node = self.cache[key] - node.value = value - - self._remove(node) - self._add_to_head(node) - else: - node = Node(key, value) - self.cache[key] = node - self._add_to_head(node) - - if len(self.cache) > self.limit: - lru_node = self.tail.prev - self._remove(lru_node) - del self.cache[lru_node.key] + node = self.cache.get(key) + if node is not None: + node.value.value = value + self.order.remove(node) + self.cache[key] = self.order.push_head(node.value) + return + + entry = _CacheEntry(key, value) + self.cache[key] = self.order.push_head(entry) + + if len(self.cache) > self.limit: + least_recently_used = self.order.pop_tail() + del self.cache[least_recently_used.key] diff --git a/Sprint-2/implement_lru_cache/lru_cache_test.py b/Sprint-2/implement_lru_cache/lru_cache_test.py index d37df018..be1f6517 100644 --- a/Sprint-2/implement_lru_cache/lru_cache_test.py +++ b/Sprint-2/implement_lru_cache/lru_cache_test.py @@ -54,6 +54,18 @@ def test_eviction_order_after_gets(self): self.assertEqual(cache.get("a"), 1) self.assertEqual(cache.get("c"), 3) + def test_setting_existing_key_updates_value_and_recency(self): + cache = LruCache(limit=2) + + cache.set("a", 1) + cache.set("b", 2) + cache.set("a", 3) + cache.set("c", 4) + + self.assertIsNone(cache.get("b")) + self.assertEqual(cache.get("a"), 3) + self.assertEqual(cache.get("c"), 4) + if __name__ == "__main__": unittest.main()