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

Материал из Викиконспекты
Перейти к: навигация, поиск
(Устойчивый алгоритм)
Строка 1: Строка 1:
'''Сортировка подсчётом''' — алгоритм сортировки, в котором используется диапазон чисел сортируемого массива или списка для подсчёта совпадающих элементов. Применение сортировки подсчётом целесообразно лишь тогда, когда сортируемые числа имеют (или их можно отобразить в) диапазон возможных значений, который достаточно мал по сравнению с сортируемым множеством, например, миллион натуральных чисел меньших 1000. Эффективность алгоритма падает, если при попадании нескольких различных элементов в одну ячейку, их надо дополнительно сортировать.  
+
'''Сортировка подсчётом''' — алгоритм сортировки, в котором предполагается, что все <tex>n</tex> входных элементов {{---}} целые числа, принадлежащие интервалу от <tex>0</tex> до <tex>k</tex>, где <tex>k</tex> {{---}} некоторая целая константа. Например, миллион натуральных чисел меньших <tex>1000</tex>. Эффективность алгоритма падает, если при попадании нескольких различных элементов в одну ячейку, их надо дополнительно сортировать.
 +
 
 +
== Основная идея ==
 +
Основная идея сортировки подсчетом заключается в том, чтобы для каждого входного элемента <tex>x</tex> определить количество элементов, которые меньше <tex>x</tex>. C помощью этой информации элемент <tex>x</tex> можно разместить на той позиции выходного массива, где он должен находиться. Например, если всего имеется <tex>42</tex> элемента, которые меньше <tex>x</tex>, то в выходной последовательности элемент <tex>x</tex> должен заниматься <tex>43</tex>-ю позицию. Если допускается ситуация, когда несколько элементов имеют одно и тоже значение, то эту схему придётся модифицировать, так как мы не можем разместить все такие элементы в одной позиции.
  
 
== Простой алгоритм ==
 
== Простой алгоритм ==
 
+
Это простейший вариант алгоритма. Создать вспомогательный массив <tex>C[0..k - 1]</tex>, состоящий из нулей, затем последовательно прочитать элементы входного массива <tex>A</tex> и для каждого <tex>A[i]</tex> увеличить <tex>C[A[i]]</tex> на единицу. Теперь достаточно пройти по массиву <tex>C</tex> и для каждого <tex>number \in \{0, ..., k - 1\}</tex> в массив <tex>A</tex> последовательно записать число <tex>number\</tex> <tex> C[number]</tex> раз.
Это простейший вариант алгоритма. Создать вспомогательный массив <tex>C[0..k - 1]</tex>, состоящий из нулей, затем последовательно прочитать элементы входного массива <tex>A</tex>, для каждого <tex>A[i]</tex> увеличить <tex>C[A[i]]</tex> на единицу. Теперь достаточно пройти по массиву <tex>C</tex>, для каждого <tex>j \in \{0, ..., k - 1\}</tex> в массив <tex>A</tex> последовательно записать число <tex>j</tex> <tex> C[j]</tex> раз.
 
 
<code>
 
<code>
 
  SimpleCountingSort
 
  SimpleCountingSort
     for i = 0 to k - 1
+
     for number = 0 to k - 1
         C[i] = 0;
+
         C[number] = 0;
     for i = 0 to n - 1
+
     for i = 0 to length[A] - 1
 
         C[A[i]] = C[A[i]] + 1;
 
         C[A[i]] = C[A[i]] + 1;
     b = 0;
+
     pos = 0;
     for j = 0 to k - 1
+
     for number = 0 to k - 1
 
         for i = 0 to C[j] - 1
 
         for i = 0 to C[j] - 1
             A[b] = j;
+
             A[pos] = number;
             b = b + 1;
+
             pos = pos + 1;
 
</code>
 
</code>
 
  
 
== Устойчивый алгоритм ==
 
== Устойчивый алгоритм ==
 
