37

Is there any way of finding out the start of a loop in a link list using not more than two pointers? I do not want to visit every node and mark it seen and reporting the first node already been seen.Is there any other way to do this?

gudok
4,2092 gold badges23 silver badges31 bronze badges
asked Oct 8, 2009 at 10:28
2

16 Answers 16

169

Step1: Proceed in the usual way, you will use to find the loop, i.e. Have two pointers, increment one in single step and other in two steps, If they both meet in sometime, there is a loop.

Step2: Freeze one pointer where it was and increment the other pointer in one step counting the steps you make and when they both meet again, the count will give you the length of the loop (this is same as counting the number of elements in a circular link list).

Step3: Reset both pointers to the start of the link list, increment one pointer to the length of loop times and then start the second pointer. increment both pointers in one step and when they meet again, it will be the start of the loop (this is same as finding the nth element from the end of the link list).

Antoine Boisier-Michaud
1,6952 gold badges17 silver badges32 bronze badges
answered Oct 21, 2010 at 18:24
4
  • 1
    Thought about solving this for a bit (not that long I guess, just around 5 min), then I decided I should read the answer and after reading this it just seems so easy!!! Love/Hate these kind of questions. Commented Oct 28, 2013 at 5:36
  • 18
    The second step is totally unnecessary. Instead, after the first step you can reset only one pointer to the head of the list, and then increment both pointers one step at a time, and again, when they meet, it'll be the start of the loop. Commented Jan 26, 2015 at 6:20
  • 4
    I think the second step is necessary since the pointer that got reset could possibly reach the start of the loop while the other pointer is elsewhere in the loop. Commented Aug 22, 2016 at 18:43
  • 3
    @RoyalleBlue, I'm a bit late to the party here, but for the benefit of others: actually, the second step (and beginning of the third step) is provably unnecessary. If the root node is 'k' steps from the start of the loop, the collision point inside the loop will be exactly 'k' steps from the start of the loop also. The positions are deterministic. It's known as Floyd's algorithm. Commented Jun 8, 2018 at 0:16
39

MATHEMATICAL PROOF + THE SOLUTION

Let 'k' be the number of steps from HEADER to BEGINLOOP.
Let 'm' be the number of steps from HEADER to MEETPOINT.
Let 'n' be the number of steps in the loop.
Also, consider two pointers 'P' and 'Q'. Q having 2x speed than P.

SIMPLE CASE: When k < N

When pointer 'P' would be at BEGINLOOP (i.e. it would have traveled 'k' steps), Q would have traveled '2k' steps. So, effectively, Q is ahead of '2k-k = k' steps from P when P enters the loop, and hence, Q is 'n-k' steps behind the BEGINLOOP now.

When P would have moved from BEGINLOOP to MEETPONT, it would have traveled 'm-k' steps. In that time, Q would have traveled '2(m-k)' steps. But, since they met, and Q started 'n-k' steps behind the BEGINLOOP, so, effectively, '2(m-k) - (n-k)' should be equal to '(m-k)' So,

=> 2m - 2k - n + k = m - k
=> 2m - n = m
=> n = m

THAT MEANS, P and Q meet at the point equal to the number of steps (or multiple to be general, see the case mentioned below) in the loop. Now, at the MEETPOINT, both P and Q are 'n-(m-k)' steps behind, i.e, 'k' steps behind ,as we saw n=m. So, if we start P from HEADER again, and Q from the MEETPOINT but this time with the pace equal to P, P and Q will now be meeting at BEGINLOOP only.

GENERAL CASE: Say, k = nX + Y, Y < n (Hence, k%n = Y)

When pointer 'P' would be at BEGINLOOP (i.e. it would have traveled 'k' steps), Q would have traveled '2k' steps. So, effectively, Q is ahead of '2k-k = k' steps from P when P enters the loop. But, please note 'k' is greater than 'n', which means Q would have made multiple rounds of the loop. So, effectively, Q is 'n-(k%n)' steps behind the BEGINLOOP now.

When P would have moved from BEGINLOOP to MEETPOINT, it would have traveled 'm-k' steps. (Hence, effectively, MEETPOINT would be at '(m-k)%n' steps ahead of BEGINLOOP now.) In that time, Q would have traveled '2(m-k)' steps. But, since they met, and Q started 'n-(k%n)' steps behind the BEGINLOOP, so, effectively, new position of Q (which is '(2(m-k) - (n-k/%n))%n' from BEGINLOOP) should be equal to the new position of P (which is '(m-k)%n' from BEGIN LOOP).

So,

