Студопедия.Орг Главная | Случайная страница | Контакты | Мы поможем в написании вашей работы!  
 

Сортировка простым слиянием



Рассмотрим простой метод, когда не обращая внимания на содержимое заданного исходного файла, его рассматривают как состоящий из серий длиной 1. Разделяя этот файл на два и производя их слияние, получают упорядоченные пары, т.е. серии длиной 2. На следующих шагах упорядоченные пары сливаются в упорядоченные четвёрки, четвёрки сливаются в восьмёрки и т.д. Весь процесс повторяется до тех пор, пока не будет упорядочен весь файл. Таким образом на каждом проходе длина серии удваивается, причём если длина исходного файла Н не является степенью 2, то последняя серия будет иметь число элементов равное Н-2^к. В данном случае не требуется выполнения первого этапа сортировки, т.е. внутренней сортировки для формирования начальных серий. Такой метод называется простым слиянием.

Как было показано выше, простым слиянием состоит из двух фаз: фазы разделения серий по двум файлам и фазы слияния упорядоченных серий. В соответствии с этим напишем две процедуры: разделения и слияния.

Исходные данные представим следующим образом.

type item=record

key:integer;

{описание других полей}

end;

filetype=file of item;

var a,b,c:filetype;

l:integer;

Исходные данные разместим в файле С, в него же будем записывать результаты слияний серий. В файл А и В будем распределять серии из файла С.

Пусть М-длина серии (М=1,2,4,8,……). Тогда распределение серий длиною М по файлам А и В можно выполнить следующим образом:

begin

reset(c);rewrite(a);rewrite(b);

repeat

{переписать очередную серию из файла С в файл А}

{переписать следующую серию из файла С в файл В}

until eof(c);

close(a);close(b);close(c);

end;

Посмотрим как можно переписать серию. Для этого необходимо последовательно переписывать записи до тех пор, пока не кончится либо серия, либо файл С. Поскольку серии имеют фиксированную длину, определить конец серии не представляет труда, для этого достаточно организовать счётчик. Тогда переписать серию из файла С в А можно следующим образом.

k:=0; {счётчик}

ok:=eof(c);

while not ok do

begin

read(c,buf);write(a,buf);

k:=k+1;

ok:=eof(c) or (k=l);

end;

Аналогично переписывается серия из С в В. запишем окончательно процедуру распределения серий из С в А и В.

{процедура распределения серий}

procedure Distribute;

var buf:item;

k:integer;

ok:boolean;

begin

reset(c);rewrite(a);rewrite(b);

repeat

k:=0;ok:=eof(c);

while not ok do

begin

read(c,buf);write(a,buf);

k:=k+1;ok:=eof(c) or (k=l);

end;

k:=0;ok:=eof(c);

while not ok do

begin

read(c,buf);write(b,buf);

k:=k+1;ok:=eof(c) or (k=l);

end;

until eof(c);

close(a);close(b);close(c);

end;

Теперь рассмотрим процедуру слияния серий фиксированной длины М из файлов А и В в файл С. Вспомним алгоритм слияния. Читаем начальные элементы очередной серии из файлов А и В, сравниваем их и записываем не больший элемент в С, а на его место читаем следующий элемент из соответствующего файла. И так до тех пор, пока не закончится серия в каком-нибудь из файлов.

Попробуем записать алгоритм следующим образом:

read(a,bufa);read(bufb);k1:=0;k2:=0;

while (k1<l) and (k2<l) do {повторять, пока не закончится серия}

if bufa.key<bufb.key

then

begin;write(c,bufa);read(a,bufa);k1:=k1+1;end

else

begin;write(c,bufb);read(b,bufb);k2:=k2+1;end

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

Во первых, когда записывается в файл С последний элемент серии (К1 или К2 равны М), не надо читать следующий элемент файла, т.к. он относится уже к другой серии.

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

