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 spójnych składowych w grafie

SPIS TREŚCI
Tematy pomocnicze
Podrozdziały

Problem

Dla danego grafu nieskierowanego należy wyznaczyć wszystkie spójne składowe.

Graf jest spójny (ang. connected graph), jeśli dla każdych dwóch jego wierzchołków istnieje łącząca je ścieżka. Spójna składowa (ang. connected component), to największa grupa wierzchołków, które są wzajemnie połączone ze sobą ścieżkami. Graf spójny posiada tylko jedną spójną składową obejmującą wszystkie jego wierzchołki. Jeśli składowych takich jest więcej, to graf nazywamy niespójnym (ang. disconnected graph).

obrazek   obrazek
Graf spójny   Graf niespójny

Rozwiązanie dla grafu nieskierowanego

Co to znaczy: wyznaczyć spójną składową? To nic innego, jak zidentyfikować wszystkie wierzchołki grafu, które należą do tej składowej. W tym celu z każdym z wierzchołków w grafie możemy skojarzyć dodatkową daną, którą nazwiemy c (ang. connected : połączony) i która ma tę samą wartość dla wszystkich wierzchołków należących do tej samej składowej. Dane c mogą tworzyć tablicę, której elementy odpowiadają wartości c dla wierzchołka o numerze równym indeksowi – w ten sposób realizowaliśmy tablicę vs z informacjami o odwiedzonych wierzchołkach.

Znalezienie wartości c dla wszystkich wierzchołków grafu uzyskamy przy pomocy algorytmu DFS w sposób następujący:

Tworzymy tablicę C[ ] o tylu elementach, ile jest wierzchołków w grafie. Element C[i] odpowiada wartości c wierzchołka i-tego. Wszystkie elementy tablicy C[ ] zerujemy. Ustawiamy numer bieżącej składowej na 0. Przeglądamy kolejne elementy tablicy C[ ]. Jeśli napotkamy na element C[i] równy zero, to zwiększamy numer bieżącej składowej o 1 i od wierzchołka i-tego uruchamiamy przejście DFS grafu. W trakcie przejścia DFS we wszystkich odwiedzonych wierzchołkach ustawiamy wartość c na numer bieżącej składowej. Algorytm DFS gwarantuje nam odwiedzenie wszystkich wierzchołków, do których prowadzą ścieżki z wierzchołka startowego. Zatem w tablicy C[ ] zostaną ustawione na wartość bieżącej składowej wszystkie komórki, które odpowiadają odwiedzonym przez DFS wierzchołkom. W ten sposób znajdziemy spójną składową. Kontynuujemy przeglądanie tablicy C[ ] do jej końca.

Gdy przeglądniemy całą tablicę C[ ], numer bieżącej składowej będzie nas informował o liczbie spójnych składowych w grafie. Jeśli wyniesie on 1, to graf jest spójny. W przeciwnym razie graf zawiera kilka spójnych składowych. Składowe te zidentyfikujemy przez kilkakrotne przeglądnięcie tablicy C[ ]. Najpierw wyszukujemy w niej elementy o wartości 1. Indeksy tych elementów są numerami wierzchołków grafu, które należą do spójnej składowej nr 1. Następnie wyszukujemy elementy o wartościach 2, 3…, aż do wartości, którą miała bieżąca składowa przy zakończeniu przeglądania tablicy C[ ].

Algorytm wyszukiwania spójnych składowych w grafie nieskierowanym

Wejście:

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

Wyjście:

Informacja o liczbie spójnych składowych.
Dla każdej spójnej składowej grafu: numer składowej oraz lista wierzchołków, które do niej należą.

Elementy pomocnicze:

C[ ] : tablica o n komórkach z numerami spójnych składowych.
cn : liczba spójnych składowych; cn ∈ C.
i, j : indeksy; i, j ∈ C.
: stos liczb całkowitych.
v, u : numery wierzchołków w grafie; v, u ∈ C.

Lista kroków:

