Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions Sprint-2/implement_linked_list/linked_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from dataclasses import dataclass
from typing import Any, Optional
# to reduce memory usage we use slots true to tells Python not to create a dynamic __dict__ for every node,
@dataclass(slots=True)
class Node:
value: Any
next: Optional['Node'] = None
previous: Optional['Node'] = None


class LinkedList:
def __init__(self) -> None:
self.head: Optional[Node] = None
self.tail: Optional[Node] = None

def push_head(self, value: Any) -> Node:
new_node = Node(value)

if not self.head:
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) -> Optional[Any]:
if not self.tail:
return None

value_to_return = self.tail.value
self.remove(self.tail)
return value_to_return

def remove(self, node_handle: Optional[Node]) -> None:
if not node_handle:
return

if node_handle == self.head:
self.head = node_handle.next
if self.head:
self.head.previous = None
else:
self.tail = None

elif node_handle == self.tail:
self.tail = node_handle.previous
if self.tail:
self.tail.next = None
else:
self.head = None

else:
assert node_handle.previous is not None
assert node_handle.next is not None
node_handle.previous.next = node_handle.next
node_handle.next.previous = node_handle.previous

node_handle.next = None
node_handle.previous = None
50 changes: 50 additions & 0 deletions Sprint-2/implement_lru_cache/lru_cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import sys
import os
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../implement_linked_list")))

from linked_list import LinkedList, Node # type:ignore
from typing import Any

class LruCache:
def __init__(self, limit: int):
if limit <= 0:
raise ValueError("Limit must be greater than 0")
self.capacity = limit
# maps key
self.cache: dict[Any, Node] = {}
# tracking the usage order head first tail last.
self.list = LinkedList()

def get(self, key: Any) -> Any:
# If key not exist return None
if key not in self.cache:
return None

node_handle = self.cache[key]

# we get the value to return
_, value = node_handle.value

# we use remove() and push_head() to remove it from the position to the head
self.list.remove(node_handle)
self.cache[key] = self.list.push_head((key, value))

return value

def set(self, key: Any, value: Any) -> None:
if key in self.cache:
self.list.remove(self.cache[key])
# we do this because the position and value about to change.
del self.cache[key]

# we call pop_tail() to cut off the tail
elif len(self.cache) >= self.capacity:
# return the value stored in the tail node
oldest_item = self.list.pop_tail()
if oldest_item:
oldest_key, _ = oldest_item
del self.cache[oldest_key]

## place the new items at the head of stack and update and return the object Node.
new_node = self.list.push_head((key, value))
self.cache[key] = new_node
Loading