From b51ee115c39357f4d50cfcfd6b1a107ddac06f1e Mon Sep 17 00:00:00 2001 From: Fithi Teklom Date: Sat, 17 Oct 2026 12:26:29 +0100 Subject: [PATCH 1/2] Implement LRU cache with eviction --- Sprint-2/implement_lru_cache/lru_cache.py | 68 +++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/Sprint-2/implement_lru_cache/lru_cache.py b/Sprint-2/implement_lru_cache/lru_cache.py index e69de29b..90d6890e 100644 --- a/Sprint-2/implement_lru_cache/lru_cache.py +++ b/Sprint-2/implement_lru_cache/lru_cache.py @@ -0,0 +1,68 @@ + +class Node: + __slots__ = ("key", "value", "previous", "next") + + def __init__(self, key, value): + self.key = key + self.value = value + self.previous = None + self.next = None + +class LruCache: + def __init__(self, limit: int) -> None: + if limit <= 0: + raise ValueError("Limit must be greater than zero") + + self.limit: int = limit + self.cache: dict = {} + + self.head = Node(None, None) + self.tail = Node(None, None) + + self.head.next = self.tail + self.tail.previous = self.head + + def _add_before_tail(self, node: Node) -> None: + previous_node = self.tail.previous + + node.previous = previous_node + node.next = self.tail + + previous_node.next = node + self.tail.previous = node + + def _remove_node(self, node: Node) -> None: + node.previous.next = node.next + node.next.previous = node.previous + + node.previous = None + node.next = None + + def _mark_recently_used(self, node: Node) -> None: + self._remove_node(node) + self._add_before_tail(node) + + def get(self, key): + if key not in self.cache: + return None + + node = self.cache[key] + self._mark_recently_used(node) + + return node.value + + def set(self, key, value) -> None: + if key in self.cache: + node = self.cache[key] + node.value = value + self._mark_recently_used(node) + return + + node = Node(key, value) + self.cache[key] = node + self._add_before_tail(node) + + if len(self.cache) > self.limit: + least_recent_node = self.head.next + self._remove_node(least_recent_node) + del self.cache[least_recent_node.key] From 2f6522ea9c08d9bc36571f2056c0bbc8c34dd423 Mon Sep 17 00:00:00 2001 From: Fithi Teklom Date: Mon, 19 Oct 2026 13:28:56 +0100 Subject: [PATCH 2/2] Refactor LRU cache to use a separate linked list --- Sprint-2/implement_lru_cache/linked_list.py | 71 +++++++++++++++++++++ Sprint-2/implement_lru_cache/lru_cache.py | 56 +++------------- 2 files changed, 81 insertions(+), 46 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..c52078dc --- /dev/null +++ b/Sprint-2/implement_lru_cache/linked_list.py @@ -0,0 +1,71 @@ +from __future__ import annotations + +from typing import Generic, TypeVar + +T = TypeVar("T") + + +class Node(Generic[T]): + __slots__ = ("value", "next", "previous") + + def __init__(self, value: T) -> None: + self.value: T = value + self.next: Node[T] | None = None + self.previous: Node[T] | None = None + + +class LinkedList(Generic[T]): + def __init__(self) -> None: + self.head: Node[T] | None = None + self.tail: Node[T] | None = None + + def push_head(self, value: T) -> Node[T]: + new_node = Node(value) + + if self.head is None: + self.head = new_node + self.tail = new_node + else: + new_node.next = self.head + self.head.previous = new_node + self.head = new_node + + return new_node + + def pop_tail(self) -> T | None: + if self.tail is None: + return None + + value = self.tail.value + self.remove(self.tail) + return value + + def remove(self, node: Node[T]) -> None: + if node.previous is not None: + node.previous.next = node.next + else: + self.head = node.next + + if node.next is not None: + node.next.previous = node.previous + else: + self.tail = node.previous + + node.next = None + node.previous = None + + def move_to_head(self, node: Node[T]) -> None: + if node is self.head: + return + + self.remove(node) + + node.next = self.head + node.previous = None + + if self.head is not None: + self.head.previous = node + else: + self.tail = node + + self.head = node diff --git a/Sprint-2/implement_lru_cache/lru_cache.py b/Sprint-2/implement_lru_cache/lru_cache.py index 90d6890e..3cd74495 100644 --- a/Sprint-2/implement_lru_cache/lru_cache.py +++ b/Sprint-2/implement_lru_cache/lru_cache.py @@ -1,13 +1,4 @@ - -class Node: - __slots__ = ("key", "value", "previous", "next") - - def __init__(self, key, value): - self.key = key - self.value = value - self.previous = None - self.next = None - +from linked_list import LinkedList, Node class LruCache: def __init__(self, limit: int) -> None: if limit <= 0: @@ -15,54 +6,27 @@ def __init__(self, limit: int) -> None: self.limit: int = limit self.cache: dict = {} - - self.head = Node(None, None) - self.tail = Node(None, None) - - self.head.next = self.tail - self.tail.previous = self.head - - def _add_before_tail(self, node: Node) -> None: - previous_node = self.tail.previous - - node.previous = previous_node - node.next = self.tail - - previous_node.next = node - self.tail.previous = node - - def _remove_node(self, node: Node) -> None: - node.previous.next = node.next - node.next.previous = node.previous - - node.previous = None - node.next = None - - def _mark_recently_used(self, node: Node) -> None: - self._remove_node(node) - self._add_before_tail(node) + self.order: LinkedList[tuple] = LinkedList() def get(self, key): if key not in self.cache: return None node = self.cache[key] - self._mark_recently_used(node) - - return node.value + self.order.move_to_head(node) + return node.value[1] + def set(self, key, value) -> None: if key in self.cache: node = self.cache[key] - node.value = value - self._mark_recently_used(node) + node.value = (key, value) + self.order.move_to_head(node) return - node = Node(key, value) + node = self.order.push_head((key, value)) self.cache[key] = node - self._add_before_tail(node) if len(self.cache) > self.limit: - least_recent_node = self.head.next - self._remove_node(least_recent_node) - del self.cache[least_recent_node.key] + least_recent = self.order.pop_tail() + del self.cache[least_recent[0]]