→ R Ef:

Transkrypt

→ R Ef:
Temat: Minimalne drzewo rozpinające - algorytm Kruskala.
Struktury danych dla problemu Union-Find.
1. Minimalne drzewo rozpinające
WP: Graf spójny, nieskierowany i z wagami G = V , E , f
( V-zbiór wierzchołków, E- zbiór krawędzi, f - funkcja wag,
f : E → R+ )
WK: Acykliczny podzbiór T ⊆ E , który łączy wszystkie
wierzchołki grafu i którego łączna waga
f (T ) =
f (u , v )
∑
( )
u ,v ∈T
jest najmniejsza.
Przykład
b
c
8
d
9
4
2
14
11
a
7
i
7
e
4
10
6
8
h
g
1
2
f
1
2. Algorytm Kruskala
Operacje realizowane na zbiorze wierzchołków V:
MAKE_SETS(R) - operacja inicjalizacji polegająca na utworzeniu
jednoelementowych zbiorów złożonych z
pojedynczych wierzchołków grafu. Powstaje
n - elementowa rodzina jednoelementowych
podzbiorów, którą oznaczymy przez R.
FIND_SET(v) - operacja ustalenia zbioru (w rodzinie podzbiorów R),
do którego należy wierzchołek v.
UNION(A, B)- operacja sumowania zbiorów A i B (z rodziny R).
Zakładamy, że podzbiory A i B są rozłączne.
Algorytm Kruskala
MDR_Kruskal(G)
begin
T:=∅;
MAKE_SETS(R);
"Utwórz listę L wszystkich krawędzie zbioru E posortowaną
niemalejąco względem wag f";
(*) while ("lista L jest niepusta")
{
" usuń pierwszą krawędź listy L- krawędź usunięta: {u,v}";
A:=FIND_SET(u);
B:=FIND_SET(v);
if (A ≠ B)
{
T=T ∪{{u, v}};
UNION(A, B);
}
}
2
Przykład
Rodzina podzbiorów R po wykonaniu procedury MAKE_SETS(R);
R={{a}, {b}, {c}, {d}, {e}, {f}, {g}, {h}, {i}}
Lista krawędzi L posortowana niemalejąco według wag f.
L:
f:
{g, h} 1
{c, i} 2
{f, g} 2
{a, b} 4
{c, f} 4
{g, i} 6
{c, d} 7
{h, i} 7
{a, h} 8
{b, c} 8
{d, e} 9
{e, f} 10
{b, h} 11
{d, f} 14
Podzbiór T oraz rodzina podzbiorów R po każdym kroku pętli (*):
po I kroku:
R={{a}, {b}, {c}, {d}, {e}, {f}, {g, h}, {i}}
T={{g, h}}
po II kroku:
R={{a}, {b}, {c, i}, {d}, {e}, {f}, {g, h}}
T={{g, h}, {c, i}}
po III kroku:
R={{a}, {b}, {c, i}, {d}, {e}, {f, g, h}}
T={{g, h}, {c, i}, {f, g}}
po IV kroku:
R={{a, b}, {c, i}, {d}, {e}, {f, g, h}}
T={{a, b},{g, h}, {c, i}, {f, g}}
3
po V i VI kroku:
R={{a, b}, {d}, {e}, { c, i, f, g, h}}
T={{a, b},{g, h}, {c, i}, {f, g}, {c, f}}
po VII i VIII kroku:
R={{a, b}, {e}, { c, d, i, f, g, h}}
T={{a, b},{g, h}, {c, i}, {f, g}, {c, f}, {c, d}}
po IX i X kroku:
R={{a, b, c, d ,i, f, g, h}, {e}}
T={{a, b},{g, h}, {c, i}, {f, g}, {c, f}, {c, d}, {a, h}}
po XI, XII, XIII i XIV (ostatnim) kroku:
R={{a, b, c, d , i, f, g, h, e}}
T={{a, b}, {a, h}, {g, h}, {c, i}, {f, g}, {c, f}, {c, d}, {d, e}}
Złożoność czasowa algorytmu Kruskala zależy od sposobu
implementacji struktury danych dla zbiorów rozłącznych rodziny R,
na której realizowane są operacje FIND_SET i UNION w pętli (*).
Omówimy dwie struktury danych dla problemu UNION-FIND:
• implementację listową oraz
• implementację drzewiastą z kompresją ścieżek.
3. Implementacja listowa
Zakładamy, że każdy wierzchołek grafu jest reprezentowany numerem
od 1 do n.
W implementacji listowej:
• Elementy tego samego zbioru znajdują się na jednej liści.
Pod wskaźnikami listy zapamiętane są struktury z polami:
- nr (numer wierzchołka),
- next (adres kolejnego elementu listy),
- zbior (adres na zbiór, do którego należy element)
4
• Adresy elementów umieszczone są w tablicy jednowymiarowej
n – elementowej R
• Zbiorem jest wskaźnik na strukturę z polami:
- licz (liczba elementów zbioru)
- pocz (adres na pierwszy element zbioru)
Inicjalizacja: MAKE_SETS(R)
Powstaje n elementowa rodzina zbiorów R zawierających po jednym
elemencie od 1 do n
Przykład
n=6
pocz licz
1
1
1
1
1
1
nr zbior next
1
2
tablica wskaźników
3
R:
4
1
2
3
5
4
5
6
6
Operacja FIND_SET(v)
Zwracany jest adres zbioru, do którego należy element v.
FIND_SET(v)
FIND_SET=R[v]->zbior;
5
Operacja UNION(A, B)
UNION(A, B)
if (A->licz > B->licznik)
„zamień zbiór A ze zbiorem B”;
x=A->pocz;
while (x->next)
{
x->zbior=B;
x=x->next;
}
x->zbior=B;
x->next=B->pocz;
B->pocz=A->pocz;
B->licz=A->licz+B->licz;
„usuń zbiór A”;
}
Przykład
n=6
Rodzina zbiorów przed operacją Union(A, B)
A
B
2
1
3
2
1
3
R:
5
4
1
2
3
4
5
6
6
6
Rodzina zbiorów po operacji Union(A, B)
B
5
1
2
3
1
5
4
1 2 3 4
Złożoność czasowa algorytmu Kruskala
5
6
6
Rozmiar zadania: n - liczba wierzchołków grafu,
m - liczba krawędzi grafu
Koszt operacji MAKE_SETS: O(n)
Koszt utworzenia posortowanej listy krawędzi L: O(nlogn)
Koszt operacji FIND_SET zrealizowanych w pętli (*): O(m)
Koszt operacji UNION zrealizowanych w pętli (*): O(nlogn)
Uwaga ! Koszt pojedynczej operacji UNION, w przypadku
pesymistycznym jest O(n), ale koszt n-1 operacji UNION w pętli (*)
jest O(nlogn), a nie O(n2). Liczymy bowiem, ile razy dany element
może mieć zmieniane dowiązanie do zbioru (zmiana adresu zbior).
Za każdym razem element przechodzi do listy o rozmiarze co
najmniej dwukrotnie większym. Zmian takich może być co najwyżej
logn.
Wniosek
Złożoność czasowa algorytmu Kruskala zrealizowanego w strukturze
listowej jest O(m + nlogn)
7
4. Implementacja drzewiasta z kompresją ścieżek
W implementacji drzewiastej z kompresją ścieżek:
• Elementy tego samego zbioru znajdują się w jednym drzewie.
Pod wskaźnikami węzłów drzewa zapamiętane są struktury z
polami:
- nr (numer wierzchołka),
- w_gore (adres na „wyższy”węzeł drzewa),
- licz ( liczba elementów w zbiorze)
• Ten sam typ wskaźnika, który reprezentuje element zbioru służy
też do reprezentowania zbiorów rodziny.
• Adresy elementów umieszczone są w tablicy jednowymiarowej
n – elementowej R.
Operacja MAKE_SETS(R)
Tworzymy n elementowy las jednowęzłowych drzew, których adresy
zapamiętujemy w tablicy R. Każdy z węzłów ma pole licz ustawione
na wartości 1, pole w_ gore na adresie pustym, a pole nr na numerze
wierzchołka grafu.
Przykład
n=6
11
21
31
R 1
41
2
3
4
51
5
61
6
8
Operacja UNION(A,B)
UNION(A, B)
if (A->licz > B->licz)
“zamień zbiór A ze zbiorem B”;
A->w_gore= B;
B->licz= A->licz + B->licz;
Przykład
n=6
Rodzina podzbiorów (z pominięciem tablicy R) przed wykonaniem
operacji UNION(A, B)
A
B
52
14
2
3
4
6
Rodzina podzbiorów (z pominięciem tablicy R) po wykonaniu
operacji UNION(A, B)
B
A
16
2
5
3
4
6
9
Operacja FIND_SET(v)
FIND_SET(v)
x= R[v];
if (x->w_gore!=NULL) FIND_SET= x;
else
{
while (x->w_gore) do x=x->w_gore;
FIND_SET= x;
y=R[v];
while (y!=x)
{
temp=y->w_gore;
y->w_gore=x;
y= temp;
}
}
Przykład
x
y
R
v
10
Tw. Hopcrofta -Ullmana:
Koszt czasowy realizacji operacji MAKE_SETS a następnie n-1
operacji UNION i m operacji FIND_SET w pętli (*),
w algorytmie Kruskala wynosi O((m+n)log*n), gdzie
0 dla n = 0, 1
log* n = 
 i takie, że F (i ) < n ≤ F (i + 1) dla n > 1
 0 dla i = 0
F (i ) =  F (i −1)
dla i > 0
2
log* 0 = log* 1 = 0
log* 16 = 2, ponieważ F (2) < 16 ≤ F(3)
F (3) = 2
F (2 )
=2
2 F (1 )
F (2 ) = 2 F (1) = 22
F (0 )
=2
22
2F (0 )
= 24 = 16
= 21 = 2
11