From 4cc8ffc34c4cc0ec7d0c9bc17b0a2b33d00968af Mon Sep 17 00:00:00 2001 From: Fithi Teklom Date: Fri, 16 Oct 2026 21:52:57 +0100 Subject: [PATCH 1/2] Implement doubly linked list operations --- Sprint-2/implement_linked_list/linked_list.py | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/Sprint-2/implement_linked_list/linked_list.py b/Sprint-2/implement_linked_list/linked_list.py index e69de29b..81198e73 100644 --- a/Sprint-2/implement_linked_list/linked_list.py +++ b/Sprint-2/implement_linked_list/linked_list.py @@ -0,0 +1,75 @@ + +class Node: + def __init__(self, value): + self.value = value + self.next = None + self.previous = None + + +class LinkedList: + def __init__(self): + self.head = None + self.tail = None + + + def push_head(self, value): + # Make a new carriage + new_node = Node(value) + + # If the train is empty, this is the first carriage + if self.head is None: + self.head = new_node + self.tail = new_node + else: + # Connect the new carriage to the old first carriage + new_node.next = self.head + self.head.previous = new_node + + # The new carriage is now the first carriage + self.head = new_node + + # Give back the carriage so we can remove it later + return new_node + + + def pop_tail(self): + # If the train is empty, there is nothing to remove + if self.tail is None: + return None + + # Remember the value we're removing + value = self.tail.value + + # If there's only one carriage, the train becomes empty + if self.head == self.tail: + self.head = None + self.tail = None + else: + # Move the tail backwards by one carriage + self.tail = self.tail.previous + + # The new last carriage has no carriage after it + self.tail.next = None + + # Return the value we removed + return value + + def remove(self, node): + # Connect the previous carriage to the next carriage + if node.previous is not None: + node.previous.next = node.next + else: + # There is no previous carriage, so this was the head + self.head = node.next + + # Connect the next carriage to the previous carriage + if node.next is not None: + node.next.previous = node.previous + else: + # There is no next carriage, so this was the tail + self.tail = node.previous + + # Disconnect the removed carriage + node.next = None + node.previous = None + From 8ee3f040955e8d9b90511b75c292744494ab1655 Mon Sep 17 00:00:00 2001 From: Fithi Teklom Date: Sat, 17 Oct 2026 10:00:01 +0100 Subject: [PATCH 2/2] refactor: improve linked list implementation --- Sprint-2/implement_linked_list/linked_list.py | 61 +++++++------------ 1 file changed, 21 insertions(+), 40 deletions(-) diff --git a/Sprint-2/implement_linked_list/linked_list.py b/Sprint-2/implement_linked_list/linked_list.py index 81198e73..7aca75bc 100644 --- a/Sprint-2/implement_linked_list/linked_list.py +++ b/Sprint-2/implement_linked_list/linked_list.py @@ -1,75 +1,56 @@ -class Node: - def __init__(self, value): - self.value = value - self.next = None - self.previous = None +from __future__ import annotations +from typing import Generic, TypeVar -class LinkedList: - def __init__(self): - self.head = None - self.tail = None +T = TypeVar("T") - def push_head(self, value): - # Make a new carriage +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 the train is empty, this is the first carriage if self.head is None: self.head = new_node self.tail = new_node else: - # Connect the new carriage to the old first carriage new_node.next = self.head self.head.previous = new_node - - # The new carriage is now the first carriage self.head = new_node - # Give back the carriage so we can remove it later return new_node - - def pop_tail(self): - # If the train is empty, there is nothing to remove + def pop_tail(self) -> T | None: if self.tail is None: return None - # Remember the value we're removing value = self.tail.value - - # If there's only one carriage, the train becomes empty - if self.head == self.tail: - self.head = None - self.tail = None - else: - # Move the tail backwards by one carriage - self.tail = self.tail.previous - - # The new last carriage has no carriage after it - self.tail.next = None - - # Return the value we removed + self.remove(self.tail) return value - def remove(self, node): - # Connect the previous carriage to the next carriage + def remove(self, node: Node[T]) -> None: if node.previous is not None: node.previous.next = node.next else: - # There is no previous carriage, so this was the head self.head = node.next - # Connect the next carriage to the previous carriage if node.next is not None: node.next.previous = node.previous else: - # There is no next carriage, so this was the tail self.tail = node.previous - # Disconnect the removed carriage node.next = None node.previous = None -