В третьих, когда мы записываем в файл С последний элемент очередной серии, например, из файла А, в буфер файла В (bufb) остаётся элемент, который необходимо переписать в файл С.

С учётом вышесказанного, алгоритм запишем следующим образом:

ok:=eof(a) or eof(b);

if not ok

then

begin;read(a,bufa);read(b,bufb);end;

k1:=0;k2:=0;

while not ok do {повторять, пока не закончится}

begin

if bufa.key<bufb.key

then

begin

write(c,bufa);

k1:=k1+1;

ok:=(k1=l)or eof(a);

if not ok then read(a,bufa)

else begin

write(c,bufb);

k2:=k2+1;

end;

end

else

begin

write(c,bufb);

k2:=k2+1;

ok:=(k2=l)or eof(b);

if not ok then read(b,bufb)

else begin

write(c,bufa);

k1:=k1+1;

end;

end;

end;

После того, как полностью переписана серия какого-либо файла, необходимо переписать в файл С остаток серии из другого файла.

переписать остаток серии файла А}

while (k1<l) and not eof(a) do

begin

read(a,bufa);write(c,bufa);k1:=k1+1;

end;

{переписать остаток серии файла В}

while (k2<l) and not eof(b) do

begin

read(b,bufb);write(c,bufb);k2:=k2+1;

end;

Окончательно процедура слияния серий длиною М из файла А и В в файл С будем иметь следующий вед.

{процедура слияния серий длиной М}

procedure Merge;

var bufa,bufb:item;

k1,k2:integer;

ok:boolean;

begin

reset(a);reset(b);rewrite(c);

repeat {повторять, пока не закончатся оба файла А и В}

z:=z+1; {подсчитываем число полученных серий}

ok:=eof(a) or eof(b);

if not ok then begin;read(a,bufa);read(b,bufb);end;

k1:=0;k2:=0;

while not ok do {повторять, пока не закончится серия или файл}

begin

if bufa.key<bufb.key

then

begin

write(c,bufa);

k1:=k1+1;

ok:=(k1=l)or eof(a);

if not ok then read(a,bufa)

else begin

write(c,bufb);

k2:=k2+1;

end;

end

else

begin

write(c,bufb);

k2:=k2+1;

ok:=(k2=l)or eof(b);

if not ok then read(b,bufb)

else begin

write(c,bufa);

k1:=k1+1;

end;

end;

end;

{переписать остаток серии файла А}

while (k1<l) and not eof(a) do

begin

read(a,bufa);write(c,bufa);k1:=k1+1;

end;

{переписать остаток серии файла В}

while (k2<l) and not eof(b) do

begin

read(b,bufb);write(c,bufb);k2:=k2+1;

end;

until eof(a) and eof(b);

close(a);close(b);close(c);

end;

Здесь Z-глобальная переменная, которая необходима для определения конца процесса сортировки. Сортировка заканчивается, когда на очередном проходе мы получим одну серию (Z=1).

Теперь мы можем написать программу внешней сортировки методом простого слияния.

Программа использует уже готовый файл содержащий не отсортированные данные и не распечатывает отсортированный файл (программа только сортирует файл file1).

programma primemerge;

type item=record

key:integer;

{описание других полей}

end;

filetype=file of item;

var a,b,c:filetype;

l,z:integer;

procedure Distribute;

var buf:item;

k:integer;

ok:boolean;

begin

reset(c);rewrite(a);rewrite(b);

repeat

k:=0;ok:=eof(c);

while not ok do

begin

read(c,buf);write(a,buf);

k:=k+1;ok:=eof(c) or (k=l);

end;

k:=0;ok:=eof(c);

while not ok do

begin

read(c,buf);write(b,buf);

k:=k+1;ok:=eof(c) or (k=l);

end;

until eof(c);

close(a);close(b);close(c);

end; {distribute}

procedure Merge;

var bufa,bufb:item;

k1,k2:integer;

ok:boolean;

begin

reset(a);reset(b);rewrite(c);

