Linked List · 17 of 36
Split a Circular linked list into two halves
Cut a ring of boxes into two smaller rings of equal size by sending a fast runner and a slow runner around it once.
- Simple way
- time O(n)
room O(1) - Clever way
- time O(n)
room O(1)
The problem, simply
You get a circular linked list: a chain of boxes where the last box points back to the first one, so there is no null at the end.
Split it into two circular lists of half the size each. If the number of boxes is odd, the first ring gets one extra box. Give back the heads of both rings.
A story to remember it
Picture it
A ring of seven boxes. The arrow under the boxes shows that 70 points back to 10. There is no null.
Examples
An odd ring
- In
10 -> 20 -> 30 -> 40 -> 50 -> 60 -> 70 -> back to 10- Out
10 -> 20 -> 30 -> 40 -> back to 10, and 50 -> 60 -> 70 -> back to 50
Seven boxes: the first ring gets 4 and the second gets 3.
An even ring
- In
1 -> 2 -> 3 -> 4 -> back to 1- Out
1 -> 2 -> back to 1, and 3 -> 4 -> back to 3
Four boxes split into 2 and 2.
Two boxes
- In
5 -> 8 -> back to 5- Out
5 -> back to 5, and 8 -> back to 8
Each ring has one box that points to itself.
One box
- In
7 -> back to 7- Out
7 -> back to 7, and null
There is nothing to split off. The second ring is empty.
Way 1: the simple way
Count first, then cut.
- Walk around the ring once and count the boxes,
n. - The first ring gets
(n + 1) / 2boxes. Walk that many minus one steps from the head to findtail1. head2 = tail1.next. Walk fromhead2until the box whosenextis the old head. That istail2.- Close both rings:
tail1.next = head1andtail2.next = head2.
n is 7, so the first ring gets 4 boxes. tail1 is 40, head2 is 50 and tail2 is 70.
- n
- 7
- first ring size
- 4
- checking
- path
class Solution {
public ListNode[] split(ListNode head) {
if (head == null || head.next == head) return new ListNode[] { head, null };
int n = 1;
for (ListNode cur = head.next; cur != head; cur = cur.next) n++; // count once around
ListNode tail1 = head;
for (int i = 1; i < (n + 1) / 2; i++) tail1 = tail1.next;
ListNode head2 = tail1.next;
ListNode tail2 = head2;
while (tail2.next != head) tail2 = tail2.next; // the box before head
tail1.next = head; // close the first ring
tail2.next = head2; // close the second ring
return new ListNode[] { head, head2 };
}
}How long it takes: one full lap to count and about one more lap to find the cut and the tail, so O(n). Extra room: O(1).
Way 2: the clever way
Use two runners on the ring: slow moves one box per turn, fast moves two.
- Both start at the head.
- Keep going while
fast.nextandfast.next.nextare not the head. - When they stop,
slowis the end of the first half. If the ring is even,fastis one box short of the tail, so move it one more step. Nowfastis the last box of the ring. head2 = slow.next. Close the rings:slow.next = headandfast.next = head2.
Two runners find the middle of the ring
01 / 06
Both runners start on 10. The ring has no end, so we watch for the runners getting back to the head.
- looking now
- checking
- done
- answer
- path
- just added
For an even ring like 1 2 3 4, the loop stops with slow on 2 and fast on 3, because fast.next.next is the head. That is the case where fast takes one more step, to 4, before closing the rings.
class Solution {
public ListNode[] split(ListNode head) {
if (head == null || head.next == head) return new ListNode[] { head, null };
ListNode slow = head, fast = head;
while (fast.next != head && fast.next.next != head) {
slow = slow.next; // one step
fast = fast.next.next; // two steps
}
if (fast.next.next == head) fast = fast.next; // even ring: reach the last box
ListNode head2 = slow.next;
slow.next = head; // close the first ring
fast.next = head2; // close the second ring
return new ListNode[] { head, head2 };
}
}How long it takes: fast goes around the ring once, O(n). Extra room: O(1). It reads each box about once instead of twice.
Watch out for
- The even case: without the extra step,
fastis not the last box, and closing the second ring drops a box. - The one box ring:
head.next == head. Return it with an empty second ring before running the loop. - Close the second ring with the old tail, not with
head. If the old tail still points tohead, the two rings stay glued together.
What interviewers ask next
- Why do two runners find the middle?
fastcovers twice the distance ofslow, so whenfasthas done nearly a full lap,slowhas done half. - Second ring bigger when odd? Change the stop rule so
slowstops one box earlier, or moveslowback by one with a trailing finger. - Split a normal (not circular) list into halves? Same runners, stop on
nullinstead ofhead. This is the first step of merge sort on a list. - Check if a list is circular at all? Walk from the head. If you reach
null, it is not. If you get back tohead, it is. For a loop that does not go back to the head, use the slow and fast runners.
Remember this
Grown-up words: the ring is a circular linked list, the two runners are the fast and slow pointer technique (also called the tortoise and the hare), and the boxes are nodes.