Сравнительная скорость работы некоторых нижеприведенных алгоритмов
сортировки:
Примечание:
size: размер сортируемой последовательности
n: количество сортировок для замера времени
*: RadixSort в последнем тесте прогонялся при параметрах: size=21000; n=100
1. Пузырьковая сортировка (простым выбором, линейная)
type
arrType = array[1 .. n] of integer;
procedure Bubble(var ar: arrType; n: integer);
var i, j, T: Integer;
begin
for i := 1 to n do
for j := n downto i+1 Do
if ar[Pred(j)] > ar[j] then begin { < }
T := ar[Pred(j)]; ar[Pred(j)] := ar[j]; ar[j] := T
end
end;
Еще один вариант реализации "пузырька", в котором нет необходимости заранее описывать тип arrType,
последовательность передается через открытый массив:
procedure Bubble(var ar: array of integer);
var
i, j: integer;
T: integer;
begin
for i := low(ar) to high(ar) do
for j := high(ar) downto i + 1 do begin
if ar[Pred(j)] > ar[j] then begin { < }
T := ar[Pred(j)]; ar[Pred(j)] := ar[j]; ar[j] := T
end
end;
end;
procedure BubbleSort(Mas: Pointer; Len: LongWord); asm dec Len @CycleExt: xor ebx, ebx mov ecx, Len mov esi, 0 @CycleIn: mov edi, Mas[esi] cmp edi, Mas[esi+4] jg @Exchange add esi, 4 loop @CycleIn jmp @Check @Exchange: mov ebx, Mas[esi+4] mov Mas[esi+4], edi mov Mas[esi], ebx add esi, 4 loop @CycleIn @Check: cmp ebx, 0 je @Exit jmp @CycleExt @Exit: end;
2. Сортировка простой вставкой
type
arrType = array[1 .. n] of integer;
procedure Insert(var ar: arrType; n: integer);
var
i, j: integer;
T: integer;
begin
for i := 1 to n do begin
T := ar[i];
j := Pred(i);
while (j >= 1) and (T < ar[j]) do begin
ar[Succ(j)] := ar[j]; Dec(j);
end;
ar[Succ(j)] := T;
end;
end;
Вариант с открытым массивом:
procedure Insert(var ar: array of integer);
var
i, j: integer;
T: integer;
begin
for i := low(ar) to high(ar) do begin
T := ar[i];
j := Pred(i);
while (j >= low(ar)) and (T < ar[j]) do begin
ar[Succ(j)] := ar[j]; Dec(j);
end;
ar[Succ(j)] := T;
end;
end;
Сложность этого алгоритма сортировки составляет О(n2)procedure merge(var ar: array of integer);
function ix(X: integer): integer;
begin ix := Pred(X) end;
procedure Slit(k, q: Integer);
type
TArr = array[1 .. maxint div sizeof(integer)] of integer;
var
m: integer;
i, j, T: integer;
D: ^TArr;
begin
GetMem(d, (high(ar) - low(ar) + 1) * sizeof(integer));
m := k + (q - k) div 2;
i := k; j := Succ(m); T := 1;
while (i <= m) and (j <= q) do begin
if ar[ix(j)] <= ar[ix(i)] then begin
D^[T] := ar[ix(i)]; Inc(i)
end
else begin
D^[T] := ar[ix(j)]; Inc(j)
end;
Inc(T)
end;
while i <= m do begin
D^[T] := ar[ix(i)]; Inc(i); Inc(T)
end;
while j <= q do begin
D^[T] := ar[ix(j)]; Inc(j); Inc(T)
end;
for i := 1 to Pred(T) do
ar[ix(Pred(k + i))] := D^[i];
freeMem(d, (high(ar) - low(ar) + 1) * sizeof(integer));
end;
procedure Sort(i, j: integer);
var T: integer;
begin
if i >= j then exit;
if j - i = 1 then begin
if ar[ix(i)] <= ar[ix(j)] then begin
T := ar[ix(i)]; ar[ix(i)] := ar[ix(j)]; ar[ix(j)] := T;
end
end
else begin
Sort(i, i + (j - i) div 2);
Sort(i + (j - i) div 2 + 1, j);
Slit(i, j)
end;
end;
begin
Sort(low(ar) + 1, high(ar) - low(ar) + 1);
end;Это улучшенный метод, основанный на обмене. При "пузырьковой"
сортировке производятся обмены элементов в соседних позициях. При пирамидальной сортировке
такой обмен совершается между элементами в позициях, жестко связанных друг с другом бинарным
деревом. Ниже будет рассмотрен алгоритм сортировки К. Хоара, использующий несколько иной
механизм выбора значений для обменов. Этот алгоритм называется сортировкой с разделением или быстрой
сортировкой. Она основана на том факте, что для достижения наибольшей эффективности желательно
производить обмены элементов на больших расстояниях.
Предположим, что даны N элементов массива, расположенные в обратном порядке. Их
можно рассортировать, выполнив всего N/2 обменов, если сначала поменять местами
самый левый и самый правый элементы и так далее, постепенно продвигаясь с двух
сторон к середине. Это возможно только, если мы знаем, что элементы расположены
строго в обратном порядке.
Рассмотрим следующий алгоритм: выберем случайным образом какой-то элемент
массива (назовем его x). Просмотрим массив, двигаясь слева направо, пока не
найдем элемент a[i]>x (сортируем по возрастанию), а затем просмотрим массив
справа налево, пока не найдем элемент a[j]<x. Далее, поменяем местами эти два
элемента a[i] и a[j] и продолжим этот процесс "просмотра с обменом", пока два
просмотра не встретятся где-то в середине массива.
После такого просмотра массив разделится на две части: левую - с элементами
меньшими (или равными) x, и правую - с элементами большими (или равными) x.
Итак, пусть a[k] (k=1,...,N) - одномерный массив, и x - какой-либо элемент из a.
Надо разбить "a" на две непустые непересекающиеся части а1 и а2 так, чтобы в a1
оказались элементы, не превосходящие x, а в а2 - не меньшие x.
Рассмотрим пример. Пусть в массиве a: <6, 23, 17, 8, 14, 25, 6, 3, 30, 7>
зафиксирован элемент x=14. Просматриваем массив a слева направо, пока не найдем
a[i]>x. Получаем a[2]=23. Далее, просматриваем a справа налево, пока не найдем
a[j]<x. Получаем a[10]=7. Меняем местами a[2] и a[10]. Продолжая этот процесс,
придем к массиву <6, 7, 3, 8, 6> <25, 14, 17, 30, 23>, разделенному на две
требуемые части a1, a2. Последние значения индексов таковы: i=6, j=5. Элементы
a[1],....,a[i-1] меньше или равны x=14, а элементы a[j+1],...,a[n] больше или
равны x. Следовательно,разделение массива произошло. Описанный алгоритм прост и
эффективен, так как сравниваемые переменные i, j и x можно хранить во время
просмотра в быстрых регистрах процессора. Наша конечная цель - не только
провести разделение на указанные части исходного массива элементов, но и
отсортировать его. Для этого нужно применить процесс разделения к получившимся
двум частям, затем к частям частей, и так далее до тех пор, пока каждая из
частей не будет состоять из одного единственного элемента. Эти действия
описываются следующей программой. Процедура Sort реализует разделение массива на
две части, и рекурсивно обращается сама к себе...
type
arrType = array[1 .. n] of integer;
{ первый вариант : }
procedure HoarFirst(var ar: arrType; n: integer);
procedure sort(m, l: integer);
var i, j, x, w: integer;
begin
i := m; j := l;
x := ar[(m+l) div 2];
repeat
while ar[i] < x do Inc(i);
while ar[j] > x do Dec(j);
if i <= j then begin
w := ar[i]; ar[i] := ar[j]; ar[j] := w;
Inc(i); Dec(j)
end
until i > j;
if m < j then Sort(m, j);
if i < l then Sort(i, l)
end;
begin
sort(1, n)
end;
{ второй вариант : }
procedure HoarSecond(var ar: arrType; n: integer);
procedure Sort(m, l: integer);
var i, j, x, w: integer;
begin
if m >= l then exit;
i := m; j := l;
x := ar[(m+l) div 2];
while i < j do
if ar[i] < x then Inc(i)
else if ar[j] > x then Dec(j)
else begin
w := ar[i]; ar[i] := ar[j]; ar[j] := w;
end;
Sort(m, Pred(j));
Sort(Succ(i),l);
end;
begin
Sort(1, n)
end;
Сложность O(n*logn), на некоторых тестах работает быстрее сортировки слияниями,
но на некоторых специально подобранных работает за O(n2).
5. Пирамидальная - турнирная - HeapSort сортировка
Type
arrType = Array[1 .. n] Of Integer;
Procedure HeapSort(Var ar: arrType; n: Integer);
Var
i, Left, Right: integer;
x: Integer;
Procedure sift;
Var i, j: Integer;
Begin
i := Left; j := 2*i; x := ar[i];
While j <= Right Do Begin
If j < Right Then
If ar[j] < ar[Succ(j)] Then Inc(j);
If x >= ar[j] Then Break;
ar[i] := ar[j];
i := j; j := 2 * i
End;
ar[i] := x
end;
Var T: Integer;
Begin
Left := Succ(n div 2); Right := n;
While Left > 1 Do Begin
Dec(Left); sift
End;
While Right > 1 Do Begin
T := ar[ Left ]; ar[ Left ] := ar[ Right ]; ar[ Right ] := T;
Dec(Right); sift
End
End;
6. Распределяющая сортировка - RadixSort - цифровая - поразрядная
for i := 0 to Pred(255) Do distr[i]:=0; for i := 0 to Pred(n) Do distr[source[i]] := distr[[i]] + 1;Для нашего примера будем иметь distr = ( 0, 0, 0, 0, 1, 1, 0, 3, 1, 1 ), то есть i-ый элемент distr[] - количество ключей со значением i.
index: array[0 .. 255] of integer; index[0]:=0; for i := 1 to Pred(255) Do index[i]=index[i-1]+distr[i-1];В index[i] мы поместили информацию о будущем количестве символов в отсортированном массиве до символа с ключом i.
for i := 0 to Pred(n) Do Begin
sorted[ index[ source[i] ] ]:=source[i];
{ попутно изменяем index уже вставленных символов, чтобы одинаковые ключи шли один за другим: }
index[ source[i] ] := index[ source[i] ] + 1;
End;
сначала они в сортируем по младшему на один беспорядке: разряду: выше: и еще раз: 523 523 523 088 153 153 235 153 088 554 153 235 554 235 554 523 235 088 088 554Hу вот мы и отсортировали за O ( k*n ) шагов. Если количество возможных различных ключей ненамного превышает общее их число, то 'поразрядная сортировка' оказывается гораздо быстрее даже 'быстрой сортировки'!
Const
n = 8;
Type
arrType = Array[0 .. Pred(n)] Of Byte;
Const
m = 256;
a: arrType = (44, 55, 12, 42, 94, 18, 6, 67);
Procedure RadixSort(Var source, sorted: arrType);
Type indexType = Array[0 .. Pred(m)] Of Byte;
Var
distr, index: indexType;
i: integer;
begin
fillchar(distr, sizeof(distr), 0);
for i := 0 to Pred(n) do
inc(distr[source[i]]);
index[0] := 0;
for i := 1 to Pred(m) do
index[i] := index[Pred(i)] + distr[Pred(i)];
for i := 0 to Pred(n) do begin
sorted[ index[source[i]] ] := source[i];
index[source[i]] := index[source[i]]+1;
end;
end;
var b: arrType;
begin
RadixSort(a, b);
end.
7. Пузырьковая сортировка с просеиванием
const n = 10;
var
x: array[1 .. n] of integer;
i, j, t: integer;
flagsort: boolean;
procedure bubble_P;
begin
repeat
flagsort := true;
for i := 1 to n - 1 do
if not(x[i]<=x[i + 1]) then begin
t := x[i];
x[i] := x[i + 1];
x[i + 1] := t;
j := i;
while (j > 1) and not(x[j-1] < =x[j]) do begin
t := x[j]; x[j] := x[j - 1]; x[j - 1] := t;
dec(j);
end;
flagsort := false;
end;
until flagsort;
end;
Добавлено: Тестировалось на массиве целых чисел (25000 элементов). Прирост скорости относительно простой пузырьковой сортировки - около 75%...
8. Древесная сортировка (TreeSort)
Const n = 8;
Type
TType = Integer;
arrType = Array[1 .. n] Of TType;
Const
a: arrType = (44, 55, 12, 42, 94, 18, 6, 67);
(* Сортировка с помощью бинарного дерева *)
Type
PTTree = ^TTree;
TTree = Record
a: TType;
left, right: PTTree;
End;
{ Добавление очередного элемента в дерево }
Function AddToTree(root: PTTree; nValue: TType): PTTree;
Begin
(* При отсутствии преемника создать новый элемент *)
If root = nil Then Begin
root := New(PTTree);
root^.a := nValue;
root^.left := nil;
root^.right := nil;
AddToTree := root; Exit
End;
If root^.a < nValue Then
root^.right := AddToTree(root^.right, nValue)
Else
root^.left := AddToTree(root^.left, nValue);
AddToTree := root
End;
(* Заполнение массива *)
Procedure TreeToArray(root: PTTree; Var a: arrType);
Const maxTwo: Integer = 1;
Begin
(* При отсутствии преемников рекурсия остановится *)
If root = nil Then Exit;
(* Левое поддерево *)
TreeToArray(root^.left, a);
a[maxTwo] := root^.a; Inc(maxTwo);
(* Правое поддерево *)
TreeToArray(root^.right, a);
Dispose(root)
End;
(* Собственно процедура сортировки *)
Procedure SortTree(Var a: arrType; n: Integer);
Var
root: PTTree;
i: Integer;
Begin
root := nil;
For i := 1 To n Do
root := AddToTree(root, a[i]);
TreeToArray(root, a)
End;
Var i: Integer;
Begin
WriteLn('До сортировки:');
For i := 1 To n Do Write(a[i]:4);
WriteLn;
SortTree(a, n);
WriteLn('После сортировки:');
For i := 1 To n Do Write(a[i]:4);
WriteLn
End.
9. Сортировка методом поиска нового номера
type
TArr = array[1..100] of integer;
var
mass1, NewMass : TArr;
n: integer;
{
n - размерность массива, mass1 - исходный массив,
NewMass - будет состоять из отсотртированных элементов массива mass1
}
procedure NewNSort(var mass, Nmass: TArr; size: integer);
var i, j, NewN: integer;
begin
for i := 1 to size do begin
NewN := 0;
for j := 1 to size do
if (mass[j]<mass[i]) or ((mass[j]=mass[i]) and (j<=i)) then inc(NewN);
Nmass[NewN] := mass[i];
end;
end;
Вызов:
NewNSort(mass1,NewMass,n);Массив NewMass будет состоять из элементов массива mass1, но уже отсортированных.
10. Метод последовательного поиска минимумов
type
TArr = array[1 .. 100] of integer;
var
mass1: TArr;
n : integer;
procedure NextMinSearchSort(var mass: TArr; size: integer);
var i, j, Nmin, temp: integer;
begin
for i := 1 to size - 1 do begin
nmin := i;
for j := i + 1 to size do
if mass[j] < mass[Nmin] then Nmin:=j;
temp := mass[i];
mass[i] := mass[Nmin];
mass[Nmin] := temp;
end;
end;
Вызов:
NextMinSearchSort(mass1, n);