Partition an Array Into Three Groups in One Pass With Three Pointers
Dutch National Flag
Dijkstra's Dutch National Flag algorithm sorts an array containing only three distinct values in O(n) time and O(1) space using three pointers that maintain clear invariants throughout a single pass.
What you'll learn
- Implement Dijkstra's 3-way partitioning algorithm for the sort-colors problem
- State the invariants maintained by the low, mid, and high pointers
- Explain why the algorithm touches each element at most twice
Edsger Dijkstra coined the Dutch National Flag problem: given an array containing only 0s, 1s, and 2s (the three colours of the Dutch flag), sort it in a single pass without counting. The trick is maintaining three invariants simultaneously with three pointers.
Pointer Invariants
At any point during the scan:
- Everything before
lowis 0 - Everything from
lowtomid - 1is 1 - Everything from
midtohighis unseen (the “unknown” zone) - Everything after
highis 2
The algorithm runs mid through the unknown zone, swapping elements into the
correct region and adjusting the boundary pointers.
Implementation
function sortColors(nums) {
let low = 0;
let mid = 0;
let high = nums.length - 1;
while (mid <= high) {
if (nums[mid] === 0) {
// Swap into the 0-region
[nums[low], nums[mid]] = [nums[mid], nums[low]];
low++;
mid++;
} else if (nums[mid] === 1) {
// Already in the 1-region, just advance
mid++;
} else {
// Swap into the 2-region; don't advance mid — we haven't seen nums[mid] yet
[nums[mid], nums[high]] = [nums[high], nums[mid]];
high--;
}
}
}
const arr = [2, 0, 2, 1, 1, 0];
sortColors(arr);
console.log(arr); // [0, 0, 1, 1, 2, 2] Walked Example
Starting with [2, 0, 2, 1, 1, 0], low=0, mid=0, high=5:
| Step | nums[mid] | Action | low | mid | high |
|---|---|---|---|---|---|
| 1 | 2 | swap(mid,high), high— | 0 | 0 | 4 |
| 2 | 0 | swap(low,mid), low++, mid++ | 1 | 1 | 4 |
| 3 | 0 | swap(low,mid), low++, mid++ | 2 | 2 | 4 |
| 4 | 2 | swap(mid,high), high— | 2 | 2 | 3 |
| 5 | 1 | mid++ | 2 | 3 | 3 |
| 6 | 1 | mid++ | 2 | 4 | 3 |
| Done | mid > high | — | — | — | — |
Result: [0, 0, 1, 1, 2, 2]
Why Not Just Count?
Counting 0s, 1s, and 2s and overwriting takes two passes. The Dutch flag does it in one and is useful when the values cannot be re-enumerated (e.g., the elements are objects with a colour property rather than integers).
Complexity
| Metric | Value |
|---|---|
| Time | O(n) |
| Space | O(1) |
| Passes | 1 |
| Comparisons per element | At most 2 |
Up Next
With array patterns covered, we move to strings — starting with what a JavaScript string actually is under the hood.
Strings Deep Dive →