Your cyclic sort works beautifully on permutations — every value unique, every home address occupied by exactly one rightful owner. But real interview problems aren't that clean. Consider this array:
const nums = [1, 3, 2, 3, 5];// index: 0 1 2 3 4Walk through it mentally. Index 0 holds 1 — already home. Index 1 holds 3 —
its home is index 2. Swap nums[1] and nums[2]: now the array is [1, 2, 3, 3, 5].
Index 1 now holds 2 — home. Index 2 holds 3 — home. Move on to index 3.
Index 3 holds 3. Its home address is 3 - 1 = 2. So we check nums[2]... which
is also 3. The swap executes: we exchange nums[3] with nums[2]. But both positions
hold the same value. Nothing changes. And our while loop condition — nums[i] !== i + 1 —
is still true. Index 3 still holds 3, which isn't 4. So we swap again. Same
result. And again. And again. We're trapped in an infinite loop, endlessly swapping
identical values while the condition never becomes false. The algorithm that was O(n)
just became O(infinity).
Duplicates don't just cause wrong answers — they cause hangs. Your mailroom has two letters addressed to the same house, and the carrier keeps shuffling them back and forth forever. We need a guard.
i is at index 3 where nums[3]=3 and home=2.What happens if we swap nums[3] with nums[2]?
The fix is a single condition change, but it does double duty:
for (let i = 0; i < n; i++) { while (nums[i] !== i + 1) { const home = nums[i] - 1; // The guard: stop if the home already has the right value if (nums[i] === nums[home]) break; // <-- new [nums[i], nums[home]] = [nums[home], nums[i]]; }}Read that guard carefully: nums[i] === nums[home]. When is this true? Two cases.
First, when the value at index i is already in its correct position — nums[i]
equals nums[home] because i is home. That's the “already placed” case. Second,
when a duplicate occupies the home — some other copy of the value is already sitting
where we'd want to send this one. That's the “duplicate detected” case.
One condition, two saves. It prevents infinite loops from duplicates and skips
unnecessary swaps for values already home. The guard is cheap — one comparison per
iteration — and it preserves our O(n) budget because each break means we stop
swapping and advance i.
Now here's the interesting part: after this modified sort finishes, the array has a telltale signature. Every position either holds its “correct” value... or it holds an imposter — a duplicate that couldn't be placed. That signature is about to become very useful.