Сортировка подсчётомСортировка подсчётом[1] (англ. counting sort[2]; сортировка посредством подсчёта[3] англ. sorting by counting[4]) — алгоритм сортировки, в котором используется диапазон чисел сортируемого массива (списка) для подсчёта совпадающих элементов. Применение сортировки подсчётом целесообразно лишь тогда, когда сортируемые числа имеют (или их можно отобразить в) диапазон возможных значений, который достаточно мал по сравнению с сортируемым множеством, например, миллион натуральных чисел меньших 1000. Предположим, что входной массив состоит из целых чисел в диапазоне от до , где . Далее алгоритм будет обобщён для произвольного целочисленного диапазона. Существует несколько модификаций сортировки подсчётом, ниже рассмотрены три линейных и одна квадратичная, которая использует другой подход, но имеет то же название. Простой алгоритмЭто простейший вариант алгоритма. Создать вспомогательный массив SimpleCountingSort: for i = 0 to k C[i] = 0; for i = 0 to n - 1 C[A[i]] = C[A[i]] + 1; b = 0; for j = 0 to k + 1 for i = 0 to C[j] A[b] = j; b = b + 1; Реализация на C: //array — указатель на начало массива
//n — размер массива
//k — максимальное число в массиве
void countingSort(int* array, int n, int k) {
int* c = (int*)malloc((k+1) * sizeof(*array));
memset(c, 0, sizeof(*array)*(k+1));
for (int i = 0; i < n; ++i) {
++c[array[i]];
}
int b = 0;
for (int i = 0; i < k+1; ++i){
for (int j = 0; j < c[i]; ++j) {
array[b++] = i;
}
}
free(c);
}
Реализация на C++: #include <vector>
#include <type_traits>
#include <algorithm>
template <typename std::enable_if_t<std::is_integral_v<T>, T>>
void countingSort(std::vector<T>& array) {
T max = std::max_element(array.begin(), array.end());
auto count = std::vector<T>(max+1, T(0));
for (T elem : array)
++count[elem];
T b = 0;
for (size_t i = 0; i < max+1; ++i) {
for (T j = 0; j < count[i]; ++j) {
array[b++] = i;
}
}
}
Реализация на Java: private static void sortArrayByCountingMethod(int[] array) {
//Найдём максимальное число в массиве
int k = array[0]; //Максимальное число в массиве
for (int i = 1; i < array.length; i++) {
if (array[i] > k) {
k = array[i];
}
}
//Создадим новый массив длины k, по умолчанию наполненный нулями
int[] tempArray = new int[k + 1];
//Запишем в него количество вхождений каждого элемента поиндексно
for (int value : array) {
++tempArray[value];
}
//Вставим элементы в исходный массив
int b = 0;
for (int i = 0; i < k + 1; ++i){
for (int j = 0; j < tempArray[i]; ++j) {
array[b++] = i;
}
}
}
Алгоритм со спискомЭтот вариант (англ. pigeonhole sorting, count sort) используется, когда на вход подается массив структур данных, который следует отсортировать по ключам ( ListCountingSort for i = 0 to k - 1 C[i] = NULL; for i = 0 to n - 1 C[A[i].key].add(A[i]); b = 0; for j = 0 to k - 1 p = C[j]; while p != NULL A[b] = p.data; p = p.next(); b = b + 1; Устойчивый алгоритмВ этом варианте помимо входного массива StableCountingSort for i = 0 to k - 1 C[i] = 0; for i = 0 to n - 1 C[A[i]] = C[A[i]] + 1; for j = 1 to k - 1 C[j] = C[j] + C[j - 1]; for i = n - 1 to 0 C[A[i]] = C[A[i]] - 1; B[C[A[i]]] = A[i]; Обобщение на произвольный целочисленный диапазонВозникает несколько вопросов. Что делать, если диапазон значений (min и max) заранее не известен? Что делать, если минимальное значение больше нуля или в сортируемых данных присутствуют отрицательные числа? Первый вопрос можно решить линейным поиском min и max, что не повлияет на асимптотику алгоритма. Второй вопрос несколько сложнее. Если min больше нуля, то следует при работе с массивом АнализВ первых двух алгоритмах первые два цикла работают за и , соответственно; двойной цикл за . В третьем алгоритме циклы занимают , , и , соответственно. Итого все три алгоритма имеют линейную временную трудоёмкость . Используемая память в первых двух алгоритмах равна , а в третьем . Квадратичный алгоритм сортировки подсчётомТакже сортировкой подсчётом называют немного другой алгоритм. В нём используются входной массив SquareCountingSort for i = 0 to n - 1 c = 0; for j = 0 to i - 1 if A[j] <= A[i] c = c + 1; for j = i + 1 to n - 1 if A[j] < A[i] c = c + 1; B[c] = A[i]; АнализОчевидно, временная оценка алгоритма равна , память . Примеры реализацииПростой алгоритм. PROCEDURE CountingSort (VAR a: ARRAY OF INTEGER; min, max: INTEGER);
VAR
i, j, c: INTEGER;
b: POINTER TO ARRAY OF INTEGER;
BEGIN
ASSERT(min <= max);
NEW(b, max - min + 1);
FOR i := 0 TO LEN(a) - 1 DO INC(b[a[i] - min]) END;
i := 0;
FOR j := min TO max DO
c := b[j - min];
WHILE c > 0 DO
a[i] := j; INC(i); DEC(c)
END
END
END CountingSort;
Реализация на PascalABC.Netconst
n = 20;
begin
var a := ArrRandomInteger(n,0,100);
a.Println;
var max := a.Max;
var c := |0|*(max+1);
for var i := 0 to a.Length-1 do
c[a[i]] += 1;
var j := 0;
for var i := 0 to max do
for var k := 1 to c[i] do
begin
a[j] := i;
j += 1;
end;
a.Println;
end.
Реализация на Pythona = list(map(int, input().split())) # считывание списка
cnt = [0] * (max(a) + 1) # генерация списка нулей длиной в максимальный элемент списка a
for item in a:
cnt[item] += 1 # когда мы встречаем число в списке, увеличиваем его значение на 1
# добавляем count раз число num в результат
result = [num for num, count in enumerate(cnt) for i in range(count)]
print(result) # выводим отсортированный список
См. такжеПримечания
Литература
Ссылки
|