+
В этом варианте помимо входного массива <tex>A</tex> потребуется два вспомогательных массива — <tex>C[0..k - 1]</tex> для счётчика и <tex>B[0..n - 1]</tex> для отсортированного массива. Сначала следует заполнить массив <tex>C</tex> нулями, и для каждого <tex>A[i]</tex> увеличить <tex>C[A[i]]</tex> на 1. Далее подсчитывается число элементов меньше или равных текущему. Для этого каждый <tex>C[number]</tex>, начиная с <tex>C[1]</tex>, увеличивают на <tex>C[number - 1]</tex>. На последнем шаге алгоритма читается входной массив с конца и в каждый <tex>B[C[A[i]]]</tex> записывается <tex>A[i]</tex>, а значение <tex>C[A[i]]</tex> уменьшается на 1. Алгоритм устойчив. Устойчивость может потребоваться при [[Сортировка_подсчетом_сложных_объектов|сортировке сложных структур данных]].  
В этом варианте помимо входного массива <tex>A</tex> потребуется два вспомогательных массива — <tex>C[0..k - 1]</tex> для счётчика и <tex>B[0..n - 1]</tex> для отсортированного массива. Сначала следует заполнить массив <tex>C</tex> нулями, и для каждого <tex>A[i]</tex> увеличить <tex>C[A[i]]</tex> на 1. Далее подсчитывается число элементов меньше или равных текущему. Для этого каждый <tex>C[j]</tex>, начиная с <tex>C[1]</tex>, увеличивают на <tex>C[j - 1]</tex>. На последнем шаге алгоритма читается входной массив с конца, значение <tex>C[A[i]]</tex> уменьшается на 1 и в каждый <tex>B[C[A[i]]]</tex> записывается <tex>A[i]</tex>. Алгоритм устойчив. Устойчивость может потребоваться при [[Сортировка_подсчетом_сложных_объектов|сортировке сложных структур данных]].  
 
 
<code>
 
<code>
 
  StableCountingSort
 
  StableCountingSort
     for i = 0 to k - 1
+
     for number = 0 to k - 1
         C[i] = 0;
+
         C[number] = 0;
     for i = 0 to n - 1
+
     for i = 0 to length[A] - 1
 
         C[A[i]] = C[A[i]] + 1;
 
         C[A[i]] = C[A[i]] + 1;
     for j = 1 to k - 1
+
     for number = 1 to k - 1
 
         C[j] = C[j] + C[j - 1];
 
         C[j] = C[j] + C[j - 1];
     for i = n - 1 to 0
+
     for i = length[A] - 1 to 0
 +
        B[C[A[i]]] = A[i];
 
         C[A[i]] = C[A[i]] - 1;
 
         C[A[i]] = C[A[i]] - 1;
        B[C[A[i]]] = A[i];
 
 
</code>
 
</code>
  
 
== Обобщение на произвольный целочисленный диапазон ==
 
== Обобщение на произвольный целочисленный диапазон ==
 
 
Если диапазон значений (min и max) заранее не известен, можно воспользоваться линейным поиском min и max, что не повлияет на асимптотику алгоритма. При работе с массивом <tex>C</tex> из <tex>A[i]</tex>  необходимо вычитать min, а при обратной записи прибавлять.  
 
Если диапазон значений (min и max) заранее не известен, можно воспользоваться линейным поиском min и max, что не повлияет на асимптотику алгоритма. При работе с массивом <tex>C</tex> из <tex>A[i]</tex>  необходимо вычитать min, а при обратной записи прибавлять.  
  
 
== Анализ ==
 
== Анализ ==
 
 
В первом алгоритме первые два цикла работают за <tex>\Theta(k)</tex> и <tex>\Theta(n)</tex>, соответственно; двойной цикл за <tex>\Theta(n + k)</tex>. Во втором алгоритме циклы занимают <tex>\Theta(k)</tex>, <tex>\Theta(n)</tex>, <tex>\Theta(k)</tex> и <tex>\Theta(n)</tex>, соответственно. Итого оба алгоритма имеют линейную временную трудоёмкость <tex>\Theta(n + k)</tex>. Используемая память в первом алгоритме равна <tex>\Theta(k)</tex>, а во втором <tex>\Theta(n + k)</tex>.
 
В первом алгоритме первые два цикла работают за <tex>\Theta(k)</tex> и <tex>\Theta(n)</tex>, соответственно; двойной цикл за <tex>\Theta(n + k)</tex>. Во втором алгоритме циклы занимают <tex>\Theta(k)</tex>, <tex>\Theta(n)</tex>, <tex>\Theta(k)</tex> и <tex>\Theta(n)</tex>, соответственно. Итого оба алгоритма имеют линейную временную трудоёмкость <tex>\Theta(n + k)</tex>. Используемая память в первом алгоритме равна <tex>\Theta(k)</tex>, а во втором <tex>\Theta(n + k)</tex>.
  
 +
