You are given an array nums consisting of n elements where each element is an integer representing a color:
0 represents red
1 represents white
2 represents blue
Your task is to sort the array in-place such that elements of the same color are grouped together and arranged in the order: red (0), white (1), and then blue (2).
You must not use any built-in sorting functions to solve this problem.
Example 1:
Input: nums =[1,0,1,2]Output:[0,1,1,2]
Example 2:
Input: nums =[2,1,0]Output:[0,1,2]
Constraints:
1 <= nums.length <= 300.
0 <= nums[i] <= 2.
Follow up: Could you come up with a one-pass algorithm using only constant extra space?
Please upgrade to NeetCode Pro to view company tags.
Prerequisites
Before attempting this problem, you should be comfortable with:
Arrays and In-Place Manipulation - Modifying arrays without using extra space
Two/Three Pointers Technique - The Dutch National Flag algorithm uses three pointers to partition the array in one pass
Counting Sort - Leveraging the limited value range (only 0, 1, 2) for linear time sorting
1. Brute Force
Intuition
The simplest approach is to use a standard sorting algorithm. Since the array only contains values 0, 1, and 2, sorting will naturally arrange them in the required order. While this works, it does not take advantage of the limited value range.
Algorithm
Use the built-in sort function to sort the array in ascending order.
The array is now sorted with all 0s first, then 1s, then 2s.
Space complexity: O(1) or O(n) depending on the sorting algorithm.
2. Counting Sort
Intuition
Since there are only three possible values (0, 1, 2), we can count how many times each appears in a single pass. Then we overwrite the array in a second pass, placing the correct number of 0s, followed by 1s, followed by 2s. This is a classic application of counting sort.
Algorithm
Count the occurrences of 0, 1, and 2 in the array.
classSolution:defsortColors(self, nums: List[int])->None:"""
Do not return anything, modify nums in-place instead.
"""
count =[0]*3for num in nums:
count[num]+=1
index =0for i inrange(3):while count[i]:
count[i]-=1
nums[index]= i
index +=1
publicclassSolution{publicvoidsortColors(int[] nums){int[] count =newint[3];for(int num : nums){
count[num]++;}int index =0;for(int i =0; i <3; i++){while(count[i]-->0){
nums[index++]= i;}}}}
classSolution{public:voidsortColors(vector<int>& nums){
vector<int>count(3);for(int& num : nums){
count[num]++;}int index =0;for(int i =0; i <3; i++){while(count[i]-->0){
nums[index++]= i;}}}};
classSolution{/**
* @param {number[]} nums
* @return {void} Do not return anything, modify nums in-place instead.
*/sortColors(nums){const count =newInt32Array(3);for(let num of nums){
count[num]++;}let index =0;for(let i =0; i <3; i++){while(count[i]-->0){
nums[index++]= i;}}}}
publicclassSolution{publicvoidSortColors(int[] nums){int[] count =newint[3];foreach(int num in nums){
count[num]++;}int index =0;for(int i =0; i <3; i++){while(count[i]-->0){
nums[index++]= i;}}}}
funcsortColors(nums []int){
count :=make([]int,3)for_, num :=range nums {
count[num]++}
index :=0for i :=0; i <3; i++{for count[i]>0{
count[i]--
nums[index]= i
index++}}}
class Solution {funsortColors(nums: IntArray){val count =IntArray(3)for(num in nums){
count[num]++}var index =0for(i in0 until 3){while(count[i]-->0){
nums[index++]= i
}}}}
classSolution{funcsortColors(_ nums:inout[Int]){var count =[Int](repeating:0, count:3)for num in nums {
count[num]+=1}var index =0for i in0..<3{while count[i]>0{
count[i]-=1
nums[index]= i
index +=1}}}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(1)
3. Three Pointers - I
Intuition
The Dutch National Flag algorithm partitions the array into three sections in a single pass. We maintain pointers for the boundary of 0s (left), the boundary of 2s (right), and the current element being examined. When we see a 0, we swap it to the left section. When we see a 2, we swap it to the right section. 1s naturally end up in the middle.
Algorithm
Initialize three pointers: l (boundary for 0s), i (current element), and r (boundary for 2s).
While i <= r:
If nums[i] is 0, swap with nums[l], increment both l and i.
If nums[i] is 2, swap with nums[r], decrement r (do not increment i since the swapped element needs to be checked).
classSolution:defsortColors(self, nums: List[int])->None:"""
Do not return anything, modify nums in-place instead.
"""
l, r =0,len(nums)-1
i =0defswap(i, j):
temp = nums[i]
nums[i]= nums[j]
nums[j]= temp
while i <= r:if nums[i]==0:
swap(l, i)
l +=1elif nums[i]==2:
swap(i, r)
r -=1
i -=1
i +=1
publicclassSolution{publicvoidsortColors(int[] nums){int i =0, l =0, r = nums.length -1;while(i <= r){if(nums[i]==0){swap(nums, l, i);
l++;}elseif(nums[i]==2){swap(nums, i, r);
r--;
i--;}
i++;}}privatevoidswap(int[] nums,int i,int j){int temp = nums[i];
nums[i]= nums[j];
nums[j]= temp;}}
classSolution{public:voidsortColors(vector<int>& nums){int i =0, l =0, r = nums.size()-1;while(i <= r){if(nums[i]==0){swap(nums[l], nums[i]);
l++;}elseif(nums[i]==2){swap(nums[i], nums[r]);
r--;
i--;}
i++;}}};
classSolution{/**
* @param {number[]} nums
* @return {void} Do not return anything, modify nums in-place instead.
*/sortColors(nums){let i =0,
l =0,
r = nums.length -1;while(i <= r){if(nums[i]==0){[nums[l], nums[i]]=[nums[i], nums[l]];
l++;}elseif(nums[i]==2){[nums[i], nums[r]]=[nums[r], nums[i]];
r--;
i--;}
i++;}}}
publicclassSolution{publicvoidSortColors(int[] nums){int i =0, l =0, r = nums.Length -1;while(i <= r){if(nums[i]==0){Swap(nums, l, i);
l++;}elseif(nums[i]==2){Swap(nums, i, r);
r--;
i--;}
i++;}}privatevoidSwap(int[] nums,int i,int j){int temp = nums[i];
nums[i]= nums[j];
nums[j]= temp;}}
funcsortColors(nums []int){
i, l, r :=0,0,len(nums)-1for i <= r {if nums[i]==0{
nums[l], nums[i]= nums[i], nums[l]
l++}elseif nums[i]==2{
nums[i], nums[r]= nums[r], nums[i]
r--
i--}
i++}}
class Solution {funsortColors(nums: IntArray){var i =0var l =0var r = nums.size -1while(i <= r){if(nums[i]==0){
nums[l]= nums[i].also{ nums[i]= nums[l]}
l++}elseif(nums[i]==2){
nums[i]= nums[r].also{ nums[r]= nums[i]}
r--
i--}
i++}}}
classSolution{funcsortColors(_ nums:inout[Int]){var i =0, l =0, r = nums.count -1while i <= r {if nums[i]==0{
nums.swapAt(l, i)
l +=1}elseif nums[i]==2{
nums.swapAt(i, r)
r -=1
i -=1}
i +=1}}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(1)
4. Three Pointers - II
Intuition
This approach uses insertion boundaries for each color. We track where the next 0, 1, and 2 should be placed. When we encounter a value, we shift the boundaries by overwriting in a cascading manner. For example, when we see a 0, we write 2 at position two, then 1 at position one, then 0 at position zero, and advance all three boundaries.
Algorithm
Initialize three pointers zero, one, and two, all starting at 0.
For each element in the array:
If it is 0: write 2 at two, write 1 at one, write 0 at zero, then increment all three pointers.
If it is 1: write 2 at two, write 1 at one, then increment two and one.
classSolution:defsortColors(self, nums: List[int])->None:"""
Do not return anything, modify nums in-place instead.
"""
zero = one = two =0for i inrange(len(nums)):if nums[i]==0:
nums[two]=2
nums[one]=1
nums[zero]=0
two +=1
one +=1
zero +=1elif nums[i]==1:
nums[two]=2
nums[one]=1
two +=1
one +=1else:
nums[two]=2
two +=1
publicclassSolution{publicvoidsortColors(int[] nums){int zero =0, one =0, two =0;for(int i =0; i < nums.length; i++){if(nums[i]==0){
nums[two++]=2;
nums[one++]=1;
nums[zero++]=0;}elseif(nums[i]==1){
nums[two++]=2;
nums[one++]=1;}else{
nums[two++]=2;}}}}
classSolution{public:voidsortColors(vector<int>& nums){int zero =0, one =0, two =0;for(int i =0; i < nums.size(); i++){if(nums[i]==0){
nums[two++]=2;
nums[one++]=1;
nums[zero++]=0;}elseif(nums[i]==1){
nums[two++]=2;
nums[one++]=1;}else{
nums[two++]=2;}}}};
classSolution{/**
* @param {number[]} nums
* @return {void} Do not return anything, modify nums in-place instead.
*/sortColors(nums){let zero =0,
one =0,
two =0;for(let i =0; i < nums.length; i++){if(nums[i]==0){
nums[two++]=2;
nums[one++]=1;
nums[zero++]=0;}elseif(nums[i]==1){
nums[two++]=2;
nums[one++]=1;}else{
nums[two++]=2;}}}}
publicclassSolution{publicvoidSortColors(int[] nums){int zero =0, one =0, two =0;for(int i =0; i < nums.Length; i++){if(nums[i]==0){
nums[two++]=2;
nums[one++]=1;
nums[zero++]=0;}elseif(nums[i]==1){
nums[two++]=2;
nums[one++]=1;}else{
nums[two++]=2;}}}}
funcsortColors(nums []int){
zero, one, two :=0,0,0for i :=0; i <len(nums); i++{if nums[i]==0{
nums[two]=2
two++
nums[one]=1
one++
nums[zero]=0
zero++}elseif nums[i]==1{
nums[two]=2
two++
nums[one]=1
one++}else{
nums[two]=2
two++}}}
class Solution {funsortColors(nums: IntArray){var zero =0var one =0var two =0for(i in nums.indices){if(nums[i]==0){
nums[two++]=2
nums[one++]=1
nums[zero++]=0}elseif(nums[i]==1){
nums[two++]=2
nums[one++]=1}else{
nums[two++]=2}}}}
classSolution{funcsortColors(_ nums:inout[Int]){var zero =0, one =0, two =0for i in0..<nums.count {if nums[i]==0{
nums[two]=2
two +=1
nums[one]=1
one +=1
nums[zero]=0
zero +=1}elseif nums[i]==1{
nums[two]=2
two +=1
nums[one]=1
one +=1}else{
nums[two]=2
two +=1}}}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(1)
5. Three Pointers - III
Intuition
This is a streamlined version of the previous approach. We iterate with pointer two and always write 2 at the current position. If the original value was less than 2, we also write 1 at position one. If it was less than 1 (i.e., 0), we also write 0 at position zero. This cascading write pattern ensures correct placement.
Algorithm
Initialize pointers zero and one at 0.
Iterate through the array with pointer two:
Save the current value, then set nums[two] = 2.
If the saved value was less than 2, set nums[one] = 1 and increment one.
If the saved value was less than 1, set nums[zero] = 0 and increment zero.
classSolution:defsortColors(self, nums: List[int])->None:"""
Do not return anything, modify nums in-place instead.
"""
zero = one =0for two inrange(len(nums)):
tmp = nums[two]
nums[two]=2if tmp <2:
nums[one]=1
one +=1if tmp <1:
nums[zero]=0
zero +=1
publicclassSolution{publicvoidsortColors(int[] nums){int zero =0, one =0;for(int two =0; two < nums.length; two++){int tmp = nums[two];
nums[two]=2;if(tmp <2){
nums[one++]=1;}if(tmp <1){
nums[zero++]=0;}}}}
classSolution{public:voidsortColors(vector<int>& nums){int zero =0, one =0;for(int two =0; two < nums.size(); two++){int tmp = nums[two];
nums[two]=2;if(tmp <2){
nums[one++]=1;}if(tmp <1){
nums[zero++]=0;}}}};
classSolution{/**
* @param {number[]} nums
* @return {void} Do not return anything, modify nums in-place instead.
*/sortColors(nums){let zero =0,
one =0;for(let two =0; two < nums.length; two++){let tmp = nums[two];
nums[two]=2;if(tmp <2){
nums[one++]=1;}if(tmp <1){
nums[zero++]=0;}}}}
publicclassSolution{publicvoidSortColors(int[] nums){int zero =0, one =0;for(int two =0; two < nums.Length; two++){int tmp = nums[two];
nums[two]=2;if(tmp <2){
nums[one++]=1;}if(tmp <1){
nums[zero++]=0;}}}}
funcsortColors(nums []int){
zero, one :=0,0for two :=0; two <len(nums); two++{
tmp := nums[two]
nums[two]=2if tmp <2{
nums[one]=1
one++}if tmp <1{
nums[zero]=0
zero++}}}
class Solution {funsortColors(nums: IntArray){var zero =0var one =0for(two in nums.indices){val tmp = nums[two]
nums[two]=2if(tmp <2){
nums[one++]=1}if(tmp <1){
nums[zero++]=0}}}}
classSolution{funcsortColors(_ nums:inout[Int]){var zero =0, one =0for two in0..<nums.count {let tmp = nums[two]
nums[two]=2if tmp <2{
nums[one]=1
one +=1}if tmp <1{
nums[zero]=0
zero +=1}}}}
Time & Space Complexity
Time complexity: O(n)
Space complexity: O(1)
Common Pitfalls
Incrementing the Current Pointer After Swapping with the Right Boundary
In the Dutch National Flag algorithm, when you swap the current element with the right boundary (for value 2), you must not increment the current pointer. The swapped element from the right has not been examined yet and could be 0, 1, or 2.
Using Strict Less Than Instead of Less Than or Equal
The loop condition must be i <= r not i < r. Using strict less than causes the algorithm to miss processing the element at position r when i equals r, potentially leaving an unsorted element.
Forgetting to Increment the Left Pointer After Swapping Zeros
When swapping a 0 to the left boundary, both the left boundary pointer and the current pointer must advance. The left boundary moves to make room for the next 0, and the current pointer moves because the swapped element (which came from the left) is guaranteed to be 0 or 1, both of which are already correctly positioned.