Dutch flag: grow three regions
Picture
Arrange [2,0,2,1,1,0] so every 0 precedes every 1, which precedes every 2.
Predict
After swapping a 2 with the unknown right edge, may the scan always advance?
Walk through the small example
No. Each value belongs to one of three regions, and one unknown item is classified per iteration.
Mental model and complete Python solution
Do not think “sorting.” Think “classify the next unknown.” The invariant makes every swap’s destination obvious and guarantees the unknown region shrinks.
What the function promises: Mutate and return a list containing only integers 0, 1, and 2. Reject other values before mutation.
def sort_colors(values):
if any(value not in (0, 1, 2) for value in values):
raise ValueError("Only 0, 1, 2 are allowed")
low = scan = 0
high = len(values) - 1
while scan <= high:
if values[scan] == 0:
values[low], values[scan] = values[scan], values[low]
low += 1; scan += 1
elif values[scan] == 1:
scan += 1
else:
values[scan], values[high] = values[high], values[scan]
high -= 1
return values
assert sort_colors([2,0,2,1,1,0]) == [0,0,1,1,2,2]
Work and storage: O(n) time and O(1) extra space.
When to recognize it: Each value belongs to one of three regions, and one unknown item is classified per iteration.
Check before applying it: Advancing after a right swap can skip an unclassified incoming value.
Change one rule
There are many categories.
See what changes
Use counting for a small key range or a general sorting method; the three-region invariant does not scale directly.
Connect
invariants: Each pointer boundary gives a region a precise meaning.
Quicksort: partition around a pivot: This is a three-way partition around a middle value.