Learn/DSA Patterns
DSA PatternsLinked Listsmedium9 min read

Reorder List

Reorder a linked list so that nodes interleave from the front and back: first, last, second, second-last, … Three steps in sequence — find the middle, reverse the second half, merge the two halves alternately — each step using patterns you already know from Chapters 4.2 and 4.3.

#linked-list#reorder#three-step#merge#reverse#middle#beginner#interview
Table of contents

Before we start

This problem sits at the intersection of three techniques you have already learned. It is the clearest example of how linked list patterns compose: finding the middle (Chapter 4.2) + reversing a list (Chapter 4.3) + merging two lists (Chapter 4.4) = a problem that looks hard but writes itself once you see the structure. By the end you will be able to:

  • Break the problem into three named sub-problems and solve each independently.
  • Write the full solution from memory, correctly handling the odd/even length edge.
  • Trace the full 5-node example step by step.

Picture this first (no code yet)

A real-life story

You have a deck of numbered cards face-down in order: 1, 2, 3, 4, 5. You want to reorder them as 1, 5, 2, 4, 3 — alternating from the front and back.

Your strategy: split the deck in half. Flip the second half face-up (reverse it) so it reads 5, 4 instead of 4, 5. Now interleave: take one from the front deck (1), one from the flipped deck (5), one from the front (2), one from the flipped (4), then the remaining front card (3).

That is exactly the three-step algorithm for reorder list: find the split point, reverse the second half, merge alternately.


The actual problem

Reorder List (LC #143):

Given a linked list 1 → 2 → 3 → 4 → 5, reorder it in-place to 1 → 5 → 2 → 4 → 3.

The pattern: node 1, then node n, then node 2, then node n−1, and so on.

No extra array allowed — this must be done by pointer rewiring in O(n) time, O(1) space.


First, the slow way (so you feel the pain)

Copy all node references into an array. Use two-pointer technique on the array (left, right) to build the new order. O(n) time but O(n) space for the array. For a list of 10 million nodes, that is 80 MB of extra memory just for pointers. The in-place approach uses O(1) space — three pointer variables during each step.


The turning point

Pause & think

For 1 → 2 → 3 → 4 → 5, the result is 1 → 5 → 2 → 4 → 3. Look at it as two interleaved sequences:

  • Front half: 1 → 2 → 3
  • Second half reversed: 5 → 4

If you had both of these as separate lists, how would you merge them to produce 1 → 5 → 2 → 4 → 3? It's just alternating: one from list 1, one from list 2, repeat.

So the problem reduces to: (1) get the front half, (2) get the reversed second half, (3) merge alternately. You already know how to do each of these.


The one idea to remember

The entire pattern in one sentence

Find the middle with fast/slow pointers, cut the list in two, reverse the second half, then merge the two halves alternately — one node from each, in turn.


The three steps in code

def reorderList(head) -> None:
    if not head or not head.next:
        return

    # ── Step 1: Find the middle (first middle for even length) ──────
    slow, fast = head, head.next
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    # slow is now the last node of the first half

    # ── Step 2: Reverse the second half ─────────────────────────────
    second = slow.next   # head of second half
    slow.next = None     # cut the list: disconnect first and second halves

    prev, curr = None, second
    while curr:
        nxt       = curr.next
        curr.next = prev
        prev      = curr
        curr      = nxt
    second = prev        # prev is now the head of the reversed second half

    # ── Step 3: Merge the two halves alternately ────────────────────
    first = head
    while second:                  # second half is always ≤ first half in length
        tmp1        = first.next   # save next of first
        tmp2        = second.next  # save next of second
        first.next  = second       # wire: first → second
        second.next = tmp1         # wire: second → (next of first)
        first       = tmp1         # advance first
        second      = tmp2         # advance second

Note: this function modifies the list in-place and returns None (the list is modified through head).


Watch it happen, frame by frame

Input: 1 → 2 → 3 → 4 → 5

Step 1: Find middle

slow=1, fast=2
Iter 1: slow=2, fast=4
Iter 2: slow=3, fast=None (fast=4.next.next=None — wait, fast=4, fast.next=5, fast.next.next=None)

Let me re-trace with fast=head.next:
slow=1, fast=2
Iter 1: slow=2, fast=4   (fast=2.next.next=4)
Iter 2: slow=3, fast=None  (fast=4.next.next=None)

slow = node3 (last of first half). ✅

Step 2: Reverse second half

second = node4.  slow.next = None → first half: 123→None

Reverse 4→5→None:
prev=None, curr=4
  nxt=5, 4.next=None, prev=4, curr=5
  nxt=None, 5.next=4, prev=5, curr=None

second = node5.  Reversed: 54→None

Step 3: Merge alternately

first=node1, second=node5

Iter 1:
  tmp1=node2, tmp2=node4
  1.next=5, 5.next=2
  first=node2, second=node4
  List so far: 1→5→2→...

Iter 2:
  tmp1=node3, tmp2=None
  2.next=4, 4.next=3
  first=node3, second=None
  List so far: 1→5→2→4→3→None

second=None → while exits.

Final: 1→5→2→4→3  ✅

The even-length case: 1 → 2 → 3 → 4

Step 1: slow=1, fast=2

Iter 1: slow=2, fast=4  (fast=2.next.next=4)
Iter 2: fast.next=None → exit.
slow = node2. (first middle of even list)

First half: 1→2→None.  Second half: 3→4→None.

Step 2: Reverse 3→44→3→None. second=node4.

Step 3:

first=1, second=4
Iter 1: tmp1=2, tmp2=3. 1.next=4, 4.next=2. first=2, second=3.
Iter 2: tmp1=None, tmp2=None. 2.next=3, 3.next=None. first=None, second=None.

Final: 1423

Why slow.next = None (cutting the list) matters

Without the cut, when you reverse the second half, the first node of the second half (originally pointing forward) still has its old next pointer — which points to nodes in the first half. That creates a cycle during the merge step, leading to an infinite loop. The cut (slow.next = None) cleanly separates the two halves so the reversal and merge operate on independent lists.


Where to spot this pattern

Trigger words:

  • "reorder list" / "rearrange list" in place
  • "interleave front and back"
  • "palindrome linked list" — almost the same: find middle, reverse second, compare (no merge step)
  • any problem that needs the "second half reversed" of a list

5 disguises:

  1. Reorder List (LC #143): find middle → reverse → merge alternately.
  2. Palindrome Linked List (LC #234): find middle → reverse second → compare (no merge).
  3. Sort List (LC #148): find middle → recursive sort each half → merge (Chapter 4.4).
  4. Reverse Nodes in Even Length Groups: find group boundaries, reverse even groups — partial reversal (Chapter 4.3).
  5. Odd Even Linked List (LC #328): separate odd-indexed and even-indexed nodes into two chains, then append even chain to odd — same split-and-rejoin idea.

Common traps

Watch out for these

  • Not cutting the list at the middle. If slow.next is not set to None before reversing, the first half and second half remain connected, and the reversal creates a cycle. Always cut before reversing.
  • Using the wrong middle for even-length lists. The fast = head.next variant gives the first middle (correct here — the first half should be the same length as or one longer than the second half). Using fast = head gives the second middle, making the first half longer by one extra node — still correct but think through which half is "first" and "second."
  • Not saving first.next and second.next before rewiring in Step 3. The merge step overwrites first.next and second.next immediately — save both in tmp1 and tmp2 first or you lose the chain.
  • Forgetting to return None (or trying to return the head). LC #143 says "Do not return anything, modify head in-place instead." The modification through head is visible to the caller because we never change what head points to — we only change the .next pointers of its nodes.

Remember this forever

Reorder List — 3 steps

# Step 1: Find middle (first middle)
slow, fast = head, head.next
while fast and fast.next:
    slow = slow.next; fast = fast.next.next

# Step 2: Reverse second half
second = slow.next;  slow.next = None   # ← cut!
prev = None
while second:
    nxt = second.next; second.next = prev; prev = second; second = nxt
second = prev

# Step 3: Merge alternately
first = head
while second:
    tmp1, tmp2 = first.next, second.next
    first.next = second;  second.next = tmp1
    first, second = tmp1, tmp2

Trap: cut the list (slow.next = None) before reversing. Save tmp1/tmp2 before rewiring.


Check yourself

Why does the merge loop condition check `while second:` and not `while first and second:`?

The first half is always ≥ the second half in length (by our choice of middle). For odd-length lists, the first half has one more node; for even-length, both halves are equal. So the second half is never longer than the first — second always exhausts first (or simultaneously). When second is None, all second-half nodes have been interleaved, and any remaining first-half nodes are already correctly positioned at the end. Checking while first and second would also work but is redundant — second is the binding constraint.

After Step 2 (reversing the second half), what does `slow.next` contain? What does the tail of the reversed second half point to?

After slow.next = None, slow.next is None — the first half is cleanly terminated. The reversed second half: the head of the reversed list (prev after the reversal loop) points to the original second-to-last node, which points to the original last node, which points to... nothing, because the original last node had next = None before reversal. The tail of the reversed second half is the original first node of the second half, which now has next = None (because prev started as None and the first node in the reversal loop sets curr.next = prev = None). So both halves end cleanly with None.


Practice problems

ProblemDifficultyWhat to noticeLink
Reorder ListMediumThree-step: middle → reverse → merge alternatelyLC #143
Palindrome Linked ListEasyTwo-step: middle → reverse second → compareLC #234
Odd Even Linked ListMediumSplit into two chains (odd/even indexed); rejoinLC #328
Sort ListMediumfind-middle + recursive sort + mergeLC #148

Next up: Flatten a Linked List — when nodes have both a next pointer and a child pointer pointing to another list, a stack or iterative DFS-style unrolling merges all sub-lists into one.