Counting sort
Ultimo aggiornamento
Il counting sort è un ordinamento non basato sui confronti per numeri interi in un intervallo noto e limitato. Conta quante volte compare ogni valore, poi usa questi conteggi per scrivere ogni valore direttamente nella sua posizione ordinata, senza bisogno di confronti. Premi play qui sopra per vedere i valori contati e poi rimessi in ordine.
Il counting sort richiede tempo O(n + k), dove k è l'intervallo dei valori in ingresso. Quando k non è molto più grande di n è estremamente veloce e può battere gli ordinamenti per confronto O(n log n), ma se l'intervallo dei valori è enorme l'array dei conteggi O(k) lo rende poco pratico.
Complessità temporale e spaziale
| Caso | Complessità | Note |
|---|---|---|
| Tempo | O(n + k) | n elementi, k = intervallo dei valori |
| Spazio | O(n + k) | Array dei conteggi + array di output |
| Stabile | Sì | Se si colloca da destra a sinistra usando le somme prefisse |
| Per confronto? | No | Ordina contando, non confrontando |
| Ideale per | Piccolo intervallo di interi | k vicino a n |
Passo dopo passo
| Passo | Cosa succede |
|---|---|
| 1 | Trova il valore massimo per dimensionare l'array dei conteggi. |
| 2 | Conta quante volte compare ogni valore. |
| 3 | (Facoltativo) Trasforma i conteggi in somme prefisse per la stabilità. |
| 4 | Scrivi ogni valore nell'output tante volte quante compare. |
| 5 | Ora l'array di output è completamente ordinato. |
Esempio svolto
Ordinamento di [1, 4, 1, 2, 4] (i valori vanno da 0 a 4, quindi l'array dei conteggi ha 5 posizioni):
| Passata | Stato | Azione |
|---|---|---|
| Scansione input | count = [0, 2, 1, 0, 2] | Conta le occorrenze: 1 compare due volte, 2 una volta, 4 due volte. |
| Somme prefisse | count = [0, 2, 3, 3, 5] | Ogni posizione ora contiene quanti valori sono <= al suo indice, e questo dà le posizioni finali. |
Colloca 4 | output = [_, _, _, _, 4] | Leggi da destra a sinistra: count[4] = 5, quindi 4 va all'indice 4; decrementa a 4. |
Colloca 2 | output = [_, _, 2, _, 4] | count[2] = 3, quindi 2 va all'indice 2; decrementa a 2. |
Colloca 1 | output = [_, 1, 2, _, 4] | count[1] = 2, quindi 1 va all'indice 1; decrementa a 1. |
Colloca 4 | output = [_, 1, 2, 4, 4] | count[4] = 4, quindi questo 4 va all'indice 3; decrementa a 3. |
Colloca 1 | output = [1, 1, 2, 4, 4] | count[1] = 1, quindi questo 1 va all'indice 0. L'array è ordinato. |
Quando usare il counting sort
| Usalo quando | Evitalo quando |
|---|---|
| Ordini interi (o chiavi riconducibili a interi) in un intervallo piccolo e noto. | L'intervallo dei valori k è molto più grande del numero di elementi n. |
Ti serve un tempo lineare O(n + k) e puoi permetterti gli array extra. | La memoria è poca: l'array dei conteggi costa O(k) indipendentemente da n. |
| Ti serve un ordinamento stabile come sottoprocedura (ad es. dentro il radix sort). | Le chiavi sono float, stringhe o oggetti arbitrari senza corrispondenza con gli interi. |
| Il valore massimo è limitato e facile da calcolare in anticipo. | L'intervallo è sconosciuto o illimitato, quindi non puoi dimensionare l'array dei conteggi. |
Codice Counting Sort
Un'implementazione di Counting Sort pulita ed eseguibile in Python, JavaScript, Java, C++, C. Scegli un linguaggio, copia il codice o aprilo già caricato nel Playground di Coddy.
Codice Counting Sort in 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))Codice Counting Sort in 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));Codice Counting Sort in 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}Codice Counting Sort in 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}Codice Counting Sort in 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}Domande frequenti sul counting sort
Qual è la complessità temporale del counting sort?
O(n + k), dove n è il numero di elementi e k l'intervallo dei valori possibili. Quando k = O(n) il tempo è lineare. Usa O(n + k) di spazio extra.Il counting sort è stabile?
Può esserlo. La versione stabile costruisce le somme prefisse dei conteggi e colloca gli elementi da destra a sinistra, preservando l'ordine relativo delle chiavi uguali. La versione semplice "riscrivi per valore" mostrata qui produce un ordinamento corretto ma si usa soprattutto per interi semplici.
Quando conviene usare il counting sort?
k è molto più grande del numero di elementi, l'array dei conteggi spreca memoria ed è meglio un ordinamento per confronto.Qual è la differenza tra counting sort e radix sort?
10 per le cifre decimali). Il radix sort gestisce grandi intervalli di valori che renderebbero poco pratico un singolo counting sort.Perché il counting sort non è sempre più veloce del quicksort?
O(n + k), quindi vince solo quando l'intervallo dei valori k è paragonabile a n. Se k è enorme, per esempio ordinare 100 valori nell'intervallo da 0 a 1,000,000,000, l'array dei conteggi O(k) domina e spreca memoria, mentre un ordinamento per confronto O(n log n) come il quicksort resta veloce ed efficiente in spazio.Il counting sort gestisce i numeri negativi?
value - min, così il valore più piccolo corrisponde all'indice 0. La dimensione dell'array dei conteggi diventa max - min + 1. Dimenticare questo scostamento è un bug comune che manda in crash il programma con input negativi.