repeat {повторять, пока не закончатся оба файла А и В}

z:=z+1; {подсчитываем число полученных серий}

ok:=eof(a) or eof(b);

if not ok then begin;read(a,bufa);read(b,bufb);end;

k1:=0;k2:=0;

while not ok do {повторять, пока не закончится серия или файл}

begin

if bufa.key<bufb.key

then

begin

write(c,bufa);

k1:=k1+1;

ok:=(k1=l)or eof(a);

if not ok then read(a,bufa)

else begin

write(c,bufb);

k2:=k2+1;

end;

end

else

begin

write(c,bufb);

k2:=k2+1;

ok:=(k2=l)or eof(b);

if not ok then read(b,bufb)

else begin

write(c,bufa);

k1:=k1+1;

end;

end;

end;

{переписать остаток серии файла А}

while (k1<l) and not eof(a) do

begin

read(a,bufa);write(c,bufa);k1:=k1+1;

end;

{переписать остаток серии файла В}

while (k2<l) and not eof(b) do

begin

read(b,bufb);write(c,bufb);k2:=k2+1;

end;

until eof(a) and eof(b);

close(a);close(b);close(c);

end; {merge}

begin {main}

assign(a,'{имя внешнего файла}');

assign(b,'{имя внешнего файла}');

assign(c,'{имя внешнего файла}');

l:=1;

repeat {повторять, пока не получим одну серию}

distribute;z:=0;

merge;l:=(2*l);

until z=1;

end.

Результат работы:

0 3 86 20 27 67 32 16 37 43 8 47 7 84 6 29 92 37 77 33 70 84 72 31 16 33 47 25 83 28 48 15 87 29 77 98 49 89 83 2 14 1 4 50 2 59 1 77 65 77 71 56 21 68 59 96 64 100 24 68 30 9 77 50 88 51 57 95 68 34 1 71 99 77 75 20 14 91 78 59 86 69 29 9 63 28 88 16 27 54 96 17 16 27 18 58 50 29 16 61 74 Нажмите Enter для продолжения!   0 1 1 2 2 3 6 7 8 9 9 14 14 14 15 16 16 16 16 16 17 18 20 20 21 24 25 27 27 27 28 28 29 29 29 29 30 31 32 33 33 34 37 37 43 47 47 48 49 50 50 50 51 54 56 57 58 59 59 59 61 63 64 65 67 68 68 68 69 70 71 71 72 74 75 77 77 77 77 77 77 78 83 83 84 84 86 86 87 88 88 89 91 92 95 96 96 98 99 100 Нажмите Enter для продолжения!

В качестве примера в таблице показан файл С в исходном состояние и после каждого прохода. В таблице показаны только значения ключей, причём серии разделены апострофом.

Заметим, что требуется четыре прохода.

Пример сортировки простым слиянием.

Исходный файл

С: 44551242941806671517 14 151907

Первый проход (М=1)

А: 44129406151419

В: 55421867 1715 07

С: 44 5512 4218 9406 6715 1714 1507 19

Второй проход (М=2)

А: 44 5518 9415 1707 19

В: 12 4206 6714 15

С: 12 42 44 5506 18 67 9414 15 15 1707 19

Третий проход (М=4)

А: 12 42 44 5514 15 15 17

В: 06 18 67 9407 19

С: 06 12 18 42 44 55 67 9407 14 15 15 17 19

Четвёртый проход (М=8)

А: 06 12 18 42 44 55 67 94

В: 07 14 15 15 17 19

С: 06 07 12 14 15 15 17 18 19 42 44 55 67 94

Более совершенным методом сбалансированного слияния является сортировка естественным слиянием.





Дата публикования: 2015-02-22; Прочитано: 986 | Нарушение авторского права страницы | Мы поможем в написании вашей работы!



studopedia.org - Студопедия.Орг - 2014-2024 год. Студопедия не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования (0.021 с)...