HARD Lab · Linked List · 58

143. Reorder List

Reorder a linked list from L0 → L1 → … → Ln to L0 → Ln → L1 → Ln-1 → … in-place by combining three classic linked-list primitives: find the middle with slow/fast pointers, reverse the second half, then merge the two halves alternately.

MediumLinked ListFast & Slow PointersReverseTypeScript

PROBLEM What we're solving

Given a singly-linked list, reorder it in-place so the first node is followed by the last, then the second, then the second-to-last, and so on — without allocating a new list. For 1 → 2 → 3 → 4 → 5 the answer is 1 → 5 → 2 → 4 → 3. For 1 → 2 → 3 → 4 it is 1 → 4 → 2 → 3.

KEY IDEA Split, reverse the back, then zip

Insight → the reordering is equivalent to interleaving the first half with the reversedsecond half. That reduces the problem to three sub-problems you already know: find the midpoint (fast & slow), reverse a linked list (three-pointer dance), and merge two lists alternately.

RECIPE Three-phase in-place algorithm

  • 1 · Find the middle. Run slow at one step and fast at two steps. When fast can't take two more steps, slow sits at the last node of the first half. This is the standard Floyd midpoint.
  • 2 · Reverse the second half. Cut the list by setting slow.next = null, then reverse the tail with a three-pointer walk (prev, curr, next).
  • 3 · Merge alternately. Advance two pointers p1 (original head) and p2 (reversed head), weaving them together one node at a time. Stop when either pointer runs out (the lengths differ by at most 1).
Classic confusion → when to stop the slow/fast loop. The condition is fast.next && fast.next.next (not fast && fast.next). Using the wrong condition shifts slow one node too far, putting the cut in the wrong place for odd-length lists.

COST Complexity & trade-offs

Copy to array, index both ends
O(n)
O(n) space — violates in-place constraint.
Split / reverse / merge
O(n)
O(1) extra space — three linear passes.

The array approach is the first thing most people code, and it does work. But interviewers specifically choose this problem to test whether you can do it without a buffer. The three-phase pointer solution uses exactly O(1) extra space and three O(n) passes.

Pattern transfer → the same three building-blocks appear in Palindrome Linked List (find mid + reverse + compare), Sort List (merge-sort needs the split + merge steps), and Rotate List (find a cut point, reverse portions).

RUN IT Find middle → reverse second half → merge alternately

step 0 / 13
STARTPhase 1: find the middle. slow moves 1 step, fast moves 2.
1slow/fast2345
State
0
slow
0
fast
1 – find mid
phase
first half / L1reversing second halfreversed half / L2 / donefast pointer
slowfast

TYPESCRIPT The solution, annotated

reorderList.ts
class ListNode {
  val: number;
  next: ListNode | null;
  constructor(val: number, next: ListNode | null = null) {
    this.val = val;
    this.next = next;
  }
}

function reorderList(head: ListNode | null): void {
  if (!head || !head.next) return;

  // ── Phase 1: find the middle ──────────────────────────────────────────────
  let slow: ListNode = head;
  let fast: ListNode | null = head;
  while (fast.next && fast.next.next) {
    slow = slow.next!;
    fast = fast.next.next;
  }
  // slow is now the last node of the first half

  // ── Phase 2: reverse the second half ─────────────────────────────────────
  let prev: ListNode | null = null;
  let curr: ListNode | null = slow.next;
  slow.next = null; // cut the list in two

  while (curr) {
    const next = curr.next;
    curr.next = prev;
    prev = curr;
    curr = next;
  }
  // prev is now the head of the reversed second half

  // ── Phase 3: merge alternately ────────────────────────────────────────────
  let p1: ListNode | null = head;
  let p2: ListNode | null = prev;

  while (p1 && p2) {
    const n1 = p1.next;
    const n2 = p2.next;
    p1.next = p2;
    p2.next = n1;
    p1 = n1;
    p2 = n2;
  }
}

