Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lektsia20110211 (1).doc
Скачиваний:
0
Добавлен:
23.09.2019
Размер:
100.86 Кб
Скачать

Лекция 11 февраля 2011 г.

Дополнительно о сортировках

Далее будет рассмотрено несколько видов сортировок, которые работают статистически быстрее всех ранее изученных. Слово «статистически» означает, что быстрота наблюдается в большинстве случаев заполнения массивов случайными значениями. Однако в некоторых, редких, т.н. вырожденных случаях, быстрые сортировки могут утратить свою скорость. Примером вырождения можно считать расположение элементов массива в порядке, обратном требуемому.

В последующих текстах предполагается, что сортируемый массив объявлен в виде

mA: array[1..2*nMax] of single;

где nMax – константа.

Элементы, требующие упорядочения (по возрастанию) занимают места с 1-го по nMax-е. Следующие nMax мест зарезервированы под временное хранение данных.

Быстрая сортировка

procedure RecursiveQuickSort;

procedure InnerSort(L, R: longint);

var

i, j: longint;

w, x: single;

begin

i:=L; j:=R;

x:=mA[(L+R) div 2];

repeat

while mA[i]<x do i:=i+1;

Inc(nC);

while x<mA[j] do j:=j-1;

if i<=j then

begin

w:=mA[i]; mA[i]:=mA[j]; mA[j]:=w;

Inc(i); Dec(j);

end

until i>j;

if L<j then InnerSort(L, j);

if i<R then InnerSort(i, R);

end; // InnerSort

begin

tStart:=Time;

InnerSort(1, n);

tFinish:=Time;

tElapsed:=(tFinish-tStart)*86400.0;

end;

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

Идея сортировки состоит в следующем.

Сортируемый массив из элементов делится пополам (ясно, что это имеет смысл при ). Каждая из его половин сортируется, затем две отсортированные половины «сливаются», образуя отсортированное целое.

Фрагмент процедуры, выполняющей такую сортировку, может выглядеть так:

i:=1; // Место, с которого начинается левая половина

q := (n+1) div 2;

j:=q+1; // Место, с которого начинается правая половина

k:=n; // Место, после которого временно помещается «слитый массив»

СортировкаЛевойПоловины;

СортировкаПравойПоловины;

while (i<=q) and (j<=n) do

// Цикл выполняется, пока обе половины ещё не иссякли

begin

Inc(k);

if mA[i]<mA[j] then

begin mA[k]:=mA[i]; Inc(i); end

else

begin mA[k]:=mA[j]; Inc(j); end;

end;

while i<=q do

// Цикл выполняется, пока левая половина не иссякла

// (правая половина исчерпана)

begin

Inc(k); mA[k]:=mA[i]; Inc(i);

end;

while j<=r do

// Цикл выполняется, пока правая половина не иссякла

// (левая половина исчерпана)

begin

Inc(k); mA[k]:=mA[j]; Inc(j);

end;

for i:=1 to n do

// Цикл выполняется для переброски массива из временного места в постоянное

mA[i]:=mA[i+n];

end;

Ясно, что вызов процедур

СортировкаЛевойПоловины;

СортировкаПравойПоловины;

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

Ниже приведен полный текст процедуры MergeSort – процедуры сортировки слиянием.

procedure MergeSort;

procedure Merge(p, q, r: longint);

var

i, j, k: longint;

begin

i:=p;

j:=q+1;

k:=n;

while (i<=q) and (j<=r) do

begin

Inc(k);

if mA[i]<mA[j] then

begin

mA[k]:=mA[i];

Inc(i);

end

else

begin

mA[k]:=mA[j];

Inc(j);

end;

Inc(nM);

end;

while i<=q do

begin

Inc(k);

mA[k]:=mA[i];

Inc(i);

end;

while j<=r do

begin

Inc(k);

mA[k]:=mA[j];

Inc(j);

end;

k:=n;

for i:=p to r do

begin

Inc(k);

mA[i]:=mA[k];

end;

end;

procedure MergeSortAux(p, r: longint);

var

q: longint;

begin

if p>=r then exit;

q:=(p+r) div 2;

MergeSortAux(p, q);

MergeSortAux(q+1, r);

Merge(p, q, r);

end;

begin

MergeSortAux(1, n);

end;

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

Идея сортировки состоит в следующем.

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

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

Даже если массив не упорядочивался частично, вероятность, что все отсортированные подмассивы состоят из одного элемента, достаточно мала.

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

Пример.

Исходный массив:

3 5 16 18 1 13 17 2 9 11 4 12 7 10 15

Поиск упорядоченных подмассивов и их слияние (по два)

Ниже приведен полный текст процедуры NaturalMergeSort – процедуры сортировки слиянием.

procedure NaturalMergeSort;

var

i, nB: longint;

mB: array[0..2] of longint;

bBound, bSorted: boolean;

procedure Merge(p, q, r: longint);

var

i, j, k: longint;

begin

i:=p;

j:=q+1;

k:=n;

while (i<=q) and (j<=r) do

