Sorting an array containing only 0s and 1s involves rearranging all the elements the array such that all 0’s are grouped together at the beginning, followed by all the 1’s. This kind of problem is also known as “binary array sorting” or “segregate 0s and 1s”.
Problem Statement
The task is to sort an array that contains only two types of elements: 0s and 1s. We need to rearrange all the elements in the array such that all the 0s come before all the 1s.
For example, let’s consider the following unsorted array which contains only 0s and 1s:
[1, 0, 1, 1, 0, 1, 0, 0]
The objective is to sort this array so that all 0s appear first, followed by all 1s. The expected output after sorting would be:
[0, 0, 0, 0, 1, 1, 1, 1]
This problem is a special case of sorting, where we only have two distinct values. The challenge lies in performing this sorting efficiently, ideally in a single pass through the array.
Different Approaches
There are many approaches to solve this problem. Below are some of the major ones.
General Sorting Algorithms
We can use any standard sorting algorithms such as Bubble Sort, Insertion Sort, Selection Sort, Merge Sort, or Quick Sort to segregate zeroes and ones. These algorithms are designed to handle a wide range of sorting tasks and will successfully segregate 0s and 1s in the array. However, it’s important to note that while these methods will work, they are not specifically optimized for this particular problem.
The time complexity of these standard sorting algorithms varies depending on the chosen method. For instance, simpler algorithms like Bubble Sort, Insertion Sort, and Selection Sort typically have a time complexity of O(n^2), where n is the number of elements in the array. More efficient algorithms like Merge Sort and Quick Sort offer better performance with a time complexity of O(n log n). Similarly, the space complexity differs among these algorithms, ranging from O(1) for in-place sorting methods to O(n) for algorithms that require additional memory proportional to the input size. While these general-purpose sorting algorithms will get the job done, there are more efficient approaches specifically tailored to sorting binary arrays of zeroes and ones.
Click here to learn more about different sorting techniques
Counting Approach (Two Pass Solution)
The main idea behind Counting Approach is to count how many 0s and 1s are in the array, and then use that information to reconstruct the sorted array.
First, we go through the array once for counting the number of 0s and 1s. Once we know these counts, we don’t need to do any complex sorting. We can simply fill the beginning of the array with the correct number of 0s, and then fill the rest with 1s. This approach is faster than traditional sorting methods for this specific case, as it only requires one pass through the array to count, and another pass to fill in the sorted values.
Below is the algorithm for counting approach:
- Initialize two variables,
zeroes_countandones_count, to keep track of the number of0s and1s in the array. - Traverse the entire array once, incrementing
zeroes_countfor each0encountered andones_countfor each1encountered. - Reset a pointer to the beginning of the array.
- Fill the first
zeroes_countpositions of the array with0s. - Fill the remaining
ones_countpositions of the array with1s. - The array is now sorted with all
0s at the beginning and all1s at the end.
Below is the implementation:
def sort_binary_array(arr):
# Initialize counters for zeroes and ones
zeroes_count = 0
ones_count = 0
# Count zeroes and ones in the array
for num in arr:
if num == 0:
zeroes_count += 1
else:
ones_count += 1
# Reset the array with sorted values in-place
for i in range(len(arr)):
if i < zeroes_count:
arr[i] = 0
else:
arr[i] = 1
# No return statement needed as we're modifying the array in-place
# Input array
input_array = [1, 0, 1, 1, 0, 1, 0, 0]
print("Input array:", input_array)
# Sort the array in-place
sort_binary_array(input_array)
print("Sorted array:", input_array)
This approach is efficient when compared with regular sorting algorithms, as it requires only two passes through the array - one for counting and one for filling. It has a time complexity of O(n) and requires O(1) space to store counts of zeros and ones.
One limitation of this approach is that it does not work if sorting needs to done based on a particular attribute of an object. For example, let’s say there’s an object with one of it’s attribute as category which can be 0 or 1. Now if we wish to segregate these objects based on category attribute, counting approach can’t be used as we can’t recreate each object just from the counts. The next approach (Two-Pointer Approach) works even for this scenario.
Two-Pointer Approach (One Pass Solution)
Imagine this: Let’s say you have a line of mixed red and blue balls. Your goal is to arrange them so that all the blue balls are on the left side and all the red balls are on the right side.
To do this efficiently, you can use two people working together from opposite ends of the line. The person on the left (left pointer) looks for red balls and stops when they find one. The person on the right (right pointer) looks for blue balls and stops when they find one. When both people have stopped, they swap the balls they’re holding. This process continues until both the people meet.
In this analogy, blue balls represent 0s, and red balls represent 1s. The left pointer ensures that everything to its left is 0, while the right pointer ensures that everything to its right is 1. By working together and swapping when necessary, they efficiently sort the entire array with a single pass through all the elements.
Below is the algorithm:
- Initialize two pointers:
- Set the
leftpointer at the beginning of the array (index 0). - Set the
rightpointer at the end of the array (last index).
- Set the
- Start a loop that continues while the
leftpointer is less than therightpointer: - Inside the loop:
- Move the
leftpointer to the right until it finds a 1 or meets therightpointer. - Move the
rightpointer to the left until it finds a 0 or meets the ’left’ pointer.
- Move the
- If the
leftpointer is still less than therightpointer:- Swap the elements at the
leftandrightpositions. - Increment the
leftpointer. - Decrement the
rightpointer.
- Swap the elements at the
- Repeat steps 3-4 until the pointers meet or cross each other.
Below is the implementation:
def sort_binary_array(arr):
# Initialize two pointers
left = 0 # Left pointer starts at the beginning of the array
right = len(arr) - 1 # Right pointer starts at the end of the array
# Continue while left pointer is less than right pointer
while left < right:
# Move left pointer to the right until it finds a 1 or meets right pointer
# This helps identify misplaced 1's on the left side
while left < right and arr[left] == 0:
left += 1
# Move right pointer to the left until it finds a 0 or meets left pointer
# This helps identify misplaced 0's on the right side
while left < right and arr[right] == 1:
right -= 1
# If left is still less than right, swap elements and move pointers
# This step ensures 0's move to the left and 1's move to the right
if left < right:
arr[left], arr[right] = arr[right], arr[left]
left += 1
right -= 1
# Input array
input_array = [1, 0, 1, 1, 0, 1, 0, 0]
print("Input array:", input_array)
# Sort the array in-place
sort_binary_array(input_array)
print("Sorted array:", input_array)
The algorithm is efficient as it sorts the array in a single pass, with a time complexity of O(n) where n is the number of elements in the array. It also uses constant extra space, making it space-efficient with O(1) space complexity.
Applications
The key idea behind this problem of sorting 0s and 1s is similar to binary classification. It can be extended to problems which require elements of one category to be at the beginning of the array and all the elements of another category at the end of the array.
For example, let’s consider the below array:
[5, 2, 8, 3, 1, 7, 6, 4]
If we need to arrange all even numbers at the start of array and odd numbers at the end of array, we can use the same two pointer algorithm discussed before to do this. It can also be used to solve other problems, such as separating positive and negative numbers, or partitioning an array around a given value (which is a key step in the quicksort algorithm).
The two pointer approach to sort a binary array can be extended to solve the Dutch National Flag problem which involves sorting an array containing elements belonging to 3 distinct categories.