Пирамидальная сортировка основана на алгоритме построения пирамиды. Последовательность ai, ai+1,…,ak называется (i,k)-пирамидой, если неравенство
aj≤min(a2j, а2j+1) (*)
выполняется для каждого j, j=i,…,k для которого хотя бы один из элементов a2j, a2j+1 существует.
Например, массив А является пирамидой, а массив В ¾ не является.
А=(а2 , а3 , а4 , а5 , а6 а7 , а8 )=(3, 2, 6, 4, 5, 7)
В=(b1, b2, b3, b4, b5, b6, b7)=(3, 2, 6, 4, 5, 7)
Свойства пирамиды
Процесс построения пирамиды выглядит следующим образом. Дана последовательность as+1,…,ak, которая является (s+1, k)-пирамидой. Добавим новый элемент х и поставим его на s-тую позицию в последовательности, т.е. пирамида всегда будет расширяться влево. Если выполняется (*), то полученная последовательность – (s, k)-пирамида. Иначе найдутся элементы a2s+1,a2s такие, что либо a2s < as либо a2s+1 < as.
Пусть имеет место первый случай, второй случай рассматривается аналогично. Поменяем местами элементы as и a2s. В результате получим новую последовательность as’,as+1,…, a2s’,…, ak. Повторяем все действия для элемента a2s’ и т.д. пока не получим (s, k)-пирамиду.
Пример. Добавим в (2, 8)-пирамиду новый элемент х=6.
Условные обозначения:
новый элемент
сравнение с включаемым элементом
обмен элементов
Алгоритм на псевдокоде
Построение (L,R)-пирамиды
aL+1,…,a R – на входе пирамида (L+1,R)
aL –новый элемент
x:= aL, i:=L
DO
j:=2i
IF (j>R) OD
IF((j<R) и (aj+1£ aj)) j=j+1 FI
IF (x£aj) OD
ai= aj
i:=j
OD
ai:=x
Величины М и С в процессе построения (L, R)-пирамиды имеют следующие оценки Mпир≤log (R/L)+2, Cпир≤2 log (R/L)
Пирамидальная сортировка производится в два этапа. Сначала строится пирамида из элементов массива. По свойству (3) правая часть массива является (n/2+1, n)-пирамидой. Будем добавлять по одному элементу слева, расширяя пирамиду, пока в неё не войдут все элементы массива. Тогда по свойству (2) первый элемент последовательности – минимальный.
Произведём двустороннее усечение: уберём элементы a1,an. По свойству (1) оставшаяся последовательность является (2, n-1)-пирамидой. Элемент a1 поставим на последнее место, а элемент an добавим к пирамиде a2,…,an-1 слева. Получим новую (1, n-1)-пирамиду. В ней первый элемент является минимальным. Поставим первый элемент пирамиды на позицию n-1, а элемент an-1 добавим к пирамиде a2,…,an-1, и т.д. В результате получим обратно отсортированный массив.
Пример. Отсортировать слово методом пирамидальной сортировки.
Алгоритм на псевдокоде
Пирамидальная сортировка
L:=ën/2û
DO (L>0)
Построение (L,n) пирамиды>
L:=L-1
OD
R:=n
DO (R>1)
a1↔aR
R:=R-1
Построение (1,R) пирамиды >
OD
Общее количество операций сравнений и пересылок для пирамидальной сортировки: C ≤ 2n log n+n+2, M ≤ n log n+6.5n-4. Таким образом, С=O(n log n), М=O(n log n) при n → ∞.
Отметим некоторые свойства пирамидальной сортировки. Метод пирамидальной сортировки неустойчив и не зависит от исходной отсортированности массива.
Метод Хоара или метод быстрой сортировки заключается в следующем. Возьмём произвольный элемент массива х. Просматривая массив слева, найдём элемент ai ≥x. Просматривая массив справа, найдём aj ≤x. Поменяем местами ai и aJ . Будем продолжать процесс просмотра и обмена, до тех пор пока i не станет больше j. Тогда массив можно разбить на две части: в левой части все элементы не больше х, в правой части массива не меньше х. Затем к каждой части массива применяется тот же алгоритм.
Пример: Отсортировать слово методом быстрой сортировки.
Условные обозначения:
ведущий элемент
сравнение с ведущим элементом при просмотре справа
сравнение с ведущим элементом при просмотре слева
| разделение массива на части
обмен элементов
индекс i
индекс j
Алгоритм на псевдокоде
Сортировка части массива с границами (L,R).
Обозначим: L-левую границу рабочей части массива
R-правую границу рабочей части массива
х:=аL, i:=L, j:=R,
DО (i£ j)
DО (ai<x) i:=i+1 OD
DО (aj>x) j:=j-1 OD
IF (i<=j)
ai↔ aj,, i:=i+1, j:=j-1
FI
OD
IF (L<j)
<Сортировка части массива с границами (L,j)>
FI
IF (i<R)
<Сортировка части массива с границами (i,R)>
FI
Очевидно, трудоёмкость метода существенно зависит от выбора элемента х, который влияет на разделение массива. Максимальные значения М и С для метода быстрой сортировки достигаются при сортировке упорядоченных массивов (в прямом и обратном порядке). Тогда в этом случае в одной части остаётся только один элемент (минимальный или максимальный), а во второй – все остальные элементы. Выражения для М и С имеют следующий вид
M=3(n-1), C=(n2+5n+4)/2
Таким образом, в случае упорядоченных массивов трудоёмкость сортировки имеет квадратичный порядок.
Элемент am называется медианой для элементов aL…aR, если количество элементов меньших am равно количеству элементов больших am с точностью до одного элемента (если количество элементов нечётно). В примере буква К- медиана для КУРАПОВАЕ.
Минимальная трудоемкость метода Хоара достигается в случае, когда на каждом шаге алгоритма в качестве ведущего элемента выбирается медиана массива. Количество сравнений в этом случае C=(n+1)log(n+1)-(n+1). Количество пересылок зависит от положения элементов, но не может быть больше одного обмена на два сравнения. Поэтому количество пересылок – величина того же порядка, что и число сравнений. Асимптотические оценки для средних значений М и С имеют следующий вид
С=О(n log n), М=О(n log n) при n → ∞.
Метод Хоара неустойчив.
В теле подпрограммы доступны все объекты, описанные в основной программе, в том числе и имя самой подпрограммы. Это позволяет внутри тела подпрограммы осуществлять её вызов. Процедуры и функции, организующие вызовы “самих себя” называются рекурсивными. Рекурсия широко используется в программирование, потому что многие математические алгоритмы имеют рекурсивную природу.
В качестве примера приведём известный алгоритм вычисления факториала неотрицательного целого числа:
0!=1
1!=1
n!=(n-1)!*n
function fact (n:word):longint;
begin
if (n=0) or (n=1) then fact:=1
else fact:=fact(n-1)*n;
end;
Рекурсивное оформление программы более компактно, наглядно и эффективно. Но существует опасность переполнения стека. Каждый вызов подпрограммы требует специально отведённой области памяти, называемой фреймом. В ней хранятся фактические параметры, адреса возврата, локальные переменные и регистры УП.
Фрейм
Практический параметр |
Адрес возврата |
Регистры из программы |
Локальные переменные |
При выходе из программы эта память освобождается. Но если подпрограмма вызывает другую подпрограмму или саму себя, то в дополнение к существующему фрейму создаётся новый, т.е. n вложенных вызовов требуют выделения n фреймов в памяти.
Рассмотренный алгоритм Хоара может потребовать n вложенных вызовов (n – размер массива), т.е. глубина рекурсии достигает n. Это большой недостаток предложенного алгоритма. Попробуем уменьшить глубину рекурсии до log n. В рассмотренном алгоритме производится 2 рекурсивных вызова. Но один из них можно заменить простой итерацией, т.е. для одной части массива будем применять рекурсию, а для другого – простую итерацию. Чтобы уменьшить глубину рекурсии нужно делать рекурсивный вызов для меньшей по размеру части массива. Тогда в худшем случае, когда размеры правой и левой частей будут одинаковые, максимальная глубина рекурсии будет не больше log n. Например, для массива из 1 млн. элементов понадобиться одновременно менее 20 фреймов в памяти. Запишем новую версию алгоритма:
Алгоритм на псевдокоде
Сортировка части массива (L,R)
DO (есть хотя бы 2 элемента, т.е. L<R)
<разделение> (как в 1 версии)
IF (левая часть длиннее правой, т.е.j-L>R-i)
Сортировка части массива (i,R)
R:=j
Else
Сортировка части массива (L,j)
L:=i;
FI
OD