Last active
June 18, 2021 15:53
-
-
Save Casper-Smet/7a64715881672b821c802f146b1c6862 to your computer and use it in GitHub Desktop.
Linked list in Python based on MutableSequence
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from collections.abc import MutableSequence | |
| from dataclasses import dataclass | |
| from typing import Any, Generator | |
| @dataclass | |
| class LinkedNode(): | |
| value: Any | |
| next_node: "LinkedNode" = None | |
| def forward(self) -> "LinkedNode": | |
| return self.next_node | |
| class LinkedList(MutableSequence): | |
| def __init__(self, *values): | |
| self.length = 0 | |
| self.head = None | |
| self.tail = None | |
| if values: | |
| self.head = LinkedNode(values[0]) | |
| self.tail = self.head | |
| self.length = 1 | |
| if len(values) > 1: | |
| self.extend(values[1:]) | |
| def append(self, val: Any): | |
| new_node = LinkedNode(val) | |
| if self.tail is not None: | |
| self.tail.next_node = new_node | |
| self.tail = new_node | |
| self.length += 1 | |
| def extend(self, iterable): | |
| if not isinstance(iterable, LinkedList): | |
| for val in iterable: | |
| self.append(val) | |
| else: | |
| self.tail.next_node = iterable.head | |
| self.tail = iterable.head | |
| self.length += len(iterable) | |
| def traverse(self) -> Generator[LinkedNode, None, None]: | |
| temp = self.head | |
| if temp is None: | |
| return None | |
| yield temp | |
| while temp.next_node is not None: | |
| temp = temp.forward() | |
| yield temp | |
| def __len__(self) -> int: | |
| return self.length | |
| def _process_idx(self, idx: int) -> LinkedNode: | |
| if idx > 0: | |
| range_goal = idx + 1 | |
| elif idx < 0: | |
| range_goal = len(self) + idx + 1 | |
| else: | |
| return self.head | |
| if range_goal > len(self) or range_goal <= 0: | |
| raise IndexError( | |
| f"{self.__class__.__name__} index {idx} is out of range for range {len(self)}") | |
| for _, val in zip(range(range_goal), self.traverse()): | |
| continue | |
| return val | |
| def index(self, val: Any): | |
| for i, iter_val in enumerate(self): | |
| if iter_val == val: | |
| return i | |
| raise ValueError(f"{val} not in {self.__class__.__name__}") | |
| def __delitem__(self, idx: int) -> None: | |
| previous_node = self.head | |
| for i, node in enumerate(self.traverse()): | |
| if i == idx: | |
| break | |
| previous_node = node | |
| if i != idx: | |
| raise ValueError( | |
| f"{self.__class__.__name__}.remove(x): x not in {self.__class__.__name__}") | |
| next_node = node.forward() | |
| if node == previous_node: | |
| self.head = next_node | |
| else: | |
| previous_node.next_node = next_node | |
| del node | |
| def __getitem__(self, idx: int) -> Any: | |
| return self._process_idx(idx).value | |
| def __setitem__(self, idx: int, val: Any) -> None: | |
| self._process_idx(idx).value = val | |
| def insert(self, idx: int, val: Any) -> None: | |
| if idx < 0: | |
| raise IndexError( | |
| f"{self.__class__.__name__} insert method index should be in range [0, len({self.__class__.__name__})") | |
| elif idx == 0: | |
| old_head = self.head | |
| self.head = LinkedNode(val, old_head) | |
| else: | |
| prev_node = self._process_idx(idx - 1) | |
| old_node = prev_node.forward() | |
| new_node = LinkedNode(val, old_node) | |
| prev_node.next_node = new_node | |
| self.length += 1 | |
| def __iter__(self) -> Generator[LinkedNode, None, None]: | |
| return (node.value for node in self.traverse()) | |
| def reverse(self) -> "LinkedList": | |
| new_ll = LinkedList() | |
| new_ll.length = len(self) | |
| tail = LinkedNode(self.head.value) | |
| new_ll.tail = tail | |
| head = self.head.forward() | |
| while head is not None: | |
| tail = LinkedNode(head.value, tail) | |
| head = head.forward() | |
| new_ll.head = tail | |
| return new_ll | |
| def __reversed__(self) -> "LinkedList": | |
| return self.reverse() | |
| def __repr__(self) -> str: | |
| return str(list(self)) | |
| def main(): | |
| lst = [1, 2, 3, 4] | |
| linked_lst = LinkedList(*lst) | |
| print("Linked list: ", linked_lst) | |
| print("Reversed: ", linked_lst.reverse()) | |
| linked_lst.append(5) | |
| print("Append: ", linked_lst) | |
| print("Length: ", len(linked_lst)) | |
| print("Index 1: ", linked_lst[1]) | |
| print("Index -1: ", linked_lst[-1]) | |
| try: | |
| print(linked_lst[-6]) | |
| except IndexError: | |
| print("Below range index error successful") | |
| try: | |
| print(linked_lst[6]) | |
| except IndexError: | |
| print("Above range index error successful") | |
| linked_lst.extend(linked_lst.reverse()) | |
| print("Extend: ", linked_lst) | |
| print("Index of 5: ", linked_lst.index(5)) | |
| linked_lst.remove(5) | |
| print("Remove 5:", linked_lst) | |
| print("Pop 5: ", linked_lst.pop(4), linked_lst) | |
| print("Is 1 in: ", 1 in linked_lst) | |
| linked_lst[0] = 2 | |
| print("Index 0 to 2: ", linked_lst) | |
| linked_lst[1] += 20 | |
| print("Index 1 += 1: ", linked_lst) | |
| linked_lst[-1] = "Wow" | |
| print("Last item to 'Wow': ", linked_lst) | |
| print("Count occurrences of 4: ", linked_lst.count(4)) | |
| if __name__ == "__main__": | |
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Linked list: [1, 2, 3, 4] | |
| Reversed: [4, 3, 2, 1] | |
| Append: [1, 2, 3, 4, 5] | |
| Length: 5 | |
| Index 1: 2 | |
| Index -1: 5 | |
| Below range index error successful | |
| Above range index error successful | |
| Extend: [1, 2, 3, 4, 5, 5, 4, 3, 2, 1] | |
| Index of 5: 4 | |
| Remove 5: [1, 2, 3, 4, 5, 4, 3, 2, 1] | |
| Pop 5: 5 [1, 2, 3, 4, 4, 3, 2, 1] | |
| Is 1 in: True | |
| Index 0 to 2: [2, 2, 3, 4, 4, 3, 2, 1] | |
| Index 1 += 1: [2, 22, 3, 4, 4, 3, 2, 1] | |
| Last item to 'Wow': [2, 22, 3, 4, 4, 3, 2, 'Wow'] | |
| Count occurrences of 4: 2 |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is (afaik) a full implementation of a linked list. I based the implementation on this image from wikipedia.
The inner workings of reversing and the MutableSequence methods were thought up by me as an exercise in implementing a custom data structure and methods in Python using abstract base classes.