Documente Academic
Documente Profesional
Documente Cultură
Un graf este o pereche ordonata G=(V,M), unde V este multimea nodurilor, iar M este multimea
muchiilor, muchii alcatuite din elemene ale multimii V.
Un exemplu de graf ar fi drumurile dintr‐un oras in care intersectiile reprezinta nodurile, iar drumurile
reprezinta muchiile.
Pentru a reprezenta informatic grafic avem doua solutii: matricea de adiacenta(costisitoare ca spatiu
de memorie) si listele de adiacenta sau de vecini(reprezentarea se poate face folosind liste alocate
dinamic sau tipul vector din biblioteca vector).
Matricea de adiacenta pentru graful anterior este:
1 2 3 4 5 6 7 8 9
1 0 0 0 1 1 0 0 0 0
2 0 0 0 1 0 1 0 0 0
3 0 0 0 0 0 0 0 0 1
4 1 1 0 0 0 0 1 0 0
5 1 0 0 0 0 0 1 0 0
6 0 1 0 0 0 0 1 0 0
7 0 0 0 1 1 1 0 0 0
8 0 0 0 0 0 0 0 0 1
9 0 0 1 0 0 0 0 1 0
Se observa da daca exista muchie intre nodul i si nodul j a[i][j]=1, altfel a[i][j]=0.
Matricea de adiacenta va avea zero pe diagonala principala si 1 in rest, iar numarul de valori de 1 va
reprezenta 2*numarul de muchii al grafului.
O alta metoda de reprezentare este lista de vecini sau de adiacenta:
1: 4, 5
2: 4, 6
3: 9
4: 1, 2, 7
5: 1, 7
6: 2, 7
7: 4, 5, 6
8: 9
9: 3, 8
Numarul de muchii care trec printr‐un nod se numeste grad. Deci, gradul unui nod va fi dat de numarul
sau de vecini sau de suma valorilor de pe linia(sau coloana) corespunzatoare din matricea de
adiacenta.
Astfel 4 are gradul 3 si 8 are gradul 1
Un nod care are graul 1 se numeste nod terminal, iar un nod care are gradul 0 nod izolat.
Suma gradelor tuturor varfurilor este egala cu dublul numarului de muchii.
In general, un graf neorientat este reprezentat complet dandu‐se numarul de noduri, numarul de
muchii si apoi muchiile. Graful se citeste dintr‐un fisier text sau de la tastatura si este transformat intr‐
o matrice de adiacenta. Atunci cand se citesc muchiile se poate calcula si memora gradul fiecarui nod
intr‐un vector.
De exemplu, graful anterior se poate introduce astfel(in graf.txt):
9 9
1 4
1 5
2 4
2 6
3 9
4 7
5 7
6 7
8 9
Mai jos am citit graful anterior, l‐am transformat in matrice de adiacenta si am calculat gradul fiecarui
nod. De asemenea am scris o functie care calculeaza gradul unui nod x folosind matricea de adiacenta.
In plus am afisat lista de vecini a unui nod x si apoi listele de vecini ale grafului.
Listing graf.cpp
#include <iostream>
#include<fstream>
using namespace std;
int grad[100] = { 0 };//declar vectorul gradelor
void ReadGraph(int& n, int a[100][100])
{
int i, j, x, y, m;
ifstream fin("graf.txt");
fin >> n >> m;
//initializez matricea de adiacenta
for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++)
a[i][j] = 0;
for (i = 1; i <= m; i++)
{
fin >> x >> y;//citesc muchia
a[x][y] = a[y][x] = 1;
grad[x]++;//calculez gradul
grad[y]++;//calculez gradul
}
fin.close();
}
int DegreeOfX(int n, int a[100][100], int x)
{
int i, grad = 0;
for (i = 1; i <= n; i++)
grad += grad + a[x][i];
return grad;
}
void NeighboorOfX(int n, int a[100][100], int x)
{
int i;
for (i = 1; i <= n; i++)
if (a[x][i] == 1)
cout << i << " ";
cout << endl;
}
void Neighboors(int n, int a[100][100])
{
int i;
for (i = 1; i <= n; i++)
{
cout << i << ": ";
NeighboorOfX(n, a, i);
}
}
void WriteGraph(int n, int a[100][100])
{
int i, j;
for (i = 1; i <= n; i++)
{
for (j = 1; j <= n; j++)
cout << a[i][j] << " ";
cout << endl;
}
}
void Degrees(int n, int v[])
{
int i;
for (i = 1; i <= n; i++)
cout << "Gradul lui " << i << " este " << grad[i] << endl;
}
int main()
{
int n, a[100][100];
ReadGraph(n, a);
WriteGraph(n, a);
Degrees(n, grad);
Neighboors(n, a);
}
Tipuri particulare de grafuri:
Graf regulat: graf in care toate nodurile au acelasi grad.
Graf complet: graf in care intre orice 2 noduri exista o muchie care le leaga (gradul unui nod este n‐1,
suma gradelor este n*(n‐1), iar numarul de muchii este n*(n‐1)/2).
Un subgraf se obtine din graful initial prin eliminarea unuia sau mai multe noduri si a muchiilor care
trec prin acestea.
Un graf partial se obtine din graful initial prin eliminarea uneia sau multor muchii.
Observatie. Graful initial va reprezenta un caz particular de subgraf sau graf partial(atunci cand nu
elimin nici un nod sau nicio muhie).
Numarul total de grafuri neorientate cu n noduri este 2n*(n‐1)/2. Cum se obtine aceasta valoare?
Graful orientat cu n noduri si cele mai multe muchii este cel complet, care are n*(n‐1)/2 muchii.
Celelalte grafuri cu n noduri sunt grafuri partiale ale acestui graf obtinute prin eliminarea a cate unei
muchii, 2 muchii, …, n muchii. Astfel, numarul de grafuri cu n noduri se calculeaza astfel(stiind ca cel
mai mare numar de muchii posibil este n*(n‐1)/2):
∗ ∗
𝐶 ∗ 𝐶 ∗ 𝐶 ∗ 𝐶 ∗ 2
Mai sus, prima valoare reprezinta graful complet cu n noduri, iar ultima valoare graful cu n noduri si
nicio muchie.
Tot la fel se poate deduce ca numarul de subgrafuri cu x noduri ale unui graf initial cu n noduri este
𝐶 , iar numarul total de subgrafuri este 2n(valoarea se deduce ca mai sus).
Tot la fel se poate deduca ca numarul de grafuri partiale cu x muchii ale unui graf initial cu m muchii
este 𝐶 , iar numarul total de subgrafuri este 2m(valoarea se deduce ca mai sus).
In general, pentru a crea un subgraf prin program se va adauga pe rand cate un nod si se va verifica
proprietatea ceruta de problema, iar pentru a crea un graf partial se va adauga pe rand cate o muchie
si se va verifica proprietatea ceruta de problema.
Un lant este o insiruire de noduri, astfel incat, intre orice doua noduri vecine din insiruire sa existe o
muchie care sa le lege.
Exemplu: 1 5 7 5 este un lant
1 5 7 5 2 nu este lant
Un lant elementar este un lant in care nu se repeta nodurile.
1 5 7 6 este un lant elementar
Un lant simplu este un lant in care nu repeta muchiile
1 5 7 6 este si un lant simplu
Presupun ca in vectorul chain am retinut o insiruire de x noduri. Functiile de mai jos verifica daca chain
este lant, daca este lant elementar si daca este lant simplu:
int Chain(int a[100][100], int x, int chain[])
{
int i;
if (x == 1) return 0;
for (i = 1; i <= x ‐ 1; i++)
if (a[chain[i]][chain[i + 1]] == 0) return 0;
return 1;
}
int ElementaryChain(int a[100][100], int x, int chain[])
{
int i, j;
if (Chain(a, x, chain) == 0) return 0;
for (i = 1; i <= x ‐ 1; i++)
for (j = i + 1; j <= x; j++)
if (chain[i] == chain[j]) return 0;
return 1;
}
int SimpleChain(int a[100][100], int x, int chain[])
{
int B[100][100] = { 0 }, i, j;
//B este o matrice a lantului in care pun fiecare muchie care se repeta in lant
//Daca o muchie se repeta returnez 0
if (Chain(a, x, chain) == 0) return 0;
for (i = 1; i <= x ‐ 1; i++)
if (B[chain[i]][chain[i + 1]] == 1) return 0;
else B[chain[i]][chain[i + 1]] = B[chain[i + 1]][chain[i]] = 1;
return 1;
}
Un ciclu este un lant simplu care are cel putin patru noduri si in care primul si ultimul nod trebuie sa
fie identice.
int Cycle(int a[100][100], int x, int chain[])
{
if (x <= 3) return 0;
if (chain[1] != chain[x])return 0;
if (SimpleChain(a, x, chain) == 0) return 0;
return 1;
}
Un algoritm foarte simplu si usor de implementat, dar care consuma timp este algoritmul care
genereaza matricea lanturilor(algoritmul Floyd‐Warshall). Algoritmul se bazeaza pe urmatoarea idee
se ia fiecare nod k de la 1 la n si se procedeaza astfel: daca nu exista lant intre i si j se verifica daca
exista lant intre i si k si intre k si j. Daca exista acest lant i k j atunci va exista lant intre i si j.
void FloydWarshall(int a[100][100], int n)
{
int i, j, k;
for (k = 1; k <= n; k++)
for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++)
if (i != j and j != k and k != i and a[i][k] == 1 and
a[k][j] == 1) a[i][j] = 1;
}
Odata cu matricea lanturilor apare si notiunea de conexitate.
Un graf este conex daca intre orice doua noduri ale sale exista un lant care le leaga.
Un graf poate avea una(si atunci este graf conex) sau mai multe componente conexe.
O componenta conexa a unui graf este un subgraf conex al acestuia in care proprietatea de conexitate
este maximala(adica nu mai exista alte noduri ale grafului initial care sa poata face parte din aceasta
componenta). Un nod izolat(de grad 0) este o componenta conexa.
In graful de mai sus exista doua componente conexe: 1, 2, 4, 5, 6, 7 si 3, 8, 9.
Folosind matricea lanturilor pot verifica daca un graf este conex sau pot afisa lantul care leaga doua
noduri sau chiar componentele conexe ale unui graf.
O modalitate eficienta de a rezolva acest tip de probleme este de a utiliza cele doua parcurgeri:
parcurgerea in latime(Breath First) si parcurgerea in adancime(Depth First).
Parcurgerea in latime
Pentru parcurgerea in latime vom folosi doi vectori: q(care va functiona ca o coada – adica elementele
vor fi adaugate la sfarsitul vectorului, iar vectorul va fi parcurs de la inceput) si viz care va marca cu 1
fiecare element vizitat.
Cum se face parcurgerea? La fiecare pas se stabileste nodul curent din coada si se afiseaza vecinii lui
nevizitati.
Daca nodul din care fac parcurgerea va fi 2 parcurgerea si vectorii vor arata astfel(pe rand se vor
adauga vecinii lui 2 – 4 si 6, ai lui 4 – 1 si 7, ai lui 1 – 5):
1 2 3 4 5 6 7 8 9
q 2
viz 0 1 0 0 0 0 0 0 0
1 2 3 4 5 6 7 8 9
q 2 4 6
viz 0 1 0 1 0 1 0 0 0
1 2 3 4 5 6 7 8 9
q 2 4 6 1 7
viz 1 1 0 1 0 1 1 0 0
Pentru 6 nu adaug nimic pentru ca
vecinii lui 6 au fost deja marcati(2 si
7). Voi adauga pentru 1 doar pe 5
1 2 3 4 5 6 7 8 9
q 2 4 6 1 7 5
viz 1 1 0 1 1 1 1 0 0
Pentru 5 si 7 nu mai am pe cine sa adaug asa ca ma opresc din parcurgere. Se poate vedea ca am
parcurs doar prima componenta conexa.
void BreathFirst(int n, int a[100][100], int x)
{
int first, last, i, q[100], viz[100] = { 0 };
first = last = 1;
q[last] = x;
viz[x] = 1;
while (first <= last)
{
int current_node = q[first];
cout << current_node << " ";
for(i = 1; i <= n; i++)
if (a[current_node][i] == 1 and viz[i] == 0)
{
//cout << i;
q[++last] = i;
viz[i] = 1;
}
first++;
}
}
In cazul in care vreau sa verific daca graful este conex fac parcurgerea din orice nod si returnez variabila
last. Daca last este egal cu n graful este conex.
Parcurgerea imi va afisa elementele componentei conexe din care face parte x.
Parcurgerea in latime imi poate afisa, deci, componetele conexe ale unui graf si numraul lor astfel:
void BreathFirst(int n, int a[100][100], int x, int viz[])
{
int first, last, i, q[100];
first = last = 1;
q[last] = x;
viz[x] = 1;
while (first <= last)
{
int current_node = q[first];
cout << current_node << " ";
for (i = 1; i <= n; i++)
if (a[current_node][i] == 1 and viz[i] == 0)
{
//cout << i;
q[++last] = i;
viz[i] = 1;
}
first++;
}
}
In main:
for(i=1;i<=n;i++)
if (viz[i] == 0)
{
cc++;
cout << "Componenta conexa " << cc << ": ";
BreathFirst(n, a, i, viz);
cout << endl;
}
cout << cc << endl;
De asemenea, se mai pot adauga doi vectori pred si lg. Lg poate calcula lungimea celui mai scurt lant
dintre nodul x si oricare alt nod din graf, iar cu ajutorul lui pred s poate afisa chiar lantul. pred[i] va fi
egal cu nodul din care provine i in timpul parcurgerii in latime:
1 2 3 4 5 6 7 8 9
q 2
viz 0 1 0 0 0 0 0 0 0
pred 0
1 2 3 4 5 6 7 8 9
q 2 4 6
viz 0 1 0 1 0 1 0 0 0
pred 0 2 2
1 2 3 4 5 6 7 8 9
q 2 4 6 1 7
viz 1 1 0 1 0 1 1 0 0
pred 4 0 2 2 4
Pentru 6 nu adaug nimic pentru ca
vecinii lui 6 au fost deja marcati(2 si
7). Voi adauga pentru 1 doar pe 5
1 2 3 4 5 6 7 8 9
q 2 4 6 1 7 5
viz 1 1 0 1 1 1 1 0 0
pred 4 0 2 1 2 4
Cum se afiseaza lantul dintre 2 si 7?
Mai intai se afiseaza 7, apoi pred[7], adica 4. Apoi pred[4] adica 2, apoi pred[2] adica ma opresc.
Deci 7 4 2, care este cel mai scurt lant posibil. Pentru a afisa in ordinea 2 4 7 voi folosi recursivitatea.
void BreathFirst(int n, int a[100][100], int x, int lg[], int pred[])
{
int first, last, i, q[100], viz[100] = { 0 };
first = last = 1;
q[last] = x;
viz[x] = 1;
lg[x] = 0;
pred[x] = 0;
while (first <= last)
{
int current_node = q[first];
cout << current_node << " ";
for (i = 1; i <= n; i++)
if (a[current_node][i] == 1 and viz[i] == 0)
{
q[++last] = i;
viz[i] = 1;
lg[i] = lg[current_node] + 1;
pred[i] = current_node;
}
first++;
}
}
void lant(int x, int pred[])
{
if (x != 0)
{
lant(pred[x], pred);
cout << x << " ";
}
}
In main:
x = 2;
BreathFirst(n, a, x, lg, pred);
for (i = 1; i <= n; i++)
{
cout << "Lungimea lantului dintre " << x << " si " << i << " este " <<
lg[i]<<" iar lantul este: ";
lant(i, pred);
cout << endl;
}
Parcurgerea in adancime
Parcurgerea in adancime foloseste o stiva si un vector viz pentru parcurgere, astfel incat, in momentul
in care elementul din varful stivei nu mai are vecini este sters si se trece la urmatorul element. Se
procedeaza astfel: se afiseaza elementul, se marcheaza, se afiseaza primul lui vecin, se marcheaza etc.
Daca nodul curent nu mai are vecini se coboara un nivel in stiva si se afiseaza primul vecin nevizitat al
acestuia. Pentru a simula stiva cel mai simplu este sa se foloseasca recursivitatea:
1 2 3 4 5 6 7 8 9
stack 2
viz 0 1 0 0 0 0 0 0 0
1 2 3 4 5 6 7 8 9
stack 2 4
viz 0 1 0 1 0 0 0 0 0
1 2 3 4 5 6 7 8 9
stack 2 4 1
viz 1 1 0 1 0 0 0 0 0
1 2 3 4 5 6 7 8 9
stack 2 4 1 5
viz 1 1 0 1 1 0 0 0 0
1 2 3 4 5 6 7 8 9
stack 2 4 1 5 7
viz 1 1 0 1 1 0 1 0 0
1 2 3 4 5 6 7 8 9
stack 2 4 1 5 7 6
viz 1 1 0 1 1 1 1 0 0
Elementul din varful stivei este 6 care nu are vecini nevizitati, apoi 7, la fel etc pana cand stiva devine
goala. Daca folosim recursivitatea nu mai e nevoie sa folosesc stiva.
Vectorul viz se declara global si se initializeaza cu 0.
void DepthFirst(int n, int a[100][100], int x)
{
int i;
cout << x << " ";
viz[x] = 1;
for (i = 1; i <= n; i++)
if (a[i][x] == 1 and viz[i] == 0)
DepthFirst(n, a, i);
}
Tema. Sa se afiseze componentele conexe si lungimea acestora folosind parcurgerea in adancime.
Sa se verifice daca un graf este aciclic sau nu. Se va adapta functia de parcurgere in adancime.
Functia de mai jos verifica daca un nod face parte dintr‐un ciclu sau nu.
int HasCycle(int n, int a[100][100], int x, int pred)
{
int i;
viz[x] = 1;//marchez x ca vizitat
for (i = 1; i <= n; i++)
if (a[x][i] == 1)//daca i este vecin al lui x
if(viz[i] == 0)//daca nu a fost marcat
if (HasCycle(n, a, i, x) == 0) return 0;//daca el este
parte dintr‐un ciclu
else;//daca nu este parte a unui ciclu nu fac nimic
else if (pred != i) return 0;//daca i este marcat si nu este
predecesorul lui x returnez 0
return 1;//daca scap nu am cicluri
}
Un graf bipartit este un graf in care nodurile sale pot fi impartite in doua multimi A si B, astfel incat
intre nodurile care apartin unei multimi sa nu existe muchie.
Tema. Sa se adapteze DFS astfel incat sa se verifice daca un graf este bipartit sau nu.
Un graf este graf eulerian daca contine un ciclu eulerian. Un ciclu este eulerian daca contine toate
muchiile grafului. Un graf eulerian are gradele tuturor nodurilor pare.
Un graf este hamiltonian daca contine un ciclu hamiltonian. Un ciclu este hamiltonian daca este
elementar si contine toate nodurile grafului.
Urmatoarea functie va afisa un ciclu eulerian. Mai intai se va verifica daca graful are toate gradele
pare, apoi se va adapta DFS astfel: pentru fiecare vecin al unui nod x, acesta va fi eliminat din graf.
Fiecare nod x va fi adaugat la o coada, care va fi fisata la final.
void EulerCycle(int n, int a[100][100], int x)
{
int i;
for(i=1;i<=n;i++)
if (a[x][i] == 1)
{
a[x][i] = a[i][x] = 0;
EulerCycle(n, a, i);
}
q[++el] = x;
}
In main:
for (i = 1; i <= n and Euler == 1; i++)
if (grad[i] % 2 == 1) Euler = 0;
if (Euler == 1) EulerCycle(n, a, 1);
for (i = 1; i <= el; i++) cout << q[i] << " ";
GRAFURI ORIENTATE
Un graf orientat este o pereche ordonata G=(V,M) unde V={x/x este multimea varfurilor}, iar
M={(x,y)/x,y apartin lui V}. Daca exista (x,y) nu este obligatorie relatia inversa.
Reteaua de drumuri dintr‐un oras este un graf orientat(vezi sensurile unice).
Un graf orientat poate fi reprezentat prin matrice de adiacenta sau liste de vecini:
1 2 3 4 5 6
1 0 0 1 0 0 0
2 1 0 0 0 0 0
3 1 1 0 0 0 0
4 1 0 0 0 1 0
5 0 0 0 0 0 1
6 0 0 0 1 0 0
Matricea nu este simetrica fata de diagonala principala. Numarul de valori de 1 din matrice este egal
cu numarul de muchii.
Reprezentarea prin liste de vecini este urmatoarea:
1: 3
2: 1
3: 1, 2
4: 1, 5
5: 6
6: 4
Un nod are grad interior si grad exterior. Gradul interior este dat de numarul de arce care intra intr‐
un varf, iar gradul exterior de numarul de arce care ies dintr‐un varf.
Suma valorilor de pe linia x a matricei este gradul exterior al lui x, iar suma valorilor de pe coloana x a
matricei este gradul interior.
Varf Grad exterior Grad interior
1 1 3
2 1 1
3 2 1
4 2 1
5 1 1
6 1 1
Se observa ca suma gradelor interioare ale grafului este egala cu suma gradelor interioare si este egala
cu numarul de arce.
In general, un graf orientat este reprezentat complet dandu‐se numarul de varfuri, numarul de arce si
apoi arcele. Graful se citeste dintr‐un fisier text sau de la tastatura si este transformat intr‐o matrice
de adiacenta. Atunci cand se citesc arcele se poate calcula si memora gradul interior sau exterior al
fiecarui varf in doi vector.
De exemplu, graful anterior se poate introduce astfel(in graf.txt):
6 8
1 3
2 1
3 1
3 2
4 1
4 5
5 6
6 4
Mai jos am citit graful anterior, l‐am transformat in matrice de adiacenta si am calculat gradele fiecarui
varf. De asemenea am scris o functie care calculeaza gradul exterior unui varf x folosind matricea de
adiacenta si una care calculeaza gradul interior al unui varf folosind matricea de adiacenta. In plus am
afisat lista de vecini a unui varf x si apoi listele de vecini ale grafului.
Listing graf.cpp
#include <iostream>
#include<fstream>
using namespace std;
int gradext[100] = { 0 }, gradint[100] = { 0 };//declar vectorul gradelor
void ReadGraph(int& n, int a[100][100])
{
int i, j, x, y, m;
ifstream fin("graf.txt");
fin >> n >> m;
//initializez matricea de adiacenta
for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++)
a[i][j] = 0;
for (i = 1; i <= m; i++)
{
fin >> x >> y;//citesc muchia
a[x][y] = 1;
gradext[x]++;//calculez gradul exterior
gradint[y]++;//calculez gradul interior
}
fin.close();
}
int ExtDegreeOfX(int n, int a[100][100], int x)
{
int i, grad = 0;
for (i = 1; i <= n; i++)
grad += grad + a[x][i];
return grad;
}
int IntDegreeOfX(int n, int a[100][100], int x)
{
int i, grad = 0;
for (i = 1; i <= n; i++)
grad += grad + a[i][x];
return grad;
}
void NeighboorOfX(int n, int a[100][100], int x)
{
int i;
for (i = 1; i <= n; i++)
if (a[x][i] == 1)
cout << i << " ";
cout << endl;
}
void Neighboors(int n, int a[100][100])
{
int i;
for (i = 1; i <= n; i++)
{
cout << i << ": ";
NeighboorOfX(n, a, i);
}
}
void WriteGraph(int n, int a[100][100])
{
int i, j;
for (i = 1; i <= n; i++)
{
for (j = 1; j <= n; j++)
cout << a[i][j] << " ";
cout << endl;
}
}
void Degrees(int n, int v[])
{
int i;
for (i = 1; i <= n; i++)
cout << "Gradul exterior al lui " << i << " este " << gradext[i] <<
endl;
for (i = 1; i <= n; i++)
cout << "Gradul interior al lui " << i << " este " << gradint[i] <<
endl;
}
int main()
{
int n, a[100][100];
ReadGraph(n, a);
WriteGraph(n, a);
Degrees(n, grad);
Neighboors(n, a);
}
Numarul total de grafuri neorientate cu n noduri este 4n*(n‐1)/2.
Numarul de subgrafuri cu x varfuri ale unui graf initial cu n noduri este 𝐶 , iar numarul total de
subgrafuri este 2n(valoarea se deduce ca mai sus).
Numarul de grafuri partiale cu x muchii ale unui graf initial cu m muchii este 𝐶 , iar numarul total de
subgrafuri este 2m(valoarea se deduce ca mai sus).
Un drum este o insiruire de varfuri, astfel incat, intre orice doua noduri vecine din insiruire sa existe
un arc care sa le lege.
Un drum elementar este un drum in care nu se repeta nodurile.
Un drum simplu este un drum in care nu repeta arcele.
Un ciclu este un drum simplu care are cel putin patru varfuri si in care primul si ultimul varf trebuie sa
fie identice.
Un graf orientat este tare conex daca oricare ar fi doua varfuri x si y din graf va exista drum si intre x
si y si intre y si x.
O componenta tare conexa este un subgraf al grafului initial care este tare conex si in care proprietatea
de tare conexitate este maximala. Graful de mai sus are doua componente tare conexe:
1 2 3
4 5 6
Pentru a verifica ca un graf este tare conex se va folosi matricea drumurilor, matrice care se
construieste cu algoritmul Floyd‐Warshall.
De asemenea, se pot construi componentele tare conexe folosind matricea drumurilor, parcurgerea
in latime sau parcurgerea in adancime.