Linked List · 5 of 36
Find the starting point of the loop
A chain of boxes curls back into a loop somewhere, and you must find the exact box where the loop begins.
- Simple way
- time O(n)
room O(n) - Clever way
- time O(n)
room O(1)
The problem, simply
You get the head of a linked list. Maybe the last box points to null. Maybe it points back to some earlier box, which makes a loop.
If there is a loop, give back the box where the loop begins: the first box you would visit twice if you kept walking. If there is no loop, give back null. You may not change the list.
A story to remember it
Picture it
Seven boxes. The tail, 7, points back to 3. Boxes 1 and 2 are the driveway, and 3 to 7 are the ring. The answer is box 3.
- answer
- ignored
Examples
A loop after a driveway
- In
1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> back to 3- Out
box 3
The first box you would see twice is 3.
The whole list is the loop
- In
1 -> 2 -> back to 1- Out
box 1
The driveway has no boxes, so the loop begins at the head.
No loop
- In
1 -> 2 -> 3 -> null- Out
null
The walk ends, so there is no loop to find.
One box that points to itself
- In
5 -> back to 5- Out
box 5
A loop of one box. The head is the answer.
Same values, different boxes
- In
2 -> 2 -> 2 -> back to the second 2- Out
the second box
Compare boxes, not values. The answer is a node, and here several nodes hold the same number.
Way 1: the simple way
Carry a bag, a hash set. Put every box you step on into the bag. The first time you step on a box that is already in the bag, that box is where the loop begins, because it is the first box you ever see twice.
- Start at the head with an empty bag.
- If the box is in the bag, return it.
- Otherwise add it and step forward. If you reach
null, returnnull.
After 1 to 7 are in the bag, the next step lands on 3 again. 3 is the first repeat, so it is the answer.
- answer
class Solution {
public ListNode detectCycle(ListNode head) {
Set<ListNode> seen = new HashSet<>(); // stores boxes, not values
for (ListNode curr = head; curr != null; curr = curr.next) {
if (!seen.add(curr)) return curr; // add says false if it was there already
}
return null;
}
}How long it takes: each box goes into the bag once, O(n). Extra room: the bag holds every box, O(n).
Way 2: the clever way
Use the two cars, a slow pointer and a fast pointer.
- Phase one: move
slowone step andfasttwo steps until they meet. Iffastreachesnull, there is no loop. - Phase two: put
slowback on the head. Leavefastwhere they met. Move both one step at a time. - The box where they meet in phase two is where the loop begins.
Why it works, in numbers. Say the driveway has a boxes, the loop has L boxes, and they met b steps into the loop. Slow drove a + b steps and fast drove 2(a + b). The extra a + b steps of the fast car were whole laps, so a + b is a multiple of L. From the meeting spot, a more steps make a + b steps into the loop, which is a whole number of laps, so you are back at the gate. A car leaving the parking lot also needs a steps to reach the gate. They arrive together.
Meet inside the loop, then meet at its door
01 / 09
Two runners start on the head, 1. slow takes one step at a time, fast takes two.
- looking now
- checking
- answer
- path
class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next; // one step
fast = fast.next.next; // two steps
if (slow == fast) { // they met inside the loop
slow = head; // back to the parking lot
while (slow != fast) {
slow = slow.next; // both drive at the same speed now
fast = fast.next;
}
return slow; // the gate of the loop
}
}
return null; // fast found the end, no loop
}
}How long it takes: phase one ends within about a + L steps of slow, and phase two takes a steps, so O(n). Extra room: two pointers, O(1).
Watch out for
- Comparing values instead of boxes. Use
slow == faston the nodes. Two different boxes can hold the same number. - Checking
fast.next.nextwithout first checkingfast != null && fast.next != null. A plain list then crashes. - Starting
fastathead.nextwhileslowstarts athead. Then the phase two math is off by one and you get the wrong box. Start both at the head. - Moving
fasttwo steps in phase two. Both must move one step.
What interviewers ask next
- How long is the loop? After the first meeting, keep
faststill and walkslowaround until it returns. The number of steps isL. - Remove the loop. Find the gate with phase two, then walk from the gate around the loop to the box whose
nextis the gate and set thatnexttonull. - Find a duplicate in an array of
n + 1numbers from 1 tonwithout changing it (LeetCode 287). Treati -> nums[i]as anextarrow. The duplicate is the gate of the loop, found with the same two phases. - Why is fast guaranteed to land on slow and not jump over it? Once both are in the loop, each round fast gains exactly one step on slow, so the gap shrinks 1 at a time and hits 0.
Remember this
Grown-up words: the driveway is the part of the list before the cycle, the ring is the cycle, the gate is the cycle's entry node, the bag is a hash set, and the two cars are Floyd's tortoise and hare algorithm.