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

Сортировка методом слияний

Существует еще один метод сортировки элементов массива, эффективность которого сравнительно велика, - метод слияний.

Исторически метод слияния является одним из первых методов, предложенных для сортировки на ЭВМ еще в 1954 году, автором его является Джон фон Нейман.

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

Пусть массив а [1..n] разбивается на части длиной k, тогда первая часть - a[1], a[2], ..., a[k], вторая- a[k+l], a[k+2],..., a[2k] и так далее. Если n не делится на k, то в последней части будет менее k элементов.

После того как массивы-части упорядочены, можно объединить их в упорядоченные массивы-части, состоящие не более чем из 2k элементов, которые далее объединить в упорядоченные массивы длиной не более 4k,и так далее, пока не получится один упорядоченный массив.

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

В более узком смысле слияние означает объединение двух или более упорядоченных массивов в один упорядоченный массив. Например, слияние двух массивов – 5 8 9 и 1 3 4 10 дает массив 1 3 4 5 8 9 10.

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

Рассмотрим этот метод на примере указанных двух массивов (5, 8, 9) и (1, 3, 4, 5, 8, 9, 10).

5 8 9

1 3 4 10

выбираем 1

5 8 9

3 4 10

выбираем 3

5 8 9

4 10

выбираем 4

5 8 9

10

выбираем 5

8 9

10

выбираем 8

9

10

выбираем 9

пустой массив

10

выбираем 10

Слияние намного более простая процедура, чем сортировка, так как общий объем работы по существу пропорционален количеству элементов m+n, но при этом используется дополнительный массив.

Алгоритм двухпутевого слияния реализован в следующей программе для отсортированных массивов x[1..m], y[1..n] с результирующим массивом z[1..n+m]. Небольшое упрощение введено добавлением в конец исходных файлов искусственных стражей xm+1=yn+1=.

Procedure Merge(x:vectorm;y:vectorn; var z: vectornm);

Var i,j,k: integer;

Begin

i:=1; j:=1;

x[m+1]:=Maxint; y[n+1]:=Maxint;

for k:=1 to n+m do

if x[i]<=y[j] then begin z[k]:=x[i]; inc(i) end

else begin z[k]:=y[j]; inc(j) end;

end;

Простое слияние

Алгоритм слияния, предложенный фон Нейманом, упорядочивает пары соседних чисел. Затем упорядоченные пары сливаются в упорядоченные четверки, восьмерки и т.д. Для хранения упорядоченных серий используется вспомогательный массив Y.

X 1 7 3 6 4 2 8 5

Y 1 7 3 6 2 4 5 8

X 1 3 6 7 2 4 5 8

Y 1 2 3 4 5 6 7 8

Этот алгоритм называется иногда вверх-направленным слиянием, учитывая направление сливаемых пар.

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

X 1 7 3 6 4 2 8 5

Y 1 5 2 3 6 4 8 7

X 1 5 7 8 6 4 3 2

Y 1 2 3 4 5 6 7 8

Чтобы не запутаться в двух виртуальных массивах, будем использовать один массив двойной длины array[1..2*n]. При этом сначала исходный массив находится в первой половине, в выходной – во второй, а затем наоборот.

Заведем булевскую переменную Up, которая определяет направление слияния: true – из первой половины во вторую, false – из второй половины в первую. Индексы i, j - отвечают за место, из которого берутся элементы для слияния, а k, l – за место куда пересылаются элементы. Переменная P определяет длину сливаемых серий (1, 2, 4, …).

Procedure merge(var x: vector);

Var up: boolean;

i, j, k, l: integer;

P: integer;

Begin

Up:=true;

p:=1;

Repeat

if up then

Begin

i:=1;j:=n; k:=n+1; l:=2*n;

end

else

Begin

i:=n+1; j:=2*n; k:=1; l:=n

end;

{слияние p-серии из i, j в к, l}

Up:=not up;

P:=2*p

Until p>=n;

End;

Слияние происходит следующим образом: сначала пересылка идет по адресу k, при этом индекс увеличивается на единицу, затем по адресу l, и индекс уменьшается на 1. Для упрощения будем считать, что пересылка всегда идет по адресу k с шагом h, но после каждого слияния будем чередовать h (либо +1, либо -1), а также менять местами значения k и l.

h:=1; m:=n;

Repeat

q:=p; r:=p; m:=m-2*p;

{слияние q элементов из i и r элементов из j}

h:=-h;

{обмен k, l};

Until m=0;

Сама процедура слияния должна предусмотреть копирование оставшегося остатка:

While (q<>0) and(r<>0) do

if x[i]<x[j]

then begin x[k]:=x[i]; k:=k+h; i:=i+1; q:=q-1 end

Else begin x[k]:=x[j]; k:=k+h; j:=j-1; r:=r-1 end;

{копирование остатков из i или j}

Окончательный текст программы

Procedure merge(var x: vector);

Var up: boolean;

I, j, k, l, t: integer;

P, h, q, r, m: integer;

Begin

Up:=true;

p:=1;

Repeat

m:=n; {общее число элементов во

входных последовательностях,

которые осталось слить}

H:=1;

if up

then

Begin

i:=1;j:=n; k:=n+1; l:=2*n;

end

else

Begin

i:=n+1; j:=2*n; k:=1; l:=n

end;

Repeat

if m>= p then q:=p

else q:=m; m:=m-q;

if m>= p then r:=p

else r:=m; m:=m-r;

While (q<>0) and(r<>0) do

if x[i]<x[j]

then

begin

x[k]:=x[i]; k:=k+h; i:=i+1; q:=q-1

end

else

begin

x[k]:=x[j]; k:=k+h;

j:=j-1; r:=r-1

end;

{копирование остатков}

While r<>0 do

Begin

A[k]:=a[j]; k:=k+h;

j:=j-1; r:=r-1

End;

While q<>0 do

Begin

A[k]:=a[i]; k:=k+h;

i:=i+1; q:=q-1

End;

H:=-h; T:=k; k:=l; l:=t;

Until m=0;

Up:=not up;

P:=2*p

Until p>=n;

if not up then

for i:=1 to n do x[i]:=x[i+n]

End;

Поскольку на каждом проходе p удваивается, то сортировка требует [log2N] проходов. При каждом проходе перемещаются все N элементов. Поэтому характеристика M для сортировки слиянием оценивается как N*[log2N].

Число сравнений C меньше, чем М, так как при копировании остатков сравнения не проводятся.