diff --git a/data_structures/linked_list/has_loop.py b/data_structures/linked_list/has_loop.py index a25204e3c4ce..e5e72c4f628d 100644 --- a/data_structures/linked_list/has_loop.py +++ b/data_structures/linked_list/has_loop.py @@ -1,63 +1,19 @@ -from __future__ import annotations - -from collections.abc import Iterator -from typing import Any - - -class ContainsLoopError(Exception): - pass - - -class Node: - def __init__(self, data: Any) -> None: - self.data: Any = data - self.next_node: Node | None = None - - def __iter__(self) -> Iterator[Any]: - node = self - visited = set() - while node: - if node in visited: - raise ContainsLoopError - visited.add(node) - yield node.data - node = node.next_node - - @property - def has_loop(self) -> bool: - """ - A loop is when the exact same Node appears more than once in a linked list. - >>> root_node = Node(1) - >>> root_node.next_node = Node(2) - >>> root_node.next_node.next_node = Node(3) - >>> root_node.next_node.next_node.next_node = Node(4) - >>> root_node.has_loop - False - >>> root_node.next_node.next_node.next_node = root_node.next_node - >>> root_node.has_loop - True - """ - try: - list(self) - return False - except ContainsLoopError: +@property +def has_loop(self) -> bool: + """ + Detect whether the linked list contains a cycle using Floyd's + Cycle-Finding Algorithm (Tortoise and Hare). + + Time Complexity: O(n) + Auxiliary Space: O(1) + """ + slow = self + fast = self.next + + while fast is not None and fast.next is not None: + if slow is fast: return True + slow = slow.next + fast = fast.next.next - -if __name__ == "__main__": - root_node = Node(1) - root_node.next_node = Node(2) - root_node.next_node.next_node = Node(3) - root_node.next_node.next_node.next_node = Node(4) - print(root_node.has_loop) # False - root_node.next_node.next_node.next_node = root_node.next_node - print(root_node.has_loop) # True - - root_node = Node(5) - root_node.next_node = Node(6) - root_node.next_node.next_node = Node(5) - root_node.next_node.next_node.next_node = Node(6) - print(root_node.has_loop) # False - - root_node = Node(1) - print(root_node.has_loop) # False + return False