K01: Utwórz tablicę C[] o n elementach
K02: Wyzeruj elementy tablicy C[]
K03: cn ← 0 ; zerujemy licznik spójnych składowych
K04: Dla i = 0, 1, …, n-1:
     wykonuj kroki K05..K15
K05:   Jeśli C[i] > 0, ; szukamy nieodwiedzonego wierzchołka
       to następny obieg pętli K04
K06:   cncn+1 ; zwiększamy o 1 liczbę składowych
K07:   S.push(i) ; na stosie umieszczamy wierzchołek startowy
K08:   C[i] ← cn ; numerując wierzchołek, oznaczamy go jako odwiedzony
K09:   Dopóki S.empty() = false, ; uruchamiamy przejście DFS
       wykonuj kroki K10…K15
K10:     vS.top() ; odczytujemy wierzchołek ze stosu
K11:     S.pop() ; odczytany wierzchołek usuwamy ze stosu
K12:     Dla każdego sąsiada u wierzchołka v: ; część
         wykonuj kroki K13…K15 ; zależna od reprezentacji grafu
K13:       Jeśli C[u] > 0, ; szukamy nieodwiedzonego sąsiada
           to następny obieg pętli K12
K14:       S.push(u) ; sąsiada umieszczamy na stosie
K15:       C[u] ← cn ; i oznaczamy go jako odwiedzony oraz ponumerowany
K16: Pisz cn ; wyprowadzamy liczbę spójnych składowych
K17: Dla i = 1, 2, …, cn:
     wykonuj kroki K18…K20
K18:   Pisz i ; wypisujemy kolejne numery składowych
K19:   Dla j = 0, 1, …, n-1:
       wykonuj krok K20
K20:     Jeśli C[j] = i, ; oraz numery należących do nich wierzchołków
         to pisz j
K21: 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 nieskierowanego, wyszukuje w nim wszystkie spójne składowe, a następnie podaje należące do nich numery wierzchołków w grafie.
Przykładowe dane (spójne składowe zostały pokolorowane w celach testowych):
obrazek   17 17
0 1
0 2
0 3
1 2
1 14
4 11
4 12
5 6
5 9
6 7
6 8
10 15
11 15
12 15
13 14
13 16
14 16
Pascal
// Znajdowanie spójnych składowych
// Data: 27.08.2013
// (C)2013 mgr Jerzy Wałaszek
//--------------------------------

program spojsklad;

// 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 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 not empty then
  begin
    e := S;
    S := S^.next;
    dispose(e);
  end;
end;

// **********************
// *** Program główny ***
// **********************

var
  // Liczba wierzchołków i krawędzi
  n, m : integer;
  // Tablica list sąsiedztwa
  A : TList;
  // Tablica z numerami
  // spójnych składowych
  C : array of integer;
  // Stos
  S : Stack;
  cn, i, j, v, u : integer;
  p, r : PSLel;

