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?
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.