Cyclic Sort
Numbers in range [1..n] belong at a specific index. Swap each to its home in one pass. Then scan for violations — missing or duplicate in O(1) space.
Find missing/duplicates with a HashSet — O(n) space. Cyclic Sort — O(1) space
The insight: if numbers are in range [1..n], each number has a predetermined home. nums[i] = k belongs at index k-1. Swap it there. After one pass, every number sits at its correct index — or reveals itself as a duplicate by finding its spot already occupied by a copy.
The Key Insight: Value → Index Mapping
One constraint unlocks everything — memorize this mapping.
Condition: nums[i] ≠ i+1
Swap to: j = nums[i] − 1
j = nums[i] - 1
if nums[i] != nums[j]:
swap(i, j)Condition: nums[i] ≠ i
Swap to: j = nums[i]
j = nums[i]
if nums[i] != nums[j]:
swap(i, j)nums[i] != nums[j] before swapping — not just nums[i] != i+1. If both positions hold the same value, swapping would cause an infinite loop. Skip and advance i instead.The 3-Step Framework
Two phases — sort, then scan. Always in that order.
For range [1..n]: correct index = nums[i] - 1. For range [0..n-1]: correct index = nums[i]. This is the only formula you need to derive before writing any loop.
# Range [1..n] correct_idx = nums[i] - 1 # Range [0..n-1] correct_idx = nums[i]
While i < n: if nums[i] is not at correct_idx AND nums[i] != nums[correct_idx] → swap. Otherwise advance i. Do NOT advance i after a swap — recheck the new value at i.
while i < len(nums):
j = nums[i] - 1
if nums[i] != nums[j]: # guard!
nums[i], nums[j] = nums[j], nums[i]
else:
i += 1After sorting, scan i from 0 to n-1. If nums[i] != i+1: that index is missing i+1, and the value nums[i] is the duplicate. Collect all violations for 'find all' variants.
for i in range(len(nums)):
if nums[i] != i + 1:
missing.append(i + 1) # missing
duplicate.append(nums[i]) # duplicatePattern Recognition
The one constraint that signals cyclic sort every time.