begin
  S.init;
  // Odczytujemy liczbę
  // wierzchołków i krawędzi
  read(n, m);
  // Tworzymy tablice dynamiczne
  SetLength(A, n);
  SetLength(C, n);
  // Tablicę A[] wypełniamy pustymi
  // listami, a tablicę C[]
  // wypełniamy zerami
  for i := 0 to n-1 do
  begin
    A[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 w
    p^.v := u;
    // Dodajemy go na początek
    // listy A[v]
    p^.next := A[v];
    A[v] := p;
    // To samo
    // dla krawędzi odwrotnej
    new(p);
    p^.v := v;
    p^.next := A[u];
    A[u] := p;
  end;
  // Zerujemy licznik
  // spójnych składowych
  cn := 0;
  for i := 0 to n-1 do
    // Szukamy nieodwiedzonego
    // jeszcze wierzchołka
    if C[i] = 0 then
    begin
      // Zwiększamy licznik składowych
      inc(cn);
      // Na stosie umieszczamy
      // numer bieżącego wierzchołka
      S.push(i);
      // Wierzchołek numerujemy
      // i oznaczamy jako odwiedzony
      C[i] := cn;
      // Przechodzimy graf
      // algorytmem DFS
      while not S.empty do
      begin
        // Pobieramy wierzchołek
        v := S.top;
        // Usuwamy go ze stosu
        S.pop;
        // Przeglądamy sąsiadów
        // wierzchołka v
        p := A[v];
        while p <> nil do
        begin
          // Numer sąsiada do u
          u := p^.v;
          if C[u] = 0 then
          begin
            // Na stos idą
            // sąsiedzi nieodwiedzeni
            S.push(u);
            // i ponumerowani
            C[u] := cn;
          end;
          p := p^.next;
        end;
      end;
    end;
  writeln;
  for i := 1 to cn do
  begin
    // Numer spójnej składowej
    write('CC ', i, ' : ');
    for j := 0 to n-1 do
      if C[j] = i then
        // Wierzchołki
        // tej składowej
        write(j:2, ' ');
    writeln;
  end;
  writeln;
  // Usuwamy tablicę
  // list sąsiedztwa
  for i := 0 to n-1 do
  begin
    p := A[i];
    while p <> nil do
    begin
      r := p;
      p := p^.next;
      dispose(r);
    end;
  end;
  SetLength(A, 0);
  SetLength(C, 0);
  S.destroy;
end.
C++
// Znajdowanie spójnych składowych
// Data: 27.08.2013
// (C)2013 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();
    ~Stack();
    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(empty())
    return -1;
  else
    return S->v;
}

// Zapisuje na stos
//-----------------
void Stack::push(int v)
{
  SLel * e = new SLel;
  e->v = v;
  e->next = S;
  S = e;
}

// Usuwa ze stosu
//---------------
void Stack::pop(void)
{
  if(S)
  {
    SLel * e = S;
    S = S->next;
    delete e;
  }
}

// **********************
// *** Program główny ***
// **********************

int main()
{
  // Liczba wierzchołków
  // i krawędzi
  int n, m;
  // Tablica list sąsiedztwa
  SLel ** A;
  // Tablica z numerami
  // spójnych składowych
  int * C;
  // Stos
  Stack S;
  int cn, i, j, v, u;
  SLel *p, *r;

  // Odczytujemy liczbę
  // wierzchołków i krawędzi
  cin >> n >> m;
  // Tworzymy tablice dynamiczne
  A = new SLel * [n];
  C = new int [n];
  // Tablicę A wypełniamy pustymi
  // listami, a tablicę C
  // wypełniamy zerami
  for(i = 0; i < n; i++)
  {
    A[i] = NULL;
    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 A[v]
    p->next = A[v];
    A[v] = p;
    // To samo dla krawędzi odwrotnej
    p = new SLel;
    p->v = v;
    p->next = A[u];
    A[u] = p;
  }
  // Zerujemy licznik
  // spójnych składowych
  cn = 0;
  for(i = 0; i < n; i++)
    // Szukamy nieodwiedzonego
    // jeszcze wierzchołka
    if(!C[i])
    {
      // Zwiększamy licznik składowych
      cn++;
      // Na stosie umieszczamy numer
      // bieżącego wierzchołka
      S.push(i);
      // i oznaczamy go jako
      // odwiedzonego i ponumerowanego
      C[i] = cn;
      // Przechodzimy graf
      // algorytmem DFS
      while(!S.empty())
      {
        // Pobieramy wierzchołek
        v = S.top();
        // Usuwamy go ze stosu
        S.pop();
        // Przeglądamy sąsiadów
        // wierzchołka v
        for(p = A[v]; p; p = p->next)
        {
          // Numer sąsiada do u
          u = p->v;
          if(!C[u])
          {
            // Na stos idą sąsiedzi
            // nieodwiedzeni
            S.push(u);
            // i ponumerowani
            C[u] = cn;
          }
        }
      }
    }
  cout << endl;
  for(i = 1; i <= cn; i++)
  {
    // Numer spójnej składowej
    cout << "CC : " << i << ": ";
    for(j = 0; j < n; j++)
      if(C[j] == i)
        // Wierzchołki tej składowej
        cout << setw(2) << j << " ";
    cout << endl;
  }
  cout << endl;
  // Usuwamy tablicę
  // list sąsiedztwa
  for(i = 0; i < n; i++)
  {
    p = A[i];
    while(p)
    {
      r = p;
      p = p->next;
      delete r;
    }
  }
  delete [] A;
  delete [] C;
  return 0;
}
Basic
' Znajdowanie spójnych składowych
' Data: 27.08.2013
' (C)2013 mgr Jerzy Wałaszek
'--------------------------------

