diff --git a/data_structures/linked_list/circular_linked_list.py b/data_structures/linked_list/circular_linked_list.py new file mode 100644 index 000000000000..cf523f0a4380 --- /dev/null +++ b/data_structures/linked_list/circular_linked_list.py @@ -0,0 +1,186 @@ +from typing import Any + + +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 + + +class CircularLinkedList: + """ + Class to represent the CircularLinkedList. + + CircularLinkedList has following attributes. + * head + * length + """ + + def __init__(self): + self.head = None + self.length = 0 + + def __len__(self) -> int: + """ + Dunder method to return length of the CircularLinkedList + >>> cll = CircularLinkedList() + >>> len(cll) + 0 + >>> cll.append(1) + >>> len(cll) + 1 + """ + 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 + + 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 + else: + self.head = new_node + + self.length += 1 + + def prepend(self, data: Any) -> None: + """ + Adds a ndoe 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 + + 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: + """ + if not self.head: + raise IndexError("Deleting from an empty list") + + current_node = self.head + + if current_node.next_ptr == current_node: + self.head, self.length = None, 0 + 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 + + 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: + """ + 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, self.length = None, 0 + else: + while current_node.next_ptr != self.head: + temp_node = current_node + current_node = current_node.next_ptr + + temp_node.next_ptr = current_node.next_ptr + + self.length -= 1 + + +if __name__ == "__main__": + import doctest + + doctest.testmod()