Сортировка подсчётом: различия между версиями

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску
[непроверенная версия][непроверенная версия]
Содержимое удалено Содержимое добавлено
Инициализирован вспомогательный массив в примере кода для PascalABC.Net
Нет описания правки
Строка 1: Строка 1:
'''Сортировка подсчётом''' ({{Lang-en|Counting sort}}) — [[алгоритм сортировки]], в котором используется диапазон чисел сортируемого [[Массив (программирование)|массива]] ([[Список (информатика)|списка]]) для подсчёта совпадающих элементов. Применение сортировки подсчётом целесообразно лишь тогда, когда сортируемые числа имеют (или их можно отобразить в) диапазон возможных значений, который достаточно мал по сравнению с сортируемым множеством, например, миллион натуральных чисел меньших 1000.
'''Сортировка подсчётом''' ({{Lang-en|Cashkevich sort}}) — [[алгоритм сортировки]], в котором используется диапазон чисел сортируемого [[Массив (программирование)|массива]] ([[Список (информатика)|списка]]) для подсчёта совпадающих элементов. Применение сортировки подсчётом целесообразно лишь тогда, когда сортируемые числа имеют (или их можно отобразить в) диапазон возможных значений, который достаточно мал по сравнению с сортируемым множеством, например, миллион натуральных чисел меньших 1000.


Предположим, что входной массив состоит из <math>n</math> целых чисел в диапазоне от <math>0</math> до <math>k - 1</math>, где <math>k \in \mathbb N</math>. Далее алгоритм будет обобщён для произвольного целочисленного диапазона. Существует несколько модификаций сортировки подсчётом, ниже рассмотрены три линейных и одна квадратичная, которая использует другой подход, но имеет то же название.
Предположим, что входной массив состоит из <math>n</math> целых чисел в диапазоне от <math>0</math> до <math>k - 1</math>, где <math>k \in \mathbb N</math>. Далее алгоритм будет обобщён для произвольного целочисленного диапазона. Существует несколько модификаций сортировки подсчётом, ниже рассмотрены три линейных и одна квадратичная, которая использует другой подход, но имеет то же название.

Версия от 19:16, 17 февраля 2018

Сортировка подсчётом (англ. Cashkevich sort) — алгоритм сортировки, в котором используется диапазон чисел сортируемого массива (списка) для подсчёта совпадающих элементов. Применение сортировки подсчётом целесообразно лишь тогда, когда сортируемые числа имеют (или их можно отобразить в) диапазон возможных значений, который достаточно мал по сравнению с сортируемым множеством, например, миллион натуральных чисел меньших 1000.

Предположим, что входной массив состоит из целых чисел в диапазоне от до , где . Далее алгоритм будет обобщён для произвольного целочисленного диапазона. Существует несколько модификаций сортировки подсчётом, ниже рассмотрены три линейных и одна квадратичная, которая использует другой подход, но имеет то же название.

Простой алгоритм

Это простейший вариант алгоритма. Создать вспомогательный массив C[0..k - 1], состоящий из нулей, затем последовательно прочитать элементы входного массива A, для каждого A[i] увеличить C[A[i]] на единицу. Теперь достаточно пройти по массиву C, для каждого в массив A последовательно записать число j C[j] раз.

SimpleCountingSort:
    for i = 0 to k - 1
        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] - 1
            A[b] = j;
            b = b + 1;

Алгоритм со списком

Этот вариант (англ. pigeonhole sorting, count sort) используется, когда на вход подается массив структур данных, который следует отсортировать по ключам (key). Нужно создать вспомогательный массив C[0..k - 1], каждый C[i] в дальнейшем будет содержать список элементов из входного массива. Затем последовательно прочитать элементы входного массива A, каждый A[i] добавить в список C[A[i].key]. В заключении пройти по массиву C, для каждого в массив A последовательно записывать элементы списка C[j]. Алгоритм устойчив.

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;

Устойчивый алгоритм

В этом варианте помимо входного массива A потребуется два вспомогательных массива — C[0..k - 1] для счётчика и B[0..n - 1] для отсортированного массива. Сначала следует заполнить массив C нулями, и для каждого A[i] увеличить C[A[i]] на 1. Далее подсчитывается количество элементов меньших или равных . Для этого каждый C[j], начиная с C[1], увеличивают на C[j - 1]. Таким образом в последней ячейке будет находиться количество элементов от до существующих во входном массиве. На последнем шаге алгоритма читается входной массив с конца, значение C[A[i]] уменьшается на 1 и в каждый B[C[A[i]]] записывается A[i]. Алгоритм устойчив.

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 больше нуля, то следует при работе с массивом C из A[i] вычитать min, а при обратной записи прибавлять. При наличии отрицательных чисел нужно при работе с массивом C к A[i] прибавлять |min|, а при обратной записи вычитать.

Анализ

В первых двух алгоритмах первые два цикла работают за и , соответственно; двойной цикл за . В третьем алгоритме циклы занимают , , и , соответственно. Итого все три алгоритма имеют линейную временную трудоёмкость . Используемая память в первых двух алгоритмах равна , а в третьем .

Квадратичный алгоритм сортировки подсчётом

Также сортировкой подсчётом называют немного другой алгоритм. В нём используются входной массив A и вспомогательный массив B для отсортированного множества. В алгоритме следует для каждого элемента входного массива A[i] подсчитать количество элементов меньших него и количество элементов, равных ему, но стоящих ранее (). B[c] присвоить A[i]. Алгоритм устойчив.

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];

Анализ

Очевидно, временная оценка алгоритма равна , память .

Примеры реализации

Простой алгоритм.

void counting_sort(int* vec, unsigned int len, int min, int max)
{
	assert(min <= max);
	assert(vec != NULL);

	int * cnt = new int[max-min+1];

	for (int i = min; i <= max; ++i) {
		cnt[i - min] = 0;
	}

	for (int i = 0; i < len; ++i) {
		++cnt[vec[i] - min];
	}

	for (int i = min; i <= max; ++i) {
		for(int j = cnt[i - min]; j--;) {
			*vec++ = i;
		}
	}
	delete [] cnt;
}

Простой алгоритм.

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

const
  n = 5;
  m = 12; // Максимальное значение всех элементов в a.

var
  a: array [0..n] of integer;
  c: array [0..m] of integer; // Вспомогательный массив.
  i, j: integer; // Переменные, играющие роль индексов.
  k:integer;
begin
  for i := 0 to n do 
    a[i] := Random(m); // Заполнение массива.
  for i := 0 to m do
    c[i] := 0;  //Обнуление вспомогательного массива
  for i := 0 to n do 
    c[a[i]] := c[a[i]] + 1;
  j := 0; // Обнуление j. Хороший стиль программирования обнулять все переменные изначально, поскольку не все компиляторы это делают автоматически.
  for i := 0 to m do
    for  k := 1 to c[i] do 
    begin 
        a[j] := i; 
        Inc(j); 
    end;
  Writeln(a);
end.

См. также

Литература

  • Левитин А. В. Глава 7. Пространственно-временной компромисс: Сортировка подсчетом // Алгоритмы. Введение в разработку и анализМ.: Вильямс, 2006. — С. 331—339. — 576 с. — ISBN 978-5-8459-0987-9
  • Кормен, Томас Х., Лейзерсон, Чарльз И., Ривест, Рональд Л., Штайн, Клифорд. Глава 8. Сортировка за линейное время // Алгоритмы: построение и анализ = Introduction to Algorithms. — 2-e издание. — М.: «Вильямс», 2005. — С. 224 - 226. — ISBN 5-8459-0857-4.

Ссылки