Linked List · 4 of 36
Write a program to Delete loop in a linked list
Fix a chain of boxes that bends back into a circle by finding the box where the circle starts and cutting the arrow that leads back to it.
- Simple way
- time O(n)
room O(n) - Clever way
- time O(n)
room O(1)
The problem, simply
You get a linked list that may have a loop: the last box points back to an earlier box instead of to null. Remove the loop so the chain ends in null again, without losing any box. If there is no loop, leave the chain alone.
To cut the loop you must find one special box: the last box of the loop, the one whose next points back to where the loop starts. Set its next to null.
A story to remember it
Picture it
Box 8 points back to box 2. The loop is 2, 7, 4, 8. The arrow to cut is the one leaving box 8.
- rejected
- path
Examples
A loop in the middle
- In
5 -> 9 -> 2 -> 7 -> 4 -> 8 -> back to 2- Out
5 -> 9 -> 2 -> 7 -> 4 -> 8 -> null
Box 8's arrow is cut. All six boxes stay.
The loop starts at the head
- In
1 -> 2 -> 3 -> back to 1- Out
1 -> 2 -> 3 -> null
The box to cut is 3. This is the case many solutions get wrong, by cutting after the head instead.
A box pointing to itself
- In
4 -> 6 -> back to 6- Out
4 -> 6 -> null
The loop is a single box. Its own arrow is the one to cut.
No loop
- In
1 -> 2 -> 3 -> null- Out
1 -> 2 -> 3 -> null
Nothing to cut. Return the chain unchanged.
Way 1: the simple way
Keep a guest book of boxes, a hash set. Walk with prev and curr. The first time curr lands on a box already in the book, prev is the last box of the loop, so cut prev.next.
prev = null,curr = head.- If
curris in the book, setprev.next = nulland stop. - Otherwise add
currto the book and step both forward.
All six boxes are in the book. curr follows the back arrow to box 2, which is already there, so prev (box 8) gets its arrow cut.
- checking
- seen
- rejected
import java.util.*;
class Solution {
public ListNode removeLoop(ListNode head) {
Set<ListNode> seen = new HashSet<>();
ListNode prev = null;
for (ListNode curr = head; curr != null; curr = curr.next) {
if (seen.contains(curr)) { // stepped back into the loop
prev.next = null; // prev is the last box of the loop
break;
}
seen.add(curr);
prev = curr;
}
return head;
}
}How long it takes: every box is visited once, O(n). Extra room: the book holds every box, O(n).
Way 2: the clever way
Three short walks with fingers, no book.
- Find a meeting box.
slowmoves 1 box,fastmoves 2. Iffastreachesnull, there is no loop; stop. If they meet, both are on the loop. - Find the loop start. Put
slowback on the head and leavefaston the meeting box. Move both one box at a time. They meet exactly at the start of the loop. - Find the last box. Walk from the start around the loop until the box whose
nextis the start. Cut its arrow.
Why step 2 works: if the path before the loop has m boxes, then walking m boxes forward from the meeting box also lands on the loop start. So two fingers, one at the head and one at the meeting box, arrive at the start together.
Meet, find the start, cut the last arrow
01 / 09
Step 1. slow and fast start on the head, 5.
- looking now
- checking
- seen
- done
- answer
- rejected
class Solution {
public ListNode removeLoop(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) { // step 1: find a meeting box
slow = slow.next;
fast = fast.next.next;
if (slow == fast) break;
}
if (fast == null || fast.next == null) return head; // no loop
slow = head; // step 2: find the loop start
while (slow != fast) {
slow = slow.next;
fast = fast.next;
}
ListNode last = slow; // step 3: find the last box
while (last.next != slow) last = last.next;
last.next = null; // cut the loop
return head;
}
}How long it takes: step 1 is at most about one walk plus one lap, step 2 at most m steps, step 3 one lap. All together O(n). Extra room: three fingers, O(1). When the loop starts at the head, step 2 ends at once, and step 3 still walks the full lap to find the real last box, so that case needs no special code.
Watch out for
- No loop:
fastreachesnull. Check this before step 2, or step 2 runs off the end. - Use
==on boxes, never their numbers. - In C or C++ you only cut an arrow here; nothing is freed, since every box is still part of the chain.
What interviewers ask next
- Prove step 2. Say
mboxes come before the loop and the loop haskboxes. When they meet,slowwalkedm + aboxes andfastwalked twice that, which ism + aplus some whole laps. Som + ais a multiple ofk, and walkingmmore boxes from the meeting point completes a lap and lands on the start. - How long is the loop? From the meeting box, count steps until you are back.
- Can you do it with the loop length? Yes. Count the loop length
k, put one fingerkboxes ahead of another at the head, then move both until they meet at the start. Watch the head case again. - How do you know you did not lose boxes? Count boxes with the hash set version before and with a normal walk after. Both must match.
Remember this
Grown-up words: the loop is a cycle, the two trains are slow and fast pointers (Floyd's tortoise and hare), the switch is the cycle's entry node, and the guest book is a hash set of nodes.