Najpierw powtarzać jak sortowanie przez zliczanie działa:
- hrabia jak często istnieje każdy klucz w tablicy mają być sortowane. Liczby te są zapisywane w tablicy o rozmiarze
k
.
- Obliczyć sumy częściowe zliczeń kluczy. Daje to pozycję początkową dla każdego bin równego klucza w posortowanej tablicy.
- Przenieś elementy w tablicy do ich ostatecznego położenia, zwiększając pozycję początkową odpowiedniego pojemnika dla każdej pozycji.
Teraz pytanie brzmi, jak wykonać ostatni krok w miejscu. Standardowe podejście do permutacji lokalnej polega na wybraniu pierwszego elementu i zamianie go na element, który zajmuje właściwą pozycję. Ten krok powtarza się z zamienionym elementem, dopóki nie trafimy na element należący do pierwszej pozycji (cykl został zakończony). Następnie cała procedura jest powtarzana dla elementów na pozycji drugiej, trzeciej itd., Dopóki cała macierz nie zostanie przetworzona.
Problem z sortowaniem zliczającym polega na tym, że końcowe pozycje nie są łatwo dostępne, ale są obliczane poprzez zwiększenie pozycji wyjściowej każdego pojemnika w końcowej pętli. Aby nigdy nie zwiększać dwukrotnie pozycji wyjściowej dla elementu, musimy znaleźć sposób na ustalenie, czy element w danej pozycji został już tam przeniesiony. Można to zrobić, śledząc oryginalną pozycję początkową dla każdego pojemnika. Jeśli element znajduje się pomiędzy początkową pozycją początkową a pozycją następnego elementu pojemnika, został już dotknięty.
Oto implementacja w C99, która działa w O(n+k)
i wymaga tylko dwóch tablic o rozmiarze k
jako dodatkowej pamięci. Ostateczny etap permutacji nie jest stabilny.
#include <stdlib.h>
void in_place_counting_sort(int *a, int n, int k)
{
int *start = (int *)calloc(k + 1, sizeof(int));
int *end = (int *)malloc(k * sizeof(int));
// Count.
for (int i = 0; i < n; ++i) {
++start[a[i]];
}
// Compute partial sums.
for (int bin = 0, sum = 0; bin < k; ++bin) {
int tmp = start[bin];
start[bin] = sum;
end[bin] = sum;
sum += tmp;
}
start[k] = n;
// Move elements.
for (int i = 0, cur_bin = 0; i < n; ++i) {
while (i >= start[cur_bin+1]) { ++cur_bin; }
if (i < end[cur_bin]) {
// Element has already been processed.
continue;
}
int bin = a[i];
while (bin != cur_bin) {
int j = end[bin]++;
// Swap bin and a[j]
int tmp = a[j];
a[j] = bin;
bin = tmp;
}
a[i] = bin;
++end[cur_bin];
}
free(start);
free(end);
}
Edit: Oto kolejna wersja używając tylko jednego wachlarz rozmiarów k
opartej na podejściu mohit Bhura użytkownika.
#include <stdlib.h>
void in_place_counting_sort(int *a, int n, int k)
{
int *counts = (int *)calloc(k, sizeof(int));
// Count.
for (int i = 0; i < n; ++i) {
++counts[a[i]];
}
// Compute partial sums.
for (int val = 0, sum = 0; val < k; ++val) {
int tmp = counts[val];
counts[val] = sum;
sum += tmp;
}
// Move elements.
for (int i = n - 1; i >= 0; --i) {
int val = a[i];
int j = counts[val];
if (j < i) {
// Process a fresh cycle. Since the index 'i' moves
// downward and the counts move upward, it is
// guaranteed that a value is never moved twice.
do {
++counts[val];
// Swap val and a[j].
int tmp = val;
val = a[j];
a[j] = tmp;
j = counts[val];
} while (j < i);
// Move final value into place.
a[i] = val;
}
}
free(counts);
}
Patrz sekcja [algorytmy Variant] (http://en.wikipedia.org/wiki/Counting_sort#Variant_algorithms) w Wikipedii (ostatni akapit). – nwellnhof
'" Możesz użyć pamięci O (k) poza tablicą wejściową "' - po prostu brzmi jak zwykły typ zliczania, który prawdopodobnie wpada w jakąś wypaczoną definicję "na miejscu". Możesz także zrobić sortowanie zliczające na miejscu z pewną dodaną złożonością, używając rekursji i wartości ujemnych dla zliczeń (przyjmując k <= n), ale technicznie przestrzeń stosu byłaby najgorszym przypadkiem O (n), więc to tak naprawdę nie praca. Dość pewny, że sortowanie zliczania nie może być stabilne. – Dukeling
Potrzebujemy pamięci masowej O (n + k) w zwykłym sortowaniu liczącym. Link wiki podany powyżej wspomina tylko, że "można zmienić sortowanie zliczania, aby można było to zrobić w miejscu", ale nie ma informacji, jak to zrobić !! – Roronoa