Skip to content

Commit 3a2c647

Browse files
authored
Add Floyd's Cycle Detection Algorithm
1 parent fdb0635 commit 3a2c647

File tree

1 file changed

+136
-0
lines changed

1 file changed

+136
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
"""
2+
Floyd's cycle detection algorithm is a popular algorithm used to detect cycles
3+
in a linked list. It uses two pointers, a slow pointer and a fast pointer,
4+
to traverse the linked list. The slow pointer moves one node at a time while the fast
5+
pointer moves two nodes at a time. If there is a cycle in the linked list,
6+
the fast pointer will eventually catch up to the slow pointer and they will
7+
meet at the same node. If there is no cycle, the fast pointer will reach the end of
8+
the linked list and the algorithm will terminate.
9+
10+
For more information: https://en.wikipedia.org/wiki/Cycle_detection#Floyd's_tortoise_and_hare
11+
"""
12+
13+
from collections.abc import Iterator
14+
from dataclasses import dataclass
15+
from typing import Any, Self
16+
17+
18+
@dataclass
19+
class Node:
20+
"""
21+
A class representing a node in a singly linked list.
22+
"""
23+
24+
data: Any
25+
next_node: Self | None = None
26+
27+
28+
@dataclass
29+
class LinkedList:
30+
"""
31+
A class representing a singly linked list.
32+
"""
33+
34+
head: Node | None = None
35+
36+
def __iter__(self) -> Iterator:
37+
"""
38+
Iterates through the linked list.
39+
40+
Returns:
41+
Iterator: An iterator over the linked list.
42+
43+
Examples:
44+
>>> linked_list = LinkedList()
45+
>>> list(linked_list)
46+
[]
47+
>>> linked_list.add_node(1)
48+
>>> tuple(linked_list)
49+
(1,)
50+
"""
51+
node = self.head
52+
while node:
53+
yield node.data
54+
node = node.next_node
55+
56+
def add_node(self, data: Any) -> None:
57+
"""
58+
Adds a new node to the end of the linked list.
59+
60+
Args:
61+
data (Any): The data to be stored in the new node.
62+
"""
63+
new_node = Node(data)
64+
65+
if self.head is None:
66+
self.head = new_node
67+
return
68+
69+
current_node = self.head
70+
while current_node.next_node is not None:
71+
current_node = current_node.next_node
72+
73+
current_node.next_node = new_node
74+
75+
def detect_cycle(self) -> bool:
76+
"""
77+
Detects if there is a cycle in the linked list using
78+
Floyd's cycle detection algorithm.
79+
80+
Returns:
81+
bool: True if there is a cycle, False otherwise.
82+
83+
Examples:
84+
>>> linked_list = LinkedList()
85+
>>> linked_list.add_node(1)
86+
>>> linked_list.add_node(2)
87+
>>> linked_list.add_node(3)
88+
>>> linked_list.add_node(4)
89+
90+
>>> linked_list.detect_cycle()
91+
False
92+
93+
# Create a cycle in the linked list
94+
>>> linked_list.head.next_node.next_node.next_node = linked_list.head.next_node
95+
96+
>>> linked_list.detect_cycle()
97+
True
98+
"""
99+
if self.head is None:
100+
return False
101+
102+
slow_pointer: Node | None = self.head
103+
fast_pointer: Node | None = self.head
104+
105+
while fast_pointer is not None and fast_pointer.next_node is not None:
106+
slow_pointer = slow_pointer.next_node if slow_pointer else None
107+
fast_pointer = fast_pointer.next_node.next_node
108+
if slow_pointer == fast_pointer:
109+
return True
110+
111+
return False
112+
113+
114+
if __name__ == "__main__":
115+
import doctest
116+
117+
doctest.testmod()
118+
119+
linked_list = LinkedList()
120+
linked_list.add_node(1)
121+
linked_list.add_node(2)
122+
linked_list.add_node(3)
123+
linked_list.add_node(4)
124+
125+
# Create a cycle in the linked list
126+
# It first checks if the head, next_node, and next_node.next_node attributes of the
127+
# linked list are not None to avoid any potential type errors.
128+
if (
129+
linked_list.head
130+
and linked_list.head.next_node
131+
and linked_list.head.next_node.next_node
132+
):
133+
linked_list.head.next_node.next_node.next_node = linked_list.head.next_node
134+
135+
has_cycle = linked_list.detect_cycle()
136+
print(has_cycle) # Output: True

0 commit comments

Comments
 (0)