diff --git a/DIRECTORY.md b/DIRECTORY.md index f08c31794b1e..3227711c7853 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -646,6 +646,8 @@ * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_67/sol1.py) * Problem 76 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_76/sol1.py) + * Problem 97 + * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_97/sol1.py) * Problem 99 * [Sol1](https://github.com/TheAlgorithms/Python/blob/master/project_euler/problem_99/sol1.py) diff --git a/data_structures/linked_list/circular_linked_list.py b/data_structures/linked_list/circular_linked_list.py index 19d6ee6c6cb3..f67c1e8f2cf7 100644 --- a/data_structures/linked_list/circular_linked_list.py +++ b/data_structures/linked_list/circular_linked_list.py @@ -2,201 +2,137 @@ class Node: - """ - Class to represent a single node. - - Each node has following attributes - * data - * next_ptr - """ - def __init__(self, data: Any): self.data = data - self.next_ptr = None + self.next = None class CircularLinkedList: - """ - Class to represent the CircularLinkedList. - - CircularLinkedList has following attributes. - * head - * length - """ - def __init__(self): self.head = None - self.length = 0 + self.tail = None - def __len__(self) -> int: - """ - Dunder method to return length of the CircularLinkedList - >>> cll = CircularLinkedList() - >>> len(cll) - 0 - >>> cll.append(1) - >>> len(cll) - 1 - >>> cll.prepend(0) - >>> len(cll) - 2 - >>> cll.delete_front() - >>> len(cll) - 1 - >>> cll.delete_rear() - >>> len(cll) - 0 - """ - return self.length - - def __str__(self) -> str: - """ - Dunder method to represent the string representation of the CircularLinkedList - >>> cll = CircularLinkedList() - >>> print(cll) - Empty linked list - >>> cll.append(1) - >>> cll.append(2) - >>> print(cll) - => - """ - current_node = self.head - if not current_node: - return "Empty linked list" - - results = [current_node.data] - current_node = current_node.next_ptr - - while current_node != self.head: - results.append(current_node.data) - current_node = current_node.next_ptr - - return " => ".join(f"" for result in results) - - def append(self, data: Any) -> None: - """ - Adds a node with given data to the end of the CircularLinkedList - >>> cll = CircularLinkedList() - >>> cll.append(1) - >>> print(f"{len(cll)}: {cll}") - 1: - >>> cll.append(2) - >>> print(f"{len(cll)}: {cll}") - 2: => - """ - current_node = self.head - - new_node = Node(data) - new_node.next_ptr = new_node + def __iter__(self): + node = self.head + while self.head: + yield node.data + node = node.next + if node == self.head: + break - if current_node: - while current_node.next_ptr != self.head: - current_node = current_node.next_ptr + def __len__(self) -> int: + return len(tuple(iter(self))) - current_node.next_ptr = new_node - new_node.next_ptr = self.head - else: - self.head = new_node + def __repr__(self): + return "->".join(str(item) for item in iter(self)) - self.length += 1 + def insert_tail(self, data: Any) -> None: + self.insert_nth(len(self), data) - def prepend(self, data: Any) -> None: - """ - Adds a node with given data to the front of the CircularLinkedList - >>> cll = CircularLinkedList() - >>> cll.prepend(1) - >>> cll.prepend(2) - >>> print(f"{len(cll)}: {cll}") - 2: => - """ - current_node = self.head + def insert_head(self, data: Any) -> None: + self.insert_nth(0, data) + def insert_nth(self, index: int, data: Any) -> None: + if index < 0 or index > len(self): + raise IndexError("list index out of range.") new_node = Node(data) - new_node.next_ptr = new_node - - if current_node: - while current_node.next_ptr != self.head: - current_node = current_node.next_ptr - - current_node.next_ptr = new_node - new_node.next_ptr = self.head - - self.head = new_node - self.length += 1 - - def delete_front(self) -> None: - """ - Removes the 1st node from the CircularLinkedList - >>> cll = CircularLinkedList() - >>> cll.delete_front() - Traceback (most recent call last): - ... - IndexError: Deleting from an empty list - >>> cll.append(1) - >>> cll.append(2) - >>> print(f"{len(cll)}: {cll}") - 2: => - >>> cll.delete_front() - >>> print(f"{len(cll)}: {cll}") - 1: - >>> cll.delete_front() - >>> print(f"{len(cll)}: {cll}") - 0: Empty linked list - """ - if not self.head: - raise IndexError("Deleting from an empty list") - - current_node = self.head - - if current_node.next_ptr == current_node: - self.head = None + if self.head is None: + new_node.next = new_node # first node points itself + self.tail = self.head = new_node + elif index == 0: # insert at head + new_node.next = self.head + self.head = self.tail.next = new_node else: - while current_node.next_ptr != self.head: - current_node = current_node.next_ptr - - current_node.next_ptr = self.head.next_ptr - self.head = self.head.next_ptr - - self.length -= 1 - if not self.head: - assert self.length == 0 - - def delete_rear(self) -> None: - """ - Removes the last node from the CircularLinkedList - >>> cll = CircularLinkedList() - >>> cll.delete_rear() - Traceback (most recent call last): - ... - IndexError: Deleting from an empty list - >>> cll.append(1) - >>> cll.append(2) - >>> print(f"{len(cll)}: {cll}") - 2: => - >>> cll.delete_rear() - >>> print(f"{len(cll)}: {cll}") - 1: - >>> cll.delete_rear() - >>> print(f"{len(cll)}: {cll}") - 0: Empty linked list - """ - if not self.head: - raise IndexError("Deleting from an empty list") - - temp_node, current_node = self.head, self.head - - if current_node.next_ptr == current_node: - self.head = None + temp = self.head + for _ in range(index - 1): + temp = temp.next + new_node.next = temp.next + temp.next = new_node + if index == len(self) - 1: # insert at tail + self.tail = new_node + + def delete_front(self): + return self.delete_nth(0) + + def delete_tail(self) -> None: + return self.delete_nth(len(self) - 1) + + def delete_nth(self, index: int = 0): + if not 0 <= index < len(self): + raise IndexError("list index out of range.") + delete_node = self.head + if self.head == self.tail: # just one node + self.head = self.tail = None + elif index == 0: # delete head node + self.tail.next = self.tail.next.next + self.head = self.head.next else: - while current_node.next_ptr != self.head: - temp_node = current_node - current_node = current_node.next_ptr + temp = self.head + for _ in range(index - 1): + temp = temp.next + delete_node = temp.next + temp.next = temp.next.next + if index == len(self) - 1: # delete at tail + self.tail = temp + return delete_node.data + + def is_empty(self): + return len(self) == 0 - temp_node.next_ptr = current_node.next_ptr - self.length -= 1 - if not self.head: - assert self.length == 0 +def test_circular_linked_list() -> None: + """ + >>> test_circular_linked_list() + """ + circular_linked_list = CircularLinkedList() + assert len(circular_linked_list) == 0 + assert circular_linked_list.is_empty() is True + assert str(circular_linked_list) == "" + + try: + circular_linked_list.delete_front() + assert False # This should not happen + except IndexError: + assert True # This should happen + + try: + circular_linked_list.delete_tail() + assert False # This should not happen + except IndexError: + assert True # This should happen + + try: + circular_linked_list.delete_nth(-1) + assert False + except IndexError: + assert True + + try: + circular_linked_list.delete_nth(0) + assert False + except IndexError: + assert True + + assert circular_linked_list.is_empty() is True + for i in range(5): + assert len(circular_linked_list) == i + circular_linked_list.insert_nth(i, i + 1) + assert str(circular_linked_list) == "->".join(str(i) for i in range(1, 6)) + + circular_linked_list.insert_tail(6) + assert str(circular_linked_list) == "->".join(str(i) for i in range(1, 7)) + circular_linked_list.insert_head(0) + assert str(circular_linked_list) == "->".join(str(i) for i in range(0, 7)) + + assert circular_linked_list.delete_front() == 0 + assert circular_linked_list.delete_tail() == 6 + assert str(circular_linked_list) == "->".join(str(i) for i in range(1, 6)) + assert circular_linked_list.delete_nth(2) == 3 + + circular_linked_list.insert_nth(2, 3) + assert str(circular_linked_list) == "->".join(str(i) for i in range(1, 6)) + + assert circular_linked_list.is_empty() is False if __name__ == "__main__":