' Typy dla dynamicznej tablicy
' list sąsiedztwa i stosu
Type SLel
  next As SLel Ptr
  v As Integer
End Type

Type Stack
  Private:
    ' 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 empty = 1 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

' **********************
' *** Program główny ***
' **********************

' Liczba wierzchołków
' i krawędzi
Dim As Integer n, m
' Tablica list sąsiedztwa
Dim As SLel Ptr Ptr A
' Tablica z numerami
' spójnych składowych
Dim As Integer Ptr C
' Stos
Dim As Stack S
Dim As Integer cn, i, j, v, u
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]
C = New Integer [n]
' Tablicę A wypełniamy pustymi listami,
' a tablicę C wypełniamy zerami
For i = 0 To n-1
  A[i] = 0
  C[i] = 0
Next
' Odczytujemy kolejne
' definicje krawędzi.
For i = 0 To m-1
  ' 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
  ' To samo dla krawędzi odwrotnej
  p = new SLel
  p->v = v
  p->next = A[u]
  A[u] = p
Next
' Zerujemy licznik
' spójnych składowych
cn = 0
For i = 0 To n-1
  ' Szukamy nieodwiedzonego
  ' jeszcze wierzchołka
  If C[i] = 0 Then
    ' Zwiększamy licznik składowych
    cn += 1
    ' Na stosie umieszczamy
    ' numer bieżącego wierzchołka
    S.push(i)
    ' oznaczając go jako
    ' odwiedzonego i ponumerowanego
    C[i] = cn
    ' Przechodzimy graf algorytmem DFS
    While S.empty = 0
      ' Pobieramy wierzchołek
      v = S.top
      ' Usuwamy go ze stosu
      S.pop
      ' Przeglądamy sąsiadów
      ' wierzchołka v
      p = A[v]
      While p
        ' Numer sąsiada do u
        u = p->v
        If C[u] = 0 Then
          ' Na stos idą
          ' sąsiedzi nieodwiedzeni
          S.push(u)
          ' i ponumerowani
          C[u] = cn
        End If
        p = p->next
      Wend
    Wend
  End If
Next
Print
For i = 1 To cn
  ' Numer spójnej składowej
  Print "CC";i;":";
  For j = 0 To n-1
    ' Wierzchołki tej składowej
    If C[j] = i Then _
      Print Using "###";j;
  Next
  Print
Next
Print
' Usuwamy tablicę
' list sąsiedztwa
For i = 0 To n-1
  p = A[i]
  While p
    r = p
    p = p->next
    Delete r
  Wend
Next
Delete [] A
Delete [] C
End
Python (dodatek)
# Znajdowanie spójnych składowych
# Data: 31.12.2024
# (C)2024 mgr Jerzy Wałaszek
#--------------------------------

# Klasy
#------
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.empty():
            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


# **********************
# *** Program główny ***
# **********************

# Stos
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
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 A[v]
    p.next = a[v]
    a[v] = p
    # To samo dla
    # krawędzi odwrotnej
    p = SLel()
    p.v = v
    p.next = a[u]
    a[u] = p
