Counting sort works by counting how many times each value appears, then using those counts to compute the exact final position of every value. It never compares two values against each other. Instead it uses each value as an index into a count array, builds prefix sums so each entry tells it where to place the next instance of that value, then writes every value directly into its sorted position.
Press play, or step through it one operation at a time. Orange highlights the value being read, yellow marks a position being written, and green means the final sorted result.
Ready. 10 values, unsorted.
0 comparisons · 0 placements
Ready. 10 values, unsorted.
- Comparing
- Placing
- Sorted
Code
- def counting_sort(a):
- if not a:
- return a
- min_val = min(a)
- max_val = max(a)
- count = [0] * (max_val - min_val + 1)
- for val in a:
- count[val - min_val] += 1
- for i in range(1, len(count)):
- count[i] += count[i - 1]
- output = [0] * len(a)
- for i in range(len(a) - 1, -1, -1):
- output[count[a[i] - min_val] - 1] = a[i]
- count[a[i] - min_val] -= 1
- return output
Try Sorted and Reversed. Both take exactly the same number of steps, because
counting sort does not look at the overall order. It counts, builds prefix sums,
and places, mechanically. The work is the same on any input of the same size and
range.
Time and space complexity
| Case | Complexity | Why |
|---|---|---|
| Best | O(n + k) | Always does the same work: count, prefix sum, place. Input order is irrelevant. |
| Average | O(n + k) | Same. No input makes it faster or slower. |
| Worst | O(n + k) | Same. The range k can dominate if it is much larger than n. |
| Space | O(n + k) | A count array of size k plus an output array of size n. |
| Stable | Yes | Right-to-left placement preserves relative order of equal values. |
Here k is the range: max - min + 1. For values between 10 and 82, k is 73. If
all values fit in 0 to 99, k is at most 100, and the algorithm is effectively
linear in n.
Step by step
- Find the minimum and maximum values to determine the range k.
- Create a count array of size k, initialised to zero.
- Scan the input. For each value, increment its corresponding count.
- Build prefix sums: each
count[i]becomes the total of all counts up to and including index i. After this,count[v - min]tells you how many values are less than or equal to v. - Create an output array of size n.
- Scan the input from right to left. For each value, read its prefix sum to find its final position, place it there, and decrement the count.
- The output array is now sorted.
Scanning right to left in step 6 is what makes counting sort stable. Among equal values, the rightmost in the input gets the highest position in the output, so their relative order is preserved.
Worked example
Sorting [4, 2, 4, 1] (min = 1, max = 4, k = 4):
| Phase | State | What happened |
|---|---|---|
| Count | [1, 1, 0, 2] | 1 appears once, 2 once, 3 zero times, 4 twice. |
| Prefix sums | [1, 2, 2, 4] | count[0] stays; each next adds the previous. |
| Place index 3 (val 1) | output[0] = 1 | count[0] was 1, place at position 0, decrement to 0. |
| Place index 2 (val 4) | output[3] = 4 | count[3] was 4, place at position 3, decrement to 3. |
| Place index 1 (val 2) | output[1] = 2 | count[1] was 2, place at position 1, decrement to 1. |
| Place index 0 (val 4) | output[2] = 4 | count[3] was 3, place at position 2, decrement to 2. |
Result: [1, 2, 4, 4]. Four value inspections and four placements. The two 4s
kept their original left-to-right order because we scanned right to left.
When to use it
| Reach for it when | Avoid it when |
|---|---|
| The range of values is small relative to n (scores 0-100, ages, ASCII codes) | The range is large — a count array of a million slots for ten values wastes memory |
| You need guaranteed linear time and the data fits integer keys | Values are floating-point or complex objects without a natural integer key |
| Stability matters and the range is bounded | You need an in-place sort — counting sort needs O(n + k) extra space |
| You are building a sub-routine for radix sort | The data is already nearly sorted and you want an adaptive algorithm like insertion sort |
The code
Every version below is the stable variant: it scans right to left in the placement phase, so equal values keep their original order.
def counting_sort(a):
if not a:
return a
min_val = min(a)
max_val = max(a)
count = [0] * (max_val - min_val + 1)
for val in a:
count[val - min_val] += 1
for i in range(1, len(count)):
count[i] += count[i - 1]
output = [0] * len(a)
for i in range(len(a) - 1, -1, -1):
output[count[a[i] - min_val] - 1] = a[i]
count[a[i] - min_val] -= 1
return output
nums = [38, 27, 43, 10, 82, 15, 27, 61, 45, 38]
result = counting_sort(nums)
print(result)
Common questions
What is the time complexity of counting sort?
O(n + k), where n is the number of values and k is the range (maximum minus minimum plus one). The algorithm makes one pass to count, one to build prefix sums over the range, and one to place values. When k is small relative to n, this is effectively linear.
Is counting sort a comparison sort?
No. It never compares two values against each other. It uses each value as an index into a count array, which is how it avoids the O(n log n) lower bound that comparison sorts cannot beat.
Is counting sort stable?
Yes, when the placement phase scans the input from right to left. That way, among values with the same key, the one that appeared last in the input is placed last in the output, preserving their original relative order.
Why does counting sort need O(n + k) space?
It needs a count array of size k (one slot per distinct value in the range) and an output array of size n. If the range is much larger than n, the count array dominates and the algorithm wastes memory on empty slots.
When is counting sort better than quicksort?
When the range of values is small relative to n. Sorting a million integers all between 0 and 1000 takes counting sort about 2n + k operations, while quicksort does roughly 20 million comparisons. But if the range is millions wide, counting sort allocates a huge array and loses.
What is the difference between counting sort and radix sort?
Counting sort handles the full value in one pass and needs space proportional to the value range. Radix sort processes one digit at a time and uses counting sort (or another stable sort) as a sub-routine at each digit position. Radix sort is better when values are large but have few digits; counting sort is better when the range is genuinely small.