Facebook Pixel

75. Sort Colors

Problem Description

This problem asks you to sort an array containing only three distinct values: 0, 1, and 2. These numbers represent three colors - red (0), white (1), and blue (2). The goal is to rearrange the array in-place so that all elements of the same color are grouped together, following the specific order: all 0s first, then all 1s, and finally all 2s.

The key constraints are:

  • You must modify the original array directly without using extra space for another array
  • You cannot use built-in sorting functions
  • The final arrangement must have all reds (0s) at the beginning, whites (1s) in the middle, and blues (2s) at the end

For example, if the input array is [2, 0, 2, 1, 1, 0], after sorting it should become [0, 0, 1, 1, 2, 2].

This is also known as the "Dutch National Flag Problem" because the Dutch flag consists of three horizontal bands of red, white, and blue colors.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Since we only have three distinct values (0, 1, 2), we can think of partitioning the array into three regions. The key insight is that we don't need to sort in the traditional sense - we just need to group elements by their value.

Imagine maintaining three boundaries in the array:

  • Everything to the left of one boundary should be 0s
  • Everything to the right of another boundary should be 2s
  • Everything in between should be 1s

We can achieve this with a single pass through the array by maintaining three pointers:

  • Pointer i marks the rightmost position of the 0s region (initially -1 since no 0s are placed yet)
  • Pointer j marks the leftmost position of the 2s region (initially at array length since no 2s are placed yet)
  • Pointer k is our current scanning position

As we scan through the array with k:

  • When we encounter a 0, we know it belongs at the beginning. We swap it with the element right after the last placed 0 (at position i+1), expand the 0s region by incrementing i, and move forward with k.
  • When we encounter a 2, we know it belongs at the end. We swap it with the element right before the 2s region (at position j-1) and shrink the unprocessed region by decrementing j. We don't increment k here because the swapped element from position j-1 hasn't been examined yet.
  • When we encounter a 1, it's already in the correct middle region, so we just move k forward.

This approach ensures that after one pass, all 0s are grouped at the beginning, all 2s at the end, and all 1s naturally fall in the middle.

Learn more about Two Pointers and Sorting patterns.

Solution Approach

The implementation uses the three-pointer technique described in the intuition. Let's walk through the algorithm step by step:

Initialization:

  • Set i = -1 to mark the rightmost boundary of the 0s region (initially empty)
  • Set j = len(nums) to mark the leftmost boundary of the 2s region (initially empty)
  • Set k = 0 as the current scanning pointer starting from the beginning

Main Loop: The algorithm continues while k < j, meaning we still have unprocessed elements between the current position and the 2s region.

For each element at position k, we have three cases:

  1. When nums[k] == 0:

    • Increment i by 1 to expand the 0s region
    • Swap nums[i] with nums[k] to place the 0 in the correct region
    • Increment k by 1 to move to the next element
    • We can safely increment k because we know the swapped element from position i was either a 0 (already processed) or a 1 (which is fine in the middle region)
  2. When nums[k] == 2:

    • Decrement j by 1 to expand the 2s region from the right
    • Swap nums[j] with nums[k] to place the 2 in the correct region
    • Do NOT increment k because the element we just swapped from position j hasn't been examined yet
  3. When nums[k] == 1:

    • Simply increment k by 1 since 1s belong in the middle region where they currently are

Final State: After the loop completes, the array is partitioned into three regions:

  • [0, i]: Contains all 0s
  • [i+1, j-1]: Contains all 1s
  • [j, n-1]: Contains all 2s

This algorithm achieves O(n) time complexity with a single pass through the array and O(1) space complexity since we only use three pointer variables.

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's trace through the algorithm with the array [2, 0, 1, 2, 0].

Initial State:

  • Array: [2, 0, 1, 2, 0]
  • i = -1 (rightmost boundary of 0s region)
  • j = 5 (leftmost boundary of 2s region)
  • k = 0 (current scanning position)