# Zerujemy licznik
# spójnych składowych
cn = 0
for i in range(n):
    # Szukamy nieodwiedzonego
    # jeszcze wierzchołka
    if not c[i]:
        # Zwiększamy
        # licznik składowych
        cn += 1
        # Na stosie umieszczamy
        # numer bieżącego
        # wierzchołka
        s.push(i)
        # oznaczając go jako
        # odwiedzonego
        # i ponumerowanego
        c[i] = cn
        # Przechodzimy graf
        # algorytmem DFS
        while not s.empty():
            # Pobieramy wierzchołek
            v = s.top()
            # Usuwamy go ze stosu
            s.pop()
            # Przeglądamy sąsiadów
            # wierzchołka v
            p = a[v]
            while p:
                # Numer sąsiada
                # idzie do u
                u = p.v
                if not c[u]:
                    # Na stos idą
                    # sąsiedzi
                    # nieodwiedzeni
                    s.push(u)
                    # oraz 
                    # ponumerowani
                    c[u] = cn
                p = p.next
print()
for i in range(1,cn+1):
    # Numer spójnej składowej
    print("CC",i,":",end="")
    for j in range(n):
        # Wierzchołki tej
        # składowej
        if c[j] == i:
            print("%3d"%j,end="")
    print()
print()
# Usuwamy tablicę
# list sąsiedztwa
for i in range(n):
    while a[i]:
        a[i] = a[i].next
del a,c
Wynik:
17 17
0 1
0 2
0 3
1 2
1 14
4 11
4 12
5 6
5 9
6 7
6 8
10 15
11 15
12 15
13 14
13 16
14 16

CC 1 :  0  1  2  3 13 14 16
CC 2 :  4 10 11 12 15
CC 3 :  5  6  7  8  9

do podrozdziału  do strony 

Rozwiązanie z wykorzystaniem zbiorów rozłącznych

Wierzchołki grafu należą do tej samej spójnej składowej, jeśli łączy je krawędź. Wykorzystując strukturę zbiorów rozłącznych, można w prosty sposób rozwiązać problem znajdowania spójnych składowych w grafie. W tym celu dla każdego wierzchołka grafu tworzymy osobny zbiór rozłączny. Następnie przechodzimy przez wszystkie krawędzie grafu, wykonując operację union_sets( ) dla wierzchołków tworzących te krawędzie. Algorytm staje się banalnie prosty:

Algorytm wyszukiwania spójnych składowych w grafie nieskierowanym za pomocą zbiorów rozłącznych

Wejście:

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

Wyjście:

T[ ] : tablica elementów struktury zbiorów rozłącznych reprezentujących wierzchołki grafu, w której zawarta jest informacja o przynależności poszczególnych wierzchołków do określonych spójnych składowych.

Elementy pomocnicze:

i, u, v : indeks i numery wierzchołków;  i, u, v ∈ C.
make_set(x) : tworzy jednoelementowy zbiór z wierzchołkiem x.
union_sets(x, y) : łączy ze sobą zbiory zawierające wierzchołki x i y.

Lista kroków:

K01: Twórz tablicę T[] n elementową
     i zainicjuj ją kolejnymi numerami
     wierzchołków grafu
K02: Dla i = 0, 1, …, n-1: ; tworzymy zbiory jednoelementowe
     wykonuj: make_set(i)
K03: Dla każdej krawędzi uv grafu ; łączymy zbiory zawierające
     wykonuj: union_sets(u, v) ; wierzchołki tworzące krawędź
K05: Zakończ z wynikiem T[]

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 nieskierowanego, wyszukuje w nim wszystkie spójne składowe, a następnie podaje należące do nich numery wierzchołków w grafie.
Przykładowe dane (spójne składowe zostały pokolorowane w celach testowych):
obrazek   17 17
0 1
0 2
0 3
1 2
1 14
4 11
4 12
5 6
5 9
6 7
6 8
10 15
11 15
12 15
13 14
13 16
14 16
Pascal
// Znajdowanie spójnych składowych
// za pomocą zbiorów rozłącznych
// Data: 1.04.2014
// (C)2014 mgr Jerzy Wałaszek
//--------------------------------

