Sort 0s, 1s and 2s Without using Extra Space

Difficulty: Medium, Asked-in: Google, Facebook, Microsoft, Amazon, Paytm, Morgan Stanley, Hike, Walmart, Adobe, SAP Labs, Qualcomm

Key takeaway

  • A special sorting problem that can be solved using O(n) time complexity.
  • There is a lot of variation available to this coding problem.
  • We are using a single loop and three-pointers to solve the problem efficiently.
  • This problem is a variation of the famous Dutch national flag problem.

Let’s understand the problem

Given an array X[] consisting of 0s, 1s, and 2s, write a program to sort the array of 0’s, 1’s, and 2’s in ascending order.

Examples

Input: X[] = [0, 2, 1, 0, 1, 2, 1, 0], Output: X[] = [0, 0, 0, 1, 1, 1, 2, 2]

Input: X[]= [0, 1, 1, 0, 1, 2, 1, 2, 0, 0], Output: X[] = [0, 0, 0, 0, 1, 1, 1, 1, 2, 2]

Input: X[] = [2, 1, 0], Output: X[] = [0, 1, 2]

Discussed solution approaches

  • Brute force approach using double traversal
  • Efficient approach using single scan: Three-way partitioning

Brute force approach using double traversal

Solution idea and steps

The basic idea that comes to mind is to simply count the number of 0’s, 1’s, and 2’s. Then, place all 0’s at the beginning of the array followed by 1’s and 2's.

  • We declare three variables countZero, countOne, countTwo to store the frequency of 0’s, 1’s, and 2’s in the array.

    int countZero = 0, countOne = 0, countTwo = 0
  • Now we traverse through the array to count the frequency of 0’s, 1’s, and 2's.

    for (int i = 0; i < n; i = i + 1)
    {
      if (X[i] == 0) countZero = countZero + 1
      if (X[i] == 1) countOne = countOne + 1
      if (X[i] == 2) countTwo = countTwo + 1   
    }
  • Now, we again traverse the array and replace the first countZero places of the array as 0, next countOne places by 1, and last countTwo places by 2.

Solution code C++

void sort_0_1_2(int X[], int n)
{
    int countZero = 0, countOne = 0, countTwo = 0;
    for (int i = 0; i < n; i = i + 1)
    {
        if (X[i] == 0) countZero = countZero + 1;
        if (X[i] == 1) countOne = countOne + 1;
        if (X[i] == 2) countTwo = countTwo + 1;
    }
    int i = 0;
    while (countZero != 0)
    {
        X[i] = 0;
        i = i + 1;
        countZero = countZero - 1;
    }
    while (countOne != 0)
    {
        X[i] = 1;
        i = i + 1;
        countOne = countOne - 1;
    }
    while (countTwo != 0)
    {
        X[i] = 2;
        i = i + 1;
        countTwo = countTwo - 1;
    }
}

Solution code Python

def sort_0_1_2(X, n):
    count_zero = 0
    count_one = 0
    count_two = 0
    for i in range(n):
        if X[i] == 0:
            count_zero = count_zero + 1
        elif X[i] == 1:
            count_one = count_one + 1
        elif X[i] == 2:
            count_two = count_two + 1
    i = 0
    while count_zero > 0:
        X[i] = 0
        i = i + 1
        count_zero = count_zero - 1
    while count_one > 0:
        X[i] = 1
        i = i + 1
        count_one = count_one - 1
    while count_two > 0:
        X[i] = 2
        i = i + 1
        count_two = count_two - 1

Solution analysis

Time complexity = Time complexity of counting 0’s, 1’s, and 2’s + Time complexity of placing 0’s, 1’s, and 2’s in the sorted order = O(n) + O(n) = O(n). As we are using constant extra space, so space complexity = O(1).

Efficient approach using single scan: Three-way partitioning

Solution idea

The critical question is: can we solve the problem in a single traversal of the array? We have only three unique elements in the input, so can we use an idea similar to the partition process of the quicksort to place the elements at their correct position in the sorted output? Let's think!

We can solve the problem using a single scan by maintaining the correct order of 0’s, 1’s, and 2’s using variables. Actually, we have three types of elements to be placed in sorted order, so we divide the given array into four sections using three-pointers. Let us name these pointers as lowmid, and high.

  • X[0 … low - 1] to fill zeroes.
  • X[low ... mid - 1] to fill ones.
  • X[mid … high] unknown
  • X[high + 1 ... n - 1] only twos

Solution steps

We initialise three pointers low = 0, mid = 0 and high = n - 1.

Now we scan the array using the mid pointer. In other words, we run a loop until mid <= high:

  • If (X[mid] == 0): We swap value at X[mid] with value at X[low] and increment both low and mid pointers by 1 (shrinking the unknown range).
  • If (X[mid] == 1): We simply increment mid pointer by 1 (further shrinking unknown range).
  • If (X[mid] == 2): We swap value at X[mid] with value at X[high] and decrement high pointer by 1. Now at this point, we again traverse the array from the same position of mid pointer, as the element at this index has not been considered yet. Think!

Solution code C++

void sort_0_1_2(int X[], int n)
{
    int low = 0, mid = 0, high = n - 1;
    while (mid <= high)
    {
        if (X[mid] == 0)
        {
            swap(X[low], X[mid]);
            low = low + 1;
            mid = mid + 1;
        }
        else if (X[mid] == 1)
        {
            mid = mid + 1;
        }
        else if (X[mid] == 2)
        {
            swap(X[mid], X[high]);
            high = high - 1;
        }
    }
}

Solution code Python

def sort_0_1_2(X, n):
    low = 0
    mid = 0
    high = n - 1
    while mid <= high:
        if X[mid] == 0:
            X[low], X[mid] = X[mid], X[low]
            low = low + 1
            mid = mid + 1
        elif X[mid] == 1:
            mid = mid + 1
        elif X[mid] == 2:
            X[mid], X[high] = X[high], X[mid]
            high = high - 1

Time and space complexity analysis

In the worst case, we will be traversing and accessing each element of the array only once. Time complexity = O(n). Space complexity = O(1), As we are using a constant number of variables.

Critical ideas to think!

  • Can we optimize the above code and reduce the swap operations?
  • Can we implement the above code using the continue or switch statement?
  • Is this algorithm stable? If not, then how to make it stable?
  • Why are we not increasing the mid-index if (X[mid] ==2)?
  • Can we solve the problem using the partition process of the quick sort?
  • Can we solve this problem using two-pointers?
  • How can we modify the above code for a similar problem to sort the array containing 0s,1s, 2s, and 3s?
  • What are the famous linear time sorting algorithms? Can we apply the idea of counting sort to solve the above problem?

Comparisons of time and space complexities

  • Brute force approach: Time = O(n), Space = O(1)
  • Three-way partitioning: Time = O(n), Space = O(1)

Suggested coding questions to practice

Please write in the message below if you find any errors or you want to share more insights. Enjoy learning, Enjoy coding, Enjoy algorithms!

More from EnjoyAlgorithms

Self-paced Courses and Blogs