Сортировка подсчётом — различия между версиями
 (→Устойчивый алгоритм)  | 
				Nechaev (обсуждение | вклад)   | 
				||
| Строка 1: | Строка 1: | ||
| − | '''Сортировка подсчётом''' — алгоритм сортировки, в котором   | + | '''Сортировка подсчётом''' — алгоритм сортировки, в котором предполагается, что все <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>  | ||
<code>  | <code>  | ||
  SimpleCountingSort  |   SimpleCountingSort  | ||
| − |       for   | + |       for number = 0 to k - 1  | 
| − |           C[  | + |           C[number] = 0;  | 
| − |       for i = 0 to   | + |       for i = 0 to length[A] - 1  | 
          C[A[i]] = C[A[i]] + 1;  |           C[A[i]] = C[A[i]] + 1;  | ||
| − | + |       pos = 0;  | |
| − |       for   | + |       for number = 0 to k - 1  | 
          for i = 0 to C[j] - 1  |           for i = 0 to C[j] - 1  | ||
| − |               A[  | + |               A[pos] = number;  | 
| − | + |               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[  | ||
<code>  | <code>  | ||
  StableCountingSort  |   StableCountingSort  | ||
| − |       for   | + |       for number = 0 to k - 1  | 
| − |           C[  | + |           C[number] = 0;  | 
| − |       for i = 0 to   | + |       for i = 0 to length[A] - 1  | 
          C[A[i]] = C[A[i]] + 1;  |           C[A[i]] = C[A[i]] + 1;  | ||
| − |       for   | + |       for number = 1 to k - 1  | 
          C[j] = C[j] + C[j - 1];  |           C[j] = C[j] + C[j - 1];  | ||
| − |       for i =   | + |       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;  | ||
| − | |||
</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/Сортировка_подсчётом Сортировка подсчетом {{---}} Википедия]  | ||
| − | [  | + | [[Категория:Дискретная математика и алгоритмы]]  | 
| + | [[Категория:Сортировка]]  | ||
Версия 14:36, 7 мая 2012
Сортировка подсчётом — алгоритм сортировки, в котором предполагается, что все входных элементов — целые числа, принадлежащие интервалу от до , где — некоторая целая константа. Например, миллион натуральных чисел меньших . Эффективность алгоритма падает, если при попадании нескольких различных элементов в одну ячейку, их надо дополнительно сортировать.
Содержание
Основная идея
Основная идея сортировки подсчетом заключается в том, чтобы для каждого входного элемента определить количество элементов, которые меньше . C помощью этой информации элемент можно разместить на той позиции выходного массива, где он должен находиться. Например, если всего имеется элемента, которые меньше , то в выходной последовательности элемент должен заниматься -ю позицию. Если допускается ситуация, когда несколько элементов имеют одно и тоже значение, то эту схему придётся модифицировать, так как мы не можем разместить все такие элементы в одной позиции.
Простой алгоритм
Это простейший вариант алгоритма. Создать вспомогательный массив , состоящий из нулей, затем последовательно прочитать элементы входного массива  и для каждого  увеличить  на единицу. Теперь достаточно пройти по массиву  и для каждого  в массив  последовательно записать число   раз.
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;
Устойчивый алгоритм
В этом варианте помимо входного массива  потребуется два вспомогательных массива —  для счётчика и  для отсортированного массива. Сначала следует заполнить массив  нулями, и для каждого  увеличить  на 1. Далее подсчитывается число элементов меньше или равных текущему. Для этого каждый , начиная с , увеличивают на . На последнем шаге алгоритма читается входной массив с конца и в каждый  записывается , а значение  уменьшается на 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, что не повлияет на асимптотику алгоритма. При работе с массивом из необходимо вычитать min, а при обратной записи прибавлять.
Анализ
В первом алгоритме первые два цикла работают за и , соответственно; двойной цикл за . Во втором алгоритме циклы занимают , , и , соответственно. Итого оба алгоритма имеют линейную временную трудоёмкость . Используемая память в первом алгоритме равна , а во втором .
На практике сортировка подсчетом применяется, когда , а в этом случае время работы алгоритма равно
Источники
- Томас Кормен, Чарльз Лейзерсон, Рональд Ривест, Клиффорд Штайн. «Алгоритмы. Построение и анализ» — «Вильямс», 2011 г. — 1296 стр. — ISBN 978-5-8459-0857-5, 5-8459-0857-4, 0-07-013151-1
 - Сортировка подсчетом — Википедия