// Typ danych dla drzew struktury
// zbiorów rozłącznych
type
  PTnode = ^Tnode;
  Tnode =  record
    // Rodzic węzła
    up   : PTnode;
    // Ranga
    rank : integer;
    // Zawartość węzła
    Data: integer;
  end;

// Tworzy drzewo jednowęzłowe
//---------------------------
procedure make_set(x : PTnode);
begin
  // x staje się korzeniem drzewa
  x^.up := x;
  // Rangę zerujemy
  x^.rank := 0;
end;

// Zwraca korzeń drzewa
// i ustawia pola up
// wszystkich węzłów
// nadrzędnych aż do korzenia
//---------------------------
function find_set(x : PTnode)
                    : PTnode;
begin
  if x^.up <> x then
    x^.up := find_set(x^.up);
  find_set := x^.up;
end;

// Łączy ze sobą drzewa z x i z y
//-------------------------------
procedure union_sets(x, y : PTnode);
var
  rx, ry : PTnode;
begin
  // Wyznaczamy korzeń drzewa
  // z węzłem x
  rx := find_set(x);
  // Wyznaczamy korzeń drzewa
  // z węzłem y
  ry := find_set(y);
  // Korzenie muszą być różne
  if rx <> ry then
  begin
    // Porównujemy rangi drzew
    if rx^.rank > ry^.rank then
      // rx większe, dołączamy ry
      ry^.up := rx
    else
    begin
      // równe lub ry większe,
      // dołączamy rx
      rx^.up := ry;
      if rx^.rank = ry^.rank then
        inc(ry^.rank);
    end;
  end;
end;

// **********************
// *** PROGRAM GŁÓWNY ***
// **********************

var
  // Liczba wierzchołków i krawędzi
  n, m : integer;
  T : array of Tnode;
  i, j, v, u : integer;
begin
  // Odczytujemy liczbę wierzchołków
  // i krawędzi
  read(n, m);
  // Ustalamy rozmiar tablicy
  // zbiorów rozłącznych
  SetLength(T, n);
  // Tablicę T inicjujemy
  for i := 0 to n-1 do
  begin
    // Numer węzła
    T[i].Data:= i;
    // Tworzymy zbiór
    // jednoelementowy
    make_set(addr(T[i]));
  end;
  // Odczytujemy kolejne
  // definicje krawędzi.
  for i := 0 to m-1 do
  begin
    // Wierzchołki tworzące krawędź
    read(v, u);
    // Łączymy zbiory z u i v
    union_sets(addr(T[v]), addr(T[u]));
  end;
  // Wypisujemy wyniki
  writeln;
  for i := 0 to n-1 do
    if i = find_set(addr(T[i]))^.data then
    begin
      write('CC =', i:3, ' : ');
      for j := 0 to n-1 do
        if i = find_set(addr(T[j]))^.data then
          write(j:3);
      writeln;
    end;
  // Usuwamy tablicę zbiorów rozłącznych
  SetLength(T, 0);
end.
C++
// Znajdowanie spójnych składowych
// za pomocą zbiorów rozłącznych
// Data: 1.04.2014
// (C)2014 mgr Jerzy Wałaszek
//--------------------------------

#include <iostream>
#include <iomanip>

using namespace std;

// Typ danych dla drzew struktury
// zbiorów rozłącznych
struct Tnode
{
  // Rodzic węzła
  Tnode *up;
  // Ranga
  int rank;
  // Zawartość węzła
  int data;
};

// Tworzy drzewo jednowęzłowe
//---------------------------
void make_set(Tnode * x)
{
  // x staje się korzeniem drzewa
  x->up = x;
  // Rangę zerujemy
  x->rank = 0;
}

// Zwraca korzeń drzewa
// i ustawia pola up
// wszystkich węzłów
// nadrzędnych aż do korzenia
//---------------------------
Tnode * find_set(Tnode * x)
{
  if(x->up != x)
    x->up = find_set(x->up);
  return x->up;
}

