Sort Colors
Problem
Given an array nums with n objects colored red, white, or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white, and blue. Use integers 0, 1, and 2 to represent the colors red, white, and blue respectively.
- n == nums.length
- 1 ≤ n ≤ 300
- nums[i] is 0, 1, or 2
Example
nums = [2,0,2,1,1,0][0,0,1,1,2,2]The algorithm maintains three pointers: left, right, and i. Initially, left and i start at 0, and right starts at the last index. The pointer i traverses the array. When nums[i] is 0, it swaps with nums[left] and increments both left and i, ensuring all 0s are moved to the front. When nums[i] is 2, it swaps with nums[right] and decrements right, moving all 2s to the end. When nums[i] is 1, it simply moves forward. This process partitions the array into three sections in a single pass.
Approach
Straightforward Solution
A naive approach would count the number of 0s, 1s, and 2s in one pass, then overwrite the array in a second pass. This requires two passes and is less elegant. Alternatively, sorting the array directly is O(n log n), which is unnecessary given the limited range of values.
Core Observation
The problem requires partitioning the array into three distinct groups (0s, 1s, and 2s) in a single pass with constant space. This is a classic three-way partitioning problem where the relative order within each group does not matter.
Path to Optimal
The key insight is to use three pointers to maintain boundaries for the three groups. By moving through the array once and swapping elements to their correct partitions, the algorithm achieves O(n) time and O(1) space. The pointer i scans the array, left tracks the boundary of 0s, and right tracks the boundary of 2s. Swapping elements when encountering 0 or 2 ensures correct placement without revisiting elements unnecessarily.
Optimal Approach
Use the Dutch National Flag algorithm with three pointers: left, i, and right. Iterate while i <= right. If nums[i] == 0, swap with nums[left], increment left and i. If nums[i] == 2, swap with nums[right], decrement right but do not increment i because the swapped element needs evaluation. If nums[i] == 1, just increment i. This guarantees a single pass partitioning with constant space.
Time
O(n)
Each element is visited at most once by the pointer i. Swaps occur in constant time, and the pointers move inward without backtracking, resulting in a linear time complexity.
Space
O(1)
The algorithm uses only three integer pointers and performs swaps in-place, requiring constant auxiliary space.
Pattern Spotlight
Two Pointers (Three-Way Partitioning / Dutch National Flag)
Maintain three pointers to delimit the boundaries of each partition and move through the array once, swapping elements to their correct partitions on the fly, ensuring each element is processed at most once.
Solution
| 1 | class Solution: |
| 2 | def sortColors(self, nums: List[int]) -> None: |
| 3 | |
| 4 | left = 0 |
| 5 | right = len(nums) - 1 |
| 6 | i = 0 |
| 7 | |
| 8 | while i <= right: |
| 9 | |
| 10 | if nums[i] == 0: |
| 11 | nums[left], nums[i] = nums[i], nums[left] |
| 12 | left += 1 |
| 13 | i += 1 |
| 14 | |
| 15 | elif nums[i] == 2: |
| 16 | nums[right], nums[i] = nums[i], nums[right] |
| 17 | right -= 1 |
| 18 | |
| 19 | else: |
| 20 | i += 1 |
Step-by-Step Solution
Initialize Three Pointers to Partition the Array
| 4 | left = 0 |
| 5 | right = len(nums) - 1 |
| 6 | i = 0 |
Objective
To set up pointers that track the boundaries of the three partitions: 0s, 1s, and 2s.
Key Insight
The left pointer marks the boundary where all elements to its left are 0s. The right pointer marks the boundary where all elements to its right are 2s. The pointer i scans through the array to classify elements. This setup enables a single pass partitioning without extra space.
Interview Quick-Check
Core Logic
left and right pointers define the boundaries for 0s and 2s respectively, while i scans the array to place elements correctly.
State & Boundaries
left starts at 0, right at the last index, and i at 0 to cover the entire array.
Partition Array by Swapping Elements Based on Their Value
| 8 | while i <= right: |
| 10 | if nums[i] == 0: |
| 11 | nums[left], nums[i] = nums[i], nums[left] |
| 12 | left += 1 |
| 13 | i += 1 |
| 15 | elif nums[i] == 2: |
| 16 | nums[right], nums[i] = nums[i], nums[right] |
| 17 | right -= 1 |
| 19 | else: |
| 20 | i += 1 |
Objective
To iterate through the array and swap elements to their correct partitions using the three pointers.
Key Insight
When nums[i] is 0, swapping with nums[left] moves the 0 to the front, and both left and i advance because the swapped element at i is guaranteed to be processed. When nums[i] is 2, swapping with nums[right] moves the 2 to the end, and right moves inward, but i does not advance because the swapped element at i needs evaluation. When nums[i] is 1, it is already in the correct middle partition, so i advances. This logic ensures all elements are sorted in one pass.
Interview Quick-Check
Core Logic
Swapping 0s to the front and 2s to the back while scanning with i ensures a single pass partitioning.
State & Boundaries
The loop continues while i <= right to ensure all elements are processed.
Common Pitfalls & Bugs
Not decrementing right after swapping a 2 or incrementing i incorrectly can cause infinite loops or missed elements.
Line Analysis
This solution has 2 Critical lines interviewers watch for.
nums[left], nums[i] = nums[i], nums[left]
Swap the current element with the element at the left pointer.
Swapping moves the 0 to the front partition, ensuring all 0s are grouped at the start of the array.
nums[right], nums[i] = nums[i], nums[right]
Swap the current element with the element at the right pointer.
Swapping moves the 2 to the end partition, ensuring all 2s are grouped at the end of the array.
if nums[i] == 0:
Check if the current element is 0.
Identifying 0s is critical to move them to the front partition, maintaining the order red, white, blue.
elif nums[i] == 2:
Check if the current element is 2.
Identifying 2s is essential to move them to the end partition, maintaining the order red, white, blue.
left = 0
Initialize the left pointer at the start of the array.
The left pointer tracks the boundary where all elements to the left are 0s, enabling in-place partitioning of red color.
right = len(nums) - 1
Initialize the right pointer at the end of the array.
The right pointer tracks the boundary where all elements to the right are 2s, enabling in-place partitioning of blue color.
while i <= right:
Start a loop that continues while i is less than or equal to right.
The loop condition ensures all elements between left and right pointers are processed, covering the entire array in one pass.
left += 1
Increment the left pointer after placing a 0.
Advancing left updates the boundary of the 0 partition, preparing for the next 0 placement.
i += 1
Increment the scanning pointer i to continue processing.
Moving i forward after handling a 0 ensures the next element is evaluated, maintaining progress through the array.
right -= 1
Decrement the right pointer after placing a 2.
Moving right inward updates the boundary of the 2 partition, preparing for the next 2 placement.
else:
Handle the case where the current element is 1 by moving the scanning pointer forward.
Since 1s belong in the middle partition, no swap is needed; simply advancing i continues the scan.
i += 1
Increment the scanning pointer i to continue processing.
Advancing i after encountering a 1 ensures the algorithm progresses through the array without unnecessary swaps.
i = 0
Initialize the scanning pointer i at the start of the array.
Pointer i scans through the array to classify elements and swap them into correct partitions.
Test Your Understanding
Why does the pointer i not increment after swapping with the right pointer when nums[i] == 2?
Because the element swapped from the right end has not been evaluated yet, so i must stay to check this new element to ensure it is correctly placed.
Related Problems
Two Pointers pattern
Don't just read it. Drill it.
Reconstruct Sort Colors from memory until it sticks. AlgoDrill blanks out key lines and makes you fill them back in, step by step.
Unlock the Sort Colors drill