|
Serwis Edukacyjny w I-LO w Tarnowie
Materiały dla uczniów liceum |
Wyjście Spis treści Wstecz Dalej
Autor artykułu: mgr Jerzy Wałaszek |
©2026 mgr Jerzy Wałaszek
|
Dla danego grafu 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).
![]() |
![]() |
|
| Graf spójny | Graf niespójny |
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ą
Znalezienie wartości
Tworzymy tablicę
Gdy przeglądniemy całą tablicę
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: cn ← cn+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: v ← S.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
|
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):
|
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 |
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ę
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 u–v grafu ; łączymy zbiory zawierające wykonuj: union_sets(u, v) ; wierzchołki tworzące krawędź K05: Zakończ z wynikiem T[]
|
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):
|
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.
|
// 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 |
![]() |
Zespół Przedmiotowy Chemii-Fizyki-Informatyki w I Liceum Ogólnokształcącym im. Kazimierza Brodzińskiego w Tarnowie ul. Piłsudskiego 4 ©2026 mgr Jerzy Wałaszek |
Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email:
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.