// Łączy ze sobą drzewa
// z x i z y
//---------------------
void union_sets(Tnode *x,
                Tnode *y)
{
  Tnode *rx, *ry;

  // Wyznaczamy korzeń drzewa
  // z węzłem x
  rx = find_set(x);
  // Wyznaczamy korzeń drzewa
  // z węzłem y
  ry = find_set(y);
  // Korzenie muszą być różne
  if(rx != ry)
  {
    // Porównujemy rangi drzew
    if(rx->rank > ry->rank)
      // rx większe, dołączamy ry
      ry->up = rx;
    else
    {
      // równe lub ry większe,
      // dołączamy rx
      rx->up = ry;
      if(rx->rank == ry->rank)
        ry->rank++;
    }
  }
}

// **********************
// *** PROGRAM GŁÓWNY ***
// **********************

int main()
{
  // Liczba wierzchołków i krawędzi
  int n, m;
  Tnode * T;
  int i, j, v, u;

  // Odczytujemy liczbę
  // wierzchołków i krawędzi
  cin >> n >> m;
  // Tworzymy tablicę
  // zbiorów rozłącznych
  T = new Tnode [n];
  // Tablicę T inicjujemy
  for(i = 0; i < n; i++)
  {
    // Numer węzła
    T[i].data = i;
     // Tworzymy zbiór
     // jednoelementowy
     make_set(&T[i]);
  }
  // Odczytujemy kolejne
  // definicje krawędzi.
  for(i = 0; i < m; i++)
  {
    // Wierzchołki tworzące krawędź
    cin >> v >> u;
    // Łączymy zbiory z u i v
    union_sets(&T[v], &T[u]);
  }
  // Wypisujemy wyniki
  cout << endl;
  for(i = 0; i < n; i++)
    if(i == find_set(&T[i])->data)
    {
      cout << "CC" << setw(3) << i << ":";
      for(j = 0; j < n; j++)
        if(i == find_set(&T[j])->data)
          cout << setw(3) << j;
      cout << endl;
    }
  // Usuwamy tablicę zbiorów rozłącznych
  delete [] T;
  return 0;
}
Basic
' Znajdowanie spójnych składowych
' za pomocą zbiorów rozłącznych
' Data: 1.04.2014
' (C)2014 mgr Jerzy Wałaszek
'--------------------------------

' Typ danych dla drzew struktury
' zbiorów rozłącznych
Type Tnode
  ' Rodzic węzła
  up As Tnode Ptr
  ' Ranga
  rank As Integer
  ' Zawartość węzła
  data As Integer
End Type

' Tworzy drzewo jednowęzłowe
'---------------------------
Sub make_set(ByVal x As Tnode Ptr)
  ' x staje się korzeniem drzewa
  x->up = x
  ' Rangę zerujemy 
  x->rank = 0
End Sub

' Zwraca korzeń drzewa i ustawia pola up
' wszystkich węzłów nadrzędnych
' aż do korzenia
'---------------------------------------
Function find_set(ByVal x As Tnode Ptr) _
                          As Tnode Ptr
  If x->up <> x Then _
    x->up = find_set(x->up)
  Return x->up
End Function

' Łączy ze sobą drzewa z x i z y
'--------------------------------
Sub union_sets(ByVal x As Tnode Ptr, _
               ByVal y As Tnode Ptr)

  Dim As Tnode Ptr rx, ry

  ' Wyznaczamy korzeń drzewa z węzłem x
  rx = find_set(x)
  ' Wyznaczamy korzeń drzewa z węzłem y
  ry = find_set(y)
  ' Korzenie muszą być różne
  If rx <> ry Then
    ' Porównujemy rangi drzew
    If rx->rank > ry->rank Then
      ' rx większe, dołączamy ry
      ry->up = rx
    Else
      ' równe lub ry większe, dołączamy rx
      rx->up = ry
      If rx->rank = ry->rank Then _
        ry->rank += 1
    End If
  End If
End Sub

' **********************
' *** PROGRAM GŁÓWNY ***
' **********************

