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 e69de29b..3cd74495 100644 --- a/Sprint-2/implement_lru_cache/lru_cache.py +++ b/Sprint-2/implement_lru_cache/lru_cache.py @@ -0,0 +1,32 @@ +from linked_list import LinkedList, Node +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.order: LinkedList[tuple] = LinkedList() + + def get(self, key): + if key not in self.cache: + return None + + node = self.cache[key] + 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 = (key, value) + self.order.move_to_head(node) + return + + node = self.order.push_head((key, value)) + self.cache[key] = node + + if len(self.cache) > self.limit: + least_recent = self.order.pop_tail() + del self.cache[least_recent[0]]