« poprzedni punkt | następny punkt » |
W tym punkcie wrócimy na chwilę do
znanych nam już algorytmów
przeglądania grafu metodą "wszerz" BFS i "w głąb" DFS, por. wykład VII
p1 . Wykorzystamy, je najpierw do zbadania, czy dany graf
niezorientowany jest spójny, a następnie do budowy drzewa rozpinającego
grafu.
Szkic algorytmu BFS
1.Włóż do kolejki q wybrany wierzchołek grafu i zamarkuj go.
2. Dopóki kolejka q nie jest pusta :
Wez pierwszy
element kolejki q.
Dopisz
do kolejki wszystkie wierzchołki z nim incydentne, o ile nie były
jeszcze zamarkowane i
każdy z nich
zamarkuj.
Usuń pierwszy
element kolejki q.
Idea przeglądania grafu metodą DFS "w głąb" polega na rekurencyjnym
odwiedzaniu wierzchołków coraz bardziej odległych od wierzchołka
początkowego, wzdłuż jednej ścieżki, tak daleko jak to jest możliwe.
Jeśli kontynuacja nie jest możliwa, wracamy do poprzedniego
wierzchołka, wybieramy jakiś nieodwiedzony wierzchołek z jego listy
incydencji i powtarzamy postępowanie.
Szkic algorytmu DFS
1.Zaznacz wybrany wierzchołek jako odwiedzony.
2. Dla każdego wierzchołka z listy incydencji wybranego wierzchołka:
Jeśli wierzchołek nie był jeszcze zamarkowany, to
zastosuj rekurencyjnie procedurę odwiedzania do tego wierzchołka.
Przykład 2.1
Koszt
Koszt postępowania w obu algorytmach jest podobny. Dla każdego
wierzchołka grafu przeglądamy jego listę incydencji. W przypadku BFS,
każdy wierzchołek może być co najwyżej raz wpisany do kolejki, bo po
wpisaniu markujemy go jako odwiedzony. W przypadku metody "w głąb",
procedurę DFS wywołuje się rekurencyjnie co najwyżej raz dla każdego
wierzchołka, gdyż wywołuje się ją tylko dla wierzchołków
niezamarkowanych, a każde wywołanie DFS powoduje zamarkowanie
wierzchołka będącego argumentem procedury. Pętle w obu algorytmach są
zatem skończone. Ponieważ, w najgorszym razie, przeglądamy
wszystkie krawędzie grafu., zatem koszt przeglądania można
w obu przypadkach oszacować przez O(|V| + |E|), gdzie V jest zbiorem
wierzchołków, a E zbiorem krawędzi grafu.
Zwróćmy uwgę, że postępownie opisane w
metodach BFS i DFS nie musi koniecznie spowodować
odwiedzenia wszystkich wierzchołków grafu. Jeżeli w danym
grafie są
wierzchołki, nie połączone żadną drogą, to zaczynając odwiedzanie
grafu od jednego z nich, nigdy nie dojdziemy do drugiego.
Odwrotnie,
jeżeli przechodzenie grafu metodą BFS lub DFS nie spowoduje odwiedzenia
wszystkich wierzchołków grafu, to tylko dlatego, że między jakimiś
wierzchołkami grafu nie istnieje droga, tzn. są takie
wierzchołki, że
jeden z nich nie jest osiągalny z drugiego. Wynika stąd, że algorytmy
BFSi DFS można użytć do badania spójności grafu: jeśli po
zakończeniu
algorytmu wszystkie wierzchołki były odwiedzone, to graf jest spójny,
jeśli istnieją jeszcze nieodwiedzone wierzchołki, to graf nie jest
spójny.
DFS |
(v : integer){ |
|
m[v] := true; x := Tab[v]; |
||
while
(not x = null) do |
//przeglądamy listę incydencji
węzła v |
|
w :=
x.val; |
||
if (m[w] = false) then DFS(w) fi |
// o ile w nie był odwiedzony |
|
x := x.next; |
//wywołujemy rekurencyjnie DFS |
|
od; |
||
result := true; |
// badamy, czy wszystkie
wierzchołki |
|
for
i := 1 to n do result :=m[i] and result; od; |
//były zamarkowane |
|
} |
Algorytm BFS - Badanie spójności
grafu
BFS |
(v : integer){ |
|
q := in(v,q) ; |
||
while
(not empty(q)) do |
||
v :=
first(q); q := out(q); |
||
x := Tab[v]; | ||
while (not x= null) do |
//przeglądamy listę incydencji węzła v | |
w := x.val; |
||
if
(m[w] = false) then |
// o ile w nie był odwiedzony |
|
q := in(w,q); m[w]:= true | //wpisujemy go do kolejki i
markujemy |
|
fi; |
||
x := x.next; |
||
od; |
||
od; |
||
result := true; |
// badamy, czy wszystkie
wierzchołki |
|
for
i := 1 to n do result :=m[i] and result; od; |
//były zamarkowane |
|
} |
Załóżmy teraz, że graf G jest spójny i
zmodyfikujmy algorytmy przeglądania w taki sposób, by wypisywały
krawędzie, którymi dochodzi się do kolejno odwiedzanych wierzchołków.
Zbiór odwiedzonych krawędzi tworzy drzewo, gdyż nie ma w nim cykli:
funkcje BFS i DFS powodują odwiedzanie tylko nie odwiedzonych jeszcze
wierzchołków. Drzewa uzyskane w ten sposób, nazywamy drzewami
przeszukiwania "wszerz" i "w głąb" odpowiednio. Ponieważ, w przypadku,
gdy graf jest spójny, zostaną odwiedzone wszystkie wierzchołki grafu,
zatem drzewa przeszukiwań są drzewami rozpinającymi grafu. Algorytmy
przeglądania DFS i BFS można więc wykorzystać do konstruowania drzewa
rozpinającego grafu.
Algorytm DFS - Konstrukcja drzewa
rozpinającego
DFS_ST |
(v : integer){ |
|
m[v] := true; x := Tab[v]; |
//Niezmiennik pętli: |
|
while
(not x = null) do |
//Zbiór wypisanych krawędzi
tworzy drzewo |
|
w := x.val; |
||
if (m[w] = false) then |
||
m[w] := true; |
||
wypisz krawędź (v,w); |
||
DFS(w); |
||
fi; |
||
x := x.next; |
||
od; |
||
} |
Algorytm BFS - Konstrukcja drzewa
rozpinającego grafu
BFS_ST |
(v : integer){ |
|
q := in(v,q) ; |
||
while
(not empty(q)) do |
// Niezmiennik: |
|
v :=
first(q); q := out(q); |
// zbiór wypisanych krawędzi
tworzy drzewo |
|
x := Tab[v]; | ||
while (not x= null) do |
|
|
w := x.val; |
||
if
(m[w] = false) then fi |
|
|
q := in(w,q); m[w]:= true | ||
wypisz krawędź (v,w); |
||
fi; |
||
x := x.next; |
||
od; |
||
od; |
||
} |
Przykład 2.2
Na rysunku 12.3 przedstawiono wynik działania procedury BFS_ST, a na
rysunku 12.4 wynik działania procedury DFS_ST: dwa drzewa rozpinające
grafu z rysunku 12.2 .
Pytanie 3: Jaki jest koszt drzewa rozpinającego niezorientowanego grafu spójnego o n wierzchołkach i m krawędziach, jeśli funkcja kosztu tego grafu przyjmuje stale wartość const?
« poprzedni punkt | następny punkt » |