Порівняльний аналіз дії алгоритмів на тестовому прикладі
На малюнку показаний вихідний граф з позначеними ребрами(ліворуч) і його остовне дерево мінімальної вартості(праворуч).
Проілюструємо процес вирішення завдання за допомогою алгоритму Прима:
Послідовність вирішення завдання за допомогою алгоритму Крускала:
Тут ребра вартістю 1, 2, 3 і 4 розглянуті першими й усі включені в P, оскільки їхнє додавання не приводить до циклів. Ребра (1, 4) і (3, 4) вартістю 5 не можна включити в P, тому що вони з'єднують вершини одного й того ж компонента й тому замикають цикл. Але ребро, що залишилося (2, 3) також вартості 5 не створює цикл. Після включення цього ребра в P формування остовного дерева завершується.
Час виконання алгоритму Прима має порядок О(п2), де п — кількість вершин у даному графі(дивитеся пункт «Програмна реалізація алгоритмів»). Якщо значення п досить велике, то використання цього алгоритму не раціонально. Алгоритм Крускала виконується за час порядку О(е log2e), де е — кількість ребер у даному графі. Якщо е значно менше n2, то алгоритм Крускала переважніше, але якщо е близько до n2, рекомендується застосовувати алгоритм Прима.
Програмна реалізація алгоритмів
Алгоритм Прима
Якщо ввести два масиви, то можна порівняно просто організувати на кожному кроці алгоритму вибір ребра з найменшою вартістю, що з'єднує множини U і V\U. Масив CLOSEST[i] для кожної вершини i з множини V\U містить вершину з U, з якої він з'єднаний ребром мінімальної вартості (це ребро вибирається серед ребер, інцедентних вершині i, і які ведуть у множину U). Інший масив LOWCOST[i] зберігає значення вартості ребра (i, CLOSEST[i]).
На кожному кроці алгоритму проглядається масив LOWCOST, знаходимо мінімальне значення LOWCOST[k]. Вершина k множині V\U і з'єднана ребром з вершиною з множини U. Потім друкуємо ребро (k, CLOSEST[k]). Тому що вершина k приєднується до множини U, те внаслідок цього змінюються масиви LOWCOST і CLOSEST.
Після знаходження чергової вершини k остовного дерева LOWCOST[k] покладемо рівним infinity (нескінченність), дуже великому числу, такому, щоб ця вершина надалі не розглядалася. Значення числа infinity повинне бути більше вартості будь-якого ребра графа.
На вхід програми надходить масив З розміру n n, чиї елементи C[i, j] рівні вартості ребер (i, j). Якщо ребра (i, j) не існують, то елемент C[i, j] покладається рівним деякому досить великому числу.
Фрагмент програми мовою Паскаль, що реалізує алгоритм Прима:
procedure Prim ( З: array[l..n, l..n] of integer );
{ Prim друкує ребра остовного дерева мінімальної вартості графа з вершинами {1, ..., n} і матрицею вартості З}
var
LOWCOST: array[l..n] of integer;
CLOSEST: array[l..n] of integer;
i,j,k,min:integer;
{ i і j - індекси. При перегляді масиву LOWCOST, k - номер знайденої вершини, min = LONCOST[k] }
begin
{1} for i:= 2 to n do begin
{ спочатку в U тільки вершина 1 }
{2} LOWCOST[i]:= C[l,i];
CLOSEST[i]:=1;
{3} end;
{4} for i:= 2 to n do begin
{ пошук поза U найкращої вершини k, що має інцедентне ребро, що кінчається в U}
{5} min:= LOWCOST[2] ;
{6} k:= 2;
{7} for j:= 3 to n do
{8} if LOWCOST[j] < min then begin
{9} min: = LOWCOST[j] ;
{10}k:= j
end;
{11} writeln(k, CLOSEST[k]); { друк ребра }
{12} LOWCOST[k]:= infinity; (k додається в U )
{13} for j:= 2 to n do {коректування вартостей в U }
{14} if (C[k, j] < LOWCOST[j]) and (LOWCOST[j] < infinity) then begin {15}LOWCOST[j]:= C[k, j];
{16}CLOSEST[j]:= k
end
end
end;
Час виконання алгоритму має порядок О(п2), оскільки виконується п-1 ітерація зовнішнього циклу рядків (4) -(16), а кожна ітерація цього циклу вимагає часу порядку О(п) для виконання внутрішніх циклів у рядках (7) -(10) і (13) -(16).
Алгоритм Крускала
Його програмна реалізація близько відповідає відповідній алгоритму-схемі й опису, наведеному в розділі «Алгоритм Крускала», але щоб уникнути повторення ідентифікаторів перепозначимо в програмі ребро (і, v) на (в, v), і, отже, вектор Mark[u] на Mark[v].
Програма, що реалізує алгоритм:
Program Cruscal;
Uses wincrt;
Const
nVer=50; {Максимальна кількість вершин}
nRib=1000; (Максимальна кількість ребер}
Турі
Vertex=array[1..nVer] of Integer;
Rib=array[1..nRib] of Integer;
Var
n :Integer; { Кількість вершин у графі }
n:Integer; { Кількість ребер у графі }
Mark :Ver; { Мітки приналежності вершин }
U:Ver; { Список вершин графа }
E:Rib; { Реберний список графа }
nEo:Integer; { Кількість ребер в остовному дереві}
Eo:Rib; { Ребра остовного дерева }
We:Rib; { Ваги ребер графа }
i,j:integer;
f:text;
Procedure Init; { Перепризначення міток вершин }
Var
i,j,m :Integer;
begin
for i:=l to 2*n do Eo[i]:=l;
for i:=l to 2*n do
for j:=i+l to 2*n do
if Eo[j]=l then
if E[j]=E[i] then Eo[j]:=0;
n:=0;
for i:=l to 2*n do
if Eo[i]=l then begin
n:=n+l;
U[n]:=E[i];
end;
for i:=l to 2*n do {Нові мітки}
for m:=l to n do
if E[i]=U[m] then begin E[i]:=m; break;
end;
end;
Procedure Sort; { Сортування списку ребер no їхнім вагам }
var
i,j,k :Integer;
w:Integer;
begin
for i:=l to n do
for j:=l to nE-i do
if We[j]>We[j+l] then begin
w:=We[j];
We[j]:=We[j+1] ;
We[j+l]:=w;
w:=E[2*j-l] ;
E[2*j-l]:=E[2*(j +1)-1];
E[2*(j+1)-1]:=w;
w:=E[2*j];
E[2*j]:=E[2*(j+1)];
E[2*( j+1)]:=w;
end;
end;
Procedure Ostov; { Будуємо мінімальне остовне дерево }
Var
i,y,v,z :Integer;
s:Integer;
begin
for i:=l to n do Mark[i]:=i;
Sort; {Сортування ребер по вазі}
nEo:=0; {Порожня б Eo}
s:=l; {Початкове ребро в сортованій E}
while nEo<nU-l do begin
y:=E[2*s]; {Вибір нового ребра зі списку}
v:=E[2*sE-l] ;
if Mark[y]<>Mark [v] then begin
nEo:=nEo+l;
z:=Mark[y]; {Злиття}
for i:=l to n do
if Mark[i]=z then
Mark[i] :=Mark[y] ;
Write(U[y],', ');
Write(U[v]);
writeln;
end;
s:=s+l; {Видалити ребро (y,v) зі списку E}
end;
end;
begin
Assign(f,'з:/graf.txt');
Reset (f) ; {Файл відкритий для читання}
Read(f,n); {Кількість ребер у реберному списку графа}
for i:=l to n do Read (f ,E[2*i-l] ) ; { Перші вершини ребер }
for i:=l to n do Read (f , E [2*i] ) ; { Другі вершини ребер}
for i:=l to n do Read (f , We [i] ) ; { Ваги ребер }
Close (f) ;
Init;
Sort;
Writeln('Min ostov:');
Ostov;
end.
Для програми цього алгоритму вихідні дані графа на задаються реберним списком у текстовому файлі graf.txt з наступною структурою:
у першому рядку файлу записується кількість ребер у списку;
у другому й третьому рядках вказуються ребра своїми вершинами: одна вершина в другому рядку, інша вершина ребра в третьому рядку;
у четвертому рядку розташовуються значення ваг відповідних ребер.
Алгоритм Крускала виконується за час порядку О(е log2e), де е — кількість ребер у даному графі.