Сортировка подсчетом сложных объектов — различия между версиями
Murtaught (обсуждение | вклад) (Исправлена ошибка в псевдокоде) |
Murtaught (обсуждение | вклад) (Тире вместо дефисов, P вместо B и пояснение перед описанием) |
||
Строка 2: | Строка 2: | ||
== Постановка задачи == | == Постановка задачи == | ||
− | Иногда бывает очень желательно применить быстрый алгоритм [[Сортировка подсчетом|сортировки подсчетом]] для упорядочивания набора каких-либо "сложных" данных. Под "сложными объектами" здесь подразумеваются структуры, содержащие в себе несколько полей. Одно из них мы выделим и назовем ключом, сортировка будет идти именно по нему (предполагается, что значения, принимаемые ключом - целые числа в диапазоне от <tex>0</tex> до <tex>k-1</tex>). | + | Иногда бывает очень желательно применить быстрый алгоритм [[Сортировка подсчетом|сортировки подсчетом]] для упорядочивания набора каких-либо "сложных" данных. Под "сложными объектами" здесь подразумеваются структуры, содержащие в себе несколько полей. Одно из них мы выделим и назовем ключом, сортировка будет идти именно по нему (предполагается, что значения, принимаемые ключом {{---}} целые числа в диапазоне от <tex>0</tex> до <tex>k-1</tex>). |
Мы не сможем использовать здесь в точности тот же алгоритм, что и для сортировки подсчетом обычных целых чисел, потому что в наборе могут быть различные структуры, имеющие одинаковые ключи. Существует два способа справиться с этой проблемой {{---}} использовать списки для хранения структур в отсортированном массиве или заранее посчитать количество структур с одинаковыми ключами для каждого значения ключа. | Мы не сможем использовать здесь в точности тот же алгоритм, что и для сортировки подсчетом обычных целых чисел, потому что в наборе могут быть различные структуры, имеющие одинаковые ключи. Существует два способа справиться с этой проблемой {{---}} использовать списки для хранения структур в отсортированном массиве или заранее посчитать количество структур с одинаковыми ключами для каждого значения ключа. | ||
== Использование списков == | == Использование списков == | ||
− | Пусть далее исходная последовательность из <tex>n</tex> структур хранится в массиве <tex>A</tex>, а отсортированная - в массиве <tex>P</tex> с индексами от <tex>0</tex> до <tex>k-1</tex>. | + | Пусть далее исходная последовательность из <tex>n</tex> структур хранится в массиве <tex>A</tex>, а отсортированная {{---}} в массиве <tex>P</tex> с индексами от <tex>0</tex> до <tex>k-1</tex>. |
− | Сделаем из каждой ячейки массива <tex> | + | Сделаем из каждой ячейки массива <tex>P</tex> список, в который будем добавлять структуры с одинаковыми ключами. |
[[Файл:List_solution.png|500px|]] | [[Файл:List_solution.png|500px|]] | ||
Строка 18: | Строка 18: | ||
== Подсчет числа различных ключей == | == Подсчет числа различных ключей == | ||
=== Описание === | === Описание === | ||
− | Здесь исходная последовательность из <tex>n</tex> структур хранится в массиве <tex>A</tex>, а отсортированная - в массиве <tex>B</tex> того же размера. Кроме того используется вспомогательный массив <tex>P</tex> с индексами от <tex>0</tex> до <tex>k-1</tex>. | + | Здесь исходная последовательность из <tex>n</tex> структур хранится в массиве <tex>A</tex>, а отсортированная {{---}} в массиве <tex>B</tex> того же размера. Кроме того используется вспомогательный массив <tex>P</tex> с индексами от <tex>0</tex> до <tex>k-1</tex>. |
+ | |||
+ | Идея алгоритма состоит в предварительном подсчете количества элементов с различными ключами в исходном массиве и разделении результирующего массива на части соответствующей длины. Затем при повторном проходе исходного массива каждый его элемент копируется в специально отведенный его ключу блок, в первую свободную ячейку. Это осуществляется с помощью массива индексов <tex>P</tex>, в котором хранятся индексы начала блоков для различных ключей. <tex>P[key]</tex> {{---}} индекс в результирующем массиве, соответствующий первому элементу блока для ключа <tex>key</tex>. | ||
* Пройдем по исходному массиву <tex>A</tex> и запишем в <tex>P[i]</tex> количество структур, ключ которых равен <tex>i</tex>. | * Пройдем по исходному массиву <tex>A</tex> и запишем в <tex>P[i]</tex> количество структур, ключ которых равен <tex>i</tex>. | ||
Строка 29: | Строка 31: | ||
[[Файл:P_after_adding.png]] | [[Файл:P_after_adding.png]] | ||
− | * Теперь "сдвинем" массив <tex>P</tex> на элемент вперед: в новом массиве <tex>P[0] = 0</tex>, а для <tex>i > 0</tex> <tex>P[i] = P_{old}[i-1]</tex>, где <tex>P_{old}</tex> - старый массив <tex>P</tex>. <br> Это можно сделать за один проход по массиву <tex>P</tex>, причем одновременно с предыдущим шагом. <br> После этого действия в массиве <tex>P</tex> будут хранится индексы массива <tex>B</tex>. <tex>P[key]</tex> указывает на начало блока в <tex>B</tex>, соответствующего ключу <tex>key</tex>. | + | * Теперь "сдвинем" массив <tex>P</tex> на элемент вперед: в новом массиве <tex>P[0] = 0</tex>, а для <tex>i > 0</tex> <tex>P[i] = P_{old}[i-1]</tex>, где <tex>P_{old}</tex> {{---}} старый массив <tex>P</tex>. <br> Это можно сделать за один проход по массиву <tex>P</tex>, причем одновременно с предыдущим шагом. <br> После этого действия в массиве <tex>P</tex> будут хранится индексы массива <tex>B</tex>. <tex>P[key]</tex> указывает на начало блока в <tex>B</tex>, соответствующего ключу <tex>key</tex>. |
[[Файл:P_as_array_of_pointers.png]] | [[Файл:P_as_array_of_pointers.png]] | ||
Строка 42: | Строка 44: | ||
Здесь <tex>A</tex> и <tex>B</tex> {{---}} массивы структур размера <tex>n</tex>, с индексами от <tex>0</tex> до <tex>n-1</tex>. | Здесь <tex>A</tex> и <tex>B</tex> {{---}} массивы структур размера <tex>n</tex>, с индексами от <tex>0</tex> до <tex>n-1</tex>. | ||
− | <tex>P</tex> {{---}} целочисленный массив размера <tex>k</tex> | + | <tex>P</tex> {{---}} целочисленный массив размера <tex>k</tex>, с индексами от <tex>0</tex> до <tex>k-1</tex>, где <tex>k</tex> {{---}} количество различных ключей. <br> <tex>i</tex>, <tex>carry</tex>, <tex>temporary</tex> {{---}} целочисленные переменные. |
ComplexCountingSort | ComplexCountingSort | ||
Строка 74: | Строка 76: | ||
* [http://ru.wikipedia.org/wiki/Сортировка_подсчётом Википедия {{---}} Сортировка подсчетом] | * [http://ru.wikipedia.org/wiki/Сортировка_подсчётом Википедия {{---}} Сортировка подсчетом] | ||
* [http://en.wikipedia.org/wiki/Counting_sort Wikipedia {{---}} Counting sort] | * [http://en.wikipedia.org/wiki/Counting_sort Wikipedia {{---}} Counting sort] | ||
− | * Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 224-226. | + | * Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 224{{---}}226. |
[[Категория: Дискретная математика и алгоритмы]] | [[Категория: Дискретная математика и алгоритмы]] | ||
[[Категория: Сортировки]] | [[Категория: Сортировки]] |
Версия 16:19, 21 мая 2012
Содержание
Постановка задачи
Иногда бывает очень желательно применить быстрый алгоритм сортировки подсчетом для упорядочивания набора каких-либо "сложных" данных. Под "сложными объектами" здесь подразумеваются структуры, содержащие в себе несколько полей. Одно из них мы выделим и назовем ключом, сортировка будет идти именно по нему (предполагается, что значения, принимаемые ключом — целые числа в диапазоне от до ).
Мы не сможем использовать здесь в точности тот же алгоритм, что и для сортировки подсчетом обычных целых чисел, потому что в наборе могут быть различные структуры, имеющие одинаковые ключи. Существует два способа справиться с этой проблемой — использовать списки для хранения структур в отсортированном массиве или заранее посчитать количество структур с одинаковыми ключами для каждого значения ключа.
Использование списков
Пусть далее исходная последовательность из
структур хранится в массиве , а отсортированная — в массиве с индексами от до .Сделаем из каждой ячейки массива
список, в который будем добавлять структуры с одинаковыми ключами.Этот вариант плох тем, что надо поддерживать сам список, что не является самым простым решением. Еще придется хранить дополнительную информацию в виде ссылок на следующий элемент в списке. И кроме того, такое представление отсортированного массива неудобно в использовании. Избавиться от этих недостатков можно используя другую модификацию алгоритма сортировки подсчетом.
Подсчет числа различных ключей
Описание
Здесь исходная последовательность из
структур хранится в массиве , а отсортированная — в массиве того же размера. Кроме того используется вспомогательный массив с индексами от до .Идея алгоритма состоит в предварительном подсчете количества элементов с различными ключами в исходном массиве и разделении результирующего массива на части соответствующей длины. Затем при повторном проходе исходного массива каждый его элемент копируется в специально отведенный его ключу блок, в первую свободную ячейку. Это осуществляется с помощью массива индексов
, в котором хранятся индексы начала блоков для различных ключей. — индекс в результирующем массиве, соответствующий первому элементу блока для ключа .- Пройдем по исходному массиву и запишем в количество структур, ключ которых равен .
- Мысленно разобьем массив на блоков, длина каждого из которых равна соответственно , , ..., .
- Теперь массив нам больше не нужен. Превратим его в массив, хранящий в сумму элементов от до старого массива .
- Теперь "сдвинем" массив
Это можно сделать за один проход по массиву , причем одновременно с предыдущим шагом.
После этого действия в массиве будут хранится индексы массива . указывает на начало блока в , соответствующего ключу . на элемент вперед: в новом массиве , а для , где — старый массив .
- Произведем саму сортировку. Еще раз пройдем по исходному массиву и для всех будем помещать структуру в массив на место , а затем увеличивать на . Здесь — это ключ структуры, находящейся в массиве на -том месте.
Таким образом после завершения алгоритма в
будет содержаться исходная последовательность в отсортированном виде (так как блоки расположены по возрастанию соответствующих ключей).Стоит также отметить, что эта сортировка является устойчивой, так как два элемента с одинаковыми ключами будут добавлены в том же порядке, в каком просматривались в исходном массиве
.Псевдокод
Здесь
, , — целочисленные переменные.
ComplexCountingSort for i = 0 to k - 1 P[i] = 0; for i = 0 to length[A] - 1 P[A[i].key] = P[A[i].key] + 1; carry = 0; for i = 0 to k - 1 temporary = P[i]; P[i] = carry; carry = carry + temporary; for i = 0 to length[A] - 1 B[P[A[i].key]] = A[i]; P[A[i].key] = P[A[i].key] + 1;
Здесь шаги 3 и 4 из описания объединены в один цикл. Обратите внимание, что в последнем цикле инструкцией
B[P[A[i].key]] = A[i];
копируется структура
целиком, а не только её ключ.Анализ
Весь алгоритм состоит из двух проходов по массиву
размера и одного прохода по массиву , размера . Его трудоемкость, таким образом, равна . На практике сортировку подсчетом имеет смысл применять, если значительно меньше , поэтому можно считать время работы алгоритма равным . Как и в обычной сортировке подсчетом, алгоритму требуется дополнительной памяти.Источники
- Википедия — Сортировка подсчетом
- Wikipedia — Counting sort
- Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. — 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 224—226.