Counting Sort: A New Pespective on Sorting
Prerequisite Knowledge
Before reading this article, you need to learn:
In One Sentence
The idea of counting sort is simple: count how many times each element appears, then figure out the index of each element in the sorted array, and finally finish sorting.
The time and space complexity of counting sort is , where is the length of the array, and is the range of the numbers in the array.
Here is a visualization panel for selection sort. You can click the code sorted[count[index] - 1] = nums[i] to see how the sorted array is formed:
Algorithm Visualization
For example, if the input array nums contains two 1s, one 3, and three 6s, then we just need to fill the array with two 1s, one 3, and three 6s in order. The sorted result is [1, 1, 3, 6, 6, 6].
Let’s look at a simple problem to understand this. See LeetCode Problem 75: “Sort Colors”:
75. Sort Colors | LeetCode | 🟠
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.
We will use the integers 0, 1, and 2 to represent the color red, white, and blue, respectively.
You must solve this problem without using the library's sort function.
Example 1:
Input: nums = [2,0,2,1,1,0] Output: [0,0,1,1,2,2]
Example 2:
Input: nums = [2,0,1] Output: [0,1,2]
Constraints:
n == nums.length1 <= n <= 300nums[i]is either0,1, or2.
Follow up: Could you come up with a one-pass algorithm using only constant extra space?
There are many ways to solve this problem. The best way uses the two-pointer technique to sort the array in one pass. I will explain this in Array Two-Pointer Practice Problems. Here, we use counting sort to solve it. In short, you need to sort an array where the values are only 0, 1, or 2.
We can create a count array of size 3. count[0], count[1], and count[2] store how many 0s, 1s, and 2s are in the array. Then we fill the original array in order according to the count array.
class Solution {
public void sortColors(int[] nums) {
// count the number of 0, 1, 2
int[] count = new int[3];
for (int element : nums) {
count[element]++;
}
// fill the original array according to the count array
int index = 0;
for (int element = 0; element < 3; element++) {
for (int i = 0; i < count[element]; i++) {
nums[index] = element;
index++;
}
}
}
}class Solution {
public:
void sortColors(vector<int>& nums) {
// count the number of 0, 1, 2
vector<int> count(3, 0);
for (int element : nums) {
count[element]++;
}
// fill the original array according to the count array
int index = 0;
for (int element = 0; element < 3; element++) {
for (int i = 0; i < count[element]; i++) {
nums[index] = element;
index++;
}
}
}
};class Solution:
def sortColors(self, nums: List[int]) -> None:
# count the number of 0, 1, 2
count = [0] * 3
for element in nums:
count[element] += 1
# fill the original array according to the count array
index = 0
for element in range(3):
for _ in range(count[element]):
nums[index] = element
index += 1func sortColors(nums []int) {
// count the number of 0, 1, 2
count := make([]int, 3)
for _, element := range nums {
count[element]++
}
// fill the original array according to the count array
index := 0
for element := 0; element < 3; element++ {
for i := 0; i < count[element]; i++ {
nums[index] = element
index++
}
}
}var sortColors = function(nums) {
// count the number of 0, 1, 2
const count = [0, 0, 0];
for (const element of nums) {
count[element]++;
}
// fill the original array according to the count array
let index = 0;
for (let element = 0; element < 3; element++) {
for (let i = 0; i < count[element]; i++) {
nums[index] = element;
index++;
}
}
};This is a simple counting sort. But this problem is easy because it only has three types of numbers: 0, 1, and 2. Next, let’s see a more general counting sort algorithm.
General Counting Sort
Although counting sort is simple, there are still some coding tricks in the general version.
Let’s start with the problems. Counting sort needs to use the values in the array as indexes in the count array. So we can ask:
Does this mean we can only use counting sort when all numbers in
numsare non-negative integers? What if there are negative numbers? What about custom types?Since counting sort only cares about how many times each value appears, and does not care about the order of the same numbers, is counting sort unstable?
Because counting sort uses the value of the element as the index of the
countarray, what if the largest value in the array is very large? Will thecountarray use too much space?
Let’s think about these questions step by step and try to solve them.