На практике сортировка подсчетом применяется, когда <tex>k = O(n)</tex>, а в этом случае время работы алгоритма равно <tex>\Theta(n)</tex>
  
== Ссылки ==
+
== Источники ==
 +
* Томас Кормен, Чарльз Лейзерсон, Рональд Ривест, Клиффорд Штайн. «Алгоритмы. Построение и анализ» {{---}} «Вильямс», 2011 г. {{---}} 1296 стр. {{---}} ISBN 978-5-8459-0857-5, 5-8459-0857-4, 0-07-013151-1
 +
* [http://ru.wikipedia.org/wiki/Сортировка_подсчётом Сортировка подсчетом {{---}} Википедия]
  
[http://ru.wikipedia.org/wiki/%D0%A1%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0_%D0%BF%D0%BE%D0%B4%D1%81%D1%87%D1%91%D1%82%D0%BE%D0%BC Википедия: Сортировка подсчетом]
+
[[Категория:Дискретная математика и алгоритмы]]
 +
[[Категория:Сортировка]]

Версия 14:36, 7 мая 2012

Сортировка подсчётом — алгоритм сортировки, в котором предполагается, что все [math]n[/math] входных элементов — целые числа, принадлежащие интервалу от [math]0[/math] до [math]k[/math], где [math]k[/math] — некоторая целая константа. Например, миллион натуральных чисел меньших [math]1000[/math]. Эффективность алгоритма падает, если при попадании нескольких различных элементов в одну ячейку, их надо дополнительно сортировать.

Основная идея

Основная идея сортировки подсчетом заключается в том, чтобы для каждого входного элемента [math]x[/math] определить количество элементов, которые меньше [math]x[/math]. C помощью этой информации элемент [math]x[/math] можно разместить на той позиции выходного массива, где он должен находиться. Например, если всего имеется [math]42[/math] элемента, которые меньше [math]x[/math], то в выходной последовательности элемент [math]x[/math] должен заниматься [math]43[/math]-ю позицию. Если допускается ситуация, когда несколько элементов имеют одно и тоже значение, то эту схему придётся модифицировать, так как мы не можем разместить все такие элементы в одной позиции.

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

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

SimpleCountingSort
    for number = 0 to k - 1
        C[number] = 0;
    for i = 0 to length[A] - 1
        C[A[i]] = C[A[i]] + 1;
    pos = 0;
    for number = 0 to k - 1
        for i = 0 to C[j] - 1
            A[pos] = number;
            pos = pos + 1;

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

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

StableCountingSort
    for number = 0 to k - 1
        C[number] = 0;
    for i = 0 to length[A] - 1
        C[A[i]] = C[A[i]] + 1;
    for number = 1 to k - 1
        C[j] = C[j] + C[j - 1];
    for i = length[A] - 1 to 0
        B[C[A[i]]] = A[i];
        C[A[i]] = C[A[i]] - 1;

Обобщение на произвольный целочисленный диапазон

Если диапазон значений (min и max) заранее не известен, можно воспользоваться линейным поиском min и max, что не повлияет на асимптотику алгоритма. При работе с массивом [math]C[/math] из [math]A[i][/math] необходимо вычитать min, а при обратной записи прибавлять.

Анализ

В первом алгоритме первые два цикла работают за [math]\Theta(k)[/math] и [math]\Theta(n)[/math], соответственно; двойной цикл за [math]\Theta(n + k)[/math]. Во втором алгоритме циклы занимают [math]\Theta(k)[/math], [math]\Theta(n)[/math], [math]\Theta(k)[/math] и [math]\Theta(n)[/math], соответственно. Итого оба алгоритма имеют линейную временную трудоёмкость [math]\Theta(n + k)[/math]. Используемая память в первом алгоритме равна [math]\Theta(k)[/math], а во втором [math]\Theta(n + k)[/math].

На практике сортировка подсчетом применяется, когда [math]k = O(n)[/math], а в этом случае время работы алгоритма равно [math]\Theta(n)[/math]

Источники

  • Томас Кормен, Чарльз Лейзерсон, Рональд Ривест, Клиффорд Штайн. «Алгоритмы. Построение и анализ» — «Вильямс», 2011 г. — 1296 стр. — ISBN 978-5-8459-0857-5, 5-8459-0857-4, 0-07-013151-1
  • Сортировка подсчетом — Википедия