Cyclic Sort
An in-place O(n) pattern that places each value into its index-determined home by repeated swaps, exploiting the fact that the keys are a bounded range like 1..n.
Why it matters
It is less a general sort than an interview pattern for arrays holding a permutation of 1..n (or 0..n-1). Whenever a problem says “n numbers in range [1, n]” and asks for a missing, duplicate, or first-positive value, cyclic sort solves it in O(n) time and O(1) space — beating the sort-then-scan or hash-set approaches. The key insight: value v belongs at index v-1, so the array can sort itself by following those cycles.
How it works
Walk index i; if a[i] is not already at its correct slot, swap it there. Only advance i when the current slot is satisfied. Each value reaches home in O(1) amortized swaps.
i = 0
while i < n:
home = a[i] - 1 # for 1..n; use a[i] for 0..n-1
if a[i] != a[home]: # compare by value, not by index
swap(a[i], a[home]) # send a[i] to its place
else:
i += 1 # in place (or a duplicate) -> move on- O(n) total swaps: every swap puts at least one element permanently home, so there are at most n swaps despite the inner loop.
- The guard
a[i] != a[home](noti != home) is what lets it terminate on duplicates instead of swapping forever. - After sorting, a single scan finds anomalies: the first index where
a[i] != i+1is the missing number; the value seen at an occupied slot is the duplicate (see two-pointers for the scan).
Example
[3, 1, 5, 4, 2], n = 5. i=0: a[0]=3 → swap with index 2 → [5,1,3,4,2]; a[0]=5 → swap with index 4 → [2,1,3,4,5]; a[0]=2 → swap with index 1 → [1,2,3,4,5]; now a[0]=1 is home, advance. The rest are already placed. Result sorted in 3 swaps, O(1) space. With [3,1,5,4,2,?] missing one of 1..6, the post-scan flags the first index where a[i] != i+1.
Pitfalls
- Off-by-one between 1..n and 0..n-1. Mixing
home = a[i]-1with a 0-based problem (or vice-versa) corrupts indices; fix the convention first. - Guarding on index instead of value (
while i != home) infinite-loops on duplicates; always comparea[i] != a[home]. - Out-of-range values. Real inputs may contain 0, negatives, or values > n; ignore/skip them or the home index goes out of bounds — central to “first missing positive.”
- Not a comparison sort. It only works when keys map to indices; arbitrary keys need sorting or sorting.