Сортировка методом слияний
Существует еще один метод сортировки элементов массива, эффективность которого сравнительно велика, - метод слияний.
Этот метод состоит в разбиении данного массива на несколько частей, которые сортируются по отдельности и впоследствии “сливаются” в одну.
Пусть массив а [1...n ] разбивается на части длиной k, тогда первая часть - а [ 1 ], а [ 2 ], ...., а [ k ], вторая - а [ k +1 ], а [ k + 2 ], ...., а [ 2k ] и так далее. Если n не делится на k, то в последней части будет менее k элементов.
После того как массивы - части упорядочены, можно объединить их в упорядоченные массивы - части, состоящие не более чем из 2 k элементов, которые далее объединить в упорядоченные массивы длиной не более 4 k, и так далее, пока не получится один упорядоченный массив.
Таким образом, чтобы получить отсортированный массив этим методом, нужно многократно “сливать” два упорядоченных отрезка массива в один упорядоченный отрезок. При этом другие части массива не затрагиваются.
Задача
Дан массив а [ 1...k ] целых чисел и числа k, m, n, такие, что k ≤ m ≤ n. Описать процедуру, которая сливает отрезки массива а [ k + 1 ], ..., а [ m ] и а [ m + 1 ], ,...., а [ n ] в упорядоченный отрезок а [ k + 1 ], ..., а [ n ].
Основная идея решения состоит в сравнении очередных элементов каждой части, выяснении, какой из них меньше, переносе его в массив с, который является вспомогательным, и продвижении по тому массиву - части, из которого взят элемент. Когда одна из частей будет пройдена до конца, остается только перенести в с оставшиеся элементы второй части, сохраняя их порядок.
Рассмотрим пример.
Пусть первая часть ( часть а ) состоит из 5 элементов:
... 3 5 8 11 16 ...,
а вторая часть ( часть b ) - из 8 элементов:
... 1 5 7 9 12 13 18 20 ...
По условию оба массива - части упорядочены. Нужно сформировать массив с, который будет содержать 13 элементов.
Введем обозначения:
i - номер обрабатываемого элемента части а;
j - номер обрабатываемого элемента части b;
g - номер заполняемого массива части с.
Поскольку заполнение массива с ведется с его начала, на первом шаге значение g =1.
1 -й шаг: на первое место в массиве с претендуют а [k + 1] = 3 и
b [ m + 1 ] = 1 ( i = k + 1, j = m + 1 ). Так как 1 < 3, в массив с нужно занести 1 и перейти к следующему элементу части b, то есть увеличить j на 1 (значение j станет равно m + 2 ), а также увеличить на 1 значение g ( значение g будет равно 2 ).
2 -й шаг: теперь нужно сравнить а [ k + 1 ] = 3 и b [ m + 2 ] = 5 . 3 < 5, следовательно, на второе место в с надо занести а [ k + 1 ] = 3. Затем нужно увеличить на одно значение i и g.
3 -й шаг: на третье место массива с претендуют два одинаковых элемента - а [ k + 2 ] = 5 и b [ m + 2 ] = 5. В этом и подобных случаях договоримся заносить в с первым элемент из части а. Таким образом, с [ 3 ] = а [ k + 2 ], а значение
g = 4, i = k + 3, j остается равным m + 2.
4-11 - й шаги: рассуждая аналогичным образом, нужно занести в с элементы 5, 7, 8, 9, 11, 12, 13, 16.
После выполнения 11 - го шага первая часть будет занесена в с полностью, значение g равно m + 7, значение g = 12.
12 - й шаг: так как часть а закончилась, а “хвост” части b упорядочен по условию, то двенадцатым элементом массива с будет первый элемент “хвоста” b, то есть b [ m + 7 ] = 18.
13 -й шаг: последним элементом массива с будет последний элемент b – 20.
На рисунке схематично изображен процесс заполнения массива c.
часть a
... 3 5 8 11 16 ...
шаг 2 шаг 3 шаг 6 шаг 8 шаг 11
3>6 5=5 8<9 11<12 16<19
C
1 |
шаг 1 шаг 4 шаг 5 шаг 7 шаг 9 шаг 10 шаг 12
1<3 5<8 7<8 9<11 12<16 13<16
... часть b
Сейчас можем описать процедуру слияния двух упорядоченных массивов размерностей m-k и n-m соответственно в третий упорядоченный массив, размерности n-k.
Procedure sorting4(k,m,n:integer);
var i,j:integer;
begin
i:=k+1;
j:=m+1;
k:=1;
while (i<=m) and (j<=n) do
{пока не закончилась хотя бы одна часть}
begin
if a[i]<=b[j] then
begin c[k]:=a[i]; inc(i); end
else begin c[k]:=b[j]; inc(j); end;
inc(k);
end;
{один из массивов - частей обработан полностью}
{осталось перенести в с остаток другого массива - части}
while i<=m do
begin c[k]:=a[i]; inc(i); inc(k); end;
while j<=n do
begin c[k]:=b[j]; inc(j); inc(k); end;
end;
Приведем пример программы, использующий эту процедуру в некоторой переработке ( вместо a[k+1], ..., a[m] используем a[1..k], а вместо a[m+1], ..., a[n] используем b[1..m], где k+m = n ). Эта программа делит исходный массив на две части, затем каждая часть сортируется методом “пузырьковой” сортировки, после чего части соединяются с помощью данной процедуры в искомый массив. Очевидно, скорость такого метода выше, чем просто использование “пузырьковой ” сортировки.
program n4;
const n=5;
type ar=array[1..n] of integer;
var k,m,i:integer;
a,b,c:ar;
Procedure sorting4;
var s,i,j:integer;
begin
i:=1;
j:=1;
s:=1;
while (i<=k) and (j<=m) do
{пока не закончилась хотя бы одна часть}
begin
if a[i]<=b[i] then
begin c[s]:=a[i]; inc(i); end
else begin c[s]:=b[j]; inc(j); end;
inc(s);
end;
{один из массивов - частей обработан полностью}
{осталость перенести в с остаток другого массива - части}
while i<=k do
begin c[s]:=a[i]; inc(i); inc(s); end;
while j<=m do
begin c[s]:=b[j]; inc(j); inc(s); end;
end;
procedure sorting2(var p:ar;len:integer);
var f,i,t:integer;
{f - номер просмотра (изменяется от 1 до n-1)
i - номер рассматриваемой пары
t - промежуточная переменная для перестановки местами элементов}
begin
for f:=1 to len-1 do
{цикл по номеру просмотра}
for i:=1 to len-f do
if p[i]>p[i+1] then
{перестановка элементов}
begin
t:=p[i];
p[i]:=p[i+1];
p[i+1]:=t;
end;
end;
begin
k:=n div 2;
m:=n-k;
writeln('Введите исходный массив:');
for i:=1 to n do read(c[i]);
for i:=1 to k do a[i]:=c[i];
for i:=1 to m do b[i]:=c[k+i];
sorting2(a,k);
sorting2(b,m);
sorting4;
writeln('Отсортированный массив:');
for i:=1 to n do write(c[i],' ');
writeln;
end.