Sort an array of only 0s, 1s, and 2s in place. The Dutch National Flag partition does it in a single pass with three pointers — no counting array, no sort.
The array holds only three distinct values — 0, 1, 2. Sort it in place so all 0s come first, then all 1s, then all 2s. nums=[2,0,2,1,1,0] → [0,0,1,1,2,2]. The challenge: do it in one pass and O(1) extra space.
[0, low) is settled 0s, [low, mid) is settled 1s, (high, end] is settled 2s, and [mid, high] is the unknown region. mid scans forward, shrinking the unknown band from the left (on 0/1) or from the right (on 2) until it is empty.low = mid = 0, high = n - 1. Loop while mid ≤ high— that's the unknown region.nums[low] with nums[mid], then low++ and mid++. The swapped-in value at mid is already scanned (it was a 1 or empty), so mid may advance.mid++.nums[mid] with nums[high], then high--. Do NOT advance mid — the value just pulled in from high is unscanned and must be re-checked.mid on a 0-swap but not on a 2-swap? Because low ≤ mid always, so the cell you swap from low was already examined (it can only be a 1). But high sits in unexplored territory, so the incoming value is fresh — leave mid put and look again.Both are O(n) time, but the flag partition touches each element at most once and uses only three index variables → strictly O(1) extra space in a single pass. A generic comparison sort would be O(n log n) — needlessly slow when there are only three keys.
low=0, mid=0, high=5. Scan with mid while mid ≤ high.1function sortColors(nums: number[]): void {2 let low = 0; // boundary: everything left of low is 03 let mid = 0; // scanner / unknown frontier4 let high = nums.length - 1; // boundary: everything right of high is 256 while (mid <= high) {7 if (nums[mid] === 0) { // 0: send left8 [nums[low], nums[mid]] = [nums[mid], nums[low]];9 low++; mid++;10 } else if (nums[mid] === 1) { // 1: already home11 mid++;12 } else { // 2: send right, re-check mid13 [nums[mid], nums[high]] = [nums[high], nums[mid]];14 high--;15 }16 }17}
function sortColors(nums: number[]): void {
let low = 0; // boundary: everything left of low is 0
let mid = 0; // scanner / unknown frontier
let high = nums.length - 1; // boundary: everything right of high is 2
while (mid <= high) {
if (nums[mid] === 0) { // 0: send left
[nums[low], nums[mid]] = [nums[mid], nums[low]];
low++; mid++;
} else if (nums[mid] === 1) { // 1: already home
mid++;
} else { // 2: send right, re-check mid
[nums[mid], nums[high]] = [nums[high], nums[mid]];
high--;
}
}
}low is 0, everything after high is 2, and [mid, high] is still unknown. low and mid both start at 0; high at the last index.mid <= high. Once they cross, the unknown region is empty and the array is fully partitioned.low and mid together. The cell coming from low was already scanned, so it is safe to move mid past it.mid++.high--. Crucially mid stays put: the value pulled in from high has never been examined.mid or retreats high, so the loop runs at most n times. O(1) extra space (three indices). Single in-place pass.[2,2] to show the bug.function sortColors(nums: number[]): void {
let low = 0; // boundary: everything left of low is 0
let mid = 0; // scanner / unknown frontier
let high = nums.length - 1; // boundary: everything right of high is 2
while (mid <= high) {
if (nums[mid] === 0) { // 0: send left
[nums[low], nums[mid]] = [nums[mid], nums[low]];
low++; mid++;
} else if (nums[mid] === 1) { // 1: already home
mid++;
} else { // 2: send right, re-check mid
[nums[mid], nums[high]] = [nums[high], nums[mid]];
high--;
}
}
}mid or retreats high, so the loop runs at most n times. O(1) extra space (three indices). Single in-place pass.Count how many 0s, 1s, and 2s there are, then overwrite the array in order. Simplest correct idea, but it reads the array twice instead of partitioning in one pass.
function sortColors(nums: number[]): void {
const count = [0, 0, 0];
for (const v of nums) count[v]++; // pass 1: tally
let i = 0;
for (let c = 0; c <= 2; c++) { // pass 2: rewrite
for (let k = 0; k < count[c]; k++) nums[i++] = c;
}
}| array of only 0/1/2 (or 3 keys) | Dutch National Flag partition |
| "sort in place, one pass, O(1)" | low / mid / high pointers |
| < pivot / = pivot / > pivot | 3-way partition (quicksort) |
| on a 2-swap, recheck same index | advance high only, not mid |
let low = 0, mid = 0, high = nums.length - 1;
while (mid <= high) {
if (nums[mid] === 0) {
[nums[low], nums[mid]] = [nums[mid], nums[low]];
low++; mid++;
} else if (nums[mid] === 1) {
mid++;
} else { // === 2
[nums[mid], nums[high]] = [nums[high], nums[mid]];
high--; // DON'T advance mid
}
}[0,low) = settled 0s, [low,mid) = settled 1s, (high,end] = settled 2s, [mid,high] = unknown.