mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-11-27 15:01:08 +00:00
Add Floyd's Cycle Detection Algorithm (#10833)
* Add Floyd's Cycle Detection Algorithm * Add tests for add_node function * Apply suggestions from code review * Update floyds_cycle_detection.py --------- Co-authored-by: Tianyi Zheng <tianyizheng02@gmail.com>
This commit is contained in:
parent
54e2aa67e8
commit
30122062b9
150
data_structures/linked_list/floyds_cycle_detection.py
Normal file
150
data_structures/linked_list/floyds_cycle_detection.py
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
"""
|
||||||
|
Floyd's cycle detection algorithm is a popular algorithm used to detect cycles
|
||||||
|
in a linked list. It uses two pointers, a slow pointer and a fast pointer,
|
||||||
|
to traverse the linked list. The slow pointer moves one node at a time while the fast
|
||||||
|
pointer moves two nodes at a time. If there is a cycle in the linked list,
|
||||||
|
the fast pointer will eventually catch up to the slow pointer and they will
|
||||||
|
meet at the same node. If there is no cycle, the fast pointer will reach the end of
|
||||||
|
the linked list and the algorithm will terminate.
|
||||||
|
|
||||||
|
For more information: https://en.wikipedia.org/wiki/Cycle_detection#Floyd's_tortoise_and_hare
|
||||||
|
"""
|
||||||
|
|
||||||
|
from collections.abc import Iterator
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Any, Self
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Node:
|
||||||
|
"""
|
||||||
|
A class representing a node in a singly linked list.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data: Any
|
||||||
|
next_node: Self | None = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LinkedList:
|
||||||
|
"""
|
||||||
|
A class representing a singly linked list.
|
||||||
|
"""
|
||||||
|
|
||||||
|
head: Node | None = None
|
||||||
|
|
||||||
|
def __iter__(self) -> Iterator:
|
||||||
|
"""
|
||||||
|
Iterates through the linked list.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Iterator: An iterator over the linked list.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
>>> linked_list = LinkedList()
|
||||||
|
>>> list(linked_list)
|
||||||
|
[]
|
||||||
|
>>> linked_list.add_node(1)
|
||||||
|
>>> tuple(linked_list)
|
||||||
|
(1,)
|
||||||
|
"""
|
||||||
|
visited = []
|
||||||
|
node = self.head
|
||||||
|
while node:
|
||||||
|
# Avoid infinite loop in there's a cycle
|
||||||
|
if node in visited:
|
||||||
|
return
|
||||||
|
visited.append(node)
|
||||||
|
yield node.data
|
||||||
|
node = node.next_node
|
||||||
|
|
||||||
|
def add_node(self, data: Any) -> None:
|
||||||
|
"""
|
||||||
|
Adds a new node to the end of the linked list.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data (Any): The data to be stored in the new node.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
>>> linked_list = LinkedList()
|
||||||
|
>>> linked_list.add_node(1)
|
||||||
|
>>> linked_list.add_node(2)
|
||||||
|
>>> linked_list.add_node(3)
|
||||||
|
>>> linked_list.add_node(4)
|
||||||
|
>>> tuple(linked_list)
|
||||||
|
(1, 2, 3, 4)
|
||||||
|
"""
|
||||||
|
new_node = Node(data)
|
||||||
|
|
||||||
|
if self.head is None:
|
||||||
|
self.head = new_node
|
||||||
|
return
|
||||||
|
|
||||||
|
current_node = self.head
|
||||||
|
while current_node.next_node is not None:
|
||||||
|
current_node = current_node.next_node
|
||||||
|
|
||||||
|
current_node.next_node = new_node
|
||||||
|
|
||||||
|
def detect_cycle(self) -> bool:
|
||||||
|
"""
|
||||||
|
Detects if there is a cycle in the linked list using
|
||||||
|
Floyd's cycle detection algorithm.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if there is a cycle, False otherwise.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
>>> linked_list = LinkedList()
|
||||||
|
>>> linked_list.add_node(1)
|
||||||
|
>>> linked_list.add_node(2)
|
||||||
|
>>> linked_list.add_node(3)
|
||||||
|
>>> linked_list.add_node(4)
|
||||||
|
|
||||||
|
>>> linked_list.detect_cycle()
|
||||||
|
False
|
||||||
|
|
||||||
|
# Create a cycle in the linked list
|
||||||
|
>>> linked_list.head.next_node.next_node.next_node = linked_list.head.next_node
|
||||||
|
|
||||||
|
>>> linked_list.detect_cycle()
|
||||||
|
True
|
||||||
|
"""
|
||||||
|
if self.head is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
slow_pointer: Node | None = self.head
|
||||||
|
fast_pointer: Node | None = self.head
|
||||||
|
|
||||||
|
while fast_pointer is not None and fast_pointer.next_node is not None:
|
||||||
|
slow_pointer = slow_pointer.next_node if slow_pointer else None
|
||||||
|
fast_pointer = fast_pointer.next_node.next_node
|
||||||
|
if slow_pointer == fast_pointer:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import doctest
|
||||||
|
|
||||||
|
doctest.testmod()
|
||||||
|
|
||||||
|
linked_list = LinkedList()
|
||||||
|
linked_list.add_node(1)
|
||||||
|
linked_list.add_node(2)
|
||||||
|
linked_list.add_node(3)
|
||||||
|
linked_list.add_node(4)
|
||||||
|
|
||||||
|
# Create a cycle in the linked list
|
||||||
|
# It first checks if the head, next_node, and next_node.next_node attributes of the
|
||||||
|
# linked list are not None to avoid any potential type errors.
|
||||||
|
if (
|
||||||
|
linked_list.head
|
||||||
|
and linked_list.head.next_node
|
||||||
|
and linked_list.head.next_node.next_node
|
||||||
|
):
|
||||||
|
linked_list.head.next_node.next_node.next_node = linked_list.head.next_node
|
||||||
|
|
||||||
|
has_cycle = linked_list.detect_cycle()
|
||||||
|
print(has_cycle) # Output: True
|
Loading…
Reference in New Issue
Block a user