- •Тема № 5. Алгоритмы сортировки. Сравнительный анализ
- •5.1. Введение
- •5.2. Описание алгоритмов
- •3.Пиромидальная сортировка (сортировка с помощью кучи).
- •4. Быстрая сортировка.
- •5.3. Теоретические аспекты определения времени выполнения сортировок
- •5.3.1 Алгоритм сортировки Insertion Sort
- •5.3.2. Алгоритм сортировки Quick Sort.
- •5.3.3. Алгоритм сортировки Heap Sort.
- •5.3.4. Алгоритм сортировки Merge Sort.
- •5.4. Практические аспекты сравнения
3.Пиромидальная сортировка (сортировка с помощью кучи).
Рассмотрим еще один алгоритм сортировки, а именно - пирамидальную сортировку. Время работы этого алгоритма, как и время работы алгоритма сортировки слиянием (и в отличие от времени работы алгоритма сортировки вставкой), равно O(nlgn). Как и сортировка методом вставок, и в отличие от сортировки слиянием, пирамидальная сортировка выполняется без привлечения дополнительной памяти: в любой момент времени требуется память для хранения вне массива только некоторого постоянного количества элементов. Таким образом, в пирамидальной сортировке сочетаются лучшие особенности двух рассмотренных ранее алгоритмов сортировки.
Пирамида (binaryheap) - это структура данных, представляющая собой объект-массив, который можно рассматривать как почти полное бинарное дерево (см. тему № 2). Каждый узел этого дерева соответствует определенному элементу массива. На всех уровнях, кроме, может быть, последнего, дерево полностью заполнено (заполненный уровень — это такой, который содержит максимально возможное количество узлов). Последний уровень заполняется слева направо до тех пор, пока в массиве не закончатся элементы. Представляющий пирамиду массивА является объектом с двумя атрибутами:length [A], т.е. количество элементов массива, иheap_size [A], т.е. количество элементов пирамиды, содержащихся в массивеА. Другими словами, несмотря на то, что в массивеА [1..length [А]] все элементы могут быть корректными числами, ни один из элементов, следующих после элементаА[heap__size[A]], гдеheap_size [А] length [А], не является элементом пирамиды. В корне дерева находится элементА [1], а дальше оно строится по следующему принципу: если какому-то узлу соответствует индексi, то индекс его родительского узла вычисляется с помощью представленной ниже процедурыParent(i), индекс левого дочернего узла - с помощью процедурыLEFT(i), а индекс правого дочернего узла — с помощью процедурыRight(i):
Рис. 5.3. Построение пирамиды.
Принцип работы метода.
Алгоритм основан на том, что сортируемому массиву может быть поставлено в соответствие двоичное дерево, каждый узел которого соответствует одному элементу массива с некоторым номером kи этот узел содержит ссылки на два других узла, соответствующих элементам с номерами2k+1и2k+2. Корень дерева соответствует элементу с номером0(ноль).
Дальнейшие действия следующие - массив переупорядочивается так, чтобы для любого kвыполнялись неравенства:
Затем массив еще раз переупорядочивается, уже по возрастанию номеров. Каждый из этих шагов требует операций.
Естественно, такой алгоритм работает быстрее и требует меньше памяти, но, наверное, он еще менее очевиден, чем рекурсивный.
Работа алгоритма пирамидальной сортировки начинается с вызова процедуры BuilD_Max__HEAP, с помощью которой из входного массиваA[1..n], где
n=length [А], создается невозрастающая пирамида. Поскольку наибольший элемент массива находится в корне, т.е. в элементеА [1], его можно поместить в окончательную позицию в отсортированном массиве, поменяв его местами с элементомА [п]. Выбросив из пирамиды узелп (путем уменьшения на единицу величиныhcap_size [А]}, мы обнаружим, что подмассивА [1.. (n- 1)] легко преобразуется в невозрастающую пирамиду. Пирамиды, дочерние по отношению к корневому узлу, после обмена элементовA[1] и А [n] и уменьшения размера массива остаются невозрастающими, однако новый корневой элемент может нарушить свойство невозрастания пирамиды. Для восстановления этого свойства достаточно вызвать процедуруМах_НеаРIFY(A,1), после чего подмассивА [1.. (n— 1)] превратится в невозрастающую пирамиду. Затем алгоритм пирамидальной сортировки повторяет описанный процесс для невозрастающих пирамид размераn-1,n-2,... ,2.
Heapsort(A)
1 Build_Max_Heap(A)
2 for i tength[A] downto 2
3 do Обменять A[1] A[i]
heap_size[A]hеар_size[A] - 1
Max_Heapify(A, 1)
На рис. 5.4 показан пример пирамидальной сортировки после предварительного построения невозрастающей пирамиды. В каждой части этого рисунка изображена невозрастающая пирамида перед выполнением очередной итерации цикла forв строках 2-5. В частиа) этого рисунка показана исходная невозрастающая пирамида, полученная при помощи процедурыBU1LD_MAX_HEAP. В частяхб)-к) показаны пирамиды, получающиеся в результате вызова процедурыMAX_HEAPIFYв строке 5. В каждой из этих частей указано значение индексаi. В пирамиде содержатся только узлы, закрашенные светло-серым цветом. В части л) показан получившийся в конечном итоге массивА.
Время работы процедуры Heapsort равно О (nlgn), поскольку вызов процедурыBuild_Max_Heap требует времени0(п), а каждый изп — 1 вызовов процедурыMax_Heapify — времениО (lgn).
Рис. 5.4. Работа процедуры Heapsort.
Приведем псевдокоды процедур BU1LD_MAX_HEAP(создание пирамиды из неупорядоченного массива данных) иMAX_HEAPIFY(поддержка свойств пирамиды).
Max_Heapify(A,j)
l LEFT(i)
r RlGHT(i)
if l heap_size[A] и А[l] > A[i]
then largestl
else largest i
6 if r heap_size[A] и A[r] > A[largest]
7 then largest r
8 if largest i
9 then Обменять A[i] A[largest] 10 MaX_HeaPIFY(A, largest)
Build_Max_Heap(A)
heap_size[A] length[A]
for i downto 1
3 do MAX_HEAPIFY(A, i)