Serwis Edukacyjny w I-LO w Tarnowie Materiały dla uczniów liceum |
Wyjście Spis treści Wstecz Dalej Autor artykułu: mgr Jerzy Wałaszek |
©2024 mgr Jerzy Wałaszek |
ProblemDla każdego wierzchołka grafu należy wyznaczyć cykl, który go zawiera. |
Problem rozwiązujemy za pomocą przejścia DFS. Dany wierzchołek będzie częścią cyklu, jeśli DFS wróci do któregoś z sąsiadów wierzchołka, a nie będzie to sąsiad, od którego DFS rozpoczęło przejście grafu. W trakcie przechodzenia grafu algorytm DFS zapamiętuje na osobnym stosie kolejno odwiedzane wierzchołki. Gdy wykryje cykl, stos ten będzie zawierał wierzchołki tworzące ten cykl. Wtedy wystarczy pobrać ze stosu wszystkie wierzchołki i przerwać dalsze przeglądanie grafu.
graf | : | zadany w dowolnie wybrany sposób, algorytm tego nie precyzuje. |
v | : | wierzchołek startowy, v ∈ C. |
w | : | wierzchołek bieżący, w ∈ C. |
S | : | stos liczb całkowitych będących numerami wierzchołków. |
visited | : | tablica logiczna wierzchołków odwiedzonych. |
u | : | wierzchołek, u ∈ C. |
K01: | visited [w] ← true | oznaczamy bieżący wierzchołek jako odwiedzony |
K02: | Dla każdego sąsiada u
wierzchołka w : wykonuj kroki K03…K07 |
przeglądamy kolejnych sąsiadów wierzchołka w |
K03: | Jeśli u
= S.top(), to następny obieg pętli K02 |
sąsiad jest wierzchołkiem, z którego przyszliśmy |
K04: | S.push (w) | wierzchołek w umieszczamy na stosie |
K05: | Jeśli u
= v, to zakończ |
znaleźliśmy cykl, kończymy rekurencję |
K06: | Jeśli (
visited [u] = false)
(DFSfindCycle (graf, v, u, S,
visited) = true), to zakończ z wynikiem true |
rekurencyjnie odwiedzamy nieodwiedzonych sąsiadów |
K07: | S.pop() | usuwamy ze stosu wierzchołek bieżący |
K08: | Zakończ z wynikiem false |
n | : | liczba wierzchołków w grafie. |
graf | : | zadany w dowolnie wybrany sposób, algorytm tego nie precyzuje. |
i, u | : | wierzchołki, i, u ∈ C. |
visited | : | tablica logiczna. |
S | : | stos liczb całkowitych. |
K01: | Utwórz n-elementową tablicę visited | |
K02: | Utwórz pusty stos S | |
K03: | Dla i = 0, 1, …, n
- 1: wykonuj kroki K04…K10 |
|
K04: | Ustaw wszystkie elementy visited na false | |
K05: | S.push (-1) | początek ścieżki |
K06: | Jeśli
DFSfindCycle (graf, i, i, S,
visited) = false, to S.pop() i następny obieg pętli K03 |
szukamy cyklu, wynik będzie na stosie |
K07: | Pisz i | wypisujemy wierzchołek startowy |
K08: | Dopóki
S.empty() = false, wykonuj kroki K09…K10 |
wypisujemy wierzchołki tworzące cykl |
K09: | u ← S.top(); S.pop() | pobieramy wierzchołek |
K10: |
Jeśli u > -1, to pisz u |
wypisujemy go, jeśli nie jest początkiem ścieżki |
K11: | Zakończ |
Uwaga: algorytm wypisuje cykle w odwrotnej kolejności niż były przechodzone przez DFS. Jeśli chcesz mieć normalną kolejność, to dane odczytane ze stosu S należy wypisać w kolejności odwrotnej (można tego prosto dokonać za pomocą dodatkowego stosu).
Uwaga:
Zanim uruchomisz program, przeczytaj wstęp do tego artykułu, w którym wyjaśniamy funkcje tych programów oraz sposób korzystania z nich. |
Program odczytuje definicję grafu nieskierowanego i dla każdego wierzchołka wypisuje znaleziony cykl. Jeśli cyklu nie ma, to zostaje wyświetlona odpowiednia informacja (wykorzystujemy informację o znalezieniu cyklu, która jest zwracana przez funkcję rekurencyjną DFSfindCycle()). |
Dane przykładowe (przekopiuj do
schowka i wklej do okna konsoli):
|
Pascal// Wyszukiwanie cykli w grafie nieskierowanym // Data: 22.12.2013 // (C)2013 mgr Jerzy Wałaszek //------------------------------------------- program fndcycles; // Typy dla dynamicznej tablicy list sąsiedztwa oraz stosu type PSLel = ^SLel; SLel = record next : PSLel; v : integer; end; TList = array of PSLel; // Definicja typu obiektowego Stack //--------------------------------- Stack = object private S : PSLel; // lista przechowująca stos public constructor init; destructor destroy; function empty : boolean; function top : integer; procedure push (v : integer); procedure pop; end; // Konstruktor //------------ constructor Stack.init; begin S := nil; end; // Destruktor //----------- destructor Stack.destroy; begin while S <> nil do pop; end; // Sprawdza, czy stos jest pusty //------------------------------ function Stack.empty : boolean; begin if S = nil then empty := true else empty := false; end; // Zwraca liczbę ze szczytu stosu //---------------------------------- function Stack.top : integer; begin top := S^.v; end; // Umieszcza dane na stosie //------------------------- procedure Stack.push (v : integer); var e : PSLel; begin new (e); e^.v := v; e^.next := S; S := e; end; // Usuwa dane ze stosu //-------------------- procedure Stack.pop; var e :PSLel; begin if S <> NIL then begin e := S; S := S^.next; dispose (e); end; end; // Funkcja rekurencyjna wyszukująca cykl //-------------------------------------- function DFSfindCycle (var graf : TList; v, w : integer; var S : Stack; var visited : array of boolean) : boolean; var u : integer; p : PSLel; begin visited [w] := true; // Oznaczamy wierzchołek jako odwiedzony p := graf [w]; // Rozpoczynamy przeglądanie sąsiadów while p <> nil do begin u := p^.v; // u-numer wierzchołka będącego sąsiadem if u <> S.top then // Pomijamy wierzchołek, z którego przyszliśmy begin S.push (w); // Na stos wierzchołek bieżący if u = v then Exit (true); // Cykl znaleziony, kończymy if(visited [u] = false) and DFSfindCycle (graf, v, u, S, visited) then Exit (true); S.pop; end; p := p^.next; end; Exit (false); end; // ********************** // *** Program główny *** // ********************** var n, m, i, j, u, v1, v2 : integer; visited : array of boolean; S : Stack; p, r : PSLel; A : TList; begin read (n, m); // Czytamy liczbę wierzchołków i krawędzi SetLength (A, n); // Tworzymy tablicę list sąsiedztwa // Tablice wypełniamy pustymi listami for i := 0 to n-1 do A [i] := nil; // Odczytujemy kolejne definicje krawędzi for i := 0 to m-1 do begin read (v1, v2); // Wierzchołek startowy i końcowy krawędzi new (p); // Tworzymy nowy element p^.v := v2; // Numerujemy go jako v2 p^.next := A [v1]; // Dodajemy go na początek listy A [v1] A [v1] := p; new (p); // Krawędź w drugą stronę, bo graf jest nieskierowany p^.v := v1; p^.next := A [v2]; A [v2] := p; end; writeln; SetLength (visited, n); // Tworzymy tablicę odwiedzin S.init; // Tworzymy stos wierzchołków for i := 0 to n-1 do // Przechodzimy przez kolejne wierzchołki grafu begin for j := 0 to n-1 do // Zerujemy tablicę odwiedzin visited [j] := false; S.push (-1); // Na stos znacznik początku ścieżki write (i); // Wypisujemy wierzchołek startowy cyklu if DFSfindCycle (A, i, i, S, visited) = false then // Szukamy cyklu begin S.pop; // Usuwamy ze stosu początek ścieżki writeln('-no cycle'); // Komunikat end else while S.empty = false do // Wypisujemy cykl, jeśli istnieje begin u := S.top; S.pop; // Pobieramy ze stosu numer wierzchołka if u > -1 then write (' ', u)// i wypisujemy go else writeln; end; end; // Usuwamy dynamiczne struktury danych SetLength (visited, 0); S.destroy; for i := 0 to n-1 do begin p := A [i]; while p <> nil do begin r := p; p := p^.next; dispose (r); end; end; SetLength (A, 0); end. |
// Wyszukiwanie cykli w grafie nieskierowanym // Data: 22.12.2013 // (C)2013 mgr Jerzy Wałaszek //------------------------------------------- #include <iostream> using namespace std; // Typy dla dynamicznej tablicy list sąsiedztwa i stosu struct SLel { SLel * next; int v; }; class Stack { private: SLel * S; // lista przechowująca stos public: Stack(); // konstruktor ~Stack(); // destruktor bool empty (void); int top (void); void push (int v); void pop (void); }; //--------------------- // Metody obiektu Stack //--------------------- // Konstruktor //------------ Stack::Stack() { S = NULL; } // Destruktor-zwalnia tablicę dynamiczną //---------------------------------------- Stack::~Stack() { while(S) pop(); } // Sprawdza, czy stos jest pusty //------------------------------ bool Stack::empty (void) { return !S; } // Zwraca szczyt stosu //-------------------- int Stack::top (void) { return S->v; } // Zapisuje na stos //----------------- void Stack::push (int v) { SLel * e = new SLel; e->v = v; e->next = S; S = e; } // Usuwa ze stosu //--------------- void Stack::pop (void) { if(S) { SLel * e = S; S = S->next; delete e; } } // Funkcja rekurencyjna wyszukująca cykl //-------------------------------------- bool DFSfindCycle (SLel ** graf, int v, int w, Stack & S, bool * visited) { int u; SLel * p; visited [w] = true; // Oznaczamy wierzchołek jako odwiedzony p = graf [w]; // Rozpoczynamy przeglądanie sąsiadów while(p) { u = p->v; // u-numer wierzchołka będącego sąsiadem if(u != S.top()) // Pomijamy wierzchołek, z którego przyszliśmy { S.push (w); // Na stos wierzchołek bieżący if(u == v) return true; // Cykl znaleziony, kończymy if(!visited [u] && DFSfindCycle (graf, v, u, S, visited)) return true; S.pop(); } p = p->next; } return false; } // ********************** // *** PROGRAM GŁÓWNY *** // ********************** int main() { int n, m, i, j, u, v1, v2; SLel * p, * r, ** A; bool * visited; Stack S; cin >> n >> m; // Czytamy liczbę wierzchołków i krawędzi A = new SLel * [n]; // Tworzymy tablicę list sąsiedztwa // Tablice wypełniamy pustymi listami for(i = 0; i < n; i++) A [i] = NULL; // Odczytujemy kolejne definicje krawędzi for(i = 0; i < m; i++) { cin >> v1 >> v2; // Wierzchołek startowy i końcowy krawędzi p = new SLel; // Tworzymy nowy element p->v = v2; // Numerujemy go jako v2 p->next = A [v1]; // Dodajemy go na początek listy A [v1] A [v1] = p; p = new SLel; // Krawędź w drugą stronę, bo graf jest nieskierowany p->v = v1; p->next = A [v2]; A [v2] = p; } cout << endl; visited = new bool [n]; // Tworzymy tablicę odwiedzin for(i = 0; i < n; i++) // Przechodzimy przez kolejne wierzchołki grafu { for(j = 0; j < n; j++) // Zerujemy tablicę odwiedzin visited [j] = false; S.push (-1); // Na stos znacznik początku ścieżki cout << i; // Wypisujemy wierzchołek startowy cyklu if(!DFSfindCycle (A, i, i, S, visited)) // Szukamy cyklu { S.pop(); // Usuwamy ze stosu początek ścieżki cout << "-no cycle\n"; // Komunikat } else while(!S.empty()) // Wypisujemy cykl, jeśli istnieje { u = S.top(); S.pop(); // Pobieramy ze stosu numer wierzchołka if(u > -1) cout << " " << u; // i wypisujemy go else cout << endl; } } // Usuwamy dynamiczne struktury danych delete [] visited; for(i = 0; i < n; i++) { p = A [i]; while(p) { r = p; p = p->next; delete r; } } delete [] A; return 0;} |
Basic' Wyszukiwanie cykli w grafie nieskierowanym ' Data: 22.12.2013 ' (C)2013 mgr Jerzy Wałaszek '------------------------------------------- ' Typy dla dynamicznej tablicy list sąsiedztwa i stosu Type SLel next As SLel Ptr v As Integer End Type Type Stack Private: S As SLel Ptr ' lista zawierająca stos Public: Declare Constructor() Declare Destructor() Declare Function empty() As Integer Declare Function top As Integer Declare Sub push (ByVal v As Integer) Declare Sub pop() End Type '--------------------- ' Metody obiektu Stack '--------------------- ' Konstruktor '------------ Constructor Stack() S = 0 End Constructor ' Destruktor '----------- Destructor Stack() While S pop() Wend End Destructor ' Sprawdza, czy stos jest pusty '------------------------------ Function Stack.empty() As Integer If S = 0 Then Return 1 Return 0 End Function ' Zwraca szczyt stosu. '------------------------------ Function Stack.top() As Integer top = S->v End Function ' Zapisuje na stos '----------------- Sub Stack.push (ByVal v As Integer) Dim e As SLel Ptr e = New SLel e->v = v e->Next = S S = e End Sub ' Usuwa ze stosu '--------------- Sub Stack.pop() Dim e As SLel Ptr If S Then e = S S = S->Next Delete e End If End Sub ' Funkcja rekurencyjna wyszukująca cykl '-------------------------------------- Function DFSfindCycle (ByVal graf As SLel Ptr Ptr, _ ByVal v As integer, ByVal w As integer, _ ByRef S As Stack, _ ByVal visited As Integer Ptr) As Integer Dim As Integer u Dim As SLel Ptr p visited [w] = 1 ' Oznaczamy wierzchołek jako odwiedzony p = graf [w] ' Rozpoczynamy przeglądanie sąsiadów While p u = p->v ' u-numer wierzchołka będącego sąsiadem If u <> S.top() Then ' Pomijamy wierzchołek, z którego przyszliśmy S.push (w) ' Na stos wierzchołek bieżący If u = v Then Return 1 ' Cykl znaleziony, kończymy if(visited [u] = 0) AndAlso (DFSfindCycle (graf, v, u, S, visited) = 1) Then Return 1 S.pop() End If p = p->Next Wend Return 0 End Function ' ********************** ' *** PROGRAM GŁÓWNY *** ' ********************** Dim As Integer n, m, i, j, u, v1, v2 Dim As SLel Ptr p, r Dim As SLel Ptr Ptr A Dim As Integer Ptr visited Dim As Stack S Open Cons For Input As #1 Input #1, n, m ' Czytamy liczbę wierzchołków i krawędzi A = new SLel Ptr [n] ' Tworzymy tablicę list sąsiedztwa ' Tablicę wypełniamy pustymi listami For i = 0 To n-1 A [i] = 0 Next ' Odczytujemy kolejne definicje krawędzi For i = 0 To m -1 Input #1, v1, v2 ' Wierzchołek startowy i końcowy krawędzi p = new SLel ' Tworzymy nowy element p->v = v2 ' Numerujemy go jako v2 p->next = A [v1] ' Dodajemy go na początek listy A [v1] A [v1] = p p = new SLel ' Krawędź w drugą stronę, bo graf jest nieskierowany p->v = v1 p->next = A [v2] A [v2] = p Next Close #1 Print visited = New Integer [n] ' Tworzymy tablicę odwiedzin For i = 0 To n-1 ' Przechodzimy przez kolejne wierzchołki grafu For j = 0 To n-1 ' Zerujemy tablicę odwiedzin visited [j] = 0 Next S.push (-1) ' Na stos znacznik początku ścieżki Print i; ' Wypisujemy wierzchołek startowy cyklu If DFSfindCycle (A, i, i, S, visited) = 0 Then ' Szukamy cyklu S.pop() ' Usuwamy ze stosu początek ścieżki Print "-no cycle" ' Komunikat Else While S.empty() = 0 ' Wypisujemy cykl, jeśli istnieje u = S.top(): S.pop() ' Pobieramy ze stosu numer wierzchołka If u > -1 Then Print u; ' i wypisujemy go Else Print End If Wend End If Next ' Usuwamy dynamiczne struktury danych Delete [] visited For i = 0 To n-1 p = A [i] While p r = p p = p->Next Delete r Wend Next Delete [] A End |
Wynik: |
9 10 0 1 0 3 1 8 2 4 2 5 3 7 3 8 4 6 5 6 5 7 0 1 8 3 0 1 0 3 8 1 2 4 6 5 2 3 0 1 8 3 4 2 5 6 4 5 2 4 6 5 6 4 2 5 6 7-no cycle 8 1 0 3 8 |
Problem rozwiązujemy za pomocą przejścia DFS. Dany wierzchołek będzie częścią cyklu, jeśli DFS znajdzie wierzchołek w grafie, od którego prowadzi krawędź do wierzchołka startowego. Zadanie to jest nawet prostsze od wyszukiwania cyklu w grafie nieskierowanym, ponieważ z listy sąsiadów nie musimy eliminować wierzchołków, z których przyszliśmy.
graf | : | zadany w dowolnie wybrany sposób, algorytm tego nie precyzuje. |
v | : | wierzchołek startowy. |
w | : | wierzchołek bieżący. |
S | : | stos liczb całkowitych będących numerami wierzchołków. |
visited | : | tablica logiczna wierzchołków odwiedzonych. |
u | : | wierzchołek, u ∈ C. |
K01: | visited [w] ← true | oznaczamy bieżący wierzchołek jako odwiedzony |
K02: | S.push (w) | na stosie umieszczamy bieżący wierzchołek |
K03: | Dla każdego sąsiada u
wierzchołka w : wykonuj kroki K04…K05 |
przeglądamy kolejnych sąsiadów wierzchołka w |
K04: | Jeśli u
= v, to zakończ z wynikiem true |
znaleźliśmy cykl, kończymy rekurencję |
K05: | Jeśli (
visited [u] = false)
(DFSfindCycle (graf, v, u, S,
visited) = true), to zakończ z wynikiem true |
rekurencyjnie odwiedzamy nieodwiedzonych sąsiadów |
K06: | S.pop() | usuwamy ze stosu wierzchołek bieżący |
K07: | Zakończ z wynikiem false |
n | : | liczba wierzchołków w grafie. |
graf | : | zadany w dowolnie wybrany sposób, algorytm tego nie precyzuje. |
i, u | : | wierzchołki, i, u ∈ C. |
visited | : | tablica logiczna. |
S | : | stos liczb całkowitych. |
K01: | Utwórz n-elementową tablicę visited | |
K02: | Utwórz pusty stos S | |
K03: | Dla i = 0, 1, …, n-1: wykonuj kroki K04…K09 |
|
K04: | Ustaw wszystkie elementy visited na false | |
K05: | Jeśli
DFSfindCycle (graf, i, i, S,
visited) = false, to następny obieg pętli K03 |
szukamy cyklu, wynik będzie na stosie |
K06: | Pisz i | wypisujemy wierzchołek startowy |
K07: | Dopóki
S.empty() = false, wykonuj kroki K08…K09 |
wypisujemy wierzchołki tworzące cykl |
K08: | Pisz S.top() | wypisujemy wierzchołek |
K09: | S.pop() | usuwamy go ze stosu |
K10: | Zakończ |
Uwaga: algorytm wypisuje cykle w odwrotnej kolejności. Jeśli chcesz mieć kolejność wzdłuż krawędzi, to dane odczytane ze stosu S należy wypisać wspak (można tego prosto dokonać za pomocą dodatkowego stosu-patrz przykładowe programy).
Uwaga:
Zanim uruchomisz program, przeczytaj wstęp do tego artykułu, w którym wyjaśniamy funkcje tych programów oraz sposób korzystania z nich. |
Program odczytuje definicję grafu skierowanego i dla każdego wierzchołka wypisuje znaleziony cykl. Jeśli cyklu nie ma, to zostaje wyświetlona odpowiednia informacja (wykorzystujemy informację o znalezieniu cyklu, która jest zwracana przez funkcję rekurencyjną DFSfindCycle()). Przy wyświetlaniu cyklu odwracamy kolejność wierzchołków. |
Dane przykładowe (przekopiuj do
schowka i wklej do okna konsoli):
|
Pascal// Wyszukiwanie cykli w grafie skierowanym // Data: 19.12.2013 // (C)2013 mgr Jerzy Wałaszek //---------------------------------------- program fndcycles; // Typy dla dynamicznej tablicy list sąsiedztwa oraz stosu type PSLel = ^SLel; SLel = record next : PSLel; v : integer; end; TList = array of PSLel; // Definicja typu obiektowego Stack //--------------------------------- Stack = object private S : PSLel; // lista przechowująca stos public constructor init; destructor destroy; function empty : boolean; function top : integer; procedure push (v : integer); procedure pop; end; // Konstruktor //------------ constructor Stack.init; begin S := nil; end; // Destruktor //----------- destructor Stack.destroy; begin while S <> nil do pop; end; // Sprawdza, czy stos jest pusty //------------------------------ function Stack.empty : boolean; begin if S = nil then empty := true else empty := false; end; // Zwraca liczbę ze szczytu stosu //---------------------------------- function Stack.top : integer; begin top := S^.v; end; // Umieszcza dane na stosie //------------------------- procedure Stack.push (v : integer); var e : PSLel; begin new (e); e^.v := v; e^.next := S; S := e; end; // Usuwa dane ze stosu //-------------------- procedure Stack.pop; var e :PSLel; begin if S <> NIL then begin e := S; S := S^.next; dispose (e); end; end; // Funkcja rekurencyjna wyszukująca cykl //-------------------------------------- function DFSfindCycle (var graf : TList; v, w : integer; var S : Stack; var visited : array of boolean) : boolean; var u : integer; p : PSLel; begin visited [w] := true; // Oznaczamy wierzchołek jako odwiedzony S.push (w); // Na stos wierzchołek bieżący p := graf [w]; // Rozpoczynamy przeglądanie sąsiadów while p <> nil do begin u := p^.v; // u-numer wierzchołka będącego sąsiadem if u = v then Exit (true); // Cykl znaleziony, kończymy if(visited [u] = false) and DFSfindCycle (graf, v, u, S, visited) then Exit (true); p := p^.next; end; S.pop; // Usuwamy bieżący wierzchołek ze stosu Exit (false); end; // ********************** // *** Program główny *** // ********************** var n, m, i, j, v1, v2 : integer; visited : array of boolean; S, T : Stack; p, r : PSLel; A : TList; begin read (n, m); // Czytamy liczbę wierzchołków i krawędzi SetLength (A, n); // Tworzymy tablicę list sąsiedztwa // Tablice wypełniamy pustymi listami for i := 0 to n-1 do A [i] := nil; // Odczytujemy kolejne definicje krawędzi for i := 0 to m-1 do begin read (v1, v2); // Wierzchołek startowy i końcowy krawędzi new (p); // Tworzymy nowy element p^.v := v2; // Numerujemy go jako v2 p^.next := A [v1]; // Dodajemy go na początek listy A [v1] A [v1] := p; end; writeln; SetLength (visited, n); // Tworzymy tablicę odwiedzin S.init; // Tworzymy stosy wierzchołków T.init; for i := 0 to n-1 do // Przechodzimy przez kolejne wierzchołki grafu begin for j := 0 to n-1 do // Zerujemy tablicę odwiedzin visited [j] := false; if DFSfindCycle (A, i, i, S, visited) = false then // Szukamy cyklu writeln(i, '-no cycle') else begin T.push (i); while S.empty = false do // Przerzucamy stos S do stosu T begin T.push (S.top); S.pop; end; while T.empty = false do // Wyświetlamy ścieżkę begin write (T.top(), ' '); T.pop; end; writeln; end; end; // Usuwamy dynamiczne struktury danych SetLength (visited, 0); S.destroy; T.destroy; for i := 0 to n-1 do begin p := A [i]; while p <> nil do begin r := p; p := p^.next; dispose (r); end; end; SetLength (A, 0); end. |
// Wyszukiwanie cykli w grafie skierowanym // Data: 19.12.2013 // (C)2013 mgr Jerzy Wałaszek //---------------------------------------- #include <iostream> using namespace std; // Typy dla dynamicznej tablicy list sąsiedztwa i stosu struct SLel { SLel * next; int v; }; class Stack { private: SLel * S; // lista przechowująca stos public: Stack(); // konstruktor ~Stack(); // destruktor bool empty (void); int top (void); void push (int v); void pop (void); }; //--------------------- // Metody obiektu Stack //--------------------- // Konstruktor //------------ Stack::Stack() { S = NULL; } // Destruktor-zwalnia tablicę dynamiczną //---------------------------------------- Stack::~Stack() { while(S) pop(); } // Sprawdza, czy stos jest pusty //------------------------------ bool Stack::empty (void) { return !S; } // Zwraca szczyt stosu //-------------------- int Stack::top (void) { return S->v; } // Zapisuje na stos //----------------- void Stack::push (int v) { SLel * e = new SLel; e->v = v; e->next = S; S = e; } // Usuwa ze stosu //--------------- void Stack::pop (void) { if(S) { SLel * e = S; S = S->next; delete e; } } // Funkcja rekurencyjna wyszukująca cykl //-------------------------------------- bool DFSfindCycle (SLel ** graf, int v, int w, Stack & S, bool * visited) { int u; SLel * p; visited [w] = true; // Oznaczamy wierzchołek jako odwiedzony S.push (w); // Na stos wierzchołek bieżący p = graf [w]; // Rozpoczynamy przeglądanie sąsiadów while(p) { u = p->v; // u-numer wierzchołka będącego sąsiadem if(u == v) return true; // Cykl znaleziony, kończymy if(!visited [u] && DFSfindCycle (graf, v, u, S, visited)) return true; p = p->next; } S.pop(); // Usuwamy bieżący wierzchołek ze stosu return false; } // ********************** // *** PROGRAM GŁÓWNY *** // ********************** int main() { int n, m, i, j, v1, v2; SLel * p, * r, ** A; bool * visited; Stack S, T; cin >> n >> m; // Czytamy liczbę wierzchołków i krawędzi A = new SLel * [n]; // Tworzymy tablicę list sąsiedztwa // Tablice wypełniamy pustymi listami for(i = 0; i < n; i++) A [i] = NULL; // Odczytujemy kolejne definicje krawędzi for(i = 0; i < m; i++) { cin >> v1 >> v2; // Wierzchołek startowy i końcowy krawędzi p = new SLel; // Tworzymy nowy element p->v = v2; // Numerujemy go jako v2 p->next = A [v1]; // Dodajemy go na początek listy A [v1] A [v1] = p; } cout << endl; visited = new bool [n]; // Tworzymy tablicę odwiedzin for(i = 0; i < n; i++) // Przechodzimy przez kolejne wierzchołki grafu { for(j = 0; j < n; j++) // Zerujemy tablicę odwiedzin visited [j] = false; if(!DFSfindCycle (A, i, i, S, visited)) // Szukamy cyklu cout << i << "-no cycle\n"; // Komunikat else { T.push (i); while(!S.empty()) // Przerzucamy stos S do stosu T { T.push (S.top()); S.pop(); } while(!T.empty()) // Wyświetlamy ścieżkę { cout << T.top() << " "; T.pop(); } cout << endl; } } // Usuwamy dynamiczne struktury danych delete [] visited; for(i = 0; i < n; i++) { p = A [i]; while(p) { r = p; p = p->next; delete r; } } delete [] A; return 0;} |
Basic' Wyszukiwanie cykli w grafie skierowanym ' Data: 22.12.2013 ' (C)2013 mgr Jerzy Wałaszek '---------------------------------------- ' Typy dla dynamicznej tablicy list sąsiedztwa i stosu Type SLel next As SLel Ptr v As Integer End Type Type Stack Private: S As SLel Ptr ' lista zawierająca stos Public: Declare Constructor() Declare Destructor() Declare Function empty() As Integer Declare Function top As Integer Declare Sub push (ByVal v As Integer) Declare Sub pop() End Type '--------------------- ' Metody obiektu Stack '--------------------- ' Konstruktor '------------ Constructor Stack() S = 0 End Constructor ' Destruktor '----------- Destructor Stack() While S pop() Wend End Destructor ' Sprawdza, czy stos jest pusty '------------------------------ Function Stack.empty() As Integer If S = 0 Then Return 1 Return 0 End Function ' Zwraca szczyt stosu. '------------------------------ Function Stack.top() As Integer top = S->v End Function ' Zapisuje na stos '----------------- Sub Stack.push (ByVal v As Integer) Dim e As SLel Ptr e = New SLel e->v = v e->Next = S S = e End Sub ' Usuwa ze stosu '--------------- Sub Stack.pop() Dim e As SLel Ptr If S Then e = S S = S->Next Delete e End If End Sub ' Funkcja rekurencyjna wyszukująca cykl '-------------------------------------- Function DFSfindCycle (ByVal graf As SLel Ptr Ptr, _ ByVal v As integer, ByVal w As integer, _ ByRef S As Stack, _ ByVal visited As Integer Ptr) As Integer Dim As Integer u Dim As SLel Ptr p visited [w] = 1 ' Oznaczamy wierzchołek jako odwiedzony S.push (w) ' Na stos wierzchołek bieżący p = graf [w] ' Rozpoczynamy przeglądanie sąsiadów While p u = p->v ' u-numer wierzchołka będącego sąsiadem If u = v Then Return 1 ' Cykl znaleziony, kończymy if(visited [u] = 0) AndAlso (DFSfindCycle (graf, v, u, S, visited) = 1) Then Return 1 p = p->Next Wend S.pop() ' Usuwamy bieżący wierzchołek ze stosu Return 0 End Function ' ********************** ' *** PROGRAM GŁÓWNY *** ' ********************** Dim As Integer n, m, i, j, v1, v2 Dim As SLel Ptr p, r Dim As SLel Ptr Ptr A Dim As Integer Ptr visited Dim As Stack S, T Open Cons For Input As #1 Input #1, n, m ' Czytamy liczbę wierzchołków i krawędzi A = new SLel Ptr [n] ' Tworzymy tablicę list sąsiedztwa ' Tablicę wypełniamy pustymi listami For i = 0 To n-1 A [i] = 0 Next ' Odczytujemy kolejne definicje krawędzi For i = 0 To m -1 Input #1, v1, v2 ' Wierzchołek startowy i końcowy krawędzi p = new SLel ' Tworzymy nowy element p->v = v2 ' Numerujemy go jako v2 p->next = A [v1] ' Dodajemy go na początek listy A [v1] A [v1] = p Next Close #1 Print visited = New Integer [n] ' Tworzymy tablicę odwiedzin For i = 0 To n-1 ' Przechodzimy przez kolejne wierzchołki grafu For j = 0 To n-1 ' Zerujemy tablicę odwiedzin visited [j] = 0 Next If DFSfindCycle (A, i, i, S, visited) = 0 Then ' Szukamy cyklu Print i;"-no cycle" ' Komunikat Else T.push (i) While S.empty() = 0 ' Przerzucamy stos S do stosu T T.push (S.top()): S.pop() Wend While T.empty() = 0 ' Wyświetlamy ścieżkę Print T.top(); T.pop() Wend Print End If Next ' Usuwamy dynamiczne struktury danych Delete [] visited For i = 0 To n-1 p = A [i] While p r = p p = p->Next Delete r Wend Next Delete [] A End |
Wynik: |
9 15 0 1 1 4 2 5 3 0 3 1 4 2 5 1 5 8 6 3 6 4 7 4 7 5 7 6 7 8 8 3 0 1 4 2 5 8 3 0 1 4 2 5 8 3 1 2 5 8 3 1 4 2 3 1 4 2 5 8 3 4 2 5 8 3 1 4 5 8 3 1 4 2 5 6-no cycle 7-no cycle 8 3 1 4 2 5 8 |
Zespół Przedmiotowy Chemii-Fizyki-Informatyki w I Liceum Ogólnokształcącym im. Kazimierza Brodzińskiego w Tarnowie ul. Piłsudskiego 4 ©2024 mgr Jerzy Wałaszek |
Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email:
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.