1

Consider the following linked list:

1->2->3->4->5->6->7->8->9->4->...->9->4.....

The above list has a loop as follows:

[4->5->6->7->8->9->4]

Drawing the linked list on a whiteboard, I tried manually solving it for different pointer steps, to see how the pointers move around -

(slow_pointer_increment, fast_pointer_increment)

So, the pointers for different cases are as follows:

(1,2), (2,3), (1,3)

The first two pairs of increments - (1,2) and (2,3) worked fine, but when I use the pair (1,3), the algorithm does not seem to work on this pair. Is there a rule as to by how much we need to increment the steps for this algorithm to hold true?

Although I searched for various increment steps for the slower and the faster pointer, I haven't so far found a single relevant answer as to why it is not working for the increment (1,3) on this list.

Bernhard Barker
  • 54,589
  • 14
  • 104
  • 138
Divij Sehgal
  • 647
  • 2
  • 11
  • 26

2 Answers2

5

The algorithm can easily be shown to be guaranteed to find a cycle starting from any position if the difference between the pointer increments and the cycle length are coprimes (i.e. their greatest common divisor must be 1).

For the general case, this means the difference between the increments must be 1 (because that's the only positive integer that's coprime to all other positive integers).

For any given pointer increments, if the values aren't coprimes, it may still be guaranteed to find a cycle, but one would need to come up with a different way to prove that it will find a cycle.

For the example in the question, with pointer increments of (1,3), the difference is 3-1=2, and the cycle length is 6. 2 and 6 are not coprimes, thus it's not known whether the algorithm is guaranteed to find the cycle in general. It does seem like this might actually be guaranteed to find the cycle (including for the example in the question), even though it doesn't reach every position (which applies with coprime, as explained below), but I don't have a proof for this at the moment.


The key to understanding this is that, at least for the purposes of checking whether the pointers ever meet, the slow and fast pointers' positions within the cycle only matters relative to each other. That is, these two can be considered equivalent: (the difference is 1 for both)

slow fast             slow fast
   ↓ ↓                   ↓ ↓
 0→1→2→3→4→5→0     0→1→2→3→4→5→0

So we can think of this in terms of the position of slow remaining constant and fast moving at an increment of fastIncrement-slowIncrement, at which point the problem becomes:

Starting at any position, can we reach a specific position moving at some speed (mod cycle length)?

Or, more generally:

Can we reach every position moving at some speed (mod cycle length)?

Which will only be true if the speed and cycle length are coprimes.

For example, look at a speed of 4 and a cycle of length 6 - starting at 0, we visit:
0, 4, 8%6=2, 6%6=0, 4, 2, 0, ... - GCD(4,6) = 2, and we can only visit every second element.
To see this in action, consider increments of (1,5) (difference = 4) for the example given above and see that the pointers will never meet.


I should note that, to my knowledge at least, the (1,2) increment is considered a fundamental part of the algorithm.

Using different increments (as per the above constraints) might work, but it would be a move away from the "official" algorithm and would involve more work (since a pointer to a linked-list must be incremented iteratively, you can't increment it by more than 1 in a single step) without any clear advantage for the general case.

Bernhard Barker
  • 54,589
  • 14
  • 104
  • 138
0

Bernhard Barker explanation is spot on. I am simply adding on to it.

Why should the difference of speeds between the pointers and the cycle length be coprime numbers?

Take a scenario where the difference of speeds between pointers(say v) and cycle length(say L) are not coprime. So there exists a GCD(v,L) greater than 1 (say G).

Therefore, we have

  • v=difference of speeds between pointers
  • L=Length of the cycle(i.e. the number of nodes in the cycle)
  • G=GCD(v,L)

Since we are considering only relative positions, essentially the slow is stationary and the fast is moving at a relative speed v. Let fast be at some node in the cycle.

Since G is a divisor of L we can divide the cycle into G/L parts. Start dividing from where fast is located.

Now, v is a multiple of G (say v=nG). Every time the fast pointer moves it will jump across n parts. So in each part the pointer arrives on a single node(basically the last node of a part). Each and every time the fast pointer will land on the ending node of every part. Refer the image below

Example image

As mentioned above by Bernhard, the question we need to answer is Can we reach every position moving at some speed?

The answer is no if we have a GCD existing. As we see the fast pointer will only cover the last nodes in every part.