Skip to content

Commit 6a68c34

Browse files
author
ganavi
committed
Optimize Node.has_loop to use constant auxiliary space (Fixes #15576)
1 parent 6e03a48 commit 6a68c34

1 file changed

Lines changed: 15 additions & 59 deletions

File tree

Lines changed: 15 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,19 @@
1-
from __future__ import annotations
2-
3-
from collections.abc import Iterator
4-
from typing import Any
5-
6-
7-
class ContainsLoopError(Exception):
8-
pass
9-
10-
11-
class Node:
12-
def __init__(self, data: Any) -> None:
13-
self.data: Any = data
14-
self.next_node: Node | None = None
15-
16-
def __iter__(self) -> Iterator[Any]:
17-
node = self
18-
visited = set()
19-
while node:
20-
if node in visited:
21-
raise ContainsLoopError
22-
visited.add(node)
23-
yield node.data
24-
node = node.next_node
25-
26-
@property
27-
def has_loop(self) -> bool:
28-
"""
29-
A loop is when the exact same Node appears more than once in a linked list.
30-
>>> root_node = Node(1)
31-
>>> root_node.next_node = Node(2)
32-
>>> root_node.next_node.next_node = Node(3)
33-
>>> root_node.next_node.next_node.next_node = Node(4)
34-
>>> root_node.has_loop
35-
False
36-
>>> root_node.next_node.next_node.next_node = root_node.next_node
37-
>>> root_node.has_loop
38-
True
1+
@property
2+
def has_loop(self) -> bool:
393
"""
40-
try:
41-
list(self)
42-
return False
43-
except ContainsLoopError:
44-
return True
4+
Detect whether the linked list contains a cycle using Floyd's
5+
Cycle-Finding Algorithm (Tortoise and Hare).
456
7+
Time Complexity: O(n)
8+
Auxiliary Space: O(1)
9+
"""
10+
slow = self
11+
fast = self.next
4612

47-
if __name__ == "__main__":
48-
root_node = Node(1)
49-
root_node.next_node = Node(2)
50-
root_node.next_node.next_node = Node(3)
51-
root_node.next_node.next_node.next_node = Node(4)
52-
print(root_node.has_loop) # False
53-
root_node.next_node.next_node.next_node = root_node.next_node
54-
print(root_node.has_loop) # True
55-
56-
root_node = Node(5)
57-
root_node.next_node = Node(6)
58-
root_node.next_node.next_node = Node(5)
59-
root_node.next_node.next_node.next_node = Node(6)
60-
print(root_node.has_loop) # False
13+
while fast is not None and fast.next is not None:
14+
if slow is fast:
15+
return True
16+
slow = slow.next
17+
fast = fast.next.next
6118

62-
root_node = Node(1)
63-
print(root_node.has_loop) # False
19+
return False

0 commit comments

Comments
 (0)