mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-12-18 17:20:16 +00:00
30122062b9
* 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>
151 lines
4.1 KiB
Python
151 lines
4.1 KiB
Python
"""
|
|
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
|