39

From several posts inside stackoverflow and outside, I have come to know how to detect cycles in a linked list, the length of a cycle. I also found the method on how to detect the start of the loop.

Here are the steps again for reference.

Detecting Loop:

Have two pointers, classically called hare and tortoise. Move hare by 2 steps and tortoise by 1. If they meet at some point, then there is surely a cycle and the meeting point is obviously inside the cycle.

Finding length of Loop:

Keep one pointer fixed at meeting point while increment the other until they are same again. Increment a counter as you go along and the counter value at meet will be the length of cycle.

Find the start of cycle

Take one pointer to start of the list and keep the other at the meeting point. Now increment both by one and the meet point is the start of loop. I verified the method using some cases on paper, but I don't understand why it should work.

Can someone, provide a mathematical proof as to why this method works?

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
Shamim Hafiz - MSFT
  • 21,454
  • 43
  • 116
  • 176
  • 1
    This should probably be on the computer science exchange: http://cstheory.stackexchange.com/ – JoshD Oct 17 '10 at 10:23
  • 1
    related: http://stackoverflow.com/questions/2936213/explain-how-finding-cycle-start-node-in-cycle-linked-list-work – Nate Kohl Jun 29 '11 at 16:25
  • @JoshD someone indeed added it [there](https://cs.stackexchange.com/q/10360/61621) :) – RBT Dec 24 '17 at 14:49

5 Answers5

57

If you represent a list by a pointer to its first node (list)

enter image description here

The algorithm to detect loops is described as follows:

  1. Declare two pointers (pFast) and (pSlow).
  2. Make pSlow and pFast point to list.
  3. Until (pSlow), (pFast) or both point to NULL:
    1. enter image description here
    2. enter image description here
    3. If enter image description here, then STOP as a loop has just been found.
  4. If this point has been reached (one or both two pointers are NULL) then there are no loops in the list.

Lets assume that this algorithm is correct. In this scheme, a loop situation is represented by the following diagram:

enter image description here

Note how every node, except the one pointing to the begining of a loop, is labeled with the number of its target minus one. So, if one node is labeled with i and it is not the begining of a loop, then it is pointed as next element by the node labeled with i-1.

The algorithm explained above can be described as a loop since its main step (3) is a set of sub-steps which repeated until the exit condition is satisfied. That forces us to represent pFast and pSlow in function of the iteration number in the algorithm execution (t).

enter image description here

If the list hadn’t have loops, pointers positions would be described in function of t as:

enter image description here

However there is a node where the loop starts and that function stops describing the evolution of the pointers. Assuming that this pointer is tagged with m, that the loop contains nodes (that is enter image description here and enter image description here), and setting t=0 as iteration value when enter image description here then:

enter image description here

If one pointer is indeed enough to detect loops using the algorithm described, then it must exist at least a solution to the equation enter image description here.

This equation is true if and only if there is a value for t that makes:

enter image description here

This ends in a function,enter image description here which generates values of t that are valid solutions to the equation described above:

enter image description here

enter image description here

Thus It is proved that one slow pointer and one fast pointer are enough to detect loop conditions in a linked list.

  • 3
    why is latex not properly rendered? – Daniel Pop Nov 13 '20 at 13:54
  • 1
    @AvramPop These are images with transparent backgrounds, are you using dark mode? I've notice they don't look pretty well with dark mode. I'll try to squeeze some time to upload white background images. In the mean time, you should see this answer just right disabling dark mode. – Pablo Francisco Pérez Hidalgo Nov 13 '20 at 14:51
  • 3
    you are right, it's something about the dark mode. switched to light mode and upvoted – Daniel Pop Nov 19 '20 at 10:36
  • 1
    Unsure why this is voted so highly. I appreciate the great formatting, but this proof only demonstrates that slow and fast pointers can meet, and that there is a loop. The question is asking how to detect the start of the loop, this is the complicated part of the problem. – Louis93 Jan 08 '21 at 02:33
  • @Louis93 `t(m,n)` provides all values where the slow and fast pointer meet. Being the slow pointer incremented in steps of one position at at time you know for sure that, in the presence of a cycle, it will pass through the beginning of the cycle. As to the fast pointer: If the cycle has an even size you get the same effect and they meet in the start of the cycle. If odd, one iteration will skip the start and the next won't or directly match the start of the cycle (depending on whether or not the pre-cycle length being odd) so it is guaranteed that `t(n,m)` will yield the start of the cycle. – Pablo Francisco Pérez Hidalgo Jan 11 '21 at 11:23
  • @PabloFranciscoPérezHidalgo You showed in the proof that the both pointers will always meet at the same place in the cycle! Also, it's clear that the meeting point is not always the start of the cycle. So I'm not getting how you claimed *...it is guaranteed that t(n,m) will yield the start of the cycle*. What am I missing here? (btw, I found this visualization helpful: https://youtu.be/PvrxZaH_eZ4?t=410) – Hans Sep 25 '21 at 07:33
  • @PabloFranciscoPérezHidalgo, is this concept for non-cyclical lists? I assume that if the lists are circularly linked lists that you might iterate until the tortoise reaches its start? – The_Redhawk Oct 23 '21 at 19:05
23

You can make your 'Find the start of cycle' proof simpler if you don't use meeting point.
Let second pointer start not at the 'meeting point', but M steps ahead of first pointer. (Where M is the length of the loop.)

This way, proof is pretty obvious. When the first pointer reaches start of the loop, second will be exactly M steps ahead: also at the start of the loop.

Nikita Rybak
  • 67,365
  • 22
  • 157
  • 181
  • 4
    `M` isn't necessarily equal to the loop length, it could be a multiple of the loop length (`N`). (This doesn't change anything, just a technicality as `M` is still congruent to 0 in `Z mod N`.) +1 – JoshD Oct 17 '10 at 10:50
  • 1
    Nikita defined `M` as "the length of the loop". In that case `M` is most definitely equal to the loop length. – Nate Cook Sep 02 '19 at 23:02
  • 2
    I am glad that this "proof" convinces you that the algorithm is correct, but it doesn't prove anything at all. Refer to the top answer here to see how to properly write proofs. -1 from me. – pavel_orekhov Feb 24 '20 at 17:43
18

Distance travelled by slowPointer before meeting = x + y

Distance travelled by fastPointer before meeting = (x + y + z) + y = x + 2y + z

Since fastPointer travels with double the speed of slowPointer, and time is constant for both when the reach the meeting point.

So by using simple speed, time and distance relation 2(x+y)= x+2y+z => x+2y+z = 2x+2y => x=z

Hence by moving slowPointer to start of linked list, and making both slowPointer and fastPointer to move one node at a time, they both have same distance to cover .

They will reach at the point where the loop starts in the linked list.

Diagram of proof

Teepeemm
  • 4,331
  • 5
  • 35
  • 58
user5449813
  • 245
  • 2
  • 2
  • 25
    care to give credit from where you used this? :) – Atul Yadav Jun 06 '17 at 18:29
  • 12
    why the assumption is made that they will meet after one rotation? – raviraja Jul 12 '18 at 18:07
  • 6
    this diagram is over simplified. since the fast pointer is always twice as fast as the slow pointer, the fast pointer can make an arbitrary number of spins around the loop while the slow pointer is moving through the distance x. – Warren MacEvoy Dec 06 '18 at 06:15
  • 1
    Copied from [here](https://cs.stackexchange.com/a/45540/95996) without credit. It's not technically correct either. FWIW, this user has only posted one answer, and that too by plagiarizing. – Abhijit Sarkar Jul 20 '21 at 09:13
0

The second part, stating that "to find the start of the loop, just move one pointer back to the start of the list then iterate both until they meet" is wrong!

It is correct only if the fast pointer has gone round the loop exactly once - i.e. that the portion before the start of the loop is shorter than the length of the loop - but if it is longer then the algorithm doesn't work; you can get stuck in an infinite loop.

Try it with a linked list with 11 elements, where the 11th points to the 7th:

1 1 -> 3 2 -> 5 3 -> 7 4 -> 9 5 -> 11 6 -> 8 7 -> 10 8 -> 7 9 -> 9 9

-- loop detected

Move one back to start and increment them: 1 9 -> 2 10 -> 3 11 -> 4 7 -> 5 8 -> 6 9 -> 7 10 -> 8 11 -> 9 7 -> 10 8 -> 11 9 -> 7 10 -> 8 11 ->...

PaulJ
  • 1
-3
/* Algorithm : P2 is moving through the list twice as fast as P1.
   If the list is circular, it will eventually get around P1 and meet */

public boolean hasCycle()
{
  DoubleNode p1,p2;
  p1=p2=firstNode;    //Start with the first loop

  try
  {
    while (p2 != null)     //If p2 reaches end of linked list, no cycle exists
    {
        p1=p1.next;   //Move to next
        p2=p2.next.next; //Move to 2 steps next
        if(p1==p2)
           return true;     //p1 and p2 met, so this means that there is a cycle
    }
  }
  catch(NullPointerException npe)
  {
      //This means that p2 could not move forward
      return false;
  }

  return false;
}
François Févotte
  • 19,520
  • 4
  • 51
  • 74
SHIVA
  • 11
  • 2