DEV Community

Cover image for A classic linked list problem: reorder the list (mental model and dry run)
Tarang
Tarang

Posted on

A classic linked list problem: reorder the list (mental model and dry run)

Reorder the linked list

A classic linked list problem — one exercise that brushes several core ideas at once:

  • finding the middle (slow / fast pointers)
  • reversing a chain in place
  • merging two lists by rewiring next pointers

Problem

Given the head of a singly linked list, the list can be written as:

L0 → L1 → … → Ln-1 → Ln
Enter fullscreen mode Exit fullscreen mode

Reorder it to:

L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → …
Enter fullscreen mode Exit fullscreen mode

Example with numbers:

1 → 2 → 3 → 4 → 5   becomes   1 → 5 → 2 → 4 → 3
1 → 2 → 3 → 4       becomes   1 → 4 → 2 → 3
Enter fullscreen mode Exit fullscreen mode

Mental model: three steps

step 1: find the middle of the list
step 2: reverse the second half
step 3: merge the two halves
Enter fullscreen mode Exit fullscreen mode
   L0 → L1 → … → slow | slow.next → … → Ln
        |
        +-- firstll                    secondll (then reversed)
        |
        v
   zip: L0 → Ln → L1 → Ln-1 → …
Enter fullscreen mode Exit fullscreen mode

After step 1 you have firstll (head) and secondll (slow.next). Set slow.next = null so the list is cut in two. Step 2 makes secondll start at the old tail. Step 3 alternates links between firstll and secondll.


Step 1 — Find the middle (dry run)

Code shape:

let slow = head;
let fast = head;
while (fast && fast.next) {
  fast = fast.next.next;
  if (!fast) {
    break;
  }
  slow = slow.next;
}
let firstll = head;
let secondll = slow.next;
slow.next = null;
Enter fullscreen mode Exit fullscreen mode

Move fast two steps first. Only if fast is still non-null do you move slow one step. If fast becomes null, stop without moving slow again.

Odd: 1 → 2 → 3 → 4 → 5

Loop fast after fast = fast.next.next slow after stop?
start 1 1
1 3 2
2 5 3
3 null 3 break (slow unchanged)
  • firstll: 1 → 2 → 3 (slow.next = null)
  • secondll: 4 → 5

Even: 1 → 2 → 3 → 4

Loop fast after jump slow after stop?
start 1 1
1 3 2
2 null 2 break
  • firstll: 1 → 2
  • secondll: 3 → 4

Step 2 — Reverse secondll (dry run)

Same loop as in your code: curr, prev, future.

let curr = secondll;
let prev = null;
let future;
while (curr) {
  future = curr.next;
  curr.next = prev;
  prev = curr;
  curr = future;
}
secondll = prev;
Enter fullscreen mode Exit fullscreen mode

Starting from 4 → 5 (odd example)

curr future after curr.next = prev prev curr next
4 5 null ← 4 4 5
5 null null ← 4 ← 5 5 null

secondll = prev → 5 → 4

Starting from 3 → 4 (even example) → 4 → 3


Step 3 — Merge (dry run)

let firstllnext;
let secondllnext;
while (firstll && secondll) {
  firstllnext = firstll.next;
  secondllnext = secondll.next;

  firstll.next = secondll;
  secondll.next = firstllnext;

  firstll = firstllnext;
  secondll = secondllnext;
}
Enter fullscreen mode Exit fullscreen mode

Each iteration: save both .next, plug second right after first, put the saved first .next after second, then walk both pointers forward.

Odd example — after step 1 and 2

firstll:  1 → 2 → 3
secondll: 5 → 4
Enter fullscreen mode Exit fullscreen mode
Round firstll secondll wires list from head
start 1 5 two separate chains
1 2 4 1→5, 5→2 1 → 5 → 2 → 3 and 4
2 3 null 2→4, 4→3 1 → 5 → 2 → 4 → 3
done 3 — loop ends 1 → 5 → 2 → 4 → 3

When secondll is null, 3 is already the tail. No extra work.

Even example

firstll:  1 → 2
secondll: 4 → 3
Enter fullscreen mode Exit fullscreen mode
Round firstll secondll list from head
1 2 3 1 → 4 → 2 and 3
2 null null 1 → 4 → 2 → 3

Full solution (JavaScript)

var reorderList = function (head) {
  let slow = head;
  let fast = head;
  while (fast && fast.next) {
    fast = fast.next.next;
    if (!fast) {
      break;
    }
    slow = slow.next;
  }
  let firstll = head;
  let secondll = slow.next;

  slow.next = null;

  let curr = secondll;
  let prev = null;
  let future;
  while (curr) {
    future = curr.next;
    curr.next = prev;
    prev = curr;
    curr = future;
  }
  secondll = prev;

  let firstllnext;
  let secondllnext;
  while (firstll && secondll) {
    firstllnext = firstll.next;
    secondllnext = secondll.next;

    firstll.next = secondll;
    secondll.next = firstllnext;

    firstll = firstllnext;
    secondll = secondllnext;
  }
  return firstll;
};
Enter fullscreen mode Exit fullscreen mode

The list is reordered in place starting from head. The return value is not what you use; the rewired nodes from head are the answer.


What to remember

Step Variables in your code
Middle slow, fast, then firstll / secondll, cut with slow.next = null
Reverse curr, prev, future → secondll = prev
Merge firstllnext, secondllnext, alternate .next

The pattern is always: split → reverse the back → zip.

Top comments (0)