Пирамидальная сортировка. Алгоритм и его сложность
Гоша: Тимофей, ты говорил, что с использованием бинарной кучи можно реализовать алгоритм сортировки. Расскажешь как?
Тимофей: А ты хорошо разобрался с тем, как работает пирамида?
Гоша: Ну да!
Какая сложность вставки нового элемента в пирамиду в худшем случае?
Какая сложность извлечения самого приоритетного элемента из пирамиды в худшем случае?
С использованием бинарной кучи можно реализовать алгоритм сортировки, который в худшем случае работает за O(nlogn).
Разберёмся, как он работает.
Дан массив:
Скопировать кодPYTHON
arr = [3, 5, 1, 6, 9, 2]
Нужно отсортировать его за O(nlogn) в худшем случае.
Отсортирован ли массив, который представляет бинарную кучу?
Массив, который является представлением бинарной кучи, не обязан быть отсортированным. Выполнение того свойства, что значение в родителе более приоритетно, чем значения в дочерних узлах, не гарантирует отсортированность.
Например, на схеме неотсортированный массив представляет бинарную кучу.
Разберёмся, как используют эту структуру данных для задачи сортировки. Общий алгоритм такой:
Создадим бинарную кучу.
Вставим в неё элементы массива.
Будем извлекать из неё наиболее приоритетные элементы, удаляя их из кучи.
Рассмотрим алгоритм на примере, приведённом выше. Так выглядит вставка элементов из массива в бинарную кучу:
На этом шаге извлечём наиболее приоритетный элемент из кучи. По свойству пирамиды самый приоритетный элемент находится в вершине. Поэтому извлечём элемент из вершины, удаляя его из кучи. Для задачи сортировки по возрастанию приоритетный — минимальный элемент.
Таким образом, мы получили отсортированный массив.
Какая сложность у рассмотренного алгоритма в лучшем случае?
Какая сложность у рассмотренного алгоритма в худшем случае?
Первый шаг — создание бинарной кучи. Сложность этой операции — O(n). Нам просто нужно создать массив из n элементов.
Это алгоритм сортировки, который в худшем случае работает за O(nlogn).
Требуется ли для такой реализации дополнительная память?
Гоша: Здорово! Какой интересный алгоритм. Только я не понимаю, чем он лучше сортировки слиянием. Она тоже в худшем случае работает за O(nlogn) и требует O(n) дополнительной памяти.
Тимофей: Можно модифицировать алгоритм пирамидальной сортировки так, что не придётся выделять память под новый массив. Рассказать как?
Гоша: Нет, я хочу сам подумать, как это сделать. Если можно реализовать пирамидальную сортировку без использования дополнительной памяти, то почему в стандартных библиотеках многих языков программирования применяют быструю сортировку? Она ведь в худшем случае работает за квадратичное время. Не понимаю.
Тимофей: Быструю сортировку применяют чаще по двум причинам:
В алгоритме быстрой сортировки используется меньше операций обмена с памятью, чем в пирамидальной сортировке. Желательно избегать лишней работы с памятью.
n обращений к последовательным ячейкам памяти исполняются быстрее, чем к случайным. Это связано с ограниченным количеством аппаратной кэш-памяти у процессоров. Некоторые алгоритмы могут быть дружелюбны к кэшу. Их называют cache-friendly. А некоторые — нет.
Пирамидальная сортировка — самая медленная из тех, которые работают за O(nlogn).
Гоша: Ясно. Тогда я всегда буду выбирать быструю сортировку из этих трёх алгоритмов.
Тимофей: Мне кажется, это неправильное решение. Подбирать алгоритм нужно специально под задачу. Выбирай пирамидальную сортировку, если хочешь быть уверен, что алгоритм всегда будет работать за O(nlogn), и в наличии нет O(n) дополнительной памяти. Кстати этот алгоритм сортировки был основным на компьютерах, выпускаемых в 1960–1970 годах.