from __future__ import annotations
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):
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:
return True
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)
root_node.next_node.next_node.next_node = root_node.next_node
print(root_node.has_loop)
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)
root_node = Node(1)
print(root_node.has_loop)