=> (2(m - k) - (n - k%n))%n = (m - k)%n
=> (2(m - k) - (n - k%n))%n = m%n - k%n
=> (2(m - k) - (n - Y))%n = m%n - Y (as k%n = Y)
=> 2m%n - 2k%n - n%n + Y%n = m%n - Y
=> 2m%n - Y - 0 + Y = m%n - Y (Y%n = Y as Y < n)
=> m%n = 0
=> 'm' should be multiple of 'n'
answered Dec 25, 2012 at 8:26
4
  • 1
    Now I like this answer more! Commented Oct 28, 2013 at 6:19
  • 1
    @pikooz, Is this proof true for any value of n, m and k? Commented Nov 18, 2013 at 23:01
  • @pikoooz, Suppose, if the loop begins after 1000 nodes. Hence, k=1000. If loop is very small, say of 4 nodes. Hence, n = 4. Hence, m will also be greater than 1000. So, how will n = m in this case? (Please correct me if I have gone wrong somewhere). Commented Nov 18, 2013 at 23:07
  • awesome explanation! +1 Commented Apr 10, 2018 at 4:41
25

First we try to find out, is there any loop in list or not. If loop exists then we try to find out starting point of loop. For this we use two pointers namely slowPtr and fastPtr. In first detection (checking for loop), fastPtr moves two steps at once but slowPtr moves by one step ahead at once.

enter image description here

slowPtr 1 2 3 4 5 6 7
fastPtr 1 3 5 7 9 5 7

It is clear that if there is any loop in list then they will meet at point (Point 7 in above image), because fastPtr pointer is running twice faster than other one.

Now, we come to second problem of finding starting point of loop.

Suppose, they meet at Point 7 (as mentioned in above image). Then, slowPtr comes out of loop and stands at beginning of list means at Point 1 but fastPtr still at meeting point (Point 7). Now we compare both pointers value, if they same then it is starting point of loop otherwise we move one step at ahead (here fastPtr is also moving by one step each time) and compare again till we find same point.

slowPtr 1 2 3 4
fastPtr 7 8 9 4

Now one question comes in mind, how is it possible. So there is good mathematical proof.

Suppose:

m => length from starting of list to starting of loop (i.e 1-2-3-4)
l => length of loop (i.e. 4-5-6-7-8-9)
k => length between starting of loop to meeting point (i.e. 4-5-6-7)
Total distance traveled by slowPtr = m + p(l) +k
where p => number of repetition of circle covered by slowPtr
Total distance traveled by fastPtr = m + q(l) + k
where q => number of repetition of circle covered by fastPtr
Since,
fastPtr running twice faster than slowPtr
Hence,
Total distance traveled by fastPtr = 2 X Total distance traveled by slowPtr
i.e
m + q(l) + k = 2 * ( m + p(l) +k )
or, m + k = q(l) - p(l)
or, m + k = (q-p) l
or, m = (q-p) l - k
So,
If slowPtr starts from beginning of list and travels "m" length then, it will reach to Point 4 (i.e. 1-2-3-4)
and
fastPtr start from Point 7 and travels " (q-p) l - k " length then, it will reach to Point 4 (i.e. 7-8-9-4),
because "(q-p) l" is a complete circle length with " (q-p) " times.

More detail here

answered Jul 3, 2016 at 12:19
1
  • Minor mistake in mathematical proof.m+q(l)+k=2*(m+p(l)+k) => m+k=q(l)-2*p(l) Commented Feb 8, 2017 at 5:37
4

Proceed in the usual way you will use to find the loop. ie. Have two pointers, increment one in single step(slowPointer) and other in two steps(fastPointer), If they both meet in sometime, there is a loop.

As you might would have already realized that meeting point is k Step before the head of the loop.

where k is size of non-looped part of the list.

now move slow to head of the loop

keep Fast at collision point

each of them are k STep from the loop start (Slow from start of the list where as fast is k step before the head of the loop- Draw the pic to get the clarity)

Now move them at same speed - They must meet at loop start

eg

slow=head
while (slow!=fast)
{
 slow=slow.next;
 fast=fast.next;
}
wattostudios
8,78013 gold badges45 silver badges58 bronze badges
answered Nov 4, 2012 at 6:05
4

This is code to find start of loop in linked List :

public static void findStartOfLoop(Node n) {
 Node fast, slow;
 fast = slow = n;
 do {
 fast = fast.next.next;
 slow = slow.next;
 } while (fast != slow); 
 fast = n;
 do {
 fast = fast.next;
 slow = slow.next;
 }while (fast != slow);
 System.out.println(" Start of Loop : " + fast.v);
}
answered Dec 5, 2013 at 4:46
1