Step 1: k = 0, nums[k] = 2

  • Since we found a 2, decrement j to 4
  • Swap nums[0] with nums[4]: [0, 0, 1, 2, 2]
  • Keep k = 0 (don't increment since we need to examine the swapped element)

Step 2: k = 0, nums[k] = 0

  • Since we found a 0, increment i to 0
  • Swap nums[0] with nums[0] (no change): [0, 0, 1, 2, 2]
  • Increment k to 1

Step 3: k = 1, nums[k] = 0

  • Since we found a 0, increment i to 1
  • Swap nums[1] with nums[1] (no change): [0, 0, 1, 2, 2]
  • Increment k to 2

Step 4: k = 2, nums[k] = 1

  • Since we found a 1, it's already in the correct middle region
  • Simply increment k to 3

Step 5: k = 3, nums[k] = 2

  • Since we found a 2, decrement j to 3
  • Swap nums[3] with nums[3] (no change): [0, 0, 1, 2, 2]
  • Keep k = 3

Termination: Now k = 3 and j = 3, so k < j is false. The loop ends.

Final Result: [0, 0, 1, 2, 2]

  • Region [0, 1] contains all 0s
  • Region [2, 2] contains all 1s
  • Region [3, 4] contains all 2s

The array is successfully sorted with all colors grouped together in the correct order.

Solution Implementation

1class Solution:
2    def sortColors(self, nums: List[int]) -> None:
3        """
4        Sort an array containing only 0s, 1s, and 2s in-place using Dutch National Flag algorithm.
5      
6        Args:
7            nums: List of integers containing only 0, 1, and 2
8      
9        Returns:
10            None (modifies the list in-place)
11        """
12        # Initialize three pointers:
13        # left_boundary: rightmost position of 0s section (starts before array)
14        # right_boundary: leftmost position of 2s section (starts after array)
15        # current: current element being examined
16        left_boundary = -1
17        right_boundary = len(nums)
18        current = 0
19      
20        # Process elements until current pointer reaches the 2s section
21        while current < right_boundary:
22            if nums[current] == 0:
23                # Move 0 to the left section
24                left_boundary += 1
25                nums[left_boundary], nums[current] = nums[current], nums[left_boundary]
26                # Move current forward since we know the swapped element is processed
27                current += 1
28            elif nums[current] == 2:
29                # Move 2 to the right section
30                right_boundary -= 1
31                nums[right_boundary], nums[current] = nums[current], nums[right_boundary]
32                # Don't increment current as the swapped element needs to be examined
33            else:
34                # Element is 1, leave it in the middle section
35                current += 1
36
1class Solution {
2    /**
3     * Sorts an array containing only 0s, 1s, and 2s in-place using Dutch National Flag algorithm.
4     * Time Complexity: O(n), Space Complexity: O(1)
5     * 
6     * @param nums Array containing values 0, 1, or 2 to be sorted
7     */
8    public void sortColors(int[] nums) {
9        // leftBoundary: rightmost index of 0s section (initially -1, before array)
10        int leftBoundary = -1;
11      
12        // rightBoundary: leftmost index of 2s section (initially at array length, after array)
13        int rightBoundary = nums.length;
14      
15        // current: current element being examined
16        int current = 0;
17      
18        // Process elements while current pointer hasn't reached the 2s section
19        while (current < rightBoundary) {
20            if (nums[current] == 0) {
21                // Move 0 to the left section and advance both pointers
22                leftBoundary++;
23                swap(nums, leftBoundary, current);
24                current++;
25            } else if (nums[current] == 2) {
26                // Move 2 to the right section, don't advance current 
27                // (need to check the swapped element)
28                rightBoundary--;
29                swap(nums, rightBoundary, current);
30            } else {
31                // Element is 1, leave it in the middle section
32                current++;
33            }
34        }
35    }
36  
37    /**
38     * Helper method to swap two elements in an array.
39     * 
40     * @param nums The array containing elements to swap
41     * @param i Index of first element
42     * @param j Index of second element
43     */
44    private void swap(int[] nums, int i, int j) {
45        int temp = nums[i];
46        nums[i] = nums[j];
47        nums[j] = temp;
48    }
49}
50
1class Solution {
2public:
3    void sortColors(vector<int>& nums) {
4        // Dutch National Flag algorithm to sort array with 3 distinct values (0, 1, 2)
5        // Three pointers approach:
6        // - left: boundary between 0s and 1s (last position of 0s)
7        // - right: boundary between 1s and 2s (first position of 2s)
8        // - current: current element being examined
9      
10        int left = -1;                    // Points to the last index of 0s region
11        int right = nums.size();          // Points to the first index of 2s region
12        int current = 0;                   // Current index being processed
13      
14        // Process elements until current pointer meets the 2s region
15        while (current < right) {
16            if (nums[current] == 0) {
17                // Found a 0: swap with element at left boundary and expand 0s region
18                left++;
19                swap(nums[left], nums[current]);
20                current++;                 // Safe to move forward as we know nums[left] was 0 or 1
21            } 
22            else if (nums[current] == 2) {
23                // Found a 2: swap with element at right boundary and shrink unprocessed region
24                right--;
25                swap(nums[right], nums[current]);
26                // Don't increment current as the swapped element needs to be examined
27            } 
28            else {
29                // Found a 1: already in correct position, just move forward
30                current++;
31            }
32        }
33    }
34};
35
1/**
2 * Sorts an array containing only 0s, 1s, and 2s in-place using Dutch National Flag algorithm.
3 * Time Complexity: O(n), Space Complexity: O(1)
4 * @param nums - Array of integers containing only 0, 1, and 2
5 * @returns void - Modifies the array in-place
6 */
7function sortColors(nums: number[]): void {
8    // Pointer for the boundary of 0s (elements before this are all 0s)
9    let zeroEndIndex: number = -1;
10  
11    // Pointer for the boundary of 2s (elements from this index onwards are all 2s)
12    let twoStartIndex: number = nums.length;
13  
14    // Current pointer traversing the array
15    let currentIndex: number = 0;
16  
17    // Process elements until current pointer reaches the boundary of 2s
18    while (currentIndex < twoStartIndex) {
19        if (nums[currentIndex] === 0) {
20            // Found a 0: move it to the beginning section
21            zeroEndIndex++;
22            // Swap current element with the element at zeroEndIndex
23            [nums[zeroEndIndex], nums[currentIndex]] = [nums[currentIndex], nums[zeroEndIndex]];
24            // Move to next element since we know what we swapped from the left
25            currentIndex++;
26        } else if (nums[currentIndex] === 2) {
27            // Found a 2: move it to the end section
28            twoStartIndex--;
29            // Swap current element with the element at twoStartIndex
30            [nums[twoStartIndex], nums[currentIndex]] = [nums[currentIndex], nums[twoStartIndex]];
31            // Don't increment currentIndex as we need to check the swapped element
32        } else {
33            // Found a 1: leave it in the middle section
34            currentIndex++;
35        }
36    }
37}
38

Time and Space Complexity

The time complexity is O(n), where n is the length of the array. The algorithm uses a single pass through the array with the pointer k traversing from index 0 to index j. While k might not increment in every iteration (specifically when nums[k] == 2), each element is examined at most twice - once when k reaches it, and potentially once more if it's swapped from the right side. Since each element is processed a constant number of times, the overall time complexity remains linear.

The space complexity is O(1). The algorithm only uses three additional integer variables (i, j, and k) to track positions in the array, regardless of the input size. The sorting is performed in-place by swapping elements within the original array without requiring any additional data structures.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

Pitfall 1: Incrementing the current pointer after swapping with a 2

The Problem: A frequent mistake is incrementing the current pointer after swapping with an element from the right boundary when encountering a 2. This can lead to incorrect sorting because the element swapped from the right hasn't been examined yet.

Incorrect Code:

elif nums[current] == 2:
    right_boundary -= 1
    nums[right_boundary], nums[current] = nums[current], nums[right_boundary]
    current += 1  # WRONG! This skips examining the swapped element

Why It Fails: Consider the array [2, 0, 1]:

  • When current = 0, we find nums[0] = 2
  • We swap with nums[2], resulting in [1, 0, 2]
  • If we increment current to 1, we skip examining the 1 that was just placed at index 0
  • The final result would be [1, 0, 2] instead of [0, 1, 2]

Solution: Don't increment current after swapping with the right boundary. The swapped element needs to be examined in the next iteration.

elif nums[current] == 2:
    right_boundary -= 1
    nums[right_boundary], nums[current] = nums[current], nums[right_boundary]
    # Don't increment current - we need to examine the swapped element

Pitfall 2: Incorrect boundary initialization

The Problem: Initializing boundaries incorrectly can cause index out of bounds errors or infinite loops.

Common Mistakes:

# Mistake 1: Starting left_boundary at 0
left_boundary = 0  # WRONG! First swap would be nums[0] with nums[0]

# Mistake 2: Starting right_boundary at len(nums) - 1
right_boundary = len(nums) - 1  # WRONG! Would place first 2 at second-to-last position

Why It Fails:

  • If left_boundary starts at 0, the first 0 encountered would increment it to 1 and swap with index 1, skipping index 0
  • If right_boundary starts at len(nums) - 1, the first 2 would be placed at index len(nums) - 2, leaving the last position unprocessed

Solution: Initialize boundaries outside the array:

left_boundary = -1  # Before the array
right_boundary = len(nums)  # After the array

Pitfall 3: Using wrong loop condition

The Problem: Using current <= right_boundary instead of current < right_boundary can cause accessing already-processed elements in the 2s region.

Incorrect Code:

while current <= right_boundary:  # WRONG! May process elements already in 2s region

Why It Fails: When current == right_boundary, the element at that position has already been designated as part of the 2s region and shouldn't be processed again.

Solution: Use strict inequality:

while current < right_boundary:  # Correct - stops before the 2s region
Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

How many ways can you arrange the three letters A, B and C?


Recommended Readings

Want a Structured Path to Master System Design Too? Donโ€™t Miss This!

Load More