Counting Sort
Última atualização
Counting sort é uma ordenação sem comparações para inteiros em um intervalo conhecido e limitado. Ele conta quantas vezes cada valor aparece e depois usa essas contagens para escrever cada valor diretamente em sua posição ordenada, sem comparações. Pressione reproduzir acima para ver os valores sendo contados e depois posicionados em ordem.
Counting sort roda em tempo O(n + k), onde k é o intervalo dos valores de entrada. Quando k não é muito maior que n, ele é extremamente rápido e pode superar ordenações por comparação de O(n log n), mas se o intervalo de valores for enorme, o vetor de contagem de O(k) o torna impraticável.
Complexidade de tempo e espaço
| Caso | Complexidade | Notas |
|---|---|---|
| Tempo | O(n + k) | n elementos, k = intervalo de valores |
| Espaço | O(n + k) | Vetor de contagem + vetor de saída |
| Estável | Sim | Ao posicionar da direita para a esquerda usando somas de prefixo |
| Comparação? | Não | Ordena por contagem, não por comparação |
| Melhor para | Intervalo inteiro pequeno | k próximo de n |
Passo a passo
| Passo | O que acontece |
|---|---|
| 1 | Encontrar o valor máximo para dimensionar o vetor de contagem. |
| 2 | Contar quantas vezes cada valor ocorre. |
| 3 | (Opcional) Converter as contagens em somas de prefixo para obter estabilidade. |
| 4 | Escrever cada valor na saída o número de vezes que ele aparece. |
| 5 | O vetor de saída já está completamente ordenado. |
Exemplo resolvido
Ordenando [1, 4, 1, 2, 4] (os valores vão de 0 a 4, então o vetor de contagem tem 5 posições):
| Passagem | Estado | Ação |
|---|---|---|
| Percorrer entrada | count = [0, 2, 1, 0, 2] | Contar ocorrências: o 1 aparece duas vezes, o 2 uma vez, o 4 duas vezes. |
| Somas de prefixo | count = [0, 2, 3, 3, 5] | Cada posição agora guarda quantos valores são <= ao seu índice, dando as posições finais. |
Posicionar 4 | output = [_, _, _, _, 4] | Ler da direita para a esquerda: count[4] = 5, então o 4 vai para o índice 4; decrementar para 4. |
Posicionar 2 | output = [_, _, 2, _, 4] | count[2] = 3, então o 2 vai para o índice 2; decrementar para 2. |
Posicionar 1 | output = [_, 1, 2, _, 4] | count[1] = 2, então o 1 vai para o índice 1; decrementar para 1. |
Posicionar 4 | output = [_, 1, 2, 4, 4] | count[4] = 4, então este 4 vai para o índice 3; decrementar para 3. |
Posicionar 1 | output = [1, 1, 2, 4, 4] | count[1] = 1, então este 1 vai para o índice 0. O vetor está ordenado. |
Quando usar o counting sort
| Use quando | Evite quando |
|---|---|
| Você ordena inteiros (ou chaves mapeáveis para inteiros) em um intervalo pequeno e conhecido. | O intervalo de valores k é muito maior que a quantidade de elementos n. |
Você precisa de tempo linear O(n + k) e pode arcar com os vetores extras. | A memória é escassa: o vetor de contagem custa O(k) independentemente de n. |
| Você precisa de uma ordenação estável como sub-rotina (por exemplo, dentro do radix sort). | As chaves são floats, strings ou objetos arbitrários sem mapeamento para inteiros. |
| O valor máximo é limitado e barato de calcular antecipadamente. | O intervalo é desconhecido ou ilimitado, então você não consegue dimensionar o vetor de contagem. |
Código de Counting Sort
Uma implementação limpa e executável de Counting Sort em Python, JavaScript, Java, C++, C. Escolha uma linguagem, copie o código ou abra-o já carregado no Playground da Coddy.
Código de Counting Sort em Python
1def counting_sort(a):2 # Works for non-negative integers with a small max value3 counts = [0] * (max(a) + 1)4 for value in a:5 counts[value] += 16 # Prefix sums turn counts into final positions7 for i in range(1, len(counts)):8 counts[i] += counts[i - 1]9 out = [0] * len(a)10 for value in reversed(a): # reversed keeps equal values stable11 counts[value] -= 112 out[counts[value]] = value13 return out14
15
16nums = [4, 2, 2, 8, 3, 3, 1]17print("Before:", nums)18print("After: ", counting_sort(nums))Código de Counting Sort em JavaScript
1function countingSort(arr) {2 // Count occurrences of each value, then rebuild in order3 const max = Math.max(...arr);4 const count = new Array(max + 1).fill(0);5 for (const x of arr) count[x]++;6 const out = [];7 count.forEach((c, value) => {8 for (let k = 0; k < c; k++) out.push(value);9 });10 return out;11}12
13const data = [4, 2, 9, 2, 7, 4, 1, 4];14console.log("Before:", data);15console.log("Sorted:", countingSort(data));Código de Counting Sort em Java
1import java.util.Arrays;2
3public class Main {4 static int[] countingSort(int[] arr) {5 int max = 0;6 for (int v : arr) max = Math.max(max, v);7 int[] count = new int[max + 1];8 for (int v : arr) count[v]++;9 // Prefix sums turn counts into final positions10 for (int i = 1; i <= max; i++) count[i] += count[i - 1];11 int[] out = new int[arr.length];12 // Walk backwards so equal values keep their order (stable)13 for (int i = arr.length - 1; i >= 0; i--) {14 out[--count[arr[i]]] = arr[i];15 }16 return out;17 }18
19 public static void main(String[] args) {20 int[] arr = {4, 2, 2, 8, 3, 3, 1};21 System.out.println("Before: " + Arrays.toString(arr));22 int[] sorted = countingSort(arr);23 System.out.println("After: " + Arrays.toString(sorted));24 }25}Código de Counting Sort em C++
1#include <algorithm>2#include <iostream>3#include <vector>4
5void printVec(const std::vector<int>& a) {6 for (int x : a) std::cout << x << " ";7 std::cout << "\n";8}9
10void countingSort(std::vector<int>& a) {11 if (a.empty()) return;12 int maxVal = *std::max_element(a.begin(), a.end());13 // count[v] = how many times v appears14 std::vector<int> count(maxVal + 1, 0);15 for (int x : a) ++count[x];16 // Rebuild the array from the counts17 size_t idx = 0;18 for (int v = 0; v <= maxVal; ++v) {19 while (count[v]-- > 0) a[idx++] = v;20 }21}22
23int main() {24 std::vector<int> data = {4, 2, 2, 8, 3, 3, 1, 7};25 std::cout << "Before: ";26 printVec(data);27 countingSort(data);28 std::cout << "After: ";29 printVec(data);30 return 0;31}Código de Counting Sort em C
1#include <stdio.h>2#include <stdlib.h>3
4void printArr(const int a[], int n) {5 for (int i = 0; i < n; i++) printf("%d ", a[i]);6 printf("\n");7}8
9void countingSort(int a[], int n) {10 int maxVal = a[0];11 for (int i = 1; i < n; i++) {12 if (a[i] > maxVal) maxVal = a[i];13 }14 // count[v] = how many times v appears15 int* count = calloc(maxVal + 1, sizeof(int));16 for (int i = 0; i < n; i++) count[a[i]]++;17 // Rebuild the array from the counts18 int idx = 0;19 for (int v = 0; v <= maxVal; v++) {20 while (count[v]-- > 0) a[idx++] = v;21 }22 free(count);23}24
25int main(void) {26 int data[] = {4, 2, 2, 8, 3, 3, 1, 7};27 int n = sizeof(data) / sizeof(data[0]);28 printf("Before: ");29 printArr(data, n);30 countingSort(data, n);31 printf("After: ");32 printArr(data, n);33 return 0;34}Perguntas frequentes sobre Counting Sort
Qual é a complexidade de tempo do counting sort?
O(n + k), onde n é o número de elementos e k é o intervalo de valores possíveis. Quando k = O(n) isso é tempo linear. Ele usa O(n + k) de espaço adicional.O counting sort é estável?
Quando devo usar o counting sort?
k for muito maior que o número de elementos, o vetor de contagem desperdiça memória e uma ordenação por comparação é melhor.Qual é a diferença entre counting sort e radix sort?
10 para dígitos decimais). Radix sort lida com intervalos de valores grandes que tornariam um único counting sort impraticável.Por que o counting sort nem sempre é mais rápido que o quicksort?
O(n + k), então ele só vence quando o intervalo de valores k é comparável a n. Se k for enorme -por exemplo, ordenar 100 valores no intervalo de 0 a 1,000,000,000- o vetor de contagem de O(k) domina e desperdiça memória, enquanto uma ordenação por comparação de O(n log n) como o quicksort permanece rápida e eficiente em espaço.O counting sort consegue lidar com números negativos?
value - min, de modo que o menor valor seja mapeado para o índice 0. O tamanho do vetor de contagem passa a ser max - min + 1. Esquecer esse deslocamento é um erro comum que falha com entradas negativas.