There are two way to find the loops in a link list. 1. Use two pointer one advance one step and other advance two steps if there is loop, in some point both pointer get the same value and never reach to null. But if there is no loop pointer reaches to null in one point and both pointer never get the same value. But in this approach we can get there is a loop in the link list but we can't tell where exactly starting the loop. This is not the efficient way as well.

  1. Use a hash function in such a way that the value should be unique. Incase if we are trying to insert the duplicate it should through the exception. Then travel through each node and push the address into the hash. If the pointer reach to null and no exception from the hash means there is no cycle in the link list. If we are getting any exception from hash means there is a cycle in the list and that is the link from which the cycle is starting.
answered Jan 8, 2010 at 12:59
1

Well I tried a way by using one pointer... I tried the method in several data sets.... As the memory for each of the nodes of a linked list are allocated in an increasing order, so while traversing the linked list from the head of the linked list, if the address of a node becomes larger than the address of the node it is pointing to, we can determine there is a loop, as well as the beginning element of the loop.

answered Jan 14, 2012 at 7:46
1
  • 1
    In general case this (address increase with N) is not guaranteed, so your method wouldn't work. Commented Jan 11, 2013 at 2:50
1

The best answer I have found was here:
tianrunhe: find-loop-starting-point-in-a-circular-linked-list

  • 'm' being distance between HEAD and START_LOOP
  • 'L' being loop length
  • 'd' being distance between MEETING_POINT and START_LOOP
  • p1 moving at V, and p2 moving at 2*V

    when the 2 pointers meet: distance run is = m+ n*L -d = 2*(m+ L -d)

    => which means (not mathematicaly demonstrated here) that if p1 starts from HEAD & p2 starts from MEETING_POINT & they move at same pace, they will meet @ START_LOOP

answered Sep 28, 2013 at 17:41
1

Refer to this link for comprehensive answer.

Ahmad
72.9k18 gold badges116 silver badges139 bronze badges
answered Aug 23, 2013 at 10:18
3
  • 1
    I too had this bookmarked but now link seems broken? Commented Feb 28, 2015 at 14:32
  • @hari I fixed the link. Commented Dec 7, 2016 at 15:59
  • 2
    the updated link for post has been changed. Please update the new link: umairsaeed.com/posts/… Commented Dec 25, 2019 at 9:54
0
  1. Proceed in the usual way you will use to find the loop. ie. Have two pointers, increment one in single step and other in two steps, If they both meet in sometime, there is a loop.

  2. Keep one of the pointers fixed get the total number of nodes in the loop say L.

  3. Now from this point(increment second pointer to the next node in the loop) in the loop reverse the linked list and count the number of nodes traversed, say X.

  4. Now using the second pointer(loop is broken) from the same point in the loop travrse the linked list and count the number of nodes remaining say Y

  5. The loop begins after the ((X+Y)-L)2円 nodes. Or it starts at the (((X+Y)-L)2円+1)th node.

answered Sep 20, 2013 at 10:35
0
  1. Proceed in the usual way you will use to find the loop. ie. Have two pointers, increment one in single step and other in two steps, If they both meet in sometime, there is a loop.

  2. Keep one of the pointers fixed get the total number of nodes in the loop say L.

  3. Now from this point(increment second pointer to the next node in the loop) in the loop reverse the linked list and count the number of nodes traversed, say X.

  4. Now using the second pointer(loop is broken) from the same point in the loop travrse the linked list and count the number of nodes remaining say Y

  5. The loop begins after the ((X+Y)-L)2円 nodes. Or it starts at the (((X+Y)-L)2円+1)th node.

answered Sep 20, 2013 at 10:42
0
void loopstartpoint(Node head){
 Node slow = head.next;;
 Node fast = head.next.next;
 while(fast!=null && fast.next!=null){
 slow = slow.next;
 fast = fast.next.next;
 if(slow==fast){
 System.out.println("Detected loop ");
 break;
 }
 }
 slow=head;
 while(slow!=fast){
 slow= slow.next;
 fast = fast.next;
 }
 System.out.println("Starting position of loop is "+slow.data);
}
answered Mar 13, 2017 at 20:40
0
  1. Take two pointers, one fast and one slow. The slow pointer moves one node at a time, the fast pointer moves two nodes at a time, ultimately they'll meet and you'll find the loop.
  2. Now comes the fun part, how do you detect the loop? That's simple as well. Let me ask you another fun question first: How will you go about searching for the n-x the node in the list in one pass? The simple answer will be to take two pointers, one at the head, one at x steps ahead of the head and move them at the same speed, when the second pointer hits the end, the first one will be at n-x.
  3. As many other people have mathematically proved in this thread if one pointer moves at twice the speed of one pointer, the distance from start to the point at where they meet is going to be a multiple of the length of the list. Why is this the case?? As fast pointer is moving at twice the speed of slow pointer, can we agree that: Distance travelled by fast pointer = 2 * (Distance travelled by slow pointer)

now:

  1. If m is the length of the loop(nodes in the loop) that has no cyle

  2. If n is the actual length of the loop.

  3. x is the number of cycles slow pointer made

  4. y is the number of cycles fast pointer made.

  5. And K is the distance from the start of the loop to the point of meeting

  6. Note that this point is the only piece of length in the path of both the pointers that are going to be extra after their number of cycles of the loop. So besides this k rest of what they travelled are cycles of the loop and the initial distance from the head to the start of the loop. Hence, both are travelling m+n*(numbers of cycles they made) + k distance after their cycles at which they met each other. So, we can say that:

    (m + nx + k) = 2(m + n*y + k)

  7. When you solve this mathematically you'll discover that m+k is a multiple of the length of the loop that is n. i.e. [m + k = (x-2y)*n]

  8. So, if you maintain a distance that is a multiple of the length and move eventually you'll meet again at the start of the loop. Why? Can't they meet somewhere else? Well fast is already at k and slow is at the head, if they both travel m distance as k+m is the multiple of n, fast would be at the start of the loop. While if slow travels m distance it'll meet k as m is the distance from head to start of the loop as we originally assumed m to be.

  9. Hence, it is mathematically proved that the distance which both the the fast and slow pointer will have to travel if set the slow pointer to head again after detecting the loop and make them both travel at the The same speed is going to be m.

public class Solution {
 public ListNode detectCycle(ListNode head) {
 if(head==null||head.next==null)return null;
 ListNode slow = head;
 ListNode fast = head;
 while(fast.next!=null&&fast.next.next!=null){
 slow = slow.next;
 fast = fast.next.next;
 if(fast==slow){
 slow=head;
 while(slow!=fast){
 slow=slow.next;
 fast=fast.next;
 }
 return slow;
 }
 }
 return null;
 }
}
answered Jan 22, 2022 at 14:31
0

Pythonic code solution based on @hrishikeshmishra solution

def check_loop_index(head):
 if head == None or head.next == None:
 return -1
 
 slow = head.next
 if head.next.next == None:
 return -1
 fast = head.next.next
 
 # searching for loops exists or not
 while fast and fast.next:
 if slow==fast:
 break
 slow = slow.next
 fast = fast.next.next
 
 # checking if there is loop 
 if slow != fast:
 return -1
 
 # reseting the slow to head and creating index
 index = 0
 slow = head
 
 # incrementing slow and fast by 1 step and incrmeenting index, if they meet 
 # hen thats the index of node where loop starts
 while slow!=fast:
 slow = slow.next
 fast = fast.next
 index+=1
 return index
answered Feb 1, 2022 at 13:23
-2
  1. detect loop
  2. copy each element's address into set. If duplicate is found that's the start of the loop
answered Jan 13, 2017 at 1:02
-4

I have heard this exact question as an interview quiz question.

The most elegant solution is:

Put both pointers at the first element (call them A and B)

Then keep looping::

  • Advance A to the next element
  • Advance A to the next element again
  • Advance B to the next element
Every time you update a pointer, check if A and B are identical. If at some point pointers A and B are identical, then you have a loop. Problem with this approach is that you may end up moving around the loop twice, but no more than twice with pointer A

If you want to actually find the element that has two pointers pointing to it, that is more difficult. I'd go out of a limb and say its impossible to do with just two pointers unless you are willing to repeat following the linked list a large number of times.

The most efficient way of doing it with more memory, would be to put the pointers to the elements in and array, sort it, and then look for a repeat.

answered Oct 8, 2009 at 10:35
4
  • this is called the "Floyd's cycle detection algorithm" aka "The tortoise and hare" en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare Commented Oct 8, 2009 at 10:37
  • 5
    The solution by balki finds it using only two pointers and just looping the list few times. Commented Jul 26, 2011 at 15:57
  • It's easy to find the start of the cycle once you have found the collision point of A and B. (Other answers explain this well.) Also, if you really wanted to use extra memory, I'd recommend using a hashset rather than sorting an array, so at least the time-complexity stays O(N). Commented Jun 7, 2018 at 23:58
  • This doesn't answer the question, of finding starting point of loop. Commented Jun 3, 2021 at 4:42

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.