
Простое слияние (с/без внутренней сортировки)
Отличие между простым слиянием и естественным слиянием, которое рассмотрено в следующем разделе, заключается в определении серии. Впростом слиянии длина серии фиксирована. На первом шаге упорядоченные отрезки имеют длину, равную единице, то есть состоят из одного элемента. Потом они объединятся, и если слияние двухпутевое, то вновь сформированные отрезки будут состоять из двух элементов, если слияние многопутевое (количество вспомогательных файлов N), то — из N элементов. Длина серии в алгоритме простого слияния зависит от количества вспомогательных файлов, на которое идет распределение серий. Если k — это номер прохода, a N— количество вспомогательных файлов, то длина серии определяется формулой Nk. Обратим внимание на то, что на первых проходах длина серии растет медленно, например, в двухпутевой сортировке, чтобы получить серию, состоящую из 100 элементов, требуется семь проходов. Это не эффективно, и поэтому для улучшения алгоритма слияния можно использовать внутреннюю сортировку в качестве вспомогательной. В этом случае на первом проходе часть данных (фиксированное количество) переписывается в оперативную память, например, в массив, и там упорядочивается. Таким образом сразу формируется серия большой длины, и для дальнейшего слияния потребуется меньшее количество проходов. Этот способ упорядочения данных называется простым слиянием с использованием внутренней сортировки.
Признаками конца сортировки простым слиянием являются следующие условия:
□ длина серии не меньше количества элементов в файле (определяется после фазы слияния);
□ количество серий — одна (определяется на фазе слияния);
□ второй по счету вспомогательный файл при однофазной сортировке после распределения серий остался пустым.
Без внутренней: (двухфазное)
F0: 5 9 8 6 13 3 4 19 6
F1: 5 8 13 4 6 len=1
F2: 9 6 3 19
F0: 5 9 6 8 3 13 4 19 6
F1: 5 9 3 13 6
F2: 6 8 4 19 len=2
F0: 5 6 8 9 3 4 13 19 6
F1: 5 6 8 9 6
F2: 3 4 13 19 len=4
F0: 3 4 5 6 8 9 13 19 8
F1: 3 4 5 6 8 9 13 19
F2: 6
F0: 3 4 5 6 6 8 9 13 19
Прекращаем когда len>= кол-ву элементов
Алгоритм:
-определить число элементов N>0
-len=1;
-repeat
Распределение (из f0 в f1,f2)
Слияние (из f1,f2 в f0)
Len=len*2;
Until len>=N
Распределение (f0,f1,f2,len)
-открыть файл(fo-чтение, f1,f2-запись)
-пока не конец f0
Пишем серию в f1
Если не конец f0
То пишем серию в f2
-закрыть файл
Слияние (f0,f1,f2,len)
-открыть файл (f1,f2-чтение, f0-запись)
-пока не конец f1 и не конец f2
Открыть серии
Пока не конец серии в f1 и в f2
Если элемент из f1<элемента из f2
То копируем из f1 в f0 и берем из f1
еще один элемент
иначе -\\- из f2
пока не конец серии в f1
копируем из него и берем следующий
пока не конец серии в f2
-\\ f2
Если не конец f1
Скопировать из f1 в f0
-закрыть файл
Type
TElem=integer;
TFile=file of TElem;
TSequence=class
Private
F:TFile;
FElem:TElem;
FEof:Boolean; //все ли записали
FCount:integer; //сколько осталось
FFileName:string;
Constructor Create(AFileName:string)
Begin
FFileName:=AFileName;
AssignFile(F,FFileName);
FEof:=true;
FCount:=0;
End;
Function SizeOf:integer;
Begin
Reset(f);
Result:=system.FileSize(F);
CloseFile(f);
End;
Procedure StartRead(len:integer);
Begin
Reset(f);
FEof:=system.Eof(f);
FCount:=len;
If not FEof then read (f,FElem);
End;
Procedure StartWrite
Rewrite (f);
Procedure CloseFile
System.CloseFile(f);
Procedure StartRun(len:integer);
FCount:=len;
Procedure Copy (y:TSequence);
Begin
Write(y.F,FElem);
FEof:=system.eof(f);
Dec(FCount)
If not FEof then read(f,FElem);
End;
Procedure CopyRun (y:TSequence);
Repeat Copy(y) until eor;
Function GetEor:Boolean;
Result:=Eof or (FCount=0)
Procedure Distribute
(f0,f1,f2:Tsequance;len:integer);
Begin
Fo.StertRead(len);
F1\f2.StartWrite;
While not f0 eof do begin
Fo.CopyRun(f1); f0.StartRun(len);
If not f0 eof then begin
Fo.copyRun(f2); fo.StartRun(len); end; end
F0\f1\f2 . CloseFile;
End;
Procedure Merge (f0,f1,f2:TSequence;len:int);
Begin
F0.StartWrite;
F1\f2. StartRead(len);
While (not f1.eof) and (not f2.eof) do begin
F1\f2. StartRun(len);
While (not f1.eof)and(not f2.eor) do
If f1.elem<f2.elem then f1.copy(f0)
Else f2.copy(f0)
If not f1.eor then f1.CopyRun(f0)
Else f2.copyRun(f0); end;
If not f1.eof then begin
F1.StartRun(len); f2.CopyRun(f0); end;
F1\f2\f0. CloseFile;
End;
Procedure Sort (FileName:string);
Var f0,f1,f2:TSequance; n,len:integer;
Begin
F0.TSequance.Create(filename)
N:=f0.SizeOf;
If N>1 then begin
F1:=TSequance.Create(‘a.tmp’);
F2:= -\\- (‘b.tmp’);
Len:=1;
Repeat
Distribute(f0,f1,f2,len);
Merge(f0,f1,f2,len);
Len:=len*2;
Until len>=N
F1.Erase; f1.Destroy;
--\\--- (f2); end;
F0.Destroy;
End;
Однофазное
Procedure MergeRun (f1,f2,f3:TSecuance;
Len:integer);
Begin
F1\f2. StartRun(len);
While (not f1.eor)and(not f2.eor) do
If f1.Elem<=f2.Elem then f1.copy(f3);
Else f2.copy (f3);
If not f1.eor then f1.copyrun(f3)
Else f2.copyrun(f3);
End;
Procedure Merge (f1,f2,f3,f4:TSequance;
Var len:integer);
Begin
F1\f2. startRead(len);
F3\f4.startWrite;
While(not f1.eof)or(not f2.eof) do begin
MergeRun(f1,f2,f3,len);
If not (f1.eof)or(not f2.eof) then
MergeRun(f1,f2,f3,len); end;
F1\f2\f3\f4. CloseFile;
Len:=len*2;
End;
Procedure Sort (filename:string);
Var f0,f1,f2,f3,f4 :TSequance;
N,len:ineteger; ok:Boolean;
Begin
F0:=TSequance.Create(filename);
N:=f0.SizeOf;
If N>1 then begin
F1\f2\f3\f4:= TSequance.Create(имя);
Len:=1; ok:=true;
Distribute (f0,f1,f2,len);
Repeat
Merge(f1,f2,f3,f4,len);
Ok:=not ok;
If len<N then begin
Merge(f3,f4,f1,f2,len);
Ok:=not ok; end;
Until len>=N
If ok then begin
F3.rename(filename); f3.erase; end;
Else begin
F3.rename(filename);
F1.erase; end;
F1\f2\f3\f4.free; end;
Fo.free;
End;