4

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

After detecting the loop using hare and tortoise algorithm, I cant understand how equal moves from starting and loop detection address is resulting in providing the address where the loop started.

    ListNode* Solution::detectCycle(ListNode* A) {

    ListNode* p=A;
    ListNode* q=A;
    while(p!=NULL && q!=NULL && q->next!=NULL)
    {
        p=p->next;
        q=q->next->next;
        if(p==q)
        {
            p=A;
            while(p!=q)
            {
                p=p->next;
                q=q->next;
            }
            return p;
        }
    }
    return NULL;

    }
Davide Spataro
  • 7,319
  • 1
  • 24
  • 36
  • I already know what is happening in the code. The problem is when p==q we have detected a cycle but when one pointer from the head that is p moved equal times to that of q (where loop was detected ) then why are we getting the beginning of the address where loop started. – Vineesh Gupta Nov 08 '19 at 07:45

2 Answers2

2

first node in the cycle is a node with 2 other nodes pointing at:

enter image description here

what can be done, is iterate over the list, keep all node addresses and check when address was already recorded. this address is the loop begin node.

Sample code:

    #include "stdio.h"
#include <iostream>
#include <vector>

struct ListNode
{
    struct ListNode* next;
};

ListNode* detectCycle(ListNode* A) {

    ListNode* p = A;
    ListNode* q = A;
    while (p != NULL && q != NULL && q->next != NULL)
    {
        p = p->next;
        q = q->next->next;
        if (p == q)
        {
            p = A;
            while (p != q)
            {
                p = p->next;
                q = q->next;
            }
            return p;
        }
    }
    return NULL;
}


template < typename T>
std::pair<bool, int > findInVector(const std::vector<T>& vecOfElements, const T& element)
{
    std::pair<bool, int > result;

    // Find given element in vector
    auto it = std::find(vecOfElements.begin(), vecOfElements.end(), element);

    if (it != vecOfElements.end())
    {
        result.second = distance(vecOfElements.begin(), it);
        result.first = true;
    }
    else
    {
        result.first = false;
        result.second = -1;
    }

    return result;
}


int main()
{
    ListNode a, b, c, d, e, f;   // a -> b -> c -> d ->e -> f -> c;

    a.next = &b;
    b.next = &c;
    c.next = &d;
    d.next = &e;
    e.next = &f;
    f.next = &c;


    ListNode* p = detectCycle(&a);

    std::cout << p;

    std::vector<ListNode*> v;

    v.push_back(&a);

    ListNode* it = a.next;

    while (findInVector(v, it).first == false)
    {
        v.push_back(it);
        it = it->next;
    }

    std::cout << " first is " << v.at(findInVector(v, it).second) << " " << &c; 
}
nivpeled
  • 1,810
  • 5
  • 17
1

It works because when you are inside this if

if (p == q)
{

you know for sure that:

  1. you are in a cycle
  2. the distance from that point to the beginning (setting p=A) of the cycle is the same as
  3. the distance you have from the beginning of the list to the beginning of the cycle.

    That is why you can reset one of the pointer to the beginning of the list and move them at the same speed. When they meet, they meet at the beginning of the cycle.

Consider two iterators p and q with velocities v_p=1 and v_q=2=2*v_p respectively. Suppose the cycle has length n and that it starts at node number A < n. When the slower iterator reaches A the faster is at location 2A. How many iteration k it will take before they meet? And at which node?

The situation is described by the following congruence:

  • A + k*v_p = 2A + 2*k*v_p mod(n)
  • 2*A + 2*k*v_p = A + k*v_p mod(n)
  • A + 2*k*v_p = k*v_p mod(n)
  • A +k*v_p = 0 mod(n)
  • A +k = 0 mod(n) !!!

which has solution k = n-A.

This means that the two pointer they will meet after k=n-A iteration of the slower iterator. This means that they will meet at A nodes before the beginning of the cycle and we can use this fact to count A nodes from the beginning of the list to deduce the starting point of the cycle.

The same reasoning can be applied for the case A > n. To further clarify what those congruences mean check this image below:

enter image description here


I wrote a blog article about this algorithm. Check it out for more info.

Davide Spataro
  • 7,319
  • 1
  • 24
  • 36