Algorithms

Find Loop in a Linked List

9 months, 2 weeks ago ; 133 views
Share this

Find Loop in a Linked List

This case solves the problem of finding the starting node of a loop in a linked list.

A node in a linked list with a loop exists where the next pointer points to a previously visited node, creating a cycle. The task is to detect this loop and return the starting node of the loop if it exists.

 

For example, given a linked list like:

 

1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10
            ^                            
            |_____________________________|

 

The node with value 3 is the starting node of the loop.


This problem involves using Floyd's Tortoise and Hare algorithm, also known as the cycle detection algorithm, which uses two pointers moving at different speeds to detect the presence of a loop. If a loop is found, the algorithm determines the starting node of the loop using additional pointer manipulations.

The solution I am providing below implements this algorithm to detect and return the starting node of the loop or None if no loop is present in the linked list.

Solution

My Approach Thought Process

The find_loop function takes the head node of a linked list as input and returns the starting node of a loop in the linked list, if it exists.

It uses Floyd's Tortoise and Hare algorithm to detect the loop in the linked list. Two pointers (first and second) are initialized to traverse the linked list at different speeds until they meet at a common node within the loop.

After detecting the loop, one pointer (first) is reset to the head of the linked list, and both pointers are moved one step at a time until they meet again at the starting node of the loop.

The function returns the loop's starting node or None if no loop is found.

The TestFindLoop class contains unit tests to verify the correctness of the findLoop function for various scenarios, including cases with no loop, loops starting from different nodes in the linked list, and loops starting from the first node.

Code Implementation

class ListNode:
    def __init__(self, value=0, next=None):
        self.value = value
        self.next = next

# O(n) time | O(1) space
def find_loop(head):
    """
    Find the starting node of a loop in a linked list, if it exists.

    Args:
        head (ListNode): The head node of the linked list.

    Returns:
        ListNode: The starting node of the loop. Returns None if no loop is found.
    """
    # Initialize two pointers: first and second
    first = head.next
    second = head.next.next
    
    # Move first and second pointers until they meet at a common node within the loop
    while first != second:
        if not second or not second.next:
            return None  # No loop found
        first = first.next
        second = second.next.next
    
    # Reset first pointer to the head and move both pointers one step at a time until they meet
    first = head
    while first != second:
        first = first.next
        second = second.next
    
    # Return the node where the pointers meet, which is the starting node of the loop
    return first

 

Time Complexity

The code's time complexity is O(n), with n being the number of nodes in the linked list. That is because the code iterates through the linked list once to detect the loop using Floyd's Tortoise and Hare algorithm.

Space Complexity

The code's space complexity is O(1) because it does not use extra space in the linked list's size. This constant space is used to store a few pointers (first and second) and temporary variables and does not depend on the size of the linked list.

Unit Test

import unittest
class TestFindLoop(unittest.TestCase):
    def test_no_loop(self):
        head = ListNode(1, ListNode(2, ListNode(3)))
        self.assertIsNone(find_loop(head))

    def test_loop_starting_from_second_node(self):
        head = ListNode(1)
        node2 = ListNode(2)
        head.next = node2
        node3 = ListNode(3)
        node2.next = node3
        node4 = ListNode(4)
        node3.next = node4
        node5 = ListNode(5)
        node4.next = node5
        node6 = ListNode(6)
        node5.next = node6
        node6.next = node2  # Create a loop
        self.assertEqual(find_loop(head), node2)

    def test_loop_starting_from_last_node(self):
        head = ListNode(1)
        node2 = ListNode(2)
        head.next = node2
        node3 = ListNode(3)
        node2.next = node3
        node4 = ListNode(4)
        node3.next = node4
        node5 = ListNode(5)
        node4.next = node5
        node6 = ListNode(6)
        node5.next = node6
        node6.next = node5  # Create a loop
        self.assertEqual(find_loop(head), node5)

    def test_loop_starting_from_first_node(self):
        head = ListNode(1)
        node2 = ListNode(2)
        head.next = node2
        node3 = ListNode(3)
        node2.next = node3
        node4 = ListNode(4)
        node3.next = node4
        node5 = ListNode(5)
        node4.next = node5
        node6 = ListNode(6)
        node5.next = node6
        node6.next = head  # Create a loop
        self.assertEqual(find_loop(head), head)

if __name__ == "__main__":
    unittest.main()

 

 

 

 

 

 

Become a member
Get the latest news right in your inbox. We never spam!

Read next

Island Perimeter

 This solution seeks to find the perimeter of an island in a grid.  The problem considers a grid where each cell represents land (1) or … Read More

Kibsoft 5 months, 3 weeks ago . 119 views

Pacific Atlantic Waterflow

5 months, 3 weeks ago . 119 views