Skip to content
dsa450for five year olds

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

linked list
10203040506070head

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.

  1. Walk around the ring once and count the boxes, n.
  2. The first ring gets (n + 1) / 2 boxes. Walk that many minus one steps from the head to find tail1.
  3. head2 = tail1.next. Walk from head2 until the box whose next is the old head. That is tail2.
  4. Close both rings: tail1.next = head1 and tail2.next = head2.
linked list
10203040506070tail2head2tail1head1

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
java
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.

  1. Both start at the head.
  2. Keep going while fast.next and fast.next.next are not the head.
  3. When they stop, slow is the end of the first half. If the ring is even, fast is one box short of the tail, so move it one more step. Now fast is the last box of the ring.
  4. head2 = slow.next. Close the rings: slow.next = head and fast.next = head2.

Two runners find the middle of the ring

01 / 06

linked list
10203040506070fastslow

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.

java
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, fast is 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 to head, the two rings stay glued together.

What interviewers ask next

  • Why do two runners find the middle? fast covers twice the distance of slow, so when fast has done nearly a full lap, slow has done half.
  • Second ring bigger when odd? Change the stop rule so slow stops one box earlier, or move slow back by one with a trailing finger.
  • Split a normal (not circular) list into halves? Same runners, stop on null instead of head. 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 to head, 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.