Say we have the following
unsortedArray = [4, 3, 4, 2]
Here is the algorithm:
Step 1) Iterate over the unsortedArray
and use each element as the index into a new array we call countingArray
. The value we will hold in each position is the count of times that that number appears. Each time we access a position we increment it by 1.
countingArray = [0, 0, 0, 0, 0, ..., 0, 0, 0, 0] // before iteration
countingArray = [0, 0, 0, 0, 1, ..., 0, 0, 0, 0] // after handling 4
countingArray = [0, 0, 0, 1, 1, ..., 0, 0, 0, 0] // after handling 3
countingArray = [0, 0, 0, 1, 2, ..., 0, 0, 0, 0] // after the second 4
countingArray = [0, 0, 1, 1, 2, ..., 0, 0, 0, 0] // after handling 2
We can allocate countingArray
in advance because the range of the numbers we wish to sort is limited and known a-priori. In your example countingArray
will have 101 elements.
Time complexity of this step is O(n) because you are iterating over n elements from unsortedArray
. Inserting them into countingArray
has constant time complexity.
Step 2) As shown in the example above countingArray
is going to have positions with value 0 where there were no numbers to count in unsortedArray
. We are going to skip these positions in the following iteration we will describe.
In countingArray
non-zero positions define a number that we want to sort, and the content in that position define the count of how many times that number should appear in the final sortedArray
.
We iterate over countingArray
and starting at the first position of sortedArray
put that number into count number of adjacent positions. This builds sortedArray
and takes O(n).
countingArray = [0, 0, 1, 1, 2, ..., 0, 0, 0, 0]
// After skipping the first 2 0s and seeing a count of 1 in position 2
sortedArray = [2, 0, 0, 0]
// After seeing a count of 1 in position 3
sortedArray = [2, 3, 0, 0]
// In position 4 we have a count of 2 so we fill 4 in 2 positions
sortedArray = [2, 3, 4, 4]
=======
Total time complexity is O(n) * 2 = O(n)