LeetCode #143 Medium

Reorder List

Reorder a linked list from L0 -> L1 -> ... -> Ln into L0 -> Ln -> L1 -> Ln-1 -> ... in place.

Constraints
  • The number of nodes in the list is in the range [1, 5 * 10⁴].
  • 1 <= Node.val <= 1000
linked-listtwo-pointersreversal
Open on LeetCode ↗
02

Intuition

Reorder list rearranges a list from L0 → L1 → … → Ln into L0 → Ln → L1 → Ln-1 → …, interleaving from both ends. It must be done in place, without altering node values. The difficulty is that a singly linked list cannot be walked backwards, yet the target order needs nodes from the tail. Copying node references into an array solves it at O(n) space; the in-place solution composes three standard techniques: - Find the middle, reverse the second half, then merge the two halves alternately. Each piece is a problem you already know — Middle of the Linked List, Reverse Linked List, and a merge — which is what makes this worth recognising rather than inventing. After reversing the second half, the two halves run in opposite directions, so taking one node from each in turn produces exactly the required interleaving. The step that breaks implementations is severing the first half. After locating the middle, the first half's tail must be set to null. Without it the halves remain connected, the reversal creates a cycle, and the merge loops forever. For odd-length lists the first half should hold the extra node, so the interleaving ends cleanly on the middle element. The two-speed walk with fast && fast.next as the condition gives this naturally. The merge must handle the halves differing in length by one, stopping when the second half is exhausted. Three linear passes give O(n) time and O(1) space, against O(n) space for the array approach.

How to spot this pattern

Three textbook sub-routines composed: find the middle with slow/fast, reverse the second half, then zip the two halves together. Recognising a problem as a composition of known primitives — rather than one new algorithm — is what makes it tractable in an interview.

03

Approach

Try it first

Before reading on: price up what the direct approach costs here, then ask which pointers must be saved before you overwrite anything. Aim for O(n) time and O(1) space.

1

See why the tail is hard to reach

The target order needs nodes from the end, but a singly linked list cannot be walked backwards. Reversing half the list is what makes them reachable.

2

Find the middle

Advance one pointer two nodes for every one the other moves. With fast && fast.next as the condition, the first half keeps the extra node on odd-length lists.

3

Sever the first half

Set the first half's tail to null. Leaving the halves connected turns the reversal into a cycle and the merge never terminates — the defining bug here.

4

Reverse the second half

Rewrite the next pointers in place. The halves now run in opposite directions, which is what the alternating merge requires.

5

Merge alternately

Take one node from each half in turn. The halves can differ in length by one, so stop when the second is exhausted.

6

Recognise the composition

This is Middle of the Linked List, Reverse Linked List, and a merge in sequence. Naming the pieces makes it routine rather than something to invent.

7

Cost of the approach

Three linear passes give O(n) time and O(1) space, against O(n) space for the array-of-references alternative.

04

Solution & live demo

▶1class Solution:
▶2 def reorderList(self, head):
▶3 if not head or not head.next:
▶4 return
▶5 slow, fast = head, head.next
▶6 while fast and fast.next:
▶7 slow = slow.next
▶8 fast = fast.next.next
▶9 second = slow.next
▶10 slow.next = None
▶11 prev = None
▶12 while second:
▶13 nxt = second.next
▶14 second.next = prev
▶15 prev = second
▶16 second = nxt
▶17 first, second = head, prev
▶18 while second:
▶19 f, s = first.next, second.next
▶20 first.next = second
▶21 second.next = f
▶22 first, second = f, s
▶23 return
05

Common pitfalls

Not severing the first half

✗ Wrong
second = slow.next
# reverse second...
✓ Right
second = slow.next
slow.next = None

Without cutting, the first half's tail still points into the second half, which after reversal points back — creating a cycle. The merge loop then never terminates.

Starting fast at head instead of head.next

✗ Wrong
slow, fast = head, head
✓ Right
slow, fast = head, head.next

On an even-length list this leaves slow one node too far right, so the second half is shorter than the first and the interleave drops a node. Starting fast one ahead makes slow stop at the end of the first half for both parities.

Not saving both next pointers before relinking

✗ Wrong
first.next = second
second.next = first.next
✓ Right
f, s = first.next, second.next
first.next = second
second.next = f

The first assignment overwrites first.next, so reading it afterwards yields the node just linked rather than the original successor — the list folds back on itself. Both successors must be captured before any pointer is written.

06

Edge cases

Empty or single-node list

Already in the target order — return early.

Two nodes

The pattern is unchanged, so the merge is a no-op.

Forgetting to cut at the middle

The reversed half still links back into the first, producing a cycle.

Odd length

The middle node ends up last, which the slow/fast convention handles without a special case.

07

Complexity

Time
O(n)
Space
O(1)
Middle, reverse, merge — three linear passes with no extra storage.