Serwis Edukacyjny
w I-LO w Tarnowie
obrazek

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

Znajdowanie silnie spójnych składowych w grafie

SPIS TREŚCI
Podrozdziały

Problem

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) (w grafach nieskierowanych każdy graf spójny jest również silnie spójny).

obrazek   obrazek
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ą: (0) (3) (6) (7).

Rozwiązanie naiwne

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ą C[ ], której komórki będą odpowiadały n wierzchołkom grafu. Będziemy w niej umieszczać numery silnie spójnych składowych, do których należą poszczególne wierzchołki. Tablicę C[ ] wstępnie zerujemy. Umawiamy się przy tym, iż numer 0 nie oznacza żadnej spójnej składowej.

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 C[v] = 0. Gdy go znajdziemy, zwiększamy cn o 1, do C[v] wprowadzamy cn i rozpoczynamy przejście DFS od tego wierzchołka. W każdym odwiedzonym wierzchołku u uruchamiamy drugie przejście DFS, które sprawdza, czy istnieje ścieżka od wierzchołka u do wierzchołka startowego v. Jeśli tak, to do C[u] zapisujemy cn.

Po przetworzeniu wszystkich wierzchołków w tablicy C[ ] otrzymamy informację o tym, które wierzchołki grafu należą do poszczególnych spójnych składowych. Dodatkowo cn określa liczbę tych silnie spójnych składowych.

Algorytm naiwny wyszukiwania silnie spójnych składowych w grafie skierowanym

Algorytm przejścia DFSback(u, v, n, graf)

Wejście:

u : wierzchołek bieżący; u ∈ C.
v : wierzchołek startowy; v ∈ C.
n : liczba wierzchołków w grafie; n ∈ C.
graf : zadany w dowolnie wybrany sposób, algorytm tego nie precyzuje.

Wyjście:

Zwraca true, jeśli istnieje ścieżka u do v. W przeciwnym razie zwraca false.

Elementy pomocnicze:

x, y : wierzchołki; x, y ∈ C.
S : pusty stos wierzchołków.
vs : n elementowa logiczna tablica odwiedzin.

Lista kroków:

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:   xS.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

Algorytm przejścia DFSscc(v, n, graf, cn, C)

Wejście:

v : wierzchołek startowy; v ∈ C.
n : liczba wierzchołków w grafie; n ∈ C.
graf : zadany w dowolnie wybrany sposób, algorytm tego nie precyzuje.
cn : numer bieżącej silnie spójnej składowej; cn ∈ C.
C[ ] : n elementowa tablica numerów silnie spójnych składowych.

Wyjście:

Przeglądnięcie wszystkich wierzchołków grafu, do których prowadzą ścieżki z wierzchołka v. Jeśli z któregoś z tych wierzchołków prowadzi ścieżka powrotna do wierzchołka v, to w tablicy C[ ] dla wierzchołka v i wierzchołka bieżącego zostanie ustawiona wartość cn.

Elementy pomocnicze:

u, w : wierzchołki; u, w ∈ C.
S : pusty stos wierzchołków.
vs : n elementowa logiczna tablica odwiedzin.

Lista kroków:

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:   uS.top() ; pobieramy ze stosu wierzchołek
K08:   S.pop() ; pobrany wierzchołek usuwamy ze stosu
K09:   Jeśli (uv) obrazek (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

Algorytm główny

Wejście:

n : liczba wierzchołków w grafie; n ∈ C.
graf : zadany w dowolnie wybrany sposób, algorytm tego nie precyzuje.

Wyjście:

C[ ] : n elementowa tablica liczb całkowitych. Dla każdego wierzchołka v grafu C[v] zawiera numer silnie spójnej składowej, do której ten wierzchołek należy.
cn : liczba silnie spójnych składowych; cn ∈ C.

Elementy pomocnicze:

v : numer wierzchołka w grafie; v ∈ C.

Lista kroków:

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:   cncn + 1 ; zwiększamy licznik
K07:   DFSscc(v,n,graf,cn,C) ; uruchamiamy przejście DFS od wierzchołka v
K08: Zakończ

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, 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):
obrazek   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
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.
C++
// 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
obrazek

do podrozdziału  do strony 

Algorytm Kosaraju

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.

Algorytm Korsaraju wyszukiwania silnie spójnych składowych w grafie skierowanym

Algorytm przejścia DFSStack(v,vs,S,graf)

Wejście:

v : wierzchołek startowy; v ∈ C.
vs : tablica logiczna odwiedzin.
S : stos wierzchołków.
graf : graf zadany w dowolny sposób, algorytm tego nie precyzuje.