Reading it block by block

Lines 13–14 — trivial guard. A list of 0 or 1 nodes is already ordered; return immediately to avoid null-pointer issues in subsequent phases.
Lines 16–21 — Phase 1: find the middle. The classic Floyd two-pointer trick. slow advances one step and fast advances two. When the loop ends, slow lands on the last node of the first half. The loop condition fast.next && fast.next.next is the critical detail — it leavesslow at the correct cut point for both even- and odd-length lists.
Lines 23–31 — Phase 2: reverse the second half. Set slow.next = null to physically split the list, then walk curr forward while threading each node back to prev. After the loop, prev is the new head of the reversed tail.
Lines 33–43 — Phase 3: merge alternately. Two pointers walk the two halves in lockstep. Each iteration saves the next pointers, stitches p1 → p2 → n1, then advances both. Because the second half is at most as long as the first, the loop ends cleanly when either pointer is exhausted.
Complexity → O(n) time — three linear passes (find mid, reverse, merge), each touching every node at most once. O(1) extra space — only a constant number of pointer variables are allocated; no new nodes or arrays.

INTERVIEWFollow-ups they'll ask

  • "What if the list has exactly 2 nodes?" The while condition fast.next && fast.next.next is immediately false, so slow stays at head; the second-half list is just the second node reversed, and the merge produces 1 → 2 (already correct).
  • "Can you do it recursively?" Yes, but the call stack is O(n) deep, which violates the spirit of the in-place constraint. The iterative solution is preferred in interviews.
  • "What if you're given the length?" You can skip the slow/fast step and walk exactly length / 2 steps to find the midpoint — same asymptotic cost, slightly simpler code.
  • "Generalise: every k-th node from the front pairs with k-th from the back?" Still the same pattern — split at the midpoint, reverse the second half, zip. The merge step just changes which node you pick next.
  • "How would you handle a doubly-linked list?" Maintain prev pointers during the merge phase; the split and reverse are simpler since you already have back-references.

MNEMONIC The one-liner

"Find the seam, flip the back, then zipper the two halves together."

TRIGGERS When you see ___ → reach for ___

"reorder / interleave first and last"split + reverse + merge
find midpoint of a linked listslow/fast (Floyd) pointers
reverse a linked list in-placeprev/curr/next three-pointer walk
"palindrome linked list" checkfind mid + reverse second half + compare

SKELETON The reusable shape

skeleton.ts
// Phase 1: find middle
let slow = head, fast = head;
while (fast?.next?.next) { slow = slow.next!; fast = fast.next.next; }

// Phase 2: reverse second half
let prev: ListNode | null = null, curr = slow.next;
slow.next = null;
while (curr) { const nxt = curr.next; curr.next = prev; prev = curr; curr = nxt; }

// Phase 3: merge alternately
let p1: ListNode | null = head, p2 = prev;
while (p1 && p2) {
  const n1 = p1.next, n2 = p2.next;
  p1.next = p2; p2.next = n1;
  p1 = n1; p2 = n2;
}

FLASHCARDS Tap to flip

What are the three phases of Reorder List?
Find the middle (slow/fast), reverse the second half (three-pointer), merge alternately.
tap to flip
1 / 7
Answer to reveal explanations. No penalty for retries.Score: 0 / 7
QUESTION 01
What is the overall time and space complexity of the optimal in-place solution?
QUESTION 02
For the input list 1 → 2 → 3 → 4, what is the reordered output?
QUESTION 03
What is the correct slow/fast loop termination condition to find the midpoint?
QUESTION 04
Why must slow.next be set to null before reversing the second half?
QUESTION 05
During the merge phase, why is it safe to stop when either p1 or p2 becomes null?
QUESTION 06
Which sibling problem uses the same "find mid + reverse + compare" pattern?
QUESTION 07
After phase 2 the reversed second half of 1→2→3→4→5 is: