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.
Table of contents
- Before we start
- Picture this first (no code yet)
- The actual problem
- First, the slow way (so you feel the pain)
- The turning point
- The one idea to remember
- The three steps in code
- Watch it happen, frame by frame
- The even-length case: 1 → 2 → 3 → 4
- Why slow.next = None (cutting the list) matters
- Where to spot this pattern
- Common traps
- Check yourself
- Practice problems
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 to1 → 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: 1→2→3→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: 5→4→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→4 → 4→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: 1→4→2→3 ✅
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:
- Reorder List (LC #143): find middle → reverse → merge alternately.
- Palindrome Linked List (LC #234): find middle → reverse second → compare (no merge).
- Sort List (LC #148): find middle → recursive sort each half → merge (Chapter 4.4).
- Reverse Nodes in Even Length Groups: find group boundaries, reverse even groups — partial reversal (Chapter 4.3).
- 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.nextis 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.nextvariant gives the first middle (correct here — the first half should be the same length as or one longer than the second half). Usingfast = headgives 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.nextandsecond.nextbefore rewiring in Step 3. The merge step overwritesfirst.nextandsecond.nextimmediately — save both intmp1andtmp2first 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
headis visible to the caller because we never change whatheadpoints to — we only change the.nextpointers 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
| Problem | Difficulty | What to notice | Link |
|---|---|---|---|
| Reorder List | Medium | Three-step: middle → reverse → merge alternately | LC #143 |
| Palindrome Linked List | Easy | Two-step: middle → reverse second → compare | LC #234 |
| Odd Even Linked List | Medium | Split into two chains (odd/even indexed); rejoin | LC #328 |
| Sort List | Medium | find-middle + recursive sort + merge | LC #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.