Problem
Given a linked list and a pivot value x, rearrange the list so that every node with a value strictly less than x appears before every node with a value of x or greater. The relative order of nodes within each group must be preserved.
[1,4,3,2,5,2]
- Input: head = [1, 4, 3, 2, 5, 2], x = 3
- Output: [1, 2, 2, 4, 3, 5]
- Explanation: 1, 2, 2 are all < 3 and appear first; 4, 3, 5 are all โฅ 3 and appear after, each group preserving its original order.
Intuition
The problem is really asking you to split one list into two โ a "less than" group and a "greater or equal" group โ then join them. Because we must preserve relative order, we can't sort or swap values: we have to redirect the actual node pointers. A dummy head on each sub-list eliminates all the edge-case checks for empty lists, letting us build both groups in a single clean pass.
Solution โ Two-Dummy Partition
Build two separate linked lists simultaneously: one collecting all nodes with values less than x, the other collecting the rest. After the single traversal, null-terminate the greater list and attach it to the tail of the less list.
- Create two dummy sentinel nodes โ
less_dummyandgreater_dummyโ and tail pointers starting at each. - Walk the original list; for each node, append it to the appropriate tail and advance that tail.
- After the loop, set
greater_tail.next = Noneto cut any stale pointer left over from the original ordering. - Join the two lists:
less_tail.next = greater_dummy.next. - Return
less_dummy.nextas the new head.
1def partition(head: Optional[ListNode], x: int) -> Optional[ListNode]:
2 less_dummy = ListNode(0)
3 greater_dummy = ListNode(0)
4 less_tail = less_dummy
5 greater_tail = greater_dummy
6
7 current = head
8 while current:
9 if current.val < x:
10 less_tail.next = current
11 less_tail = current
12 else:
13 greater_tail.next = current
14 greater_tail = current
15 current = current.next # advance before any pointer is overwritten below
16
17 greater_tail.next = None # sever stale pointer; prevents accidental cycle
18 less_tail.next = greater_dummy.next # stitch: less partition โ greater partition
19 return less_dummy.nextTime: O(n) โ every node is visited exactly once.
Space: O(1) โ only four extra pointers; no new nodes are allocated.
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| Two-dummy partition | O(n) | O(1) | Always โ this is the canonical solution |
Common Mistakes
- Forgetting
greater_tail.next = Noneโ every node still has its originalnextpointer after the loop. If the last node routed to the greater list isn't the original tail, its stale pointer creates an unintended cycle or wrong connection. - Connecting to
greater_dummyinstead ofgreater_dummy.nextโ the dummy node is a sentinel; the actual first node of the greater partition isgreater_dummy.next. - Using
<=instead of<for the partition condition โ nodes equal toxmust go into the greater group, not the less group. Using<=misplaces nodes with value exactlyx. - Swapping values instead of rearranging nodes โ swapping
node.vallooks simpler but doesn't preserve relative ordering the way the problem requires when there are equal values near the pivot. - Advancing
currentbefore saving it โ doingless_tail = current; current = current.nextworks only becausecurrent.nextis the original next (we only ever write to.nextof a tail pointer, never tocurrent.nextdirectly). Mixing up which.nextyou're writing to is a subtle source of lost nodes.
Related Problems
Merge Two Sorted Listsโ the same two-dummy-head technique used to interleave two lists in sorted orderOdd Even Linked Listโ identical pattern: split into two sub-lists by a criterion (index parity), then joinRemove Linked List Elementsโ single-pass filtering with a dummy head; simpler version with only one output listSort Listโ rearranging a linked list by redirecting node pointers, uses similar sub-list building in the merge stepReorder Listโ splitting and reconnecting parts of a linked list with the same dummy-head and tail-pointer mechanics