Wyjście:

Wpisuje na stos wierzchołki w odwrotnej kolejności odwiedzin (pierwszy na szczycie stosu jest wierzchołek odwiedzony jako pierwszy).

Elementy pomocnicze:

u : wierzchołek; u ∈ C.

Lista kroków:

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

Algorytm przejścia DFSprint(v,vs,graf)

Wejście:

: wierzchołek startowy; v ∈ C.
vs : tablica logiczna odwiedzin.
graf : graf zadany w dowolny sposób, algorytm tego nie precyzuje.

Wyjście:

Wypisuje na wyjście kolejno odwiedzane wierzchołki.

Elementy pomocnicze:

u : wierzchołek; u ∈ C.

Lista kroków:

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

Algorytm główny

Wejście:

n : liczba wierzchołków w grafie; n ∈ C.
graf : graf zadany w dowolny sposób, algorytm tego nie precyzuje.

Wyjście:

Wypisuje na wyjście wierzchołki należące do silnie spójnych składowych.

Elementy pomocnicze:

vs : n elementowa logiczna tablica odwiedzin.
S : stos wierzchołków.
v : wierzchołek; v ∈ C.
cn : licznik silnie spójnych składowych; cn ∈ C.

Lista kroków:

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:   vS.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:   cncn+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

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, 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):
obrazek   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
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
obrazek

do podrozdziału  do strony 

Algorytm Tarjana

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.

Algorytm Tarjana wyszukiwania silnie spójnych składowych w grafie skierowanym

Algorytm przejścia DFSscc(v,cvn,VN,VLow,VS,S,Lscc,graf)

Wszystkie parametry za wyjątkiem v mogą być zrealizowane jako globalne, co zmniejszy zapotrzebowanie na pamięć przy wywoływaniach rekurencyjnych.

Wejście:

v : wierzchołek startowy; v ∈ C.
cvn : numer bieżącego wierzchołka; cvn ∈ C.
VN : tablica numerów wierzchołków, które ustala DFSscc( ).
VLow : tablica parametrów Low.
VS : tablica logiczna, która informuje, czy dany wierzchołek jest na stosie S.
S : stos wierzchołków.
Lscc : jednokierunkowa lista silnie spójnych składowych. Każda silnie spójna składowa jest jednokierunkową listą wierzchołków, które do niej należą.
graf : graf zadany w dowolny sposób, algorytm tego nie precyzuje.

Wyjście:

Lista silnie spójnych składowych Lscc.

Elementy pomocnicze:

u : wierzchołek; u ∈ C.
sccp, p : wskaźniki do elementu listy wierzchołków.
listp : wskaźnik do elementu listy list.

Lista kroków:

K01: cvncvn+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: sccpnil ; tworzymy pustą listę wierzchołków
K15: uS.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: pvu
K21: pnextsccp
K22: sccpp
K23: Jeśli uv, ; 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: listpvsccp
K27: listpnextLscc
K28: Lscclistp
K29: Zakończ

Algorytm główny

Wejście:

n : liczba wierzchołków w grafie; n ∈ C.
graf : graf zadany w dowolny sposób, algorytm tego nie precyzuje.

Wyjście:

Wypisuje na wyjście wierzchołki należące do silnie spójnych składowych.

Elementy pomocnicze:

v : numer wierzchołka; v ∈ C.
cvn : numer bieżącego wierzchołka;  cvn ∈ C.
VN : tablica numerów wierzchołków, które ustala DFSscc( ).
VLow : tablica parametrów Low.
VS : tablica logiczna, która informuje, czy dany wierzchołek jest na stosie S.
S : stos wierzchołków.
Lscc : jednokierunkowa lista silnie spójnych składowych. Każda silnie spójna składowa jest jednokierunkową listą wierzchołków, które do niej należą.
listp : wskaźnik elementu listy składowych.
p : wskaźnik listy wierzchołków.

Lista kroków:

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 listpnil,
     wykonuj kroki K11…K16
K11:   plistpv ; przeglądamy listę wierzchołków
K12:   Dopóki pnil, 
       wykonuj kroki K13…K14
K13:     Pisz(pv) ; wypisujemy wierzchołki silnie spójnej składowej
K14:     ppnext ; następny wierzchołek
K15:  Przejdź do następnego wiersza wydruku
K16:  listplistpnext ; następna lista
K17: Zakończ

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, 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):
obrazek   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
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.
C++
// 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
obrazek

do podrozdziału  do strony 

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: i-lo@eduinf.waw.pl
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.

Informacje dodatkowe.