Graf skierowany (digraf) zbiór wierzchołków i zbiór krawędzi skierowanych łączących (co najwyżej jeden raz) uporządkowane pary wierzchołków. Mówimy wtedy, że krawędź łączy pierwszy wierzchołek z drugim (albo, że prowadzi od pierwszego wierzchołka do drugiego). Nie dopuszczamy krawędzi wielokrotnych, ale przyjmujemy, że każdy wierzchołek ma jedną pętlę Digraf o V wierzchołkach posiada V 2 krawędzi, zatem liczba różnych digrafów o V wierzchołkach wynosi 2 VxV
Ścieżka skierowana ciąg wierzchołków, w którym każdy jest połączony krawędzią ze swoim następnikiem. Mówimy, że wierzchołek t jest osiągalny z s, jeśli istnieje ścieżka skierowana z s do t.
Przykład grafu skierowanego Lista wierzchołków i krawędzi
Reprezentacja dla digrafów jest taka sama jak dla grafów nieskierowanych 0 1 2 3 4 5 6 7 8 9 10 11 12 0 1 1 0 0 0 1 1 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 2 1 0 1 1 0 0 0 0 0 0 0 0 0 3 0 0 1 1 0 1 0 0 0 0 0 0 0 4 0 0 1 1 1 0 0 0 0 0 0 1 0 5 0 0 0 0 1 1 0 0 0 0 0 0 0 6 0 0 0 0 1 0 1 0 0 1 0 0 0 7 0 0 0 0 0 0 1 1 1 0 0 0 0 8 0 0 0 0 0 0 0 1 1 1 0 0 0 9 0 0 0 0 0 0 0 0 0 1 1 1 0 Macierz sąsiedztwa 10 0 0 0 0 0 0 0 0 0 0 1 0 1 11 0 0 0 0 0 0 0 0 0 0 0 1 1 12 0 0 0 0 0 0 0 0 0 1 0 0 1
Lista sąsiedztwa
Jeśli każdy wierzchołek digrafu posiada pętlę oraz każdej krawędzie skierowanej odpowiada krawędź w przeciwną stronę, nie ma różnicy pomiędzy reprezentacjami grafów skierowanych i nieskierowanych.
Stopień wejściowy (indegree) wierzchołka, to liczba krawędzi wychodzących z tego wierzchołka, a stopień wyjściowy (outdegree) wierzchołka, to liczba krawędzi wychodzących z tego wierzchołka. Żaden wierzchołek nie jest osiągany z wierzchołka o stopniu wejściowym 0, zwanego ujściem. Wierzchołek o stopniu wejściowym 0, zwany źródłem, nie jest osiągany z żadnego innego wierzchołka.
Stopień wejściowy (indegree) wierzchołka, to liczba krawędzi wychodzących z tego wierzchołka, a stopień wyjściowy (outdegree) wierzchołka, to liczba krawędzi wychodzących z tego wierzchołka. Żaden wierzchołek nie jest osiągany z wierzchołka o stopniu wejściowym 0, zwanego ujściem. Wierzchołek o stopniu wejściowym 0, zwany źródłem, nie jest osiągany z żadnego innego wierzchołka. n, m ilość wierzchołków i krawędzi
Odwrotnością digrafu nazywamy digraf otrzymany przez odwrócenie kierunków wszystkich krawędzi. Odwrotności używamy wtedy, kiedy chcemy wiedzieć, skąd krawędzie przychodzą.
Reprezentacja macierzowa a odwrotność digrafu Tworzymy kopię macierzy i transponujemy ją (czyli zamieniamy wiersze z kolumnami) Albo Jeżeli wiemy, że graf nie będzie modyfikowany zamieniamy miejscami końce krawędzi przy odwołaniach do nich, np. krawędź s-t jest oznaczona 1 w adj[s][t], a w odwrotności grafu 1 byłaby w adj[t][s]. Zatem, jeśli wywołujemy edge(s, t), to w odwrotności wystarczy wywołać edge(t,s ). Albo Utrzymujemy dwie reprezentacje każdej krawędzi skierowanej, ale z dodatkowym bitem informacji określającym kierunek
template <class ingraph, class outgraph> void reverse(const ingraph &G, outgraph &R) { for(int v = 0; v < G.V(); v++) { typename ingraph::adjiterator A(G,v); for (int w = A.beg();!A.end(); w = A.nxt()) R.insert(Edge(w, v)); } } Odwracanie digrafu funkcja dodaje odwrócone krawędzie digrafu wskazanego przez pierwszy argument do digrafu z drugiego argumentu.
Dag skierowany graf acykliczny, czyli digraf bez cykli skierowanych.
Digraf jest silnie spójny, jeśli każdy jego wierzchołek jest osiągalny z dowolnego innego. Dwa wierzchołki w digrafie s i t są silnie połączone, jeśli istnieje ścieżka skierowana z s do t i z t do s. Własność 1. Digraf, który nie jest silnie spójny, składa się ze zbioru silnie spójnych składowych (silnych składowych), będących maksymalnymi podgrafami silnie spójnymi oraz ze zbioru krawędzi pomiędzy silnymi składowymi.
Definicja. Dla danego digrafu D, określamy digraf K(D) o własnościach: każdy wierzchołek odpowiada jednej silnej składowej w D, każda krawędź odpowiada zbiorowi krawędzi w D, łączących wierzchołki pomiędzy właściwymi składowymi (odpowiadającymi obu końcom krawędzi z K(D)). Wówczas K(D) jest dagiem, który nazywamy jądrem digrafu D składowe Ten digraf ma 4 silne składowe Jądro digrafu
Jakie problemy mogą pojawiać się w digrafach? Wykrywanie cykli skierowanych czy digraf posiada cykle skierowane (czy jest dagiem)? Własność Digraf jest dagiem wtedy i tylko wtedy, gdy - podczas sprawdzania go algorytmem przeszukiwania w głąb nie napotkamy krawędzi powrotnych Osiągalność z pojedynczego źródła jakie wierzchołki są osiągane z danego wierzchołka początkowego s, ile jest takich wierzchołków? Własność. Problem osiągalności z pojedynczego źródła s można rozwiązać za pomocą algorytmu przeszukiwania w głąb rozpoczętego w wierzchołku s w czasie proporcjonalnym do liczby krawędzi podgrafu indukowanego przez wierzchołki osiągalne z s.
Pozostaje rozważyć algorytm przeszukiwania w głąb DFS, czyli Depth- First-Search (jest również algorytm przeszukiwania wszerz BSF) grafu. Zaczniemy od grafu nieskierowanego. Idea jest podobna do przechodzenia przez drzewo w kolejności pre-order (najpierw rodzic, a potem rekursywnie - dzieci)
Pozostaje rozważyć algorytm przeszukiwania w głąb DFS, czyli Depth- First-Search (jest również algorytm przeszukiwania wszerz BSF) grafu. Zaczniemy od grafu nieskierowanego. Idea jest podobna do przechodzenia przez drzewo w kolejności pre-order (najpierw rodzic, a potem rekursywnie - dzieci) Algorytm odwiedza sąsiadów korzystając z rekurencji. Wędrując z u do v, rekurencyjnie odwiedzamy wszystkich sąsiadów v do tej pory nieodwiedzonych, potem wracamy do u. Możliwe jest, że w2 nie był odwiedzany, kiedy rekurencyjnie odwiedzaliśmy w1, ale zostanie odwiedzony w czasie, gdy powrócimy z wywołania rekurencyjnego.
Fragment algorytmu zapisany w pseudokodzie.
Jak to działa? Co było poprzednikiem?
RDS(7) kończy, bo już nie ma nieodwiedzonych sąsiadów.
Jak tym algorytmem znajdywać ścieżkę?
Otrzymujemy w ten sposób drzewo ścieżek DFS
Złożoność czasowa DFS z zastosowaniem list sąsiedztwa: Nigdy nie odwiedzamy wierzchołka więcej niż raz. Sprawdzamy zatem wszystkie wierzchołki grafu (wiemy, że suma stopni wszystkich wierzchołków jest równa 2E. Dla każdego wierzchołka zabiera to czas proporcjonalny do stopnia wierzchołka +1. Stąd czas wykonania DFS jest proporcjonalny do sumy ilości wierzchołków i krawędzi, czyli O(V+E).
Złożoność czasowa DFS z zastosowaniem macierzy sąsiedztwa: Czas wykonania DFS jest proporcjonalny do kwadratu ilości wierzchołków, czyli O(V 2 ). Wniosek: Czas działania algorytmu DFS jest liniowy względem rozmiaru struktury danych użytej do reprezentowania grafu.
Możemy wzbogacić rysunek drzewa dla algorytmu DFS, aby uzyskać więcej informacji. Dwie reprezentacja każdej przetwarzanej krawędzi ( w obu kierunkach) Algorytm bada wszystkie krawędzie Jedna reprezentacja każdej przetwarzanej krawędzi Drzewo DFS jest innym sposobem przedstawienia grafu
Krawędź do jeszcze nie odwiedzonego wierzchołka Prowadzą do wierzchołków, dla których DFS jest w trakcie wywołania rekurencyjnego DFS napotkał krawędź prowadzącą do wierzchołka już odwiedzonego Obejście drzewa w porządku prefiksowym jest takie samo jak kolejność badania krawędzi grafu przez DFS
Jeśli w grafie po lewej usuniemy szare wierzchołki (środkowy rysunek) i zastąpimy wierzchołki zewnętrzne krawędziami, to otrzymamy na powrót wyjściowy graf (prawy rysunek) Krawędź do jeszcze nie odwiedzonego wierzchołka Prowadzą do wierzchołków, dla których DFS jest w trakcie wywołania rekurencyjnego DFS napotkał krawędź prowadzącą do wierzchołka już odwiedzonego Obejście drzewa w porządku prefiksowym jest takie samo jak kolejność badania krawędzi grafu przez DFS
Krawędzie Drzewowe reprezentują wywołania rekurencyjne Powrotne łączą wierzchołki z ich przodkami nie będącymi ich ojcami w drzewie DFS Gałąź drzewowa krawędź z wierzchołka v do w, jeśli w jest nienaznaczony Gałąź do ojca krawędź z wierzchołka v do w, jeśli wskaźnik na ojca w jest równy v Gałąź powrotna krawędź z wierzchołka v do w, jeśli numer w porządku prefiksowym w jest mniejszy niż numer w porządku prefiksowym v Gałąź zstępująca krawędź z wierzchołka v do w, jeśli numer w porządku prefiksowym w jest większy niż numer w porządku prefiksowym v
Jeśli graf nie jest spójny, to dynamikę przeszukiwania DFS można opisać za pomocą lasu DFS, zawierającym po jednym drzewie dla każdej składowej spójnej. Używając reprezentacji grafu w postaci list sąsiedztwa odwiedzamy krawędzie w innej kolejności, niż wtedy, gdy użyjemy reprezentacji grafu w postaci macierzy sąsiedztwa.
Las DFS odpowiada przeszukiwaniu w głąb grafu reprezentowanego w postaci macierzy sąsiedztwa. Graf posiada 3 składowe spójne, więc las zawiera 3 drzewa. Do okrągłych wierzchołków prowadzą krawędzie drzewa, do kwadratowych krawędzie powrotne. Szary kolor wskazuje, że krawędzie do tych wierzchołków zostały już wcześniej napotkane w odwrotnej orientacji.
Las DFS odpowiada przeszukiwaniu w głąb grafu reprezentowanego w postaci listy sąsiedztwa. Graf posiada 3 składowe spójne, więc las zawiera 3 drzewa. Do okrągłych wierzchołków prowadzą krawędzie drzewa, do kwadratowych krawędzie powrotne. Szary kolor wskazuje, że krawędzie do tych wierzchołków zostały już wcześniej napotkane w odwrotnej orientacji.
Klasyfikacja krawędzi dla digrafów: Krawędzie drzewowe krawędzie reprezentujące wywołania rekurencyjne Krawędzie powrotne krawędzie prowadzące do przodka danego wierzchołka w drzewie DFS Krawędzie zstępujące krawędzie do potomka danego wierzchołka w drzewie DFS Krawędzie poprzeczne pozostałe krawędzie (nie prowadzą ani do potomka, ani do przodka w drzewie DFS)
Las DFS dla digrafu reprezentowanego za pomocą list sąsiedztwa Zewnętrzne węzły reprezentują wierzchołki już odwiedzone, pozostała część lasu jest digrafem o wszystkich krawędziach biegnących w dół.
Las DFS dla digrafu reprezentowanego za pomocą list sąsiedztwa Krawędź poprzeczna Zewnętrzne węzły reprezentują wierzchołki już odwiedzone, pozostała część lasu jest digrafem o wszystkich krawędziach biegnących w dół. Krawędź drzewowa do węzła wewnętrznego Krawędź zstępująca Krawędź powrotna
Własność. W lesie DFS dla digrafu krawędź do odwiedzanego węzła jest krawędzią powrotną, jeśli prowadzi do węzła o wyższym numerze postfiksowym krawędzią poprzeczną, jeśli prowadzi do węzła o niższym numerze prefiksowym krawędzią zstępującą, jeśli prowadzi do węzła o wyższym numerze prefiksowym
Lasy DFS dla tego samego digrafu mogą się znacznie różnić np. liczba drzew lasu DFS zależy od wyboru wierzchołka początkowego.
Czy digraf posiada cykle skierowane (czy digraf jest dagiem)? W grafach nieskierowanych każda krawędź do odwiedzanego wierzchołka wskazuje na istnienie cyklu, w digrafach trzeba rozważać krawędzie powrotne. Własność. Digraf jest dagiem wtedy i tylko wtedy, gdy podczas badania za pomocą DFS każdej krawędzi nie napotkamy krawędzi powrotnych.
Dowód. Każda krawędź powrotna należy do cyklu skierowanego zawierającego samą krawędź oraz ścieżkę w drzewie łączącą oba węzły, nie znajdziemy więc krawędzi powrotnych, przeszukując dag za pomocą DFS. W drugą stronę: wystarczy pokazać, że jeśli digraf posiada cykl, to DFS napotka krawędź powrotną. Jeśli v jest pierwszym wierzchołkiem cyklu odwiedzonym przez DFS, to wierzchołek ten ma najniższy numer prefiksowy spośród wszystkich krawędzi cyklu. Krawędź cyklu prowadząca do tego wierzchołka będzie więc krawędzią powrtoną: zostanie ona napotkana podczas wywołania rekurencyjnego z vi będzie prowadzić od pewnego węzła cyklu do węzła o niższym numerze prefiksowym.
Głównym celem DFS jest systematyczne odwiedzenie wszystkich wierzchołków i krawędzi Jakie wierzchołki są osiągane z danego wierzchołka początkowego s? Ile jest takich wierzchołków? Problem osiągalności z pojedynczego źródła
Głównym celem DFS jest systematyczne odwiedzenie wszystkich wierzchołków i krawędzi Jakie wierzchołki są osiągane z danego wierzchołka początkowego s? Ile jest takich wierzchołków? Problem osiągalności z pojedynczego źródła Własność Problem osiągalności z pojedynczego źródła s można rozwiązać za pomocą DFS rozpoczętego w wierzchołku s w czasie proporcjonalnym do liczby krawędzi podgrafu indukowanego przez wierzchołki osiągane z s.
Dowód. Własność ta jest prawdziwa dla grafu z jednym wierzchołkiem bez krawędzi. Dla dowolnego digrafu przeprowadzamy dowód indukcyjnie. Zakładamy, że własność zachodzi dla wszystkich digrafów o mniejszej liczbie wierzchołków. Wtedy pierwsza wzięta pod uwagę krawędź s dzieli digraf na podgrafy indukowane przez dwa zbiory wierzchołków: a) Wierzchołki możliwe do osiągnięcia po ścieżkach skierowanych niezawierających s i rozpoczynających się tą krawędzią b) Wierzchołki, do których nie da się dojść po ścieżkach skierowanych niezawierających s i rozpoczynających się tą krawędzią. Do tych podgrafów stosujemy założenie indukcyjne nie ma krawędzi z wierzchołków pierwszego podgrafu do innych niż s wierzchołków drugiego podgrafu. Krawędzie do s zostaną zignorowane, ponieważ wierzchołek s ma najniższy numer prefiksowy oraz wszystkie wierzchołki pierwszego podgrafu mają niższy numer prefiksowy niż dowolny wierzchołek drugiego podgrafu. Zatem wszystkie krawędzie z wierzchołków drugiego podgrafu wierzchołków pierwszego podgrafu zostaną pominięte.
Definicja Domknięciem przechodnim digrafu nazywamy digraf o tym samym zbiorze wierzchołków, dla którego krawędź z wierzchołka s do t istnieje wtedy i tylko wtedy, gdy w digrafie wyjściowym istnieje ścieżka skierowana z s do t. W domknięciu przechodnim z każdego wierzchołka wychodzą krawędzie do wszystkich osiągalnych z niego wierzchołków
Definicja Domknięciem przechodnim digrafu nazywamy digraf o tym samym zbiorze wierzchołków, dla którego krawędź z wierzchołka s do t istnieje wtedy i tylko wtedy, gdy w digrafie wyjściowym istnieje ścieżka skierowana z s do t. W domknięciu przechodnim z każdego wierzchołka wychodzą krawędzie do wszystkich osiągalnych z niego wierzchołków Własność Domknięcie przechodnie digrafu można wyznaczyć, konstruując dla niego macierz sąsiedztwa A, dodając pętlę dla każdego wierzchołka, a następnie obliczając A V
A V =A*A*A* *A mnożenie boolowskie macierzy boolowskiej przez siebie V razy. for(s = 0; s < V; s++) for(t = 0; t < V; t++) for(i = 0; C[s][t] = 0; i < V; i++) C[s][t] += A[s][i]*B[i][t]; C=A*B for(s = 0; s < V; s++) for(t = 0; t < V; t++) for(i = 0; C[s][t] = 0; i < V; i++) if(a[s][i] && B[i][t]) C[s][t]= 1; + or * and mnożenie macierzy boolowskich
A V =A*A*A* *A mnożenie boolowskie macierzy boolowskiej przez siebie V razy. for(s = 0; s < V; s++) for(t = 0; t < V; t++) for(i = 0; C[s][t] = 0; i < V; i++) C[s][t] += A[s][i]*B[i][t]; C=A*B for(s = 0; s < V; s++) for(t = 0; t < V; t++) for(i = 0; C[s][t] = 0; i < V; i++) if(a[s][i] && B[i][t]) C[s][t]= 1; + or * and mnożenie macierzy boolowskich C=A*A=A 2 dla każdej pary wierzchołków s i t, w C znajduje się krawędź z s do t wtedy i tylko wtedy, gdy mażna wskazać wierzchołek i, dla którego w A istnieją krawędzie z s do i oraz z i do t. Krawędzie w A 2 odpowiadają ścieżkom skierowanym o długości 2 w A. Jeśli do każdego wierzchołka dołączymy pętlę, to A 2 zawiera także wszystkie krawędzie z A.
Własność Domknięcie przechodnie digrafu można wyznaczyć, konstruując dla niego macierz sąsiedztwa A, dodając pętlę dla każdego wierzchołka, a następnie obliczając A V Dowód. Opierając się na poprzednim rozumowaniu, pokazuje się, że A 3 zawiera krawędzie odpowiadające ścieżkom o długości nie większej niż 3, A 4 o długości nie większej niż 4, itd. Ścieżki o długości większej niż V można pominąć, bo z zasady szufladkowej Dirichleta wynika, że na takiej ścieżce przynajmniej jeden wierzchołek musi wystąpić dwukrotnie (mamy ich tylko V). Taka ścieżka nie wnosi nic nowego do domknięcia przechodniego, ponieważ istnieje krótsze połączenie pomiędzy jej dwoma końcami
Złożoność czasowa: Mamy: V mnożeń macierzy, każde o czasie wykonania rzędu V 3, zatem wychodzi nam V 4. Dla mnożeń boolowskich mamy lg V operacji mnożenia macierzy, bo liczymy kolejno A 2, A 4, A 8,, aż wykładnik będzie większy bądź równy V. Stąd mamy V 3 lg V=A V.
Jeszcze prościej: Algorytm Warshalla wyznacza domknięcie przechodnie digrafu w czasie rzędu V 3. Algorytm dla gęstych digrafów: For(i = 0; i < V; i++) for(s = 0; s < V; s++) for(t = 0; t < V; t++) if(a[s][i] && A[i][t]) A[s][t]= 1; Różnica tkwi w kolejności pętli for
Czy da się jeszcze poprawić algorytm Warshalla? For(i = 0; i < V; i++) for(s = 0; s < V; s++) for(t = 0; t < V; t++) if(a[s][i] && A[i][t]) A[s][t]= 1; TAK Można skrócić czas wykonania algorytmu przesuwając sprawdzenie A[s][i] poza wewnętrzna pętlę (nie zależy od t). Unikamy t-krotnego wykonywania wewnętrznej pętli, gdy A[s][i]=0.
Czy da się jeszcze poprawić algorytm Warshalla? For(i = 0; i < V; i++) for(s = 0; s < V; s++) for(t = 0; t < V; t++) if(a[s][i] && A[i][t]) A[s][t]= 1; TAK Można skrócić czas wykonania algorytmu przesuwając sprawdzenie A[s][i] poza wewnętrzna pętlę (nie zależy od t). Unikamy t-krotnego wykonywania wewnętrznej pętli, gdy A[s][i]=0. Dla wielkich grafów rzadkich koszt wykonywania algorytmu jest wciąż duży!
Jak to poprawić? Nie jest potrzebna cała macierz, by wyznaczyć domknięcie przechodnie. Wystarczy sprawdzić istnienie jedynie niewielkiej liczby krawędzi, więc można wstępnie przetworzyć dany digraf, tzn. posługując się abstrakcyjnym typem danych, który udostępnia operację sprawdzania osiągalności posługujemy się wtedy terminem abstrakcyjnego domknięcia przechodniego.
Jak to poprawić? Nie jest potrzebna cała macierz, by wyznaczyć domknięcie przechodnie. Wystarczy sprawdzić istnienie jedynie niewielkiej liczby krawędzi, więc można wstępnie przetworzyć dany digraf, tzn. posługując się abstrakcyjnym typem danych, który udostępnia operację sprawdzania osiągalności posługujemy się wtedy terminem abstrakcyjnego domknięcia przechodniego. Własność. Sprawdzanie w stałym czasie osiągalności (abstrakcyjne domknięcie przechodnie) dla digrafu może być zrealizowane po wykonaniu operacji wstępnych w czasie rzędu V 3, przy wykorzystaniu pamięci rzędu V 2. Własność. Z wykorzystaniem DFS można uzyskać stały czas dla sprawdzania osiągalności dla abstrakcyjnego domknięcia przechodniego digrafu, wykonując operacje wstępne (wyznaczanie domknięcia przechodniego) w czasie rzędu V(V+E) i przy wykorzystaniu pamięci rzędu V 2
Dowód. DFS wyznacza wszystkie wierzchołki osiągalne z wierzchołka początkowego w czasie rzędu E, dla reprezentacji w postaci listy sąsiedztwa. Jeśli uruchomimy DFS V razy, jednokrotnie z każdego wierzchołka, to otrzymamy zbiory wierzchołków osiągalnych z każdego z wierzchołków (czyli domknięcie przechodnie) w czasie rzędu V(V+E).