' Liczba wierzchołków i krawędzi
Dim As Integer n, m
Dim As Tnode Ptr T
Dim As integer i, j, v, u

Open Cons For Input As #1
' Odczytujemy liczbę wierzchołków i krawędzi
Input #1, n, m
' Tworzymy tablicę zbiorów rozłącznych
T = New Tnode [n]
' Tablicę T inicjujemy
For i = 0 To n-1
  ' Numer węzła
  T[i].data = i
  ' Tworzymy zbiór jednoelementowy
  make_set(VarPtr(T[i]))
Next
' Odczytujemy kolejne definicje krawędzi
For i = 0 To m-1
  ' Wierzchołki tworzące krawędź
  Input #1, v, u
  ' Łączymy zbiory z u i v
  union_sets(VarPtr(T[v]),VarPtr(T[u]))
Next
Close #1
' Wypisujemy wyniki
Print
For i = 0 To n-1
  If i = find_set(VarPtr(T[i]))->data Then
    Print Using "CC ### :";i;
    For j = 0 To n-1
      If i = find_set(VarPtr(T[j]))->data _
        Then Print Using "###";j;
    Next
    Print
  End If
Next
' Usuwamy tablicę zbiorów rozłącznych
Delete [] T
End
Python (dodatek)
# Znajdowanie spójnych składowych
# za pomocą zbiorów rozłącznych
# Data: 1.01.2024
# (C)2024 mgr Jerzy Wałaszek
#--------------------------------

# Klasa dla drzew struktury
# zbiorów rozłącznych
class Tnode:
    def __init__(self):
        # Rodzic węzła
        self.up = None
        # Ranga
        self.rank = 0
        # Zawartość węzła
        self.data = 0


# Tworzy drzewo jednowęzłowe
def make_set(x):
    # x staje się korzeniem drzewa
    x.up = x
    # Rangę zerujemy 
    x.rank = 0


# Zwraca korzeń drzewa i ustawia pola up
# wszystkich węzłów nadrzędnych
# aż do korzenia
def find_set(x):
    if x.up is not x:
        x.up = find_set(x.up)
    return x.up


# Łączy ze sobą drzewa z x i z y
def union_sets(x, y):
    # Wyznaczamy korzeń drzewa
    # z węzłem x
    rx = find_set(x)
    # Wyznaczamy korzeń drzewa
    # z węzłem y
    ry = find_set(y)
    # Korzenie muszą być różne
    if rx is not ry:
        # Porównujemy rangi drzew
        if rx.rank > ry.rank:
            # rx większe, dołączamy ry
            ry.up = rx
        else:
            # równe lub ry większe,
            # dołączamy rx
            rx.up = ry
            if rx.rank == ry.rank:
                ry.rank += 1


# **********************
# *** PROGRAM GŁÓWNY ***
# **********************

# Odczytujemy liczbę
# wierzchołków i krawędzi
x = input().split()
n = int(x[0])
m = int(x[1])
# Tworzymy tablicę
# zbiorów rozłącznych
t = []
# Tablicę T inicjujemy
for i in range(n):
    # Numer węzła
    t.append(Tnode())
    t[i].data = i
    # Tworzymy zbiór
    # jednoelementowy
    make_set(t[i])
# 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])
    # Łączymy zbiory z u i v
    union_sets(t[v],t[u])
# Wypisujemy wyniki
print()
for i in range(n):
    if i == find_set(t[i]).data:
        print("CC %3d :" % i, end="")
        for j in range(n):
            if i == find_set(t[j]).data:
                print("%3d" % j, end="")
        print()
# Usuwamy tablicę zbiorów rozłącznych
del t
Wynik:
17 17
0 1
0 2
0 3
1 2
1 14
4 11
4 12
5 6
5 9
6 7
6 8
10 15
11 15
12 15
13 14
13 16
14 16

CC 1 :  0  1  2  3 13 14 16
CC 2 :  4 10 11 12 15
CC 3 :  5  6  7  8  9

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.