begin

Inc(k);

Inc(nC);

if mA[i]<mA[j] then

begin

mA[k]:=mA[i];

Inc(i);

end

else

begin

mA[k]:=mA[j];

Inc(j);

end;

Inc(nM);

end;

while i<=q do

begin

Inc(k);

mA[k]:=mA[i];

Inc(nM);

Inc(i);

end;

while j<=r do

begin

Inc(k);

mA[k]:=mA[j];

Inc(nM);

Inc(j);

end;

k:=n;

for i:=p to r do

begin

Inc(k);

mA[i]:=mA[k];

end;

end;

begin

while True do

begin

nB:=0;

mB[0]:=1;

bSorted:=True;

for i:=1 to n do

begin

bBound:=False;

if i=n then

bBound:=True

else

if mA[i]>mA[i+1] then

begin

bBound:=True;

bSorted:=False;

end;

if bBound then

begin

Inc(nB);

mB[nB]:=i;

end;

if nB=2 then

begin

Merge(mB[0], mB[1], mB[2]);

nB:=0;

mB[0]:=mB[2];

end;

end;

if bSorted then Break;

end;

end;

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

Ниже приведен текст процедуры MergeSortInFile, проводящей сортировку в файле с именем FileOfSingleName. Процедура похожа на процедуру MergeSort, некоторые из отличий пояснены комментариями (синим цветом).

procedure MergeSortInFile;

const

FileOfSingleName='p0.dat';

FileOfSingleNameAux='p0.tmp';

var

F0, F1, F2: file of single;

procedure Merge(p, q, r: longint);

var

i, j, k: longint;

mAi, mAj: single;

begin

Reset(F1);

Reset(F2);

Reset(F0);

i:=p;

j:=q+1;

// k:=n;

Seek(F1, i-1);

Read(F1, mAi);

Seek(F2, j-1);

Read(F2, mAj);

while (i<=q) and (j<=r) do

begin

// k:=k+1;

if mAi<mAj then

begin

Write(F0, mAi);

// mA[k]:=mA[i];

Inc(i);

if i<=q then Read(F1, mAi);

end

else

begin

Write(F0, mAj);

// mA[k]:=mA[j];

Inc(j);

if j<=r then Read(F2, mAj);

end;

end;

while i<=q do

begin

// k:=k+1;

// mA[k]:=mA[i];

Write(F0, mAi);

Inc(i);

if i<=q then Read(F1, mAi);

end;

while j<=r do

begin

// k:=k+1;

// mA[k]:=mA[j];

Write(F0, mAj);

Inc(j);

if j<=r then Read(F2, mAj);

end;

Close(F0);

Close(F2);

Close(F1);

Reset(F0);

Reset(F1);

i:=p;

Seek(F1, i-1);

// k:=n;

for i:=p to r do

begin

// k:=k+1;

// mA[i]:=mA[k];

Read(F0, mAi);

Write(F1, mAi);

end;

Close(F1);

Close(F0);

end;

procedure MergeSortAux(p, r: longint);

var

q: longint;

begin

if p>=r then exit;

q:=(p+r) div 2;

MergeSortAux(p, q);

MergeSortAux(q+1, r);

Merge(p, q, r);

end;

begin

Assign(F1, FileOfSingleName);

Assign(F2, FileOfSingleName);

Assign(F0, FileOfSingleNameAux);

MergeSortAux(1, n);

end;

Файлы без типа (напоминание)

Var

F: File;

Процедуры и функции для работы с файлами без типа

procedure Assign(var F: File);

procedure Close (var F: File);

procedure Seek(var F: File, RecordNumber: Longint);

procedure Truncate(var F: File);

procedure Read(var F: File; <Список ввода>);

procedure Write(var F: File; <Список вывода>);

function Eof(var F: File): boolean;

Отличия:

procedure Reset(var F: File [, RecordSize: Longint]);

procedure Rewrite(var F: File [, RecordSize: Longint]);

Если параметр RecordSize указан, он задаёт длину записи (в байтах).

Если он не указан, считается, что RecordSize=128. Вот так.

Новые процедуры:

procedure BlockRead(var F: File; var Buffer; Count: Integer

[; var AmtTransferred: Integer]);

Параметр Buffer – имя любой переменной (например, имя большого массива), в которую будут читаться данные из файла.

Параметр Count – количество записей, которое следует прочесть из файла. Таким образом, будет предпринята попытка прочесть Count* RecordSize байт из файла. Если в переменную Buffer такое количество не вместится (т.е. будет испорчена память за переменной Buffer), ответственность за тяжкие последствия несёт программист.

Параметр AmtTransferred, если он присутствует, показывает, сколько в действительности записей удалось прочитать. AmtTransferred < Count в том случае, если… . Впрочем, сообразите сами.

procedure BlockWrite(var F: File; var Buffer; Count: Integer

[; var AmtTransferred: Integer]);

Параметр Buffer – имя любой переменной, из которой будут записываться данные в файл.

С прочими параметрами, надеюсь, всё ясно.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]