Remove Loop in Linked List takes the classic cycle-detection trick one step further. It is not enough to know a loop exists — we have to find exactly where it starts and snip the single back-edge that closes it, turning a tangled list back into a clean, terminating chain.
Problem. Given the head of a singly linked list that may contain a loop, remove the loop in place
(do not change node values). A loop means some node points back to an earlier node instead of to None.
After removal the list must end in None.
Example: 1 -> 2 -> 3 -> 4 -> 5 -> 6, and node 6 points back to node 3. The answer rewires node 6
to point at None, leaving 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> None.
The slow way first
The obvious idea: walk the list and remember every node you have visited in a hash set. The moment you
step onto a node you have already seen, you have found the start of the loop — and the previous node is the
tail whose next you must clear. That works, but it costs O(n) extra space for the set.
The question to ask: can I find the loop start using only a couple of pointers? Yes. Floyd’s tortoise-and-hare detects the loop in O(1) space, and a small follow-up walk pinpoints exactly where the loop begins.
The idea: detect, then locate, then cut
Use two pointers. slow advances one node per step; fast advances two. If there is a loop, fast
eventually laps slow and they land on the same node inside the loop. That meeting point is not the
loop start — but here is the magic: reset slow to the head and advance both pointers one step at a
time. They are guaranteed to meet again exactly at the loop start. Finally, walk one pointer around the
loop until its next is the loop start; that node is the tail, so set its next = None.
The reason resetting works comes from the geometry: the distance from the head to the loop start equals the distance from the meeting point to the loop start (measured around the loop). So two pointers moving at the same speed from those two places arrive together at the start.
Walk through it
Step through the animation. In phase 1, slow and fast race until they collide on node 4 inside the
loop. In phase 2, slow jumps back to the head while fast stays put, and both advance one node per step
until they meet at node 3 — the loop start. In phase 3, fast walks on until fast.next is node 3,
landing on node 6, and we clear node 6’s next to delete the back-edge.
Pseudocode
slow = fast = head
move slow by 1 and fast by 2 until they meet (or fast falls off the end)
if fast fell off the end: there is no loop, return head
slow = head
move slow and fast each by 1 until slow == fast # this is the loop start
move fast around the loop until fast.next == slow # fast is now the tail
fast.next = None # cut the back-edge
return headThe Python solution
def remove_loop(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow is fast:
break
else:
return head # no loop
slow = head
while slow is not fast:
slow = slow.next
fast = fast.next
while fast.next is not slow:
fast = fast.next
fast.next = None # drop the back-edge
return headslow = fast = headparks both pointers at the head to begin phase 1.- The first
whileis the detection loop:slowmoves one node,fastmoves two. If they ever land on the same node (slow is fast), webreak— a loop exists. - The
while ... elseis a Python trick: theelseruns only if the loop finished without breaking, meaningfastreachedNone. That means no loop, so we return the head untouched. slow = headstarts phase 2. We then stepslowandfastone node at a time untilslow is fast; that shared node is the loop start.- The last
whileis phase 3: advancefastaround the loop untilfast.nextis the loop start. Nowfastis the tail node whose pointer closes the loop. fast.next = Nonecuts the back-edge, and the list now terminates cleanly.
Complexity
| Case | Time | Notes |
|---|---|---|
| Hash set (remember visited nodes) | O(n) (moderate) | O(n) extra space for the set |
| Floyd (this solution) | O(n) (moderate) | a few full passes, O(1) extra space |
O(1) (fast)Both approaches are linear time, but Floyd does it with only two pointers — no auxiliary structure — so it wins on space. That O(1)-space cycle handling is exactly why interviewers love this pattern.
When this pattern shows up
Any linked-list question about cycles — "does it have a loop," "where does the loop start," "find the node where two lists merge," "find the duplicate number in an array" — is the same tortoise-and-hare move. Detect with the 1-step/2-step race, then reset one pointer to the head and walk both at equal speed to pinpoint the entry node.
Cut the back-edge by clearing the tail’s next, not the loop start’s incoming view. A common bug is
stopping fast one node too early or too late. Advance fast until fast.next is the loop start, so
fast is the last node of the loop — then set fast.next = None.
Practice
In the example, slow and fast first meet at node 4. After resetting slow to the head, how many one-step moves does each pointer take before they meet again, and at which node?
1. After slow and fast first meet inside the loop, what do we do next?
2. Which node's next pointer do we set to None to remove the loop?
3. What is the extra space used by the Floyd-based solution?
4. What happens if the list has no loop?