32

What's the best (halting) algorithm for determining if a linked list has a cycle in it?

[Edit] Analysis of asymptotic complexity for both time and space would be sweet so answers can be compared better.

[Edit] Original question was not addressing nodes with outdegree> 1, but there's some talk about it. That question is more along the lines of "Best algorithm to detect cycles in a directed graph".

eeerahul
1,6254 gold badges27 silver badges41 bronze badges
asked Aug 29, 2008 at 9:30
2

12 Answers 12

51

Have two pointers iterating through the list; make one iterate through at twice the speed of the other, and compare their positions at each step. Off the top of my head, something like:

node* tortoise(begin), * hare(begin);
while(hare = hare->next)
{
 if(hare == tortoise) { throw std::logic_error("There's a cycle"); }
 hare = hare->next;
 if(hare == tortoise) { throw std::logic_error("There's a cycle"); }
 tortoise = tortoise->next;
}

O(n), which is as good as you can get.

answered Aug 29, 2008 at 9:34
16
  • 5
    Oops, you actually have a bug. The while loop should be `while (hare && hare = hare->next) otherwise you could iterate right off the end. Commented Sep 18, 2008 at 20:21
  • 2
    You don't need while(hare && hare = hare->next). The only time that would protect you is if the list was empty (root is null). Otherwise, the while loop as defined will terminate as soon as hare->next returns null. Commented Sep 26, 2008 at 15:15
  • 10
    @Herms, hare is set to hare->next within the loop body so it's possible for hare to be null at this point. Commented Oct 9, 2008 at 18:12
  • 2
    There's a very detailed treatment of this subject in Elements of Programming by Stepanov & McJones. Chapter 2, which you can read here, elementsofprogramming.com/book.html covers this. Commented Aug 18, 2009 at 13:30
  • 19
    By the way, "off the top of your head" might imply to someone that you invented this algorithm, misleading people referring to it as "DrPizza's algorithm" (!). Lets give credit where it is due. This is published by Floyd as early as 1967: en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare Commented Jul 17, 2010 at 1:59
2

Precondition: Keep track of the list size (update the size whenenver a node is added or deleted).

Loop detection:

  1. Keep a counter when traversing the list size.

  2. If the counter exceeds the list size, there is a possible cycle.

Complexity: O(n)

Note: the comparison between the counter and the list size, as well as the update operation of the list size, must be made thread-safe.

answered May 16, 2011 at 22:18
1

Take 2 pointer *p and *q , start traversing the linked list "LL" using both pointers :

1) pointer p will delete previous node each time and pointing to next node.

2) pointer q will go each time in forward direction direction only.

conditions:

1) pointer p is pointing to null and q is pointing to some node : Loop is present

2) both pointer pointing to null: no loop is there

answered Jul 24, 2015 at 10:17
0

What about using a hash table to store the already seen nodes (you look at them in order from the start of the list)? In practise, you could achieve something close to O(N).

Otherwise, using a sorted heap instead of a hash table would achieve O(N log(N)).

answered Aug 29, 2008 at 9:33
1
  • Hash table solutions are O(n) space. Commented Nov 12, 2008 at 6:37
0

I wonder if there's any other way than just going iteratively - populate an array as you step forwards, and check if the current node is already present in the array...

answered Aug 29, 2008 at 9:34
0

Konrad Rudolph's algorithm won't work if the cycle isn't pointing to the beginning. The following list will make it an infinite loop: 1->2->3->2.

DrPizza's algorithm is definitely the way to go.

answered Aug 29, 2008 at 9:46
0

In this case OysterD's code will be the fastest solution (vertex coloring)

That would really surprise me. My solution makes at most two passes through the list (if the last node is linked to the penultimate lode), and in the common case (no loop) will make only one pass. With no hashing, no memory allocation, etc.

answered Aug 29, 2008 at 9:52
0

In this case OysterD's code will be the fastest solution (vertex coloring)

That would really surprise me. My solution makes at most two passes through the list (if the last node is linked to the penultimate lode), and in the common case (no loop) will make only one pass. With no hashing, no memory allocation, etc.

Yes. I've noticed that the formulation wasn't perfect and have rephrased it. I still believe that a clever hashing might perform faster – by a hair. I believe your algorithm is the best solution, though.

Just to underline my point: the vertec coloring is used to detect cycles in dependencies by modern garbage collectors so there is a very real use case for it. They mostly use bit flags to perform the coloring.

answered Aug 29, 2008 at 9:58
0

You will have to visit every node to determine this. This can be done recursively. To stop you visiting already visited nodes, you need a flag to say 'already visited'. This of course doesn't give you loops. So instead of a bit flag, use a number. Start at 1. Check connected nodes and then mark these as 2 and recurse until the network is covered. If when checking nodes you encounter a node which is more than one less than the current node, then you have a cycle. The cycle length is given by the difference.

answered Sep 26, 2008 at 15:05
0

Two pointers are initialized at the head of list. One pointer forwards once at each step, and the other forwards twice at each step. If the faster pointer meets the slower one again, there is a loop in the list. Otherwise there is no loop if the faster one reaches the end of list.

The sample code below is implemented according to this solution. The faster pointer is pFast, and the slower one is pSlow.

bool HasLoop(ListNode* pHead)
{
 if(pHead == NULL)
 return false;
 ListNode* pSlow = pHead->m_pNext;
 if(pSlow == NULL)
 return false;
 ListNode* pFast = pSlow->m_pNext;
 while(pFast != NULL && pSlow != NULL)
 {
 if(pFast == pSlow)
 return true;
 pSlow = pSlow->m_pNext;
 pFast = pFast->m_pNext;
 if(pFast != NULL)
 pFast = pFast->m_pNext;
 }
 return false;
}

This solution is available on my blog. There is a more problem discussed in the blog: What is the entry node when there is cycle/loop in a list?

Andrew Barber
40.2k20 gold badges97 silver badges124 bronze badges
answered Dec 28, 2012 at 13:35
0

"Hack" solution (should work in C/C++):

  • Traverse the list, and set the last bit of next pointer to 1.
  • If find an element with flagged pointer -- return true and the first element of the cycle.
  • Before returning, reset pointers back, though i believe dereferencing will work even with flagged pointers.

Time complexity is 2n. Looks like it doesn't use any temporal variables.

answered Oct 31, 2013 at 8:02
0

This is a solution using a Hash table ( just a list actually) to save the pointer address.

def hash_cycle(node):
 hashl=[]
 while(node):
 if node in hashl:
 return True
 else:
 hashl.append(node)
 node=node.next
 return False
answered Jul 19, 2016 at 8:16

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.