Podstawowe algorytmy grafowe i ich zastosowania dr Andrzej Mróz (UMK w Toruniu) 2013 Projekt wspóªnansowany ze ±rodków Unii Europejskiej w ramach Europejskiego Funduszu Spoªecznego Projekt pn. Wzmocnienie potencjaªu dydaktycznego UMK w Toruniu w dziedzinach matematyczno-przyrodniczych Poddziaªanie.1.1 Programu Operacyjnego Kapitaª Ludzki
Spis tre±ci 2
1 Wst p 3 1.1 Przeszukiwanie grafu Przeszukiwanie (przegl danie) grafu = systematyczne przechodzenie wzdªu» jego kraw dzi w celu odwiedzenia wszystkich wierzchoªków. Sªu»y m.in. do zbierania informacji o strukturze grafu. Algorytmy grafowe cz sto zaczyna si od przeszukania wej±ciowego grafu. Wiele bardziej zaawansowanych algorytmów grafowych jest modykacj podstawowych algorytmów przeszukiwania. Dwie metody przeszukiwania grafu: w gª b (ang. depth-rst search, DFS), wszerz (ang. breadth-rst search, BFS). W obu metodach b dziemy iterowa po s siadach danego wierzchoªka. Dlatego najwygodniejsz reprezentacj grafu s listy s siedztwa. Oznaczenia: Adj = tablica list s siedztwa; dla u V, Adj[u] = lista s siadów u w grae G. Uwaga. Iteracj po s siadach danego wierzchoªka mo»na ªatwo zrealizowa równie» na macierzy s siedztwa. Jest to jednak bardziej kosztowne w sensie zªo»ono±ci obliczeniowej. 2 DFS 2.1 Przeszukiwanie grafu w gª b Ustalmy graf niezorientowany G = (V, E). Z ustalonego wierzchoªka ¹ródªowego w si gamy coraz gª biej w graf, je»eli jest to tylko mo»liwe. badamy wszystkie niezbadane dot d kraw dzie ostatnio odwiedzonego wierzchoªka v, gdy wszystkie kraw dzie v s zbadane, wracamy do wierzchoªka, z którego v zostaª odwiedzony, proces kontynuujemy dopóki wszystkie wierzchoªki osi galne z wierzchoªka ¹ródªowego w nie zostan odwiedzone. Je»eli po powy»szym procesie pozostanie jakikolwiek nie odwiedzony wierzchoªek u, kontynuujemy przeszukiwanie traktuj c go jako nowy wierzchoªek ¹ródªowy. Caªy proces powtarzamy, a» wszystkie wierzchoªki w grae zostan odwiedzone. Nale»y zatem zapami tywa stany, w jakich znajduj si w danej chwili wierzchoªki. Do zapisania aktualnego stanu wierzchoªka u»ywamy jednego z trzech kolorów: biaªy wszystkie wierzchoªki na pocz tku; wierzchoªek odwiedzany po raz pierwszy kolorujemy na szaro; wierzchoªek przetworzony (= lista jego s siadów jest caªkowicie zbadana) kolorujemy na czarno.
Uwaga. Tak naprawd w najprostszej implementacji wystarcz dwa stany: nieodwiedzony i odwiedzony. Jednak dokªadniejsze rozró»nienie stanów pozwala lepiej zrozumie ide DFS oraz jest wykorzystywane w niektórych zastosowaniach. DFS-Visit(G, u) 2 kolor(u) := szary; 3 for ka»dy v Adj[u] do if kolor(v) = biaªy then DFS-Visit(G, v); 5 kolor(u) := czarny; end; Przebieg algorytmu dla u = 1 5 5 5 5 5
5 5 5 5 5 5 5 5
5 5 5 5 5 5 Zauwa»my,»e po wywoªaniu DFS-Visit dla wierzchoªka u wierzchoªki, które nie s osi galne z u pozostan nieodwiedzone (biaªe). Aby zatem przejrze caªy graf, nale»y wywoªywa DFS- Visit dopóki b d biaªe wierzchoªki. Peªen przebieg algorytmu DFS jest realizowany przez poni»sz procedur :
7 DFS(G) 2 for ka»dy u V(G) do 3 kolor(u) := biaªy; for ka»dy u V(G) do 5 if kolor(u) = biaªy then DFS-Visit(G, u); 7 end; Zªo»ono± czasowa: O( V + E ): odwiedzamy ka»dy wierzchoªek i (dwukrotnie!) ka»d kraw d¹. przechowywanie kolorów, przechowywanie wierz- Zªo»ono± pami ciowa: O( V ): choªków na stosie rekurencji. 3 Zastosowania DFS 3.1 Badanie spójno±ci Podstawowe zastosowania: Sprawdzanie spójno±ci grafu. Wyznaczanie skªadowych spójno±ci grafu. Zauwa»my,»e w procedurze DFS wywoªujemy DFS-Visit dokªadnie tyle razy, ile jest skªadowych spójno±ci w grae G. W szczególno±ci, gdy graf jest spójny, DFS-Visit zostanie wywoªana dokªadnie raz przez procedur DFS. Nietrudno uzupeªni procedur DFS o kod zliczaj cy skªadowe spójno±ci, jak równie» przypisuj cy wierzchoªkom numer skªadowej. 3.2 DFS - inne zastosowania Inne zastosowania: Wyznaczanie silnie spójnych skªadowych (w wersji dla grafu skierowanego). Sortowanie topologiczne grafu zorientowanego (bez zorientowanych cykli). Generowanie labiryntów. Znajdowanie drogi w labiryncie. Zainteresowanych odsyªamy do literatury (patrz te» dodatkowe materiaªy do wykªadu i zaj laboratoryjnych). BFS.1 Przeszukiwanie grafu wszerz Z ustalonego wierzchoªka ¹ródªowego s przegl damy kolejne wierzchoªki z niego osi galne. wierzchoªki w odlegªo±ci (=najmniejszej liczbie kraw dzi) k od ¹ródªa s odwiedzane przed wierzchoªkami w odlegªo±ci k + 1,
granica mi dzy wierzchoªkami odwiedzonymi i nieodwiedzonymi jest przekraczana jednocze±nie na caªej jej szeroko±ci. Je»eli po powy»szym procesie pozostanie jakikolwiek nie odwiedzony wierzchoªek u, kontynuujemy przeszukiwanie traktuj c go jako nowy wierzchoªek ¹ródªowy. Caªy proces powtarzamy, a» wszystkie wierzchoªki w grae zostan odwiedzone. Ka»dy wierzchoªek posiada jeden z 3 kolorów: biaªy, szary lub czarny (podobnie jak w DFS). Na pocz tku wszystkie wierzchoªki s biaªe. Wierzchoªki szare s przechowywane w kolejce FIFO. Po jej opuszczeniu kolorujemy je na czarno. 8.2 Kolejka FIFO Kolejka FIFO Q jest dynamiczn struktur danych w formie ci gu, do której mo»na doª czy skªadnik tylko w jednym ko«cu (na ko«cu kolejki - tail), a usun tylko w drugim ko«cu (na pocz tku kolejki - head). Mamy zatem dwie podstawowe operacje: Enqueue(Q, v) = dodanie elementu v na ko«cu kolejki, Dequeue(Q) = usuni cie elementu z pocz tku kolejki. Dodatkowo wykorzystamy operacj Head(Q) = zwrócenie elementu z pocz tku kolejki (bez usuwania go). Uwaga. Kolejk implementujemy przy u»yciu struktur wska¹nikowych. Mo»na te» zasymulowa jej dziaªanie na zwykªej (statycznej) tablicy, albo wykorzysta gotowe struktury biblioteczne (np. queue biblioteki STL w C++)..3 Algorytm BFS-Visit(G, s) 2 kolor(s) := szary; 3 Q := {s}; while Q <> do begin 5 u := Head(Q); for ka»dy v Adj[u] do 7 if kolor(v) = biaªy then begin 8 kolor(v) := szary; 9 Enqueue(Q, v) 10 end; 11 Dequeue(Q); 12 kolor(u) := czarny 13 end 1 end Przebieg algorytmu dla s = 1 5 Q:
5 Q: 1 9 5 Q: 1 2 5 Q: 1 2 5 5 Q: 1 2 5 5 Q: 2 5 5 Q: 5 5 Q: 5 3 5 Q: 5 3 5 Q: 3 5 Q: 3 5 Q:
5 Q: Zauwa»my,»e po wywoªaniu BFS-Visit dla wierzchoªka u wierzchoªki, które nie s osi galne z u pozostan nieodwiedzone (biaªe). Aby zatem przejrze caªy graf, nale»y wywoªywa BFS- Visit dopóki b d biaªe wierzchoªki. Peªen przebieg algorytmu BFS jest realizowany przez poni»sz procedur : BFS(G) 2 for ka»dy u V(G) do 3 kolor(u) := biaªy; for ka»dy u V(G) do 5 if kolor(u) = biaªy then BFS-Visit(G, u); 7 end; Zªo»ono± czasowa: O( V + E ) (analogicznie jak dla DFS). Zªo»ono± pami ciowa: O( V ) (przechowywanie kolorów, kolejka). 5 Zastosowania BFS 5.1 Najkrótsza droga Zastosowanie: znajdowanie najkrótszej drogi pomi dzy wierzchoªkami s i v. Modykacja procedury BFS-Visit: przechowywanie wektora poprzedników π. Tj. poprzednikiem wierzchoªka v na najkrótszej drodze z s do v jest wierzchoªek π[v]; poprzednikiem wierzchoªka π[v] jest wierzchoªek π[π[v]]... Przygotowanie: for ka»dy u V(G) do begin kolor(u) := biaªy; π[u] := end; 10 5.2 Zmodykowany BFS BFS-Visit(G, s) 2 kolor(s) := szary; 3 Q := {s}; while Q <> do begin 5 u := Head(Q); for ka»dy v Adj[u] do 7 if kolor(v) = biaªy then begin 8 kolor(v) := szary; 9 π[v] := u; 10 Enqueue(Q, v) 11 end; 12 Dequeue(Q); 13 kolor(u) := czarny 1 end 15 end
Przebieg algorytmu dla s = 1 11 2 5 5 Q: 2 5 5 Q: 1 5 5 Q: 1 2 5 Q: 1 2 5 1 5 Q: 1 2 5 1 5 Q: 2 5 1 5 Q: 5 1 5 Q: 5 3
1 5 5 Q: 5 3 12 1 5 5 Q: 3 1 5 5 Q: 3 1 5 5 Q: 1 5 5 Q: 5.3 Odczytanie dróg Po wykonaniu BFS-Visit(G, s) w wektorze π zakodowane s najkrótsze drogi z wierzchoªka ¹ródªowego s do wszystkich wierzchoªków w osi galnych z s. W naszym przykªadzie s = 1: 1 5 5 Np. najkrótsza droga z s = 1 do to: π[] π[π[]], czyli 5 1. A najkrótsza droga z s = 1 do 3 to: 3 π[3] π[π[3]], czyli 3 5 1. Odczytanie i wypisanie drogi z s do v od ko«ca mo»e by zrealizowane przez poni»szy pseudokod:
13 PrintPathRev(s, v) 2 if s = v then wypisz(v) 3 else if π[v] = then wypisz('nie ma drogi') else begin 5 while v <> s do begin wypisz(v); 7 v := π[v] 8 end; 9 wypisz(s) 10 end 11 end; Jednak bardziej naturalnym byªoby wypisanie drogi z s do v od pocz tku. Do tego mo»e posªu»y poni»sza elegancka procedura rekurencyjna: PrintPath(s, v) 2 if s = v then wypisz(v) 3 else if π[v] = then wypisz('nie ma drogi') else begin 5 PrintPath(s, π[v]); wypisz(v) 7 end 8 end; 5. Podsumowanie Je»eli interesuje nas najkrótsza droga pomi dzy dwoma ustalonymi wierzchoªkami s i t, to: uruchamiamy BFS-Visit dla wierzchoªka s, odczytujemy drog z s do t z wektora π. Wektor π b dzie zawieraª, jako skutek uboczny, najkrótsze drogi z s do wszystkich wierzchoªków osi galnych z s (nie tylko t!). Wyliczania tej nadmiarowej informacji nie da si tu unikn. Zauwa»my,»e tu przez najkrótsz drog rozumieli±my drog o najmniejszej liczbie kraw dzi. Mo»na rozwa»a grafy, w których kraw dzie maj dªugo± (lub inny koszt) i poszukiwa najkrótszych dróg wzgl dem tego parametru. Tym zagadnieniem zajmiemy si na nast pnym wykªadzie.