the-honk/school/a-level/Y13 2021-2023/Advanced Data Structures/Linked List.py

202 lines
5.3 KiB
Python
Raw Normal View History

2024-10-09 17:02:48 +00:00
from __future__ import annotations
from dataclasses import dataclass
from typing import Generic, TypeVar, Iterable, Union, List
T = TypeVar("T")
@dataclass
class Node(Generic[T]):
data: T
next: Node[T] = None
def __repr__(self) -> str:
return str(self.data)
class LinkedList(Generic[T]):
def __init__(self, initialValues: Iterable[T]):
self.head: Node[T] = None
self.tail: Node[T] = None
self.extend(initialValues)
def __iter__(self) -> Iterable[T]:
"""Traverse the linked list."""
node = self.head
while node is not None:
yield node
node = node.next
def __getitem__(self, index: int) -> Node[T]:
"""Get an item by its index."""
if index > len(self) - 1:
raise IndexError("Out of bounds")
for i, node in enumerate(self):
if i == index:
return node
def __setitem__(self, index: int, data: T):
"""Set an item by its index."""
if index > len(self) - 1:
raise IndexError("Out of bounds")
for i in range(len(self)):
if i == index:
self[i].data = data
break
def __delitem__(self, index: int):
"""Delete an item using its index."""
if index > len(self) - 1:
raise IndexError("Out of bounds")
for i in range(len(self)):
if i == index:
for j in range(1, len(self) - i):
self[i + j - 1] = self[i + j].data
self[len(self) - 2].next = None
break
def __add__(self, data: Union[T, List[T], LinkedList[T]]) -> LinkedList[T]:
copy = self.copy()
if isinstance(data, LinkedList) or type(data) == list:
return copy.extend(iter(data) if type(data) == list else iter(map(lambda x: x.data, data)))
else:
return copy.append(data)
def __len__(self) -> int:
"""Get the length of the linked list."""
count = 0
for _ in self:
count += 1
return count
def append(self, value: T) -> LinkedList[T]:
"""Append data to the end of the linked list."""
node = Node(value)
if self.head is None:
self.head = node
return self
for currentNode in self:
pass
currentNode.next = node
return self
def clear(self) -> LinkedList[T]:
"""Clear the linked list."""
self.head = None
self.tail = None
return self
def count(self, value: T) -> int:
"""Count the number of occurrences of a value."""
count = 0
for node in self:
if node.data == value:
count += 1
return count
def extend(self, iterable: Iterable[T]) -> LinkedList[T]:
"""Extend the list from an iterable."""
for value in iterable:
self.append(value)
return self
def index(self, value: T) -> int:
"""Finds the first index of a value. Returns -1 if the value is not in the list."""
for i, node in enumerate(self):
if node.data == value:
return i
return -1
def insert(self, index: int, value: T) -> LinkedList[T]:
"""Inserts a value before an index."""
if index > len(self) - 1:
raise IndexError("Out of bounds")
for i in range(len(self)):
if i == index:
container = value
for j in range(len(self)):
try:
temp = self[i + j].data
self[i + j].data = container
container = temp
except IndexError:
self.append(container)
break
return self
def pop(self) -> Node[T]:
"""Pop and return the final element of the list."""
for node in self:
pass
del self[len(self) - 1]
return node
def remove(self, value: T) -> LinkedList[T]:
for i, node in enumerate(self):
if node.data == value:
del self[i]
return self
raise ValueError("Value not found")
def copy(self) -> LinkedList[T]:
"""Returns a copy of the list."""
return LinkedList[T](iter(map(lambda x: x.data, self)))
def sort(self) -> LinkedList[T]:
n = len(self)
swapped = False
for i in range(n - 1):
for j in range(0, n - i - 1):
if self[j].data > self[j + 1].data:
swapped = True
self[j], self[j + 1] = self[j + 1].data, self[j].data
if not swapped:
break
return self
def __repr__(self) -> str:
out = "["
for i, node in enumerate(self):
value = node.data
out += f'"{value}"' if type(value) == str else value
if i != len(self) - 1:
out += ", "
return out + "]"
names = LinkedList[str](["Harold", "Janet"])
names.extend(["Alfred", "Sophie"]).insert(1, "John Snow")
names.remove("Alfred")
names += "Woody"
names.sort()
print(names)