Counting Sort
Zuletzt aktualisiert
Counting Sort ist ein vergleichsfreies Sortierverfahren für ganze Zahlen in einem bekannten, begrenzten Bereich. Es zählt, wie oft jeder Wert vorkommt, und nutzt diese Zählungen dann, um jeden Wert direkt an seine sortierte Position zu schreiben - ohne Vergleiche. Drücke oben auf Abspielen, um zu sehen, wie die Werte gezählt und anschließend der Reihe nach platziert werden.
Counting Sort läuft in O(n + k)-Zeit, wobei k der Bereich der Eingabewerte ist. Wenn k nicht viel größer als n ist, ist es extrem schnell und kann O(n log n)-Vergleichssortierungen übertreffen; ist der Wertebereich jedoch riesig, macht das O(k)-Zählarray es unpraktikabel.
Zeit- und Speicherkomplexität
| Fall | Komplexität | Anmerkungen |
|---|---|---|
| Zeit | O(n + k) | n Elemente, k = Wertebereich |
| Speicher | O(n + k) | Zählarray + Ausgabearray |
| Stabil | Ja | Wenn von rechts nach links mit Präfixsummen platziert wird |
| Vergleich? | Nein | Sortiert durch Zählen, nicht durch Vergleichen |
| Am besten für | Kleiner Ganzzahlbereich | k nahe an n |
Schritt für Schritt
| Schritt | Was passiert |
|---|---|
| 1 | Den Maximalwert finden, um das Zählarray zu dimensionieren. |
| 2 | Zählen, wie oft jeder Wert vorkommt. |
| 3 | (Optional) Die Zählungen für Stabilität in Präfixsummen umwandeln. |
| 4 | Jeden Wert so oft in die Ausgabe schreiben, wie er vorkommt. |
| 5 | Das Ausgabearray ist nun vollständig sortiert. |
Durchgerechnetes Beispiel
Sortieren von [1, 4, 1, 2, 4] (die Werte reichen von 0 bis 4, daher hat das Zählarray 5 Plätze):
| Durchgang | Zustand | Aktion |
|---|---|---|
| Eingabe durchlaufen | count = [0, 2, 1, 0, 2] | Vorkommen zählen: 1 kommt zweimal vor, 2 einmal, 4 zweimal. |
| Präfixsummen | count = [0, 2, 3, 3, 5] | Jeder Platz enthält nun, wie viele Werte <= seinem Index sind, was die endgültigen Positionen ergibt. |
4 platzieren | output = [_, _, _, _, 4] | Von rechts nach links lesen: count[4] = 5, also geht 4 an Index 4; auf 4 dekrementieren. |
2 platzieren | output = [_, _, 2, _, 4] | count[2] = 3, also geht 2 an Index 2; auf 2 dekrementieren. |
1 platzieren | output = [_, 1, 2, _, 4] | count[1] = 2, also geht 1 an Index 1; auf 1 dekrementieren. |
4 platzieren | output = [_, 1, 2, 4, 4] | count[4] = 4, also geht diese 4 an Index 3; auf 3 dekrementieren. |
1 platzieren | output = [1, 1, 2, 4, 4] | count[1] = 1, also geht diese 1 an Index 0. Das Array ist sortiert. |
Wann Counting Sort verwenden
| Verwenden, wenn | Vermeiden, wenn |
|---|---|
| Du ganze Zahlen (oder auf Ganzzahlen abbildbare Schlüssel) in einem kleinen, bekannten Bereich sortierst. | Der Wertebereich k weit größer ist als die Elementanzahl n. |
Du lineare O(n + k)-Zeit brauchst und dir die zusätzlichen Arrays leisten kannst. | Der Speicher knapp ist - das Zählarray kostet unabhängig von n stets O(k). |
| Du eine stabile Sortierung als Unterroutine brauchst (z. B. innerhalb von Radix Sort). | Die Schlüssel Fließkommazahlen, Zeichenketten oder beliebige Objekte ohne Ganzzahl-Abbildung sind. |
| Der Maximalwert beschränkt und im Voraus günstig zu berechnen ist. | Der Bereich unbekannt oder unbeschränkt ist, sodass du das Zählarray nicht dimensionieren kannst. |
Counting Sort-Code
Eine saubere, lauffähige Counting Sort-Implementierung in Python, JavaScript, Java, C++, C. Wähle eine Sprache, kopiere den Code oder öffne ihn vorgeladen im Coddy-Playground.
Counting Sort-Code 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))Counting Sort-Code 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));Counting Sort-Code 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}Counting Sort-Code 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}Counting Sort-Code 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}Counting Sort FAQ
Was ist die Zeitkomplexität von Counting Sort?
O(n + k), wobei n die Anzahl der Elemente und k der Bereich der möglichen Werte ist. Wenn k = O(n) gilt, ist dies lineare Zeit. Es benötigt O(n + k) zusätzlichen Speicher.Ist Counting Sort stabil?
Wann sollte ich Counting Sort verwenden?
k viel größer als die Anzahl der Elemente ist, verschwendet das Zählarray Speicher und eine Vergleichssortierung ist besser.Was ist der Unterschied zwischen Counting Sort und Radix Sort?
10 für Dezimalziffern). Radix Sort bewältigt große Wertebereiche, die ein einzelnes Counting Sort unpraktikabel machen würden.Warum ist Counting Sort nicht immer schneller als Quicksort?
O(n + k), gewinnt also nur, wenn der Wertebereich k mit n vergleichbar ist. Ist k riesig - etwa das Sortieren von 100 Werten im Bereich 0 bis 1,000,000,000 - dominiert das O(k)-Zählarray und verschwendet Speicher, während eine O(n log n)-Vergleichssortierung wie Quicksort schnell und speichereffizient bleibt.Kann Counting Sort mit negativen Zahlen umgehen?
value - min, sodass der kleinste Wert auf Index 0 abgebildet wird. Die Größe des Zählarrays wird zu max - min + 1. Diesen Versatz zu vergessen ist ein häufiger Fehler, der bei negativen Eingaben abstürzt.