Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лк07 Сортировки.doc
Скачиваний:
63
Добавлен:
28.10.2018
Размер:
1.73 Mб
Скачать

Пирамидальная сортировка

Принципы пирамидальной сортировки будет проще понять, если рассмотреть два жизненных примера: организацию турнирной таблицы и корпоративную систему выдвижения.

1. Организация турнирной таблицы происходит следующим образом: в первом туре участники разбиты на пары и играют между собой, победители проходят в следующий тур и играют между собой и т.д. В полуфинале участвует 4 участника, в финале 2 и определяется победитель.

Теперь, чтобы определить второго по силе участника, надо вывести из турнира сильнейшего и разыграть турнир заново. Продолжая эту процедуру, найдем третьего по силе, четвертого и т.д.

Рассмотрим организацию турнира на примере «участников» - элементов массива (5, 2, 8, 9, 3, 1, 7. 2).

9

9

7

5

9

3

7

5

2

8

9

3

1

7

2

Победителем является «участник» 9. Выведем его из турнира, заменив на «чрезвычайно слабого игрока» -.

8

8

7

5

8

3

7

5

2

8

-

3

1

7

2

Победителем является «участник» 8. Выведем его из турнира, заменив на «чрезвычайно слабого игрока» -.

7

5

7

5

-

3

7

5

2

-

-

3

1

7

2

5

5

3

5

-

3

2

5

2

-

-

3

1

-

2

И т.д. Таким образом, происходит сортировка посредством выбора из дерева – выбирается максимальный элемент, находящийся в корне дерева.

2. Корпоративная система выдвижения заключается в следующем: из группы простых исполнителей на верхний уровень поднимаются лучшие (например, из группы программистов выдвигаются старшие программисты). Среди старших программистов отбирают руководителей лаборатории. Среди руководителей лаборатории отбирают руководителей отделов и т.д. При этом, когда участник поднимается с самого нижнего уровня, его заменяют «человеком с улицы» (-), а если выдвижение идет с очередного уровня, то его заменяют на лучшего из его подчиненных.

9

9

8

7

5

9

8

-

3

7

2

5

-

2

8

-

9

-

3

-

1

7

-

2

-

В результате получим корпоративную пирамиду, где каждый стоит на своем уровне компетентности.

9

8

7

5

-

3

2

-

2

-

-

-

1

-

-

Интуитивно второй способ более удобен – он позволяет избежать лишних сравнений - с -, на которые мы будет то и дело натыкаться в турнирной таблице на поздних стадиях.

Пронумеруем теперь элементы пирамиды по уровням сверху-вниз и слева-направо:

X1

X2

X3

X4

X5

X6

X7

X8

X9

X10

X11

X12

X13

X14

X15

Заметим, что отцом k-го узла является узел с номером (k div 2), а потомками - узлы с номерами 2k и 2k+1.

В пирамиде выполнено условие

Xk div 2

Xk

X2k

X2k+1

Очевидным недостатком рассмотренного метода является большое число бесполезных дыр, содержащих -. Было бы очень хорошо организовать пирамиду на N ячейках (в нашем случае на 8, а не на 15).

Эта цель была реализована Дж. У. Дж. Уильямсом (John William Joseph Williams) в 1964 году, разработавшим алгоритм пирамидальной сортировки (heap-sort). В дальнейшем сортировку развивал Р.Флайд.

Будем называть массив X1 , X2 , X3 … Xn пирамидой, если выполнено условие:

Xj div 2  Xj , при 1  j div 2 < j  N.

Тогда X1  X2, X1  X3, X2  X4, X2 X5, и т.д. В частности, наибольший ключ лежит на вершине пирамиды: X1 = max (X1 , X2, … Xn).

Если удастся каким-либо образом преобразовать массив в пирамиду, то можно будет забрать корень, обменяв его с последним элементов, а затем, уменьшив размер кучи на единицу, воспользоваться нисходящей процедурой построения кучи для корня: на место корня переместить вверх наибольшего из его потомков, затем переместить вверх наибольшего из потомков последнего и т.д.

Нисходящая процедура для k-го элемента кучи, состоящей из Count элементов, выглядит следующим образом:

Procedure downheap(k: integer);

Var i, j, t: integer;

Begin

T:=x[k];

While k<=Count div 2 do

Begin

J:=k+k;

If j<Count then if x[j] < x[j+1] then j:=j+1;

If t >= x[j] then break;

X[k]:= x[j]; k:=j;

End;

X[k]:=t;

End;

Применим нисходящую процедуру ко всем элементам, начиная с N div 2. А затем, получив кучу, заберем корень, обменяв его с последним элементом, и применим нисходящую процедуру к корню.

Например, для массива 5, 2, 8, 9, 3, 1, 7, 2 алгоритм будет выглядеть следующим образом:

Расположим массив в кучу:

5

2

8

9

3

1

7

2

Применим нисходящую процедуру к среднему элементу 9, получим массив 5, 9, 8, 2, 3, 1, 7, 2.

5

9

8

2

3

1

7

2

Применим процедуру к элементам 8, 9 и 5, получим массив 9, 5. 8, 2. 3, 1, 7, 1 - это куча.

9

5

8

2

3

1

7

2

Обменяем местами корень 9 и последний элемент 2, получим массив 2, 5. 8, 2, 3, 1, 7, 9. При этом 9 стоит уже на своем месте.

2

5

8

2

3

1

7

9

Уменьшим размер кучи на 1 и применим нисходящую процедуру к корню 2, получим массив 8, 5, 7, 2, 3, 1, 2, 9.

8

5

7

2

3

1

2

9

Обменяем корень с последним элементом, получим массив 2, 5, 7, 2, 3, 1, 8, 9. И т.д.

Procedure HeapSort(var x: vector);

Var k, t : integer;

Count; integer;

Begin

Count:=N;

For k:=N div 2 downto 1 do Downheap(k);

Repeat

T:=x[1]; x[1]:=x[Count]; x[Count]:= t;

Dec(count);

HeapDown(1);

Until Count <=1;

End;

Таким образом, алгоритм пирамидальной сортировки состоит из двух этапов:

1. Построение пирамиды из данного массива на том же месте. Делается это процедурой sift.

2. Сортировка пирамиды. Меняются значениями концевые элементы пирамиды, и она укорачивается справа на один элемент (он будет вершиной пирамиды). Полученный укороченный массив уже не может быть пирамидой, поэтому, применяя процедуру sift, делаем его пирамидой. Повторяя этот этап n-1 раз, отсортируем массив в a[n]<=a[n-1]<=...a[1].

Время работы пирамидальной сортировки имеет порядок NlogN. Пирамидальная сортировка сравнима с сортировкой Шелла или быстрой сортировкой, причем вспомогательной памяти используется мало. С ростом N пирамидальная сортировка превзойдет по скорости сортировку Шелла, но никогда не станет лучше быстрой сортировки. Однако, в отличие от быстрой сортировки ее наихудший случай не намного хуже среднего.