|
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 |
©2026 mgr Jerzy Wałaszek
|
Dla danego grafu skierowanego należy wyznaczyć wszystkie spójne składowe.
Silnie spójna składowa (ang. strongly
connected component) jest maksymalnym podgrafem, w którym
istnieją ścieżki pomiędzy każdymi dwoma wierzchołkami. Jeśli podgraf
ten obejmuje wszystkie wierzchołki grafu, to mówimy, że dany graf
skierowany jest silnie spójny (ang. strongly
connected digraph)
![]() |
![]() |
|
| Silnie
spójna składowa (1, 2, 4, 5, 8) |
Silnie spójny graf skierowany |
Silnie spójna składowa może się redukować do jednego wierzchołka,
jeśli nie jest on połączony obustronnie z żadnym innym wierzchołkiem
grafu. Na rysunku powyżej z lewej takimi składowymi są:
Rozwiązanie naiwne opiera się bezpośrednio na definicji silnie spójnej składowej. Jest ono bardzo nieefektywne i podajemy je tylko ze względów dydaktycznych. Lepsze algorytmy znajdowania silnie spójnych składowych prezentowane są w dalszej części artykułu.
Tworzymy tablicę n elementową
Do numeracji spójnych składowych będziemy używali zmiennej cn. Inicjujemy jej wartość na 0.
Przechodzimy w pętli wierzchołki grafu od pierwszego do przedostatniego.
Szukamy wierzchołka startowego v, dla którego
Po przetworzeniu wszystkich wierzchołków w tablicy
K01: Utwórz n elementową tablicę vs K02: Ustaw elementy tablicy vs na false K03: S.push(u) ; umieszczamy wierzchołek startowy na stosie K04: vs[u] ← true ; oznaczamy go jako odwiedzony K05: Dopóki S.empty() = false, ; przejście DFS wykonuj kroki K06…K12 K06: x ← S.top() ; pobieramy ze stosu wierzchołek K07: S.pop() ; pobrany wierzchołek usuwamy ze stosu K08 Dla każdego sąsiada y wierzchołka x: ; przeglądamy sąsiadów wykonuj kroki K09…K12 K09: Jeśli y = v, to zakończ z wynikiem true ; jest ścieżka K10: Jeśli vs[y] = true, ; szukamy sąsiadów nieodwiedzonych to następny obieg pętli K08 K11: S.push(y) ; sąsiada umieszczamy na stosie K12: vs[y] ← true ; i oznaczamy jako odwiedzonego K13: Zakończ z wynikiem false ; nie ma ścieżki
K01: Utwórz n elementową tablicę vs K02: Ustaw elementy tablicy vs na false K03: S.push(v) ; umieszczamy wierzchołek startowy na stosie K04: vs[v] ← true ; oznaczamy go jako odwiedzony K05: C[v] ← cn ; ustawiamy numer składowej w C[v] K06: Dopóki S.empty() = false, ; przejście DFS wykonuj kroki K07…K13 K07: u ← S.top() ; pobieramy ze stosu wierzchołek K08: S.pop() ; pobrany wierzchołek usuwamy ze stosu K09: Jeśli (u ≠ v)(DFSBack(u,v,n,graf) = true), to C[u] ← cn ; sprawdzamy istnienie ścieżki powrotnej K10 Dla każdego sąsiada w wierzchołka u : ; przeglądamy sąsiadów wykonuj kroki K11…K13 K11: Jeśli vs[w] = true, ; szukamy sąsiadów nieodwiedzonych to następny obieg pętli K10 K12: S.push(w) ; sąsiada umieszczamy na stosie K13: vs[w] ← true ; i oznaczamy jako odwiedzonego K14: Zakończ
K01: Utwórz n elementową tablicę C[] K02: Wyzeruj tablicę C[] K03: cn ← 0 ; inicjujemy licznik składowych K04: Dla v = 0, 1, …, n-1: ; przechodzimy przez kolejne wierzchołki grafu wykonuj kroki K05…K07 K05: Jeśli C[v] > 0, ; szukamy wierzchołka nowej składowej to następny obieg pętli K04 K06: cn ← cn + 1 ; zwiększamy licznik K07: DFSscc(v,n,graf,cn,C) ; uruchamiamy przejście DFS od wierzchołka v K08: Zakończ
|
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, wyszukuje w nim wszystkie silnie spójne składowe, a następnie podaje należące do nich numery wierzchołków w grafie. |
|
Dane przykładowe (przekopiuj do
schowka i wklej do okna konsoli):
|
Pascal// Wyznaczanie silnie spójnych składowych
// Data: 18.01.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------------------
program scc;
// 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
// lista przechowująca stos
S : PSLel;
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
if S <> nil then
top := S^.v
else
top := -1;
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 bada, czy istnieje
// ścieżka od u do v
// u - wierzchołek startowy ścieżki
// v - wierzchołek końcowy ścieżki
// n - liczba wierzchołków w grafie
// graf - tablica list sąsiedztwa
// Wynik:
// true - istnieje ścieżka od u do v
// false - nie istnieje ścieżka od u do v
//---------------------------------------
function DFSback(u, v, n : integer;
var graf : TList)
: boolean;
var
i, x, y : integer;
S : Stack;
vs : array of boolean;
p : PSLel;
begin
// Tworzymy tablicę odwiedzin
SetLength(vs, n);
// i wypełniamy ją wartościami false
for i := 0 to n-1 do
vs[i] := false;
// Tworzymy stos
S.init;
// Wierzchołek startowy na stos
S.push(u);
// Oznaczamy go jako odwiedzonego
vs[u] := true;
// Uruchamiamy przejście DFS
while not S.empty do
begin
// Pobieramy wierzchołek ze stosu
x := S.top;
// Pobrany wierzchołek usuwamy ze stosu
S.pop;
// Przeglądamy sąsiadów
p := graf[x];
while p <> nil do
begin
// Numer sąsiada do y
y := p^.v;
// Jeśli sąsiadem jest wierzchołek v,
if y = v then
// to ścieżka znaleziona
begin
// Zerujemy stos
S.destroy;
// Usuwamy tablicę vs
SetLength(vs, 0);
// Kończymy z wynikiem true
Exit(true);
end;
if not vs[y] then
begin
// Nieodwiedzonego sąsiada
// umieszczamy na stosie
S.push(y);
// i oznaczamy jako odwiedzonego
vs[y] := true;
end;
// Następny sąsiad
p := p^.next;
end;
end;
// Usuwamy tablicę vs
SetLength(vs, 0);
// i kończymy z wynikiem false
DFSback := false;
end;
// Procedura przechodzi przez graf
// od wierzchołka startowego v
// i umieszcza w tablicy C informację
// o przynależności wierzchołków
// do określonej silnie spójnej składowej
// v - wierzchołek startowy
// n - liczba wierzchołków w grafie
// graf - tablica list sąsiedztwa
// cn - numer silnie spójnej składowej
// C - tablica numerów silnie spójnych
// składowych dla wierzchołków
// Wynik:
// Ustawia tablicę C
//----------------------------------------
procedure DFSscc (v, n : integer;
var graf : TList;
cn : integer;
var C : array of integer);
var
i, u, w : integer;
S : Stack;
vs : array of boolean;
p : PSLel;
begin
SetLength(vs, n);
for i := 0 to n-1 do
vs[i] := false;
S.init;
// Wierzchołek startowy na stos
S.push(v);
// Oznaczamy go jako odwiedzonego
vs[v] := true;
// Ustawiamy dla v numer składowej
C[v] := cn;
// Przejście DFS
while not S.empty do
begin
// Odczytujemy wierzchołek ze stosu
u := S.top;
// Usuwamy ze stosu odczytany
// wierzchołek
S.pop;
if(u <> v) and DFSback(u,v,n,graf) then
C[u] := cn;
// Przeglądamy sąsiadów wierzchołka u
p := graf[u];
while p <> nil do
begin
w := p^.v;
if not vs [w] then
begin
// Nieodwiedzonego sąsiada
// umieszczamy na stosie
S.push(w);
// i oznaczamy jako odwiedzonego
vs[w] := true;
end;
// Następny sąsiad
p := p^.next;
end;
end;
SetLength(vs, 0);
end;
// **********************
// *** Program główny ***
// **********************
var
// Liczba wierzchołków i krawędzi
n, m : integer;
// Tablica list sąsiedztwa grafu
graf : TList;
// Tablica numerów składowych
C : array of integer;
i, v, u, cn : integer;
p, r : PSLel;
begin
// Odczytujemy liczbę
// wierzchołków i krawędzi
read(n, m);
// Tworzymy tablice dynamiczne
SetLength(graf, n);
SetLength(C, n);
// Inicjujemy tablice
for i := 0 to n-1 do
begin
graf[i] := nil;
C[i] := 0;
end;
// Odczytujemy kolejne
// definicje krawędzi.
for i := 0 to m-1 do
begin
// Wierzchołki tworzące krawędź
read(v, u);
// Tworzymy nowy element
new(p);
// Numerujemy go jako u
p^.v := u;
// i dodajemy na początek
// listy graf[v]
p^.next := graf[v];
graf[v] := p;
end;
// Wyznaczamy silnie
// spójne składowe
//------------------
// Inicjujemy licznik składowych
cn := 0;
// Przeglądamy kolejne
// wierzchołki grafu
for v := 0 to n-1 do
if C[v] = 0 then
begin
inc(cn);
// Wyznaczamy silnie
// spójną składową
DFSscc(v,n,graf,cn,C);
end;
// Wyświetlamy silnie
// spójne składowe
writeln;
for i := 1 to cn do
begin
write('SCC', i:3, ' :');
for v := 0 to n-1 do
if C [v] = i then
write(v:3);
writeln;
end;
writeln;
// Usuwamy tablice dynamiczne
for i := 0 to n-1 do
begin
p := graf[i];
while p <> nil do
begin
r := p;
p := p^.next;
dispose(r);
end;
end;
SetLength(graf, 0);
SetLength(C, 0);
end.
|
// Wyznaczanie silnie
// spójnych składowych
// Data: 18.01.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------
#include <iostream>
#include <iomanip>
using namespace std;
// Typy dla dynamicznej tablicy
// list sąsiedztwa i stosu
struct SLel
{
SLel * next;
int v;
};
class Stack
{
private:
// lista przechowująca stos
SLel * S;
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 = nullptr;
}
// Destruktor
//-----------
Stack::~Stack()
{
while(S) pop();
}
// Sprawdza, czy stos jest pusty
//------------------------------
bool Stack::empty(void)
{
return !S;
}
// Zwraca szczyt stosu
//--------------------
int Stack::top(void)
{
if(S) return S->v;
else return -1;
}
// 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 bada, czy istnieje
// ścieżka od u do v
// u - wierzchołek startowy ścieżki
// v - wierzchołek końcowy ścieżki
// n - liczba wierzchołków w grafie
// graf - tablica list sąsiedztwa
// Wynik:
// true - istnieje ścieżka od u do v
// false - nie istnieje ścieżka od u do v
//---------------------------------------
bool DFSback(int u, int v, int n,
SLel ** graf)
{
int i, x, y;
Stack S;
bool * vs;
SLel * p;
// Tworzymy tablicę odwiedzin
vs = new bool [n];
// i wypełniamy ją wartościami false
for(i = 0; i < n; i++)
vs[i] = false;
// Wierzchołek startowy na stos
S.push(u);
// Oznaczamy go jako odwiedzonego
vs[u] = true;
// Uruchamiamy przejście DFS
while(!S.empty())
{
// Pobieramy wierzchołek ze stosu
x = S.top();
// Pobrany wierzchołek
// usuwamy ze stosu
S.pop();
// Przeglądamy sąsiadów
for(p = graf[x]; p; p = p->next)
{
// Numer sąsiada do y
y = p->v;
// Jeśli sąsiadem jest
// wierzchołek v,
// to ścieżka znaleziona
if(y == v)
{
// Usuwamy tablicę vs
delete [] vs;
// Kończymy z wynikiem true
return true;
}
if(!vs[y])
{
// Nieodwiedzonego sąsiada
// umieszczamy na stosie
S.push(y);
// i oznaczamy jako odwiedzonego
vs[y] = true;
}
}
}
// Usuwamy tablicę vs
delete [] vs;
// i kończymy z wynikiem false
return false;
}
// Procedura przechodzi przez graf
// od wierzchołka startowego v
// i umieszcza w tablicy C informację
// o przynależności wierzchołków
// do określonej silnie spójnej składowej.
// v - wierzchołek startowy
// n - liczba wierzchołków w grafie
// graf - tablica list sąsiedztwa
// cn - numer silnie spójnej składowej
// C - tablica numerów silnie spójnych
// składowych dla wierzchołków
// Wynik:
// Ustawia tablicę C
//-----------------------------------------
void DFSscc(int v, int n,
SLel ** graf,
int cn, int * C)
{
int i, u, w;
Stack S;
bool * vs;
SLel * p;
vs = new bool [n];
for(i = 0; i < n; i++)
vs[i] = false;
// Wierzchołek startowy na stos
S.push(v);
// Oznaczamy go jako odwiedzonego
vs[v] = true;
// Ustawiamy dla v numer składowej
C[v] = cn;
// Przejście DFS
while(!S.empty())
{
// Odczytujemy wierzchołek
// ze stosu
u = S.top();
// Usuwamy ze stosu
// odczytany wierzchołek
S.pop();
if((u != v) && DFSback(u,v,n,graf))
C[u] = cn;
// Przeglądamy sąsiadów
// wierzchołka u
for(p = graf[u]; p; p = p->next)
{
w = p->v;
if(!vs[w])
{
// Nieodwiedzonego sąsiada
// umieszczamy na stosie
S.push(w);
// i oznaczamy jako odwiedzonego
vs[w] = true;
}
}
}
delete [] vs;
}
// **********************
// *** Program główny ***
// **********************
int main()
{
// Liczba wierzchołków i krawędzi
int n, m;
// Tablica list sąsiedztwa
SLel ** graf;
// Tablica z numerami
// spójnych składowych
int * C;
int i, v, u, cn;
SLel *p, *r;
// Odczytujemy liczbę
// wierzchołków i krawędzi
cin >> n >> m;
// Tworzymy tablice dynamiczne
graf = new SLel * [n];
C = new int [n];
// Inicjujemy tablice
for(i = 0; i < n; i++)
{
graf[i] = nullptr;
C[i] = 0;
}
// Odczytujemy kolejne
// definicje krawędzi
for(i = 0; i < m; i++)
{
// Wierzchołki tworzące krawędź
cin >> v >> u;
// Tworzymy nowy element
p = new SLel;
// Numerujemy go jako u
p->v = u;
// Dodajemy go na początek
// listy graf[v]
p->next = graf[v];
graf[v] = p;
}
// Wyznaczamy silnie spójne składowe
//----------------------------------
// Inicjujemy licznik składowych
cn = 0;
// Przeglądamy kolejne
// wierzchołki grafu
for(v = 0; v <= n-1; v++)
// Wyznaczamy silnie
// spójną składową
if(!C[v]) DFSscc(v,n,graf,++cn,C);
// Wyświetlamy silnie spójne składowe
cout << endl;
for(i = 1; i <= cn; i++)
{
cout << "SCC" << setw(3) << i << ":";
for(v = 0; v < n; v++)
if(C[v] == i) cout << setw(3) << v;
cout << endl;
}
cout << endl;
// Usuwamy tablice dynamiczne
for(i = 0; i < n; i++)
{
p = graf[i];
while(p)
{
r = p;
p = p->next;
delete r;
}
}
delete [] graf;
delete [] C;
return 0;
}
|
Basic' Wyznaczanie silnie spójnych składowych
' Data: 18.01.2014
' (C)2014 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:
' lista zawierająca stos
S As SLel Ptr
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
If S = 0 Then
top = -1
Else
top = S->v
End If
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 bada, czy istnieje ścieżka
' od u do v
' u - wierzchołek startowy ścieżki
' v - wierzchołek końcowy ścieżki
' n - liczba wierzchołków w grafie
' graf - tablica list sąsiedztwa
' Wynik:
' 1 - istnieje ścieżka od u do v
' 0 - nie istnieje ścieżka od u do v
'-----------------------------------
Function DFSback(ByVal u As Integer, _
ByVal v As integer, _
ByVal n As integer, _
ByVal graf As SLel Ptr Ptr)_
As Integer
Dim As integer i, x, y
Dim As Stack S
Dim As Byte Ptr vs
Dim As SLel Ptr p
' Tworzymy tablicę odwiedzin
vs = New Byte [n]
' i wypełniamy ją wartościami false
For i = 0 To n-1
vs[i] = 0
Next
' Wierzchołek startowy na stos
S.push(u)
' Oznaczamy go jako odwiedzonego
vs[u] = 1
' Uruchamiamy przejście DFS
While S.empty = 0
' Pobieramy wierzchołek ze stosu
x = S.top
' Pobrany wierzchołek usuwamy ze stosu
S.pop
p = graf[x]
' Przeglądamy sąsiadów
While p
' Numer sąsiada do y
y = p->v
' Jeśli sąsiadem jest wierzchołek v,
' to ścieżka znaleziona
If y = v Then
' Usuwamy tablicę vs
Delete [] vs
' Kończymy z wynikiem true
Return 1
End If
If vs[y] = 0 Then
' Nieodwiedzonego sąsiada
' umieszczamy na stosie
S.push(y)
' i oznaczamy jako odwiedzonego
vs[y] = 1
End If
' Następny sąsiad
p = p->next
Wend
Wend
' Usuwamy tablicę vs
Delete [] vs
' i kończymy z wynikiem false
Return 0
End Function
' Procedura przechodzi przez graf
' od wierzchołka startowego v
' i umieszcza w tablicy C informację
' o przynależności wierzchołków
' do określonej silnie spójnej składowej.
' v - wierzchołek startowy
' n - liczba wierzchołków w grafie
' graf - tablica list sąsiedztwa
' cn - numer silnie spójnej składowej
' C - tablica numerów silnie spójnych
' składowych dla wierzchołków
' Wynik:
' Ustawia tablicę C
'--------------------------------------
Sub DFSscc(ByVal v As integer, _
ByVal n As integer, _
ByVal graf As SLel Ptr ptr, _
ByVal cn As integer, _
ByVal C As Integer Ptr)
Dim As Integer i, u, w
Dim As Stack S
Dim As Byte Ptr vs
Dim As SLel Ptr p
vs = New Byte [n]
For i = 0 To n-1
vs[i] = 0
Next
' Wierzchołek startowy na stos
S.push(v)
' Oznaczamy go jako odwiedzonego
vs[v] = 1
' Ustawiamy dla v numer składowej
C[v] = cn
' Przejście DFS
While S.empty = 0
' Odczytujemy wierzchołek ze stosu
u = S.top
' Usuwamy ze stosu odczytany wierzchołek
S.pop
If (u <> v) AndAlso _
(DFSback(u,v,n,graf) = 1) Then _
C[u] = cn
p = graf[u]
' Przeglądamy sąsiadów wierzchołka u
While p
w = p->v
If vs[w] = 0 Then
' Nieodwiedzonego sąsiada
' umieszczamy na stosie
S.push(w)
' i oznaczamy jako odwiedzonego
vs[w] = 1
End If
p = p->Next
Wend
Wend
Delete [] vs
End Sub
' **********************
' *** Program główny ***
' **********************
' Liczba wierzchołków i krawędzi
Dim As Integer n, m
' Tablica list sąsiedztwa grafu
Dim As SLel Ptr Ptr graf
Dim As Integer Ptr C
Dim As Integer i, v, u, cn
Dim As SLel Ptr p, r
Open Cons For Input As #1
' Odczytujemy liczbę wierzchołków i krawędzi
Input #1, n, m
' Tworzymy tablice dynamiczne
graf = New SLel Ptr [n]
C = New Integer [n]
' Inicjujemy tablice
For i = 0 To n-1
graf[i] = 0
C[i] = 0
Next
' Odczytujemy kolejne definicje krawędzi.
For i = 1 To m
' Wierzchołki tworzące krawędź
Input #1, v, u
' Tworzymy nowy element
p = New SLel
' Numerujemy go jako u
p->v = u
' Dodajemy go na początek listy graf[v]
p->next = graf[v]
graf[v] = p
Next
Close #1
' Wyznaczamy silnie spójne składowe
'----------------------------------
' Inicjujemy licznik składowych
cn = 0
' Przeglądamy kolejne wierzchołki grafu
For v = 0 To n-1
If C[v] = 0 Then
cn += 1
' Wyznaczamy silnie spójną składową
DFSscc(v,n,graf,cn,C)
End If
Next
' Wyświetlamy silnie spójne składowe
Print
For i = 1 To cn
Print Using "SCC### :"; i;
For v = 0 To n-1
If C[v] = i Then Print Using "###";v;
Next
Print
Next
Print
' Usuwamy tablice dynamiczne
For i = 0 To n-1
p = graf[i]
While p
r = p
p = p->next
Delete r
Wend
Next
Delete [] graf
Delete [] C
End
|
Python
(dodatek)# Wyznaczanie silnie spójnych składowych
# Data: 3.01.2025
# (C)2025 mgr Jerzy Wałaszek
#---------------------------------------
# Klasy dla dynamicznej tablicy
# list sąsiedztwa i stosu
class SLel:
def __init__(self):
self.next = None
self.v = 0
class Stack:
# Konstruktor
def __init__(self):
self.s = None
# Destruktor
def __del__(self):
while self.s:
self.pop()
# Sprawdza, czy stos jest pusty
def empty(self):
return not self.s
# Zwraca szczyt stosu
def top(self):
if self.s:
return self.s.v
else:
return -1
# Zapisuje na stos
def push(self, v):
e = SLel()
e.v = v
e.next = self.s
self.s = e
# Usuwa ze stosu
def pop(self):
if self.s:
self.s = self.s.next
# Funkcja bada, czy istnieje ścieżka
# od u do v
# u - wierzchołek startowy ścieżki
# v - wierzchołek końcowy ścieżki
# n - liczba wierzchołków w grafie
# graf - tablica list sąsiedztwa
# Wynik:
# 1 - istnieje ścieżka od u do v
# 0 - nie istnieje ścieżka od u do v
#-----------------------------------
def dfs_back(u, v, n, graf):
s = Stack()
# Tworzymy tablicę odwiedzin
vs = [False] * n
# Wierzchołek startowy na stos
s.push(u)
# Oznaczamy go jako odwiedzonego
vs[u] = True
# Uruchamiamy przejście DFS
while not s.empty():
# Pobieramy wierzchołek ze stosu
x = s.top()
# Pobrany wierzchołek usuwamy
# ze stosu
s.pop()
p = graf[x]
# Przeglądamy sąsiadów
while p:
# Numer sąsiada do y
y = p.v
# Jeśli sąsiadem jest
# wierzchołek v, to ścieżka
# jest znaleziona
if y == v:
# Usuwamy tablicę vs
del vs
# Kończymy z wynikiem true
return True
if not vs[y]:
# Nieodwiedzonego sąsiada
# umieszczamy na stosie
s.push(y)
# i oznaczamy jako
# odwiedzonego
vs[y] = True
# Następny sąsiad
p = p.next
# Usuwamy tablicę vs
del vs
# i kończymy z wynikiem false
return False
# Procedura przechodzi przez graf
# od wierzchołka startowego v
# i umieszcza w tablicy C informację
# o przynależności wierzchołków
# do określonej silnie spójnej składowej.
# v - wierzchołek startowy
# n - liczba wierzchołków w grafie
# graf - tablica list sąsiedztwa
# cn - numer silnie spójnej składowej
# c - tablica numerów silnie spójnych
# składowych dla wierzchołków
# Wynik:
# Ustawia tablicę C
#--------------------------------------
def dfs_scc(v, n, graf, cn, c):
s = Stack()
vs = [False] * n
# Wierzchołek startowy na stos
s.push(v)
# Oznaczamy go jako odwiedzonego
vs[v] = 1
# Ustawiamy dla v numer składowej
c[v] = cn
# Przejście DFS
while not s.empty():
# Odczytujemy wierzchołek ze stosu
u = s.top()
# Usuwamy ze stosu odczytany
# wierzchołek
s.pop()
if (u != v) and dfs_back(u,v,n,graf):
c[u] = cn
p = graf[u]
# Przeglądamy sąsiadów wierzchołka u
while p:
w = p.v
if not vs[w]:
# Nieodwiedzonego sąsiada
# umieszczamy na stosie
s.push(w)
# i oznaczamy jako
# odwiedzonego
vs[w] = True
p = p.next
del vs
# **********************
# *** Program główny ***
x = input().split()
n = int(x[0])
m = int(x[1])
# Tworzymy tablice dynamiczne
graf = [None] * n
c = [0] * n
# Odczytujemy kolejne definicje krawędzi.
for i in range(m):
# Wierzchołki tworzące krawędź
x = input().split()
v = int(x[0])
u = int(x[1])
# Tworzymy nowy element
p = SLel()
# Numerujemy go jako u
p.v = u
# Dodajemy go na początek listy graf[v]
p.next = graf[v]
graf[v] = p
# Wyznaczamy silnie spójne składowe
#----------------------------------
# Inicjujemy licznik składowych
cn = 0
# Przeglądamy kolejne wierzchołki grafu
for v in range(n):
if not c[v]:
cn += 1
# Wyznaczamy silnie spójną składową
dfs_scc(v,n,graf,cn,c)
# Wyświetlamy silnie spójne składowe
print()
for i in range(1,cn+1):
print("SCC%3d:" % i, end="")
for v in range(n):
if c[v] == i:
print("%3d" % v, end="")
print()
print()
# Usuwamy tablice dynamiczne
for i in range(n):
while graf[i]:
graf[i] = graf[i].next
del graf, c
|
| Wynik: | |
13 27 0 1 0 4 1 2 1 4 1 5 1 8 1 11 2 6 3 1 3 7 4 8 5 2 5 8 6 5 6 7 6 9 8 4 9 5 9 7 10 8 10 11 11 8 11 9 11 12 12 3 12 6 12 9 SCC 1 : 0 SCC 2 : 1 3 11 12 SCC 3 : 2 5 6 9 SCC 4 : 4 8 SCC 5 : 7 SCC 6 : 10 |
![]() |
Algorytm Kosaraju wyznacza wszystkie silnie spójne składowe grafu w czasie liniowym. Wykorzystuje on stos, dwa przejścia DFS oraz transpozycję grafu. Zasada działania tego algorytmu jest następująca:
Ustawiamy wszystkie wierzchołki grafu jako nieodwiedzone. Następnie przeglądamy je po kolei od pierwszego do ostatniego. Jeśli napotkamy wierzchołek nieodwiedzony (na początku algorytmu będzie to pierwszy wierzchołek grafu), to od tego wierzchołka uruchamiamy rekurencyjne przejście w głąb DFS. Zadaniem tego przejścia jest odwiedzenie wszystkich dostępnych wierzchołków, do których wiodą ścieżki z wierzchołka startowego. Dodatkowo DFS po zakończeniu rekurencyjnego odwiedzania sąsiadów umieszcza odwiedzony wierzchołek na stosie.
Gdy algorytm się zakończy, stos będzie zawierał wierzchołki w kolejności "czasu" ich odwiedzenia przez DFS. Na szczycie stosu będzie wierzchołek startowy.
Teraz dokonujemy transpozycji grafu i znów oznaczamy wszystkie wierzchołki jako nieodwiedzone. Następnie dopóki stos zawiera wierzchołki, pobieramy ze stosu wierzchołek i jeśli jest on nieodwiedzony, to w grafie transponowanym uruchamiamy drugie przejście DFS od tego wierzchołka. Wszystkie odwiedzone przez DFS wierzchołki należą do tej samej silnie spójnej składowej. Wystarczy je wypisywać lub zapamiętywać w miarę odwiedzania przez to drugie przejście DFS.
K01: vs[v] ← true ; wierzchołek v oznaczamy jako odwiedzony K02: Dla każdego sąsiada u wierzchołka v: ; przeglądamy sąsiadów wykonuj krok K03 K03: Jeśli vs[u] = false, ; jeśli sąsiad nieodwiedzony, to DFSStack(u,vs,S,graf) ; to uruchamiamy rekurencyjne DFS K04: S.push(v) ; po przetworzeniu sąsiadów v umieszczamy na stosie K05: Zakończ
K01: vs[v] ← true ; wierzchołek v oznaczamy jako odwiedzony K02: Pisz v ; wyprowadzamy wierzchołek na wyjście K03: Dla każdego sąsiada u wierzchołka v: ; przeglądamy sąsiadów wykonuj krok K04 K04: Jeśli vs[u] = false, ; jeśli sąsiad nieodwiedzony, to DFSprint(u, vs, graf) ; to uruchamiamy rekurencyjne DFS K05: Zakończ
K01: Utwórz n elementową tablicę vs K02: Tablicę vs wypełnij wartościami false K03: Utwórz pusty stos S K04: Dla v = 0, 1, …, n-1: ; przeglądamy kolejne wierzchołki grafu wykonuj krok K05 K05: Jeśli vs[v] = false, ; w wierzchołku nieodwiedzonym to DSFStack(v,vs,S,graf) ; uruchamiamy DFS K06: Transponuj graf K07: Tablicę vs wypełnij wartościami false K08: cn ← 0 ; zerujemy licznik silnie spójnych składowych K09: Dopóki S.empty() = false, ; teraz przetwarzamy wierzchołki zgodnie ze stosem wykonuj kroki K10…K14 K10: v ← S.top(); S.pop() ; pobieramy wierzchołek ze stosu K11: Jeśli vs[v] = true, ; szukamy wierzchołka nieodwiedzonego to następny obieg pętli K09 K12: cn ← cn+1 ; zwiększamy licznik silnie spójnych składowych K13: Pisz "SCC ", cn, ": " K14: DFSprint(v,vs,graf) ; wypisujemy wierzchołki silnie spójnej składowej K15: Zakończ
|
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, wyszukuje w nim wszystkie silnie spójne składowe, a następnie podaje należące do nich numery wierzchołków w grafie. |
|
Dane przykładowe (przekopiuj do schowka i wklej do okna konsoli):
|
Pascal// Wyznaczanie silnie
// spójnych składowych
// Algorytm Korsaraju
// Data: 26.01.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------
program scc;
// 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
// lista przechowująca stos
S : PSLel;
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
if empty = true then
top := -1
else
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;
// Przechodzi graf algorytmem DFS,
// umieszczając na stosie
// napotkane po drodze wierzchołki.
// v - numer wierzchołka startowego
// vs - tablica odwiedzin
// S - stos
// graf - tablica list sąsiedztwa
//---------------------------------
procedure DFSStack(v : integer;
var vs : array of boolean;
var S : Stack;
var graf : TList);
var
p : PSLel;
begin
// Oznaczamy v jako odwiedzony
vs[v] := true;
// Przeglądamy sąsiadów v
p := graf[v];
while p <> nil do
begin
if not vs[p^.v] then
DFSStack(p^.v, vs, S, graf);
p := p^.next;
end;
S.push (v);
end;
// Wyświetla wierzchołki kolejno
// odwiedzane przez DFS
// v - wierzchołek startowy
// vs - tablica odwiedzin
// graf - tablica list sąsiedztwa
//-------------------------------
procedure DFSprint(v : integer;
var vs : array of boolean;
var graf : TList);
var
p : PSLel;
begin
vs[v] := true;
write(v:3);
p := graf[v];
while p <> nil do
begin
if not vs[p^.v] then
DFSprint(p^.v, vs, graf);
p := p^.next;
end;
end;
// **********************
// *** Program główny ***
// **********************
var
// Liczba wierzchołków i krawędzi
n, m : integer;
// Tablice list sąsiedztwa
A, AT : TList;
vs : array of boolean;
S : Stack;
i, v, u, cn : integer;
p, r : PSLel;
begin
// Odczytujemy liczbę
// wierzchołków i krawędzi
read(n, m);
// Tworzymy tablice dynamiczne
SetLength(A, n);
SetLength(AT, n);
// Inicjujemy tablice
for i := 0 to n-1 do
begin
A[i] := nil;
AT[i] := nil;
end;
// Odczytujemy kolejne
// definicje krawędzi.
for i := 0 to m-1 do
begin
// Wierzchołki tworzące krawędź
read(v, u);
// Tworzymy nowy element
new(p);
// Numerujemy go jako u
p^.v := u;
// i dodajemy na początek
// listy A[v]
p^.next := A[v];
A[v] := p;
end;
writeln;
// Wyznaczamy silnie spójne składowe
//----------------------------------
// Tworzymy tablicę odwiedzin
SetLength(vs, n);
// i wypełniamy ją wartościami false
for i := 0 to n-1 do
vs[i] := false;
// Tworzymy pusty stos
S.init;
// Przeglądamy kolejne
// wierzchołki grafu
for v := 0 to n-1 do
if not vs[v] then
DFSStack(v, vs, S, A);
// Transponujemy graf
//-------------------
// Przeglądamy kolejne wierzchołki
for v := 0 to n-1 do
begin
// Przeglądamy sąsiadów v
p := A[v];
while p <> nil do
begin
// Tworzymy nowy element listy
new(r);
// Zapamiętujemy w nim wierzchołek v
r^.v := v;
// i dodajemy do listy sąsiada
r^.next := AT[p^.v];
AT[p^.v] := r;
// Następny sąsiad
p := p^.next;
end;
end;
// Zerujemy tablicę odwiedzin
for v := 0 to n-1 do
vs[v] := false;
// Inicjujemy licznik składowych
cn := 0;
// Przetwarzamy wierzchołki ze stosu
while not S.empty do
begin
// Pobieramy wierzchołek ze stosu
v := S.top; S.pop;
if not vs[v] then
begin
// Zwiększamy licznik składowych
inc(cn);
write('SCC', cn:3, ' :');
DFSprint(v, vs, AT);
writeln;
end;
end;
// Usuwamy tablice dynamiczne
for i := 0 to n-1 do
begin
p := A[i];
while p <> nil do
begin
r := p;
p := p^.next;
dispose(r);
end;
p := AT[i];
while p <> nil do
begin
r := p;
p := p^.next;
dispose(r);
end;
end;
SetLength(A, 0);
SetLength(AT, 0);
SetLength(vs, 0);
S.destroy;
end.
|
C++// Wyznaczanie silnie
// spójnych składowych
// Algorytm Korsaraju
// Data: 26.01.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------
#include <iostream>
#include <iomanip>
using namespace std;
// Typy dla dynamicznej tablicy
// list sąsiedztwa i stosu
struct SLel
{
SLel * next;
int v;
};
class Stack
{
private:
// lista przechowująca stos
SLel * S;
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 = nullptr;
}
// Destruktor
//-----------
Stack::~Stack()
{
while(S) pop();
}
// Sprawdza, czy stos jest pusty
//------------------------------
bool Stack::empty(void)
{
return !S;
}
// Zwraca szczyt stosu
//--------------------
int Stack::top(void)
{
if(S) return S->v;
else return -1;
}
// 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;
}
}
// Przechodzi graf algorytmem DFS,
// umieszczając na stosie
// napotkane po drodze wierzchołki.
// v - numer wierzchołka startowego
// vs - tablica odwiedzin
// S - stos
// graf - tablica list sąsiedztwa
//---------------------------------
void DFSStack(int v,
bool * vs,
Stack & S,
SLel ** graf)
{
SLel * p;
// Oznaczamy v jako odwiedzony
vs[v] = true;
// Przeglądamy sąsiadów v
for(p = graf[v]; p; p = p->next)
if(!vs[p->v])
DFSStack(p->v, vs, S, graf);
S.push(v);
}
// Wyświetla wierzchołki kolejno
// odwiedzane przez DFS
// v - wierzchołek startowy
// vs - tablica odwiedzin
// graf - tablica list sąsiedztwa
//-------------------------------
void DFSprint(int v,
bool * vs,
SLel ** graf)
{
SLel * p;
vs[v] = true;
cout << setw(3) << v;
for(p = graf[v]; p; p = p->next)
if(!vs[p->v])
DFSprint(p->v, vs, graf);
}
// **********************
// *** Program główny ***
// **********************
int main()
{
// Liczba wierzchołków i krawędzi
int n, m;
// Tablice list sąsiedztwa
SLel **A, **AT;
bool * vs;
Stack S;
int i, v, u, cn;
SLel *p, *r;
// Odczytujemy liczbę
// wierzchołków i krawędzi
cin >> n >> m;
// Tworzymy tablice dynamiczne
A = new SLel * [n];
AT = new SLel * [n];
// Inicjujemy tablice
for(i = 0; i < n; i++)
A[i] = AT[i] = nullptr;
// Odczytujemy kolejne
// definicje krawędzi.
for(i = 0; i < m; i++)
{
// Wierzchołki tworzące krawędź
cin >> v >> u;
// Tworzymy nowy element
p = new SLel;
// Numerujemy go jako u
p->v = u;
// i dodajemy na początek
// listy A[v]
p->next = A[v];
A[v] = p;
}
cout << endl;
// Wyznaczamy silnie spójne składowe
//----------------------------------
// Tworzymy tablicę odwiedzin
vs = new bool [n];
// i wypełniamy ją wartościami false
for(i = 0; i < n; i++)
vs[i] = false;
// Przeglądamy kolejne
// wierzchołki grafu
for(v = 0; v < n; v++)
if(!vs[v])
DFSStack(v, vs, S, A);
// Transponujemy graf
//-------------------
// Przeglądamy kolejne wierzchołki
for(v = 0; v < n; v++)
// Przeglądamy sąsiadów v
for(p = A[v]; p; p = p->next)
{
// Tworzymy nowy element listy
r = new SLel;
// Zapamiętujemy w nim
// wierzchołek v
r->v = v;
// i dodajemy do listy sąsiada
r->next = AT[p->v];
AT[p->v] = r;
}
// Zerujemy tablicę odwiedzin
for(v = 0; v < n; v++)
vs[v] = false;
// Inicjujemy licznik składowych
cn = 0;
// Przetwarzamy wierzchołki
// ze stosu
while(!S.empty())
{
// Pobieramy wierzchołek ze stosu
v = S.top(); S.pop();
if(!vs [v])
{
cout << "SCC" << setw(3)
<< ++cn << ":";
DFSprint(v, vs, AT);
cout << endl;
}
}
// Usuwamy tablice dynamiczne
for(i = 0; i < n; i++)
{
p = A[i];
while(p)
{
r = p;
p = p->next;
delete r;
}
p = AT[i];
while(p)
{
r = p;
p = p->next;
delete r;
}
}
delete [] A;
delete [] AT;
delete [] vs;
return 0;
}
|
Basic' Wyznaczanie silnie spójnych składowych
' Algorytm Korsaraju
' Data: 25.01.2014
' (C)2014 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:
' lista zawierająca stos
S As SLel Ptr
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
If S = 0 Then
top = -1
Else
top = S->v
End If
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
' Przechodzi graf algorytmem DFS,
' umieszczając na stosie
' napotkane po drodze wierzchołki.
' v - numer wierzchołka startowego
' vs - tablica odwiedzin
' S - stos
' graf - tablica list sąsiedztwa
'---------------------------------
Sub DFSStack(ByVal v As integer, _
ByVal vs As Byte Ptr, _
ByRef S As Stack, _
ByVal graf As SLel Ptr Ptr)
Dim As SLel Ptr p
' Oznaczamy v jako odwiedzony
vs[v] = 1
' Przeglądamy sąsiadów v
p = graf[v]
While p
If vs[p->v] = 0 Then _
DFSStack(p->v, vs, S, graf)
p = p->next
Wend
S.push(v)
End Sub
' Wyświetla wierzchołki kolejno
' odwiedzane przez DFS
' v - wierzchołek startowy
' vs - tablica odwiedzin
' graf - tablica list sąsiedztwa
'-------------------------------
Sub DFSprint(ByVal v As integer, _
ByVal vs As Byte ptr, _
ByVal graf As SLel Ptr Ptr)
Dim As SLel Ptr p
vs[v] = 1
Print Using "###";v;
p = graf[v]
While p <> 0
If vs[p->v] = 0 Then _
DFSprint(p->v, vs, graf)
p = p->next
Wend
End Sub
' **********************
' *** Program główny ***
' **********************
' Liczba wierzchołków i krawędzi
Dim As Integer n, m
' Tablice list sąsiedztwa
Dim As SLel Ptr Ptr A, AT
Dim As Byte Ptr vs
Dim As Stack S
Dim As Integer i, v, u, cn
Dim As SLel Ptr p, r
Open Cons For Input As #1
' Odczytujemy liczbę
' wierzchołków i krawędzi
Input #1, n, m
' Tworzymy tablice dynamiczne
A = New SLel Ptr [n]
AT = New SLel Ptr [n]
' Inicjujemy tablice
For i = 0 To n-1
A[i] = 0
AT[i] = 0
Next
' Odczytujemy kolejne definicje krawędzi.
For i = 1 To m
' Wierzchołki tworzące krawędź
Input #1, v, u
' Tworzymy nowy element
p = New SLel
' Numerujemy go jako u
p->v = u
' Dodajemy go na początek listy A[v]
p->next = A[v]
A[v] = p
Next
Close #1
Print
' Wyznaczamy silnie spójne składowe
'----------------------------------
' Tworzymy tablicę odwiedzin
vs = New Byte [n]
' i wypełniamy ją wartościami false
For i = 0 To n-1
vs[i] = 0
Next
' Przeglądamy kolejne wierzchołki grafu
For v = 0 To n-1
If vs[v] = 0 Then _
DFSStack(v, vs, S, A)
Next
' Transponujemy graf
'-------------------
' Przeglądamy kolejne wierzchołki
For v = 0 To n-1
' Przeglądamy sąsiadów v
p = A[v]
While p
' Tworzymy nowy element listy
r = New SLel
' Zapamiętujemy w nim wierzchołek v
r->v = v
' i dodajemy do listy sąsiada
r->next = AT[p->v]
AT[p->v] = r
p = p->Next
Wend
Next
' Zerujemy tablicę odwiedzin
For v = 0 To n-1
vs[v] = 0
Next
' Inicjujemy licznik składowych
cn = 0
' Przetwarzamy wierzchołki ze stosu
While S.empty = 0
' Pobieramy wierzchołek ze stosu
v = S.top: S.pop
If vs[v] = 0 Then
cn += 1
Print Using "SCC### :";cn;
DFSprint(v, vs, AT)
Print
End If
Wend
' Usuwamy tablice dynamiczne
For i = 0 To n-1
p = A[i]
While p
r = p
p = p->Next
Delete r
Wend
p = AT[i]
While p
r = p
p = p->Next
Delete r
Wend
Next
Delete [] A
Delete [] AT
Delete [] vs
End
|
Python
(dodatek)# Wyznaczanie silnie spójnych składowych
# Algorytm Korsaraju
# Data: 25.01.2014
# (C)2014 mgr Jerzy Wałaszek
#---------------------------------------
# Klasy dla dynamicznej tablicy
# list sąsiedztwa i stosu
class SLel:
def __init__(self):
self.next = None
self.v = 0
class Stack:
# Konstruktor
def __init__(self):
# lista zawierająca stos
self.s = None
# Destruktor
def __del__(self):
while self.s:
self.pop()
# Sprawdza, czy stos jest pusty
def empty(self):
return not self.s
# Zwraca szczyt stosu.
def top(self):
if not self.s:
return -1
else:
return self.s.v
# Zapisuje na stos
def push(self, v):
e = SLel()
e.v = v
e.next = self.s
self.s = e
# Usuwa ze stosu
def pop(self):
if self.s:
self.s = self.s.next
# Przechodzi graf algorytmem DFS,
# umieszczając na stosie
# napotkane po drodze wierzchołki.
# v - numer wierzchołka startowego
# vs - tablica odwiedzin
# S - stos
# graf - tablica list sąsiedztwa
def dfs_stack(v, vs, s, graf):
# Oznaczamy v jako odwiedzony
vs[v] = True
# Przeglądamy sąsiadów v
p = graf[v]
while p:
if not vs[p.v]:
dfs_stack(p.v, vs, s, graf)
p = p.next
s.push(v)
# Wyświetla wierzchołki kolejno
# odwiedzane przez DFS
# v - wierzchołek startowy
# vs - tablica odwiedzin
# graf - tablica list sąsiedztwa
def dfs_print(v, vs, graf):
vs[v] = True
print("%3d" % v, end="")
p = graf[v]
while p:
if not vs[p.v]:
dfs_print(p.v, vs, graf)
p = p.next
# **********************
# *** Program główny ***
# **********************
s = Stack()
# Odczytujemy liczbę
# wierzchołków i krawędzi
x = input().split()
n = int(x[0])
m = int(x[1])
# Tworzymy tablice dynamiczne
a = [None] * n
at = [None] * n
# Odczytujemy kolejne definicje krawędzi.
for i in range(m):
# Wierzchołki tworzące krawędź
x = input().split()
v = int(x[0])
u = int(x[1])
# Tworzymy nowy element
p = SLel()
# Numerujemy go jako u
p.v = u
# Dodajemy go na początek listy A[v]
p.next = a[v]
a[v] = p
print()
# Wyznaczamy silnie spójne składowe
#----------------------------------
# Tworzymy tablicę odwiedzin
vs = [False] * n
# Przeglądamy kolejne wierzchołki grafu
for v in range(n):
if not vs[v]:
dfs_stack(v, vs, s, a)
# Transponujemy graf
#-------------------
# Przeglądamy kolejne wierzchołki
for v in range(n):
# Przeglądamy sąsiadów v
p = a[v]
while p:
# Tworzymy nowy element listy
r = SLel()
# Zapamiętujemy w nim wierzchołek v
r.v = v
# i dodajemy do listy sąsiada
r.next = at[p.v]
at[p.v] = r
p = p.next
# Zerujemy tablicę odwiedzin
for v in range(n):
vs[v] = False
# Inicjujemy licznik składowych
cn = 0
# Przetwarzamy wierzchołki ze stosu
while not s.empty():
# Pobieramy wierzchołek ze stosu
v = s.top()
s.pop()
if not vs[v]:
cn += 1
print("SCC%3d :" % cn, end="")
dfs_print(v, vs, at)
print()
# Usuwamy tablice dynamiczne
for i in range(n):
while a[i]:
a[i] = a[i].next
while at[i]:
at[i] = at[i].next
del a, at, vs
|
| Wynik: | |
13 27 0 1 0 4 1 2 1 4 1 5 1 8 1 11 2 6 3 1 3 7 4 8 5 2 5 8 6 5 6 7 6 9 8 4 9 5 9 7 10 8 10 11 11 8 11 9 11 12 12 3 12 6 12 9 SCC 1 : 10 SCC 2 : 0 SCC 3 : 1 3 12 11 SCC 4 : 9 6 2 5 SCC 5 : 7 SCC 6 : 4 8 |
![]() |
Opisany wcześniej algorytm Koraju wymagał wykonania dwóch przejść DFS (jednego w grafie wyjściowym oraz jednego w grafie transponowanym). Poniżej przedstawiamy algorytm opracowany przez Tarjana, który wykonuje tylko jedno przejście DFS, jest zatem szybszy.
Zasada działania algorytmu Tarjana opiera się na przejściu rekurencyjnym DFS, w trakcie którego numerujemy kolejno odwiedzane wierzchołki. Przy odwiedzaniu wierzchołków DFS wyznacza minimalny numer wierzchołka, do którego istnieje ścieżka biegnąca od bieżącego wierzchołka. Numer ten jest zapamiętywany w parametrze Low związanym z każdym wierzchołkiem grafu. Parametr Low można prosto wyznaczyć na podstawie numerów oraz parametrów Low wierzchołków sąsiednich.
Algorytm Tarjana wykorzystuje stos do składowania odwiedzanych wierzchołków oraz do identyfikowania silnie spójnych składowych. Efektem działania tego algorytmu jest lista, która zawiera listy wierzchołków należących do tej samej silnie spójnej składowej grafu wyjściowego.
K01: cvn ← cvn+1 ; zwiększamy numer wierzchołka K02: VN[v] ← cvn ; i zapamiętujemy go w tablicy VN K03: VLow[v] ← cvn ; oraz wstępnie w tablicy VLow K04: S.push(v) ; wierzchołek umieszczamy na stosie K05: VS[v] ← true ; i zapamiętujemy w VS, że jest on na stosie K06: Dla każdego sąsiada u wierzchołka v : ; przeglądamy wykonuj kroki K07…K12 ; kolejnych sąsiadów wierzchołka v K07: Jeśli VN[u] = 0, ; sprawdzamy, czy wierzchołek był odwiedzony to idź do K11 K08: Jeśli VS[u] = false, ; sprawdzamy, czy wierzchołek u jest na stosie to następny obieg pętli K06 K09: VLow[v] ← min(VLow[v], VN[u]) ; jeśli tak, to wyznaczamy najmniejszy ; numer dostępnego wierzchołka z v K10: Następny obieg pętli K06 ; i kontynuujemy pętlę K11: DFSscc(u,cvn,VN,VLow,VS,S,Lscc,graf) ; wierzchołek nieodwiedzony: ; rekurencyjnie wywołujemy dla niego DFSscc() K12: VLow[v] ← min(VLow[v],VLow[u]) ; i wyznaczamy najmniejszy numer ; dostępnego wierzchołka z v K13: Jeśli VLow[v] ≠ VN[v], ; sprawdzamy, to zakończ ; czy znaleźliśmy całą silnie spójną składową K14: sccp ← nil ; tworzymy pustą listę wierzchołków K15: u ← S.top() ; pobieramy ze stosu wierzchołek silnie spójnej składowej K16: S.pop() ; pobrany wierzchołek usuwamy ze stosu K17: VS[u] ← false ; zaznaczamy, że wierzchołka już nie ma na stosie K18: Utwórz nowy element listy wierzchołków ; wierzchołek dodajemy ; do listy silnie spójnej składowej K19: p ← adres nowego elementu listy wierzchołków K20: p→v ← u K21: p→next ← sccp K22: sccp ← p K23: Jeśli u ≠ v, ; pętlę kontynuujemy, aż do dotarcia do korzenia składowej to idź do kroku K15 K24: Utwórz nowy element listy list ; listę wierzchołków sccp dodajemy ; do listy Lscc K25: listp ← adres nowego elementu listy list K26: listp→v ← sccp K27: listp→next ← Lscc K28: Lscc ← listp K29: Zakończ
K01: Utwórz pusty stos S K02: Utwórz n elementową tablicę VN i wyzeruj ją K03: Utwórz n elementową tablicę VS i wyzeruj ją K04: Utwórz n elementową tablicę VLow K05: cvn ← 0 ; zerujemy numer wierzchołka K06: Lscc ← nil ; tworzymy pustą listę silnie spójnych składowych K07: Dla v = 0, 1, …, n-1: ; przeglądamy kolejne wierzchołki grafu wykonuj krok K08 K08: Jeśli VN[v] = 0, ; w wierzchołkach nieodwiedzonych uruchamiamy DFS to DFSscc(v,cvn,VN,VLow,VS,S,Lscc,graf) K09: listp ← Lscc ; przeglądamy listę silnie spójnych składowych K10: Dopóki listp ≠ nil, wykonuj kroki K11…K16 K11: p ← listp→v ; przeglądamy listę wierzchołków K12: Dopóki p ≠ nil, wykonuj kroki K13…K14 K13: Pisz(p→v) ; wypisujemy wierzchołki silnie spójnej składowej K14: p ← p→next ; następny wierzchołek K15: Przejdź do następnego wiersza wydruku K16: listp ← listp→next ; następna lista K17: Zakończ
|
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, wyszukuje w nim wszystkie silnie spójne składowe za pomocą algorytmu Tarjana, a następnie podaje należące do nich numery wierzchołków w grafie. |
|
Dane przykładowe (przekopiuj do
schowka i wklej do okna konsoli):
|
Pascal// Wyznaczanie silnie
// spójnych składowych
// Algorytm Tarjana
// Data: 2.02.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------
program scc;
// Typy danych
type
PSLel = ^SLel;
SLel = record
next : PSLel;
v : integer;
end;
// Typ danych dla listy
// silnie spójnych składowych
//---------------------------
PsSLel = ^sSLel;
sSLel = record
next : PsSLel;
v : PSLel;
end;
// Definicja typu
// obiektowego Stack
//------------------
Stack = object
private
// lista przechowująca stos
S : PSLel;
public
constructor init;
destructor destroy;
function empty : boolean;
function top : integer;
procedure push(v : integer);
procedure pop;
end;
// Zmienne globalne
//-----------------
var
n, m, cvn : integer;
VN, VLow : array of integer;
VS : array of boolean;
S : Stack;
graf : array of PSLel;
Lscc : PsSLel;
// Metody klasy Stack
//-------------------
// 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
if S <> nil then
top := S^.v
else
top := -1;
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;
// Zwraca mniejszą z dwóch liczb
//------------------------------
function min(a, b : integer)
: integer;
begin
if a < b then
min := a
else
min := b;
end;
// Procedura wykonująca
// przejście DFS
// i wyznaczająca
// silnie spójną składową.
// v - wierzchołek startowy dla DFS
//---------------------------------
procedure DFSscc(v : integer);
var
u : integer;
sccp, p : PSLel;
listp : PsSLel;
begin
// Zwiększamy numer wierzchołka
inc(cvn);
// Numerujemy bieżący wierzchołek
VN[v] := cvn;
// Ustalamy wstępnie parametr Low
VLow[v] := cvn;
// Wierzchołek na stos
S.push(v);
// Zapamiętujemy,
// iż v jest na stosie
VS[v] := true;
// Przeglądamy listę sąsiadów
p := graf[v];
while p <> nil do
begin
// Jeśli sąsiad jest
// nieodwiedzony, to
if VN[p^.v] = 0 then
begin
// wywołujemy dla niego
// rekurencyjnie DFS
DFSscc(p^.v);
// i obliczamy parametr Low dla v
VLow[v] := min(VLow[v],VLow[p^.v]);
end
// Jeśli sąsiad odwiedzony,
// lecz wciąż na stosie,
else if VS[p^.v] then
// to wyznaczamy parametr Low dla v
VLow[v] := min(VLow[v],VN[p^.v]);
p := p^.next;
end;
// Sprawdzamy, czy mamy
// kompletną składową
if VLow[v] = VN[v] then
begin
// Dodajemy tę składową do
// listy składowych
sccp := nil;
repeat
// Pobieramy wierzchołek
// ze stosu
u := S.top;
// Pobrany wierzchołek
// usuwamy ze stosu
S.pop;
// Zapamiętujemy, że nie ma
// go już na stosie
VS[u] := false;
// Nowy element
// listy wierzchołków
new(p);
// Zapisujemy w nim wierzchołek
p^.v := u;
// dodajemy go na początek listy
p^.next := sccp;
sccp := p;
// Kontynuujemy aż do
// korzenia składowej
until u = v;
// Nowy element listy składowych
new(listp);
// Zapisujemy w nim listę
listp^.v := sccp;
// i dołączamy na początek
// listy składowych
listp^.next := Lscc;
Lscc := listp;
end;
end;
// **********************
// *** Program główny ***
// **********************
var
i, v, u : integer;
p, r : PSLel;
listp, listr : PsSLel;
begin
// Tworzymy pusty stos
S.init;
// Odczytujemy liczbę
// wierzchołków i krawędzi
read(n, m);
// Tworzymy tablice dynamiczne
SetLength(graf, n);
SetLength(VN, n);
SetLength(VLow, n);
SetLength(VS, n);
// Inicjujemy tablice
for i := 0 to n-1 do
begin
graf[i] := nil;
VN[i] := 0;
VS[i] := false;
end;
// Odczytujemy kolejne
// definicje krawędzi.
for i := 0 to m-1 do
begin
// Wierzchołki tworzące krawędź
read(v, u);
// Tworzymy nowy element
new(p);
// Numerujemy go jako u
p^.v := u;
// i dodajemy na początek
// listy graf[v]
p^.next := graf[v];
graf[v] := p;
end;
writeln;
// Wyznaczamy silnie spójne składowe
//----------------------------------
// Zerujemy numer wierzchołka
cvn := 0;
// Tworzymy pustą listę składowych
Lscc := nil;
// Przeglądamy kolejne wierzchołki
for v := 0 to n-1 do
// W wierzchołku nieodwiedzonym
// uruchamiamy DFS
if VN[v] = 0 then DFSscc(v);
// Przeglądamy listę składowych
listp := Lscc;
// cvn jest teraz
// licznikiem składowych
cvn := 0;
while listp <> nil do
begin
// Zwiększamy numer składowej
inc(cvn);
// Wyświetlamy numer składowej
write('SCC', cvn:3, ' :');
// Przeglądamy listę wierzchołków
p := listp^.v;
while p <> nil do
begin
// Wyświetlamy wierzchołek składowej
write(p^.v:3);
// Następny wierzchołek na liście
p := p^.next;
end;
writeln;
// Następna składowa na liście
listp := listp^.next;
end;
// Usuwamy zmienne dynamiczne
for i := 0 to n-1 do
begin
p := graf[i];
while p <> nil do
begin
r := p;
p := p^.next;
dispose (r);
end;
end;
listp := Lscc;
while listp <> nil do
begin
p := listp^.v;
while p <> nil do
begin
r := p;
p := p^.next;
dispose(r);
end;
listr := listp;
listp := listp^.next;
dispose(listr);
end;
SetLength(graf, 0);
SetLength(VN, 0);
SetLength(VLow, 0);
SetLength(VS, 0);
S.destroy;
end.
|
// Wyznaczanie silnie
// spójnych składowych
// Algorytm Tarjana
// Data: 2.02.2014
// (C)2014 mgr Jerzy Wałaszek
//---------------------------
#include <iostream>
#include <iomanip>
using namespace std;
// Typy danych
struct SLel
{
SLel * next;
int v;
};
// Typ danych dla listy
// silnie spójnych składowych
struct sSLel
{
sSLel * next;
SLel * v;
};
class Stack
{
private:
// lista przechowująca stos
SLel * S;
public:
Stack(); // konstruktor
~Stack(); // destruktor
bool empty(void);
int top(void);
void push(int v);
void pop(void);
};
// Zmienne globalne
//-----------------
int n, m, cvn, *VN, *VLow;
bool *VS;
Stack S;
SLel **graf;
sSLel *Lscc;
//---------------------
// Metody obiektu Stack
//---------------------
// Konstruktor
//------------
Stack::Stack()
{
S = nullptr;
}
// Destruktor
//-----------
Stack::~Stack()
{
while(S) pop();
}
// Sprawdza, czy stos jest pusty
//------------------------------
bool Stack::empty(void)
{
return !S;
}
// Zwraca szczyt stosu
//--------------------
int Stack::top(void)
{
if(S)
return S->v;
else
return -1;
}
// 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;
}
}
// Procedura wykonująca
// przejście DFS i wyznaczająca
// silnie spójną składową.
// v - wierzchołek startowy dla DFS
//---------------------------------
void DFSscc(int v)
{
int u;
SLel *sccp, *p;
sSLel *listp;
// Numerujemy wierzchołek
// i ustawiamy wstępnie
// parametr Low
VN[v] = VLow[v] = ++cvn;
// Wierzchołek na stos
S.push(v);
// Zapamiętujemy,
// że v jest na stosie
VS[v] = true;
// Przeglądamy listę sąsiadów
for(p = graf[v];p;p = p->next)
// Jeśli sąsiad jest
// nieodwiedzony, to
if(!VN[p->v])
{
// wywołujemy dla niego
// rekurencyjnie DFS
DFSscc(p->v);
// i obliczamy parametr Low dla v
VLow[v] = min(VLow[v],VLow[p->v]);
}
// Jeśli sąsiad odwiedzony,
// lecz wciąż na stosie,
else if(VS[p->v])
// to wyznaczamy parametr Low dla v
VLow[v] = min(VLow[v],VN[p->v]);
// Sprawdzamy,
// czy mamy kompletną składową
if(VLow[v] == VN[v])
{
// Dodajemy tę składową
// do listy składowych
sccp = nullptr;
do
{
// Pobieramy wierzchołek ze stosu
u = S.top();
// Pobrany wierzchołek
// usuwamy ze stosu
S.pop();
// Zapamiętujemy,
// że nie ma go już na stosie
VS[u] = false;
// Nowy element listy wierzchołków
p = new SLel;
// Zapisujemy w nim wierzchołek
p->v = u;
// dodajemy go na początek listy
p->next = sccp;
sccp = p;
// Kontynuujemy aż
// do korzenia składowej
} while(u != v);
// Nowy element listy składowych
listp = new sSLel;
// Zapisujemy w nim listę
listp->v = sccp;
// i dołączamy na początek
// listy składowych
listp->next = Lscc;
Lscc = listp;
}
}
// **********************
// *** Program główny ***
// **********************
int main()
{
int i, v, u;
SLel *p, *r;
sSLel *listp, *listr;
// Odczytujemy liczbę
// wierzchołków i krawędzi
cin >> n >> m;
// Tworzymy tablice dynamiczne
graf = new SLel * [n];
VN = new int [n];
VLow = new int [n];
VS = new bool [n];
// Inicjujemy tablice
for(i = 0; i < n; i++)
{
graf[i] = nullptr;
VN[i] = 0;
VS[i] = false;
}
// Odczytujemy kolejne
// definicje krawędzi
for(i = 0; i < m; i++)
{
// Wierzchołki tworzące krawędź
cin >> v >> u;
// Tworzymy nowy element
p = new SLel;
// Numerujemy go jako u
p->v = u;
// i dodajemy na początek
// listy graf[v]
p->next = graf[v];
graf[v] = p;
}
cout << endl;
// Wyznaczamy silnie
// spójne składowe
//------------------
// Zerujemy numer wierzchołka
cvn = 0;
// Tworzymy pustą
// listę składowych
Lscc = NULL;
// Przeglądamy kolejne wierzchołki
for(v = 0; v < n; v++)
// W wierzchołku nieodwiedzonym
// uruchamiamy DFS
if(!VN[v]) DFSscc(v);
// cvn jest teraz
// licznikiem składowych
cvn = 0;
// Przeglądamy listę składowych
for(listp = Lscc; listp; listp = listp->next)
{
// Wyświetlamy numer składowej
cout << "SCC" << setw(3)
<< ++cvn << ":";
// Przeglądamy listę wierzchołków
for(p = listp->v; p; p = p->next)
// Wyświetlamy wierzchołek składowej
cout << setw(3) << p->v;
cout << endl;
}
// Usuwamy zmienne dynamiczne
for(i = 0; i < n; i++)
{
p = graf[i];
while(p)
{
r = p;
p = p->next;
delete r;
}
}
listp = Lscc;
while(listp)
{
p = listp->v;
while(p)
{
r = p;
p = p->next;
delete r;
}
listr = listp;
listp = listp->next;
delete listr;
}
delete [] graf;
delete [] VN;
delete [] VLow;
delete [] VS;
return 0;
}
|
Basic' Wyznaczanie silnie
' spójnych składowych
' Algorytm Tarjana
' Data: 2.02.2014
' (C)2014 mgr Jerzy Wałaszek
'---------------------------
' Typy danych
Type SLel
next As SLel Ptr
v As Integer
End Type
Type sSLel
next As sSLel Ptr
v As SLel Ptr
End Type
Type Stack
Private:
' lista zawierająca stos
S As SLel Ptr
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
' Zmienne globalne
'-----------------
Dim Shared As Integer n, m, cvn
Dim Shared As Integer Ptr VN, VLow
Dim Shared As Byte Ptr VS
Dim Shared As Stack S
Dim Shared As SLel Ptr Ptr graf
Dim Shared As sSLel Ptr Lscc
'---------------------
' 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
If S = 0 Then
top = -1
Else
top = S->v
End If
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 zwraca mniejszą z liczb a i b
'--------------------------------------
Function min(ByVal a As Integer, _
ByVal b As Integer) _
As Integer
If a < b Then
Return a
Else
Return b
EndIf
End Function
' Procedura wykonująca przejście DFS
' i wyznaczająca silnie spójną składową
' v - wierzchołek startowy dla DFS
'--------------------------------------
Sub DFSscc(ByVal v As Integer)
Dim As Integer u
Dim As SLel Ptr sccp, p
Dim As sSLel Ptr listp
' Zwiększamy bieżący
' numer wierzchołka
cvn += 1
' Numerujemy wierzchołek
VN[v] = cvn
' i ustawiamy wstępnie parametr Low
VLow[v] = cvn
' Wierzchołek na stos
S.push(v)
' Zapamiętujemy, że v jest na stosie
VS[v] = 1
p = graf[v]
' Przeglądamy listę sąsiadów
While p
' Jeśli sąsiad jest
' nieodwiedzony, to
If VN[p->v] = 0 Then
' wywołujemy dla niego
' rekurencyjnie DFS
DFSscc(p->v)
' i obliczamy parametr Low dla v
VLow[v] = min(VLow[v],VLow[p->v])
' Jeśli sąsiad odwiedzony,
' lecz wciąż na stosie,
ElseIf VS[p->v] = 1 Then
' to wyznaczamy parametr
' Low dla v
VLow[v] = min(VLow[v],VN[p->v])
End If
p = p->Next
Wend
' Sprawdzamy,
' czy mamy kompletną składową
If VLow[v] = VN[v] Then
' Dodajemy tę składową
' do listy składowych
sccp = 0
Do
' Pobieramy wierzchołek ze stosu
u = S.top
' Pobrany wierzchołek
' usuwamy ze stosu
S.pop
' Zapamiętujemy,
' że nie ma go już na stosie
VS[u] = 0
' Nowy element listy wierzchołków
p = new SLel
' Zapisujemy w nim wierzchołek
p->v = u
' dodajemy go na początek listy
p->next = sccp
sccp = p
' Kontynuujemy aż do
' korzenia składowej
Loop While u <> v
' Nowy element listy składowych
listp = new sSLel
' Zapisujemy w nim listę
listp->v = sccp
' i dołączamy na początek
' listy składowych
listp->next = Lscc
Lscc = listp
End If
End Sub
' **********************
' *** Program główny ***
' **********************
Dim As Integer i, v, u
Dim As SLel Ptr p, r
Dim As sSLel Ptr listp, listr
Open Cons For Input As #1
' Odczytujemy liczbę
' wierzchołków i krawędzi
Input #1, n, m
' Tworzymy tablice dynamiczne
graf = New SLel Ptr [n]
VN = New Integer [n]
VLow = New Integer [n]
VS = New Byte [n]
' Inicjujemy tablice
For i = 0 To n-1
graf[i] = 0
VN[i] = 0
VS[i] = 0
Next
' Odczytujemy kolejne
' definicje krawędzi
For i = 1 To m
' Wierzchołki tworzące krawędź
Input #1, v, u
' Tworzymy nowy element
p = New SLel
' Numerujemy go jako u
p->v = u
' Dodajemy go na początek
' listy graf[v]
p->next = graf[v]
graf[v] = p
Next
Close #1
Print
' Wyznaczamy silnie spójne składowe
'----------------------------------
' Zerujemy numer wierzchołka
cvn = 0
' Tworzymy pustą listę składowych
Lscc = 0
' Przeglądamy kolejne wierzchołki
For v = 0 To n-1
' W wierzchołku nieodwiedzonym
' uruchamiamy DFS
If VN[v] = 0 Then DFSscc(v)
Next
' cvn jest teraz licznikiem składowych
cvn = 0
listp = Lscc
' Przeglądamy listę składowych
While listp
cvn += 1
' Wyświetlamy numer składowej
Print Using "SCC### :";cvn;
p = listp->v
' Przeglądamy listę wierzchołków
While p
' Wyświetlamy wierzchołek składowej
Print Using "###";p->v;
' Następny wierzchołek
p = p->next
Wend
Print
' Następna składowa
listp = listp->next
Wend
' Usuwamy tablice dynamiczne
'---------------------------
For i = 0 To n-1
p = graf[i]
While p
r = p
p = p->next
Delete r
Wend
Next
listp = Lscc
While listp
p = listp->v
While p
r = p
p = p->next
Delete r
Wend
listr = listp
listp = listp->next
Delete listr
Wend
Delete [] graf
Delete [] VN
Delete [] VLow
Delete [] VS
End
|
Python
(dodatek)# Wyznaczanie silnie
# spójnych składowych
# Algorytm Tarjana
# Data: 5.01.2025
# (C)2025 mgr Jerzy Wałaszek
#---------------------------
# Klasy
class SLel:
def __init__(self):
self.next = None
self.v = 0
class SSLel:
def __init__(self):
self.next = None
self.v = None
class Stack:
# Konstruktor
def __init__(self):
self.s = None
# Destruktor
def __del__(self):
while self.s:
self.pop()
# Sprawdza, czy stos jest pusty
def empty(self):
return not self.s
# Zwraca szczyt stosu.
def top(self):
if not self.s:
return -1
else:
return self.s.v
# Zapisuje na stos
def push(self, v):
e = SLel()
e.v = v
e.next = self.s
self.s = e
# Usuwa ze stosu
def pop(self):
if self.s:
self.s = self.s.next
# Procedura wykonująca przejście DFS
# i wyznaczająca silnie spójną składową
# v - wierzchołek startowy dla DFS
def dfs_scc(v):
global cvn, vn, vs, graf, vlow, s
global lscc
# Zwiększamy bieżący
# numer wierzchołka
cvn += 1
# Numerujemy wierzchołek
vn[v] = cvn
# i ustawiamy wstępnie parametr Low
vlow[v] = cvn
# Wierzchołek na stos
s.push(v)
# Zapamiętujemy, że v jest na stosie
vs[v] = True
p = graf[v]
# Przeglądamy listę sąsiadów
while p:
# Jeśli sąsiad jest
# nieodwiedzony, to
if not vn[p.v]:
# wywołujemy dla niego
# rekurencyjnie DFS
dfs_scc(p.v)
# i obliczamy parametr Low dla v
vlow[v] = min(vlow[v],vlow[p.v])
# Jeśli sąsiad odwiedzony,
# lecz wciąż na stosie,
elif vs[p.v]:
# to wyznaczamy parametr
# Low dla v
vlow[v] = min(vlow[v],vn[p.v])
p = p.next
# Sprawdzamy,
# czy mamy kompletną składową
if vlow[v] == vn[v]:
# Dodajemy tę składową
# do listy składowych
sccp = None
while True:
# Pobieramy wierzchołek
# ze stosu
u = s.top()
# Pobrany wierzchołek
# usuwamy ze stosu
s.pop()
# Zapamiętujemy,
# że nie ma go już na stosie
vs[u] = False
# Nowy element
# listy wierzchołków
p = SLel()
# Zapisujemy w nim wierzchołek
p.v = u
# dodajemy go na początek listy
p.next = sccp
sccp = p
# Kontynuujemy aż do
# korzenia składowej
if u == v: break
# Nowy element listy składowych
listp = SSLel()
# Zapisujemy w nim listę
listp.v = sccp
# i dołączamy na początek
# listy składowych
listp.next = lscc
lscc = listp
# **********************
# *** Program główny ***
# **********************
s = Stack()
# Odczytujemy liczbę
# wierzchołków i krawędzi
x = input().split()
n = int(x[0])
m = int(x[1])
# Tworzymy tablice dynamiczne
graf = [None] * n
vn = [0] * n
vlow = [0] * n
vs = [False] * n
# Odczytujemy kolejne
# definicje krawędzi
for i in range(m):
# Wierzchołki tworzące krawędź
x = input().split()
v = int(x[0])
u = int(x[1])
# Tworzymy nowy element
p = SLel()
# Numerujemy go jako u
p.v = u
# Dodajemy go na początek
# listy graf[v]
p.next = graf[v]
graf[v] = p
print()
# Wyznaczamy silnie spójne składowe
#----------------------------------
# Zerujemy numer wierzchołka
cvn = 0
# Tworzymy pustą listę składowych
lscc = None
# Przeglądamy kolejne wierzchołki
for v in range(n):
# W wierzchołku nieodwiedzonym
# uruchamiamy DFS
if not vn[v]: dfs_scc(v)
# cvn jest teraz licznikiem składowych
cvn = 0
listp = lscc
# Przeglądamy listę składowych
while listp:
cvn += 1
# Wyświetlamy numer składowej
print("SCC%3d :" % cvn, end="")
p = listp.v
# Przeglądamy listę wierzchołków
while p:
# Wyświetlamy wierzchołek składowej
print("%3d" % p.v, end="")
# Następny wierzchołek
p = p.next
print()
# Następna składowa
listp = listp.next
# Usuwamy tablice dynamiczne
#---------------------------
for i in range(n):
while graf[i]:
graf[i] = graf[i].next
listp = lscc
while listp:
while listp.v:
listp.v = listp.v.next
listp = listp.next
del graf, vn, vlow, vs
|
| Wynik: | |
13 27 0 1 0 4 1 2 1 4 1 5 1 8 1 11 2 6 3 1 3 7 4 8 5 2 5 8 6 5 6 7 6 9 8 4 9 5 9 7 10 8 10 11 11 8 11 9 11 12 12 3 12 6 12 9 SCC 1 : 10 SCC 2 : 0 SCC 3 : 1 11 12 3 SCC 4 : 9 5 2 6 SCC 5 : 7 SCC 6 : 4 8 |
![]() |
![]() |
Zespół Przedmiotowy Chemii-Fizyki-Informatyki w I Liceum Ogólnokształcącym im. Kazimierza Brodzińskiego w Tarnowie ul. Piłsudskiego 4 ©2026 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.