Counting Sort
Última actualización
Counting sort es un ordenamiento sin comparaciones para enteros en un rango conocido y limitado. Cuenta cuántas veces aparece cada valor y luego usa esos conteos para escribir cada valor directamente en su posición ordenada, sin necesidad de comparaciones. Pulsa reproducir arriba para ver cómo se cuentan los valores y luego se colocan en orden.
Counting sort se ejecuta en tiempo O(n + k), donde k es el rango de los valores de entrada. Cuando k no es mucho mayor que n, es extremadamente rápido y puede superar a los ordenamientos por comparación de O(n log n), pero si el rango de valores es enorme, el arreglo de conteo de O(k) lo hace poco práctico.
Complejidad temporal y espacial
| Caso | Complejidad | Notas |
|---|---|---|
| Tiempo | O(n + k) | n elementos, k = rango de valores |
| Espacio | O(n + k) | Arreglo de conteo + arreglo de salida |
| Estable | Sí | Al colocar de derecha a izquierda usando sumas de prefijos |
| ¿Comparación? | No | Ordena por conteo, no por comparación |
| Mejor para | Rango entero pequeño | k cercano a n |
Paso a paso
| Paso | Qué ocurre |
|---|---|
| 1 | Encontrar el valor máximo para dimensionar el arreglo de conteo. |
| 2 | Contar cuántas veces aparece cada valor. |
| 3 | (Opcional) Convertir los conteos en sumas de prefijos para lograr estabilidad. |
| 4 | Escribir cada valor en la salida tantas veces como aparece. |
| 5 | El arreglo de salida ya está completamente ordenado. |
Ejemplo resuelto
Ordenando [1, 4, 1, 2, 4] (los valores van de 0 a 4, por lo que el arreglo de conteo tiene 5 posiciones):
| Pasada | Estado | Acción |
|---|---|---|
| Recorrer entrada | count = [0, 2, 1, 0, 2] | Contar ocurrencias: el 1 aparece dos veces, el 2 una vez, el 4 dos veces. |
| Sumas de prefijos | count = [0, 2, 3, 3, 5] | Cada posición contiene ahora cuántos valores son <= a su índice, dando las posiciones finales. |
Colocar 4 | output = [_, _, _, _, 4] | Leer de derecha a izquierda: count[4] = 5, así que el 4 va al índice 4; decrementar a 4. |
Colocar 2 | output = [_, _, 2, _, 4] | count[2] = 3, así que el 2 va al índice 2; decrementar a 2. |
Colocar 1 | output = [_, 1, 2, _, 4] | count[1] = 2, así que el 1 va al índice 1; decrementar a 1. |
Colocar 4 | output = [_, 1, 2, 4, 4] | count[4] = 4, así que este 4 va al índice 3; decrementar a 3. |
Colocar 1 | output = [1, 1, 2, 4, 4] | count[1] = 1, así que este 1 va al índice 0. El arreglo está ordenado. |
Cuándo usar counting sort
| Úsalo cuando | Evítalo cuando |
|---|---|
| Ordenas enteros (o claves convertibles a enteros) en un rango pequeño y conocido. | El rango de valores k es mucho mayor que el número de elementos n. |
Necesitas tiempo lineal O(n + k) y puedes permitirte los arreglos adicionales. | La memoria es escasa: el arreglo de conteo cuesta O(k) sin importar n. |
| Necesitas un ordenamiento estable como subrutina (por ejemplo, dentro de radix sort). | Las claves son flotantes, cadenas u objetos arbitrarios sin mapeo a enteros. |
| El valor máximo está acotado y es barato de calcular de antemano. | El rango es desconocido o no está acotado, por lo que no puedes dimensionar el arreglo de conteo. |
Código de Counting Sort
Una implementación limpia y ejecutable de Counting Sort en Python, JavaScript, Java, C++, C. Elige un lenguaje, copia el código o ábrelo ya cargado en el Playground de Coddy.
Código de Counting Sort en 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 en 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 en 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 en 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 en 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}Preguntas frecuentes sobre Counting Sort
¿Cuál es la complejidad temporal de counting sort?
O(n + k), donde n es el número de elementos y k es el rango de valores posibles. Cuando k = O(n) esto es tiempo lineal. Usa O(n + k) de espacio adicional.¿Es counting sort estable?
¿Cuándo debería usar counting sort?
k es mucho mayor que el número de elementos, el arreglo de conteo desperdicia memoria y es mejor un ordenamiento por comparación.¿Cuál es la diferencia entre counting sort y radix sort?
10 para dígitos decimales). Radix sort maneja rangos de valores grandes que harían impracticable un único counting sort.¿Por qué counting sort no siempre es más rápido que quicksort?
O(n + k), por lo que solo gana cuando el rango de valores k es comparable a n. Si k es enorme -por ejemplo, ordenar 100 valores en el rango de 0 a 1,000,000,000- el arreglo de conteo de O(k) domina y desperdicia memoria, mientras que un ordenamiento por comparación de O(n log n) como quicksort se mantiene rápido y eficiente en espacio.¿Puede counting sort manejar números negativos?
value - min, de modo que el valor más pequeño se asigne al índice 0. El tamaño del arreglo de conteo pasa a ser max - min + 1. Olvidar este desplazamiento es un error común que falla con entradas negativas.