Sunteți pe pagina 1din 16

Lectia 1. Grafuri. Parcurgeri.

Conexitate

Grafuri
Matematic, un graf este o pereche ordonată de mulțimi, G=(V,E), unde V reprezintă o mulțime
finită și nevidă de elemente numite vârfuri (sau noduri), iar E este o mulțime de perechi cu elemente din
V, numite muchii (dacă sunt neordonate) sau arce (dacă sunt ordonate). În primul caz, graful se numește
neorientat, iar în al doilea orientat (sau digraf).

Exemple de grafuri din viața de zi cu zi


Practic, un graf este un mod de a reprezenta elementele unei mulțimi și conexiunile dintre
acestea, stabilite pe baza unei anumite relații. Graful este neorientat dacă relația respectivă este
reflexivă, sau orientat în caz contrar. Iată câteva exemple:

1. Harta unui oraș

Nodurile sunt intersecții, iar o muchie dintre două noduri reprezintă faptul că există o stradă între cele
două intersecții. În cazul în care vrem să evidențiem faptul că unele străzi au sens unic, graful devine
orientat.

2. O rețea de socializare

Nodurile sunt utilizatorii, iar o muchie reprezintă relația de prietenie între doi utilizatori. Cum prietenia
este o relație reflexivă, dacă A este prieten cu B, înseamnă că și B este prieten cu A. Așadar, vorbim
despre un graf neorientat.

3. Un circuit electric

Făcând abstracție de elementele de circuit de pe laturile schemei (cum ar fi rezistorii), aceasta este un
graf în care vârfurile sunt nodurile rețelei, iar ciclurile elementare sunt ochiurile acesteia.

4. Formula de structură a unei substanțe chimice

Nodurile vor fi atomii (sau grupările de atomi), iar muchiile legăturile dintre aceștia. Eventual, costul
fiecărei muchii poate reține tipul legăturii chimice dintre extremitățile ei.

Noțiuni elementare din teoria grafurilor


Cele două noduri x și y ce formează muchia [x,y] se numesc extremități. În cazul grafurilor
orientate, pentru arcul (x,y), x se numește extremitate inițială, iar y extremitate finală. În plus, vom spune
că x și y sunt noduri adiacente, și incidente la muchia/arcul pe care îl formează.
Lectia 1. Grafuri. Parcurgeri. Conexitate

Într-un graf neorientat, nodurile cu gradul 0 se numesc noduri izolate, iar cele cu gradul 1 se numesc
noduri terminale.

Graf simplu, multigraf


Între oricare două noduri ale unui graf simplu poate exista cel mult o muchie/arc. În caz contrar,
structura de date se va numi multigraf. Într-un multigraf, muchiile cu aceeași pereche de extremități se
numesc muchii paralele. În plus, muchiile cu extremități identice (de la un nod la el însuși), se numesc
bucle.

Aici apare o discuție, deoarece unii autori de specialitate consideră că doar multigrafurile (nu și
grafurile) pot avea bucle, pe când ceilalți susțin contrariul. Preferabilă este prima variantă, pentru că altfel
multe formule clasice legate de grafuri s-ar complica. De obicei se specifică în enunțul problemelor dacă
graful poate avea bucle sau nu. Oricum, noi vom lucra doar cu grafuri simple.

Numărul maxim de muchii/arce


Numărul maxim de muchii ale unui graf neorientat cu n noduri este n(n−1)/2, pentru că, având la
dispoziție n noduri, se pot forma C2n=n(n−1)/2 perechi neordonate, iar în cel mai fericit caz, pentru oricare
două noduri x și y din V există muchia [x,y].

Similar, numărul maxim de arce ale unui graf orientat cu n noduri este 2C2n=n(n−1), pentru că
două noduri x și y pot contribui cu maxim două arce: (x,y) și (y,x).

Numărul de grafuri orientate/neorientate


De aici mai putem deduce două formule: Numărul de grafuri neorientate cu n noduri este 2n(n−1)/2,
pentru că între fiecare două noduri pot exista 0 sau 1 muchii.

Analog, numărul de grafuri orientate cu n noduri este 4n(n−1)/2, pentru că între fiecare două noduri
x și y pot exista arcul (x,y), arcul (y,x), ambele sau niciunul.

Gradul unui nod


Lectia 1. Grafuri. Parcurgeri. Conexitate

Într-un graf neorientat, gradul unui nod reprezintă numărul de muchii incidente cu acesta, și se
notează cu d(x).

Într-un graf orientat, gradul interior al nodului x se notează cu d −(x) și este egal cu numărul de
arce cu extremitatea finală x, iar gradul exterior, notat cu d+(x) este numărul arcelor cu extremitatea
inițială x.

Suma gradelor nodurilor unui graf neorientat este egală cu dublul numărului său de muchii,
pentru că fiecare muchie contribuie cu câte o unitate la gradul a două noduri:

∑ d ( x )=2|E|
x∈V

Atât suma gradelor interioare cât și suma gradelor exterioare ale nodurilor unui graf orientat sunt
egale cu numărul de arce ale grafului, pentru că fiecare muchie contribuie cu câte o unitate la fiecare
sumă:
+ ¿( x )
−¿ ( x )= ∑ d =|E| ¿
∑d x∈ V
¿
x∈V

Grafuri asociate unui graf


Fie graful G=(V,E). Graful G′=(V,E′), cu E′⊂E este un graf parțial al grafului G. Se observă că acesta
se poate obține prin eliminarea unor muchii/arce din graful inițial. Numărul grafurilor parțiale ale lui G
este 2|E| (pentru fiecare muchie avem două variante: o ștergem sau nu).

Graful G′′=(V′′,E′′) cu V′′⊂V și E′′ mulțimea tuturor muchiilor/arcelor din E cu ambele extremități
în V′′ se numește subgraf al lui G. Acesta poate fi obținut eliminând unele noduri din G, împreună cu toate
muchiile incidente la acestea. Numărul de subgrafuri ale lui G este 2|V|−1 (numărul submulțimilor lui V,
excluzând mulțimea vidă, deoarece graful nu poate avea 0 noduri).

Un subgraf parțial este, după cum îi spune și numele, un subgraf din care s-au eliminat niște muchii.
Lectia 1. Grafuri. Parcurgeri. Conexitate

Graful complementar unui graf G are aceeași mulțime de vârfuri, dar mulțimea muchiilor/arcelor
conține muchiile/arcele care nu apar în G.

Pentru un graf orientat G, graful G T=(V,ET) se numește graful transpus al lui G dacă E T={(y,x)∣(x,y)∈E}.

Tipuri speciale de grafuri


Un graf se numește complet dacă oricare două noduri ale sale sunt adiacente. Graful neorientat
complet cu n noduri se notează K n și conține n(n−1)/2 muchii.

Există un singur graf neorientat complet cu n noduri, însă grafurile orientate complete cu n noduri
sunt mai multe. Mai exact, între oricare două noduri x și y pot exista ori arcul (x,y), ori (y,x), ori ambele.

Deci, numărul grafurilor orientate complete cu n noduri este 3n(n−1)/2.

Un graf orientat se numește antisimetric în cazul în care pentru oricare două noduri x și y, dacă
există arcul (x,y), atunci nu există și arcul (y,x). Cu alte cuvinte, între două noduri există cel mult un arc.

Există 3n(n−1)/2 grafuri antisimetrice, deoarece între două noduri x și y pot exista arcul (x,y), arcul
(y,x) sau niciunul.

Un graf orientat complet și antisimetric se numește graf turneu. Numărul de grafuri turneu cu n
noduri este 2n(n−1)/2, căci între două noduri x și y poate exista arcul (x,y) sau arcul (y,x).
Lectia 1. Grafuri. Parcurgeri. Conexitate

Un graf neorientat se numește bipartit dacă mulțimea muchiilor sale poate fi partiționată în două
submulțimi A și B, astfel încât orice muchie are o extremitate în A și una în B.

Un graf bipartit complet este un graf bipartit în care fiecare nod din A este adiacent cu fiecare
nod din B.

Un graf neorientat se numește regulat dacă toate nodurile sale au același grad.

Lanț, ciclu, drum, circuit


Într-un graf neorientat, un lanț este o secvență de noduri [x1,x2,…,xk], cu proprietatea că oricare
două noduri consecutive din secvență sunt adiacente. Extremitatea inițială a lanțului este x1, iar cea finală
xk. Lungimea unui lanț este numărul muchiilor din care este compus, deci k−1.

Un lanț este elementar dacă nu conține de mai multe ori același nod.

Un lanț se numește simplu dacă nu conține de mai multe ori aceeași muchie.

Se observă că orice lanț elementar este automat și simplu.


Lectia 1. Grafuri. Parcurgeri. Conexitate

Un ciclu este un lanț simplu pentru care extremitatea inițială este aceeași cu cea finală. Ciclul este
elementar dacă nu conține de mai multe ori același nod (cu excepția extremităților).

Similar sunt definite și drumurile (pentru grafurile orientate):

Un drum este o secvență de noduri (x1,x2,…,xk) cu proprietatea că pentru oricare două noduri
consecutive xi și xi+1 există arcul (xi,xi+1). În plus, ciclul se va numi de fapt circuit.

Un lanț/drum/ciclu/circuit se numește hamiltonian dacă trece prin fiecare nod al grafului exact o
singură dată.

Un lanț/drum/ciclu/circuit se numește eulerian dacă trece prin fiecare dintre muchiile/arcele


grafului exact o singură dată.

Numărul ciclurilor hamiltoniene dintr-un graf neorientat complet


Lectia 1. Grafuri. Parcurgeri. Conexitate

Numărul de cicluri hamiltoniene din Kn este egal cu (n−1)!/2.

Cum ajungem la acest rezultat? Mai întâi fixăm primul nod al ciclului în 1, căci vorbind de un ciclu,
nu contează de unde începem. De exemplu, ciclul [1,2,4,3,1] este tot una cu [3,1,2,4,3]. Rămân de
permutat restul de n−1 noduri ale grafului, de unde rezultă acel (n−1)!. La final, împărțim rezultatul la 2
pentru a nu număra același ciclu de două ori: o dată scris de la stânga la dreapta și o dată de la dreapta la
stânga.

Conexitate
Un graf neorientat G se numește conex dacă există lanț între oricare două noduri ale sale.

O componentă conexă a lui G este un subgraf conex maximal al său.

Un graf orientat G se numește tare-conex dacă pentru oricare două noduri x și y ale sale există
atât drum de la x la y, cât și drum de la y la x.

O componentă tare-conexă a lui G este un subgraf tare-conex maximal al său.

Grafuri ponderate
Adesea, modelarea problemelor practice necesită utilizarea unor grafuri în care muchiilor/arcelor
li se asociază costuri (ponderi).

Astfel de grafuri se numesc grafuri ponderate. Funcția care asociază câte un cost fiecărei
muchii/arc a grafului se numește funcție de cost.

De exemplu, avem un graf G=(V,E) ce reprezintă harta unei țări, și funcția f:V→N∗, unde f([x,y])
reprezintă lungimea străzii dintre orașele x și y.
Lectia 1. Grafuri. Parcurgeri. Conexitate

Note
 Mulțimea vârfurilor unui graf poate conține orice fel de elemente, dar pentru simplitate am ales
V={1,2,…,|V|}.
 partiție a unei mulțimi reprezintă un set de submulțimi ale sale, nevide și disjuncte, pentru care
reuniunea lor este egală cu mulțimea respectivă.
 Perechile neordonate se notează folosind paranteze pătrate, pe când perechile ordonate se
notează folosind paranteze rotunde. Din acest motiv, muchiile și lanțurile se notează folosind
paranteze pătrate, în timp ce arcele și drumurile se notează folosind paranteze rotunde. Din acest
punct de vedere, diferența dintre lanțuri și drumuri este că un lanț este același atât citit de la
stânga la dreapta, cât și de la dreapta la stânga.
 Reprezentarea grafică a unui graf este alcătuită din niște cerculețe etichetate (nodurile), unite
prin segmente (muchiile/arcele). Segmentele sunt orientate dacă graful este orientat, adică pe ele
se pune o săgeată care indică sensul arcului. Dacă între două noduri există ambele arce, se
desenează două linii curbate cu săgeată, sau un singur segment, cu săgeată la ambele capete.

 Prin
subgraf cu proprietatea P maximal înțelegem că dacă am mai adăuga la el un nod, împreună cu
muchiile incidente la acesta, proprietatea P nu ar mai fi respectată.
 Pentru demonstrația celor mai multe formule am folosit tehnica următoare: Asociez bijectiv
grafului o funcție f:A→B, unde A este mulțimea tuturor perechilor neordonate de noduri din V, iar
B mulțimea tuturor stărilor în care se poate afla o astfel de pereche. Numărul de grafuri va fi egal
cu numărul de funcții f, care este dat de formula |B| |A|.
Lectia 1. Grafuri. Parcurgeri. Conexitate

Reprezentarea grafurilor
Grafurile se reprezinta prin trei metode: lista de muchii, matricea de adiacență și listele de adiacență.

Reprezentarea grafurilor prin lista de muchii

O soluție naivă este să reținem o listă cu toate muchiile (sau arcele) grafului. Pentru asta, putem
folosi o matrice cu două coloane astfel: mat[i][0] și mat[i][1] ar fi extremitățile muchiei i. Dacă există
costuri pe muchiile grafului, le putem reține adăugând încă o coloană la matrice: mat[i][2] ar fi costul
muchiei i.

Totuși, ar fi mai elegant să definim un struct numit Edge (muchie în engleză) care să rețină
informațiile aferente unei muchii. Deci, pentru stocarea muchiilor vom reține un vector cu elemente de
tipul Edge. Un avantaj al utilizării unui struct este că va fi mai ușor să definim un criteriu de comparare
pentru ce vom face în continuare.
struct Edge {
    int x, y; // extremitățile
    int cost;
}

Acum urmează să vedem cum putem efectua operații obișnuite legate de muchii folosind acest
mod de reprezentare. Este vorba de accesarea, ștergerea și inserarea unei muchii noi, precum și despre
parcurgerea vecinilor unui nod.

Accesarea unei muchii


Pentru a accesa o muchie, sau a verifica dacă aceasta există, trebuie să o căutăm în vector.
Desigur, căutarea liniară ar fi foarte slabă din punct de vedere al complexității.

Soluția este să folosim căutare binară, însă pentru asta trebuie să menținem vectorul sortat după
fiecare operație. Datorită acesteia, complexitatea accesării unei muchii va fi O(log 2n).

Ștergerea unei muchii


Pentru a șterge o muchie, căutăm binar muchia respectivă în vector, și mutăm cu o poziție la
stânga toate elementele care o succed. Complexitatea este O(n+log 2n).
Lectia 1. Grafuri. Parcurgeri. Conexitate

Inserarea unei muchii


Pentru a insera o muchie, căutăm binar în vector prima muchie mai mare sau egală cu aceasta.
Apoi o mutăm atât pe ea, cât și pe cele care o succed, cu o poziție la dreapta, pentru a-i face loc muchiei
noi, pe care o vom reține pe poziția găsită. Complexitatea este din nou O(n+log 2n).

Parcurgerea vecinilor unui nod


Pentru a parcurge nodurile adiacente unui nod dat x, trebuie să știm prima și ultima poziție cu
muchii de forma [x,y]. Pe ambele le putem determina prin câte o căutare binară. Apoi, afișăm toate
muchiile cuprinse între ele (inclusiv). Complexitatea este O(nrVecini+log 2n).

Reprezentarea grafurilor prin matricea de adiacență

Un mod mult mai bun de reprezentare a unui graf se realizează prin folosirea unei așa-numite
matrice de adiacență. Aceasta are numărul de linii și numărul de coloane egale cu numărul de noduri ale
grafului. Dacă e să o notăm cu ad (de la adiacență), pe ad[i][j] se găsește valoarea true dacă există muchie
de la nodul i la nodul j, sau false dacă nu.

Dacă există cost pe muchiile grafului, acesta poate fi stocat în matricea de adiacență, doar că
aceasta își va schimba numele în matrice de ponderi. Dacă există muchia [i,j], pe ad[i][j] s-ar reține costul
acesteia. Dacă nu, ad[i][j] ar avea o valoare pe care nu o poate avea niciun cost (de obicei 0).
Lectia 1. Grafuri. Parcurgeri. Conexitate

Se poate observa că în cazul unui graf neorientat, matricea de adiacență este simetrică după
diagonala principală. Altfel spus, ad[i][j] are aceeași valoare cu ad[j][i]. Este evident, din moment ce [i,j] și
[j,i] se referă de fapt la aceeași muchie.

Accesarea, inserarea și ștergerea unei muchii


Operațiile de accesare, inserare și ștergere se produc în O(1), accesând și modificând elemente
din matrice. Însă, la grafurile neorientate trebuie avută un pic de grijă: La inserarea sau ștergerea muchiei
[i,j], trebuie actualizate atât ad[i][j], cât și ad[j][i].

Parcurgerea vecinilor unui nod


Parcurgerea vecinilor unui nod i are complexitatea O(n), unde n este numărul de noduri din graf:
Trebuie parcursă întreaga linie i din matrice, și vor fi prelucrate doar elementele cu valoarea true.

Reprezentarea grafurilor prin liste de adiacență

O altă soluție este să reținem câte o listă de adiacență pentru fiecare nod al grafului.

Pentru liste am folosit container-ul vector din STL, căci este perfect pentru ce avem nevoie.

Putem menține listele sortate, însă asta are rost doar dacă va trebui să facem multe inserări și
ștergeri de muchii, pentru că astfel vom putea efectua căutări binare. Altfel, lucrurile sunt mult mai
simple.

Accesarea unei muchii


Pentru a accesa o muchie [i,j], trebuie doar să căutăm binar nodul j în lista de adiacență a nodului
i. Complexitatea este O(log2nrVecini).
Lectia 1. Grafuri. Parcurgeri. Conexitate

Ștergerea unei muchii


Pentru a șterge o muchie [i,j], căutăm binar nodul j în lista nodului i; apoi îl ștergem similar
modului descris în cazul listei de muchii. Complexitatea este O(nrVecini+log 2nrVecini).

Inserarea unei muchii


Căutăm binar în lista lui i primul nod mai mare sau egal cu j și îl inserăm pe j imediat înaintea lui,
ca mai sus. Complexitatea este din nou O(nrVecini+log 2nrVecini).

Totuși, putem face inserarea în O(1), atât timp cât nu ne interesează să avem listele sortate
pentru căutări binare. Trebuie doar să adăugăm nodul j la sfârșitul listei i, cât și invers dacă graful este
neorientat:
G[i].push_back(j);
G[j].push_back(i);

Parcurgerea vecinilor unui nod


Pentru a parcurge nodurile adiacente unui nod dat, trebuie doar să-i parcurgem lista de adiacență.
Complexitatea este O(nrVecini).
Alegerea celei mai potrivite metode de reprezentare a unui graf depinde foarte mult de restricțiile
problemei. De obicei însă, matricea de adiacență este mai bună pentru grafurile dense (cu multe muchii),
iar listele de adiacență sunt mai bune pentru grafurile rare (cu puține muchii).

Parcurgerea grafurilor
Parcurgerea unui graf presupune examinarea sistematică a vârfurilor grafului, cu scopul
prelucrării informațiilor asociate vârfurilor.
Există două metode fundamentale de parcurgere a grafurilor:
parcurgerea în adâncime (Depth First Search - DFS)
parcurgerea în lățime (Breadth First Search - BFS).

Parcurgerea în lățime (BFS)


Parcurgerea în lățime începe, de asemenea, cu un vârf inițial, denumit vârf de start. Se
vizitează mai întâi vârful de start. Se vizitează în ordine toți vecinii nevizitați ai vârfului de start. Apoi
se vizitează în ordine toți vecinii nevizitați ai vecinilor vârfului de start și așa mai departe, până la
epuizarea tuturor vârfurilor accesibile din vârful de start.
Exemplu:
Lectia 1. Grafuri. Parcurgeri. Conexitate

Considerăm nodul 3 ca nod de start.


Se vizitează mai întâi vârful de start 3. Apoi se vizitează, în ordine, vecinii nevizitați ai lui 3,
deci 4, 5 şi 9. Se vizitează apoi, în ordine, vecinii nevizitați ai lui 4 (vârfurile 1 și 2), apoi ai lui 5
(vârful 10) și apoi ai lui 9 (care nu are vecini nevizitați). Se vizitează apoi vecinii vârfului 1 (vârfurile
6 și 7) şi parcurgerea s-a încheiat (deoarece vârful 2 nu mai are vecini nevizitați, nici vârful 10 și nici
vârfurile 6 și 7).
Concluzionând, ordinea în care sunt vizitate vârfurile grafului la parcurgerea BFS cu vârful de
start 3 este : 3,4,5,9,1,2,10,6,7.
Implementare:
Graful va fi reprezentat prin liste de adiacență (în una dintre cele trei variante). Pentru a reţine
care vârfuri au fost deja vizitate în timpul parcurgerii vom utiliza un vector 𝑣𝑖𝑧, cu 𝑛 componente din
mulţimea {0,1}, cu semnificaţia 𝑣𝑖𝑧[𝑖] = 1 dacă vârful 𝑖 a fost deja vizitat, respectiv 0, în caz contrar.
Observând că ordinea de parcurgere completă a vecinilor unui nod este exact ordinea
“atingerii” lor, abordarea cea mai simplă folosită pentru parcurgerea efectivă este cea care folosește o
coadă (pentru a reține ordinea vizitării elementelor). Această coadă poate fi implementată “clasic”
(printr-un vector 𝐶 cu 𝑛 elemente; variabilele 𝑝𝑟𝑖𝑚 și 𝑢𝑙𝑡𝑖𝑚 rețin poziția de început, respectiv de
sfârșit a cozii) sau cu ajutorul STL-ului (𝑞𝑢𝑒𝑢𝑒).
#include <bits/stdc++.h>
using namespace std;
ifstream fin("BFS.in");
ofstream fout("BFS.out");
int n,m,x;
int t[101];
bitset<N>viz;
vector<int> G[101];
void Bfs(int x);
int main()
{
fin>>n>>m>>x;
int a,b;
for(int i=1;i<=m;i++)
{
fin>>a>>b;
G[a].push_back(b);
G[b].push_back(a);
}
for(int i=1;i<=n;i++)
sort(G[i].begin(),G[i].end());
Bfs(x);
return 0;
}
void Bfs(int x)
{
queue<int> q;
viz[x]=1;
q.push(x);
while(!q.empty())
{
int k=q.front();
for(vector<int> :: iterator it=G[k].begin();it<G[k].end();it++)
if(viz[*it]==0)
{
viz[*it]=1;
t[*it]=k;
q.push(*it);
}
fout<<k<<" ";
Lectia 1. Grafuri. Parcurgeri. Conexitate

q.pop();
}
}

Observații:
Parcurgerea în lățime are o proprietate remarcabilă: fiecare vârf este vizitat pe cel mai scurt
drum / lanț începând din vârful de start.
Complexitatea parcurgerii în lățime (BFS) în cazul reprezentării prin liste de adiacență este
𝑂(𝑛+𝑚) (în cazul reprezentării prin matrice de adiacență complexitatea este 𝑂(𝑛2)).

Implementare cu matrici de adiacenta


#include <fstream>
#define N 101
using namespace std;
ifstream fin("BFS.in");
ofstream fout("BFS.out");
int A[N][N],n,m,k;

void citirem()
{
int x,y;
fin>>n>>m>>k;
while(fin>>x>>y)
A[x][y]=A[y][x]=1;
///A[x][y]=1; pt orientate
}

int coada[N],viz[N],ic,sfc;
void bfsr()
{
if(ic<=sfc)
{
for(int k=1;k<=n;k++)
if(A[coada[ic]][k]==1 && viz[k]==0)
{
sfc++;
coada[sfc]=k;
viz[k]=1;
}
ic++;
bfsr();
}
}

int main()
{
citirem();
ic=sfc=1;
coada[ic]=k;
viz[k]=1;
bfsr();
for(int i=1;i<=sfc;i++) fout<<coada[i]<<" ";
return 0;
}
Lectia 1. Grafuri. Parcurgeri. Conexitate

Parcurgerea în adâncime (DFS)


Parcurgerea începe cu un vârf inițial, denumit vârf de start. Se vizitează mai întâi vârful de
start. La vizitarea unui vârf se efectuează asupra informațiilor asociate vârfului o serie de operații
specifice problemei.
Se vizitează apoi primul vecin nevizitat al vârfului de start. Vârful 𝑦 este considerat vecin al
vârfului 𝑥 dacă există muchia [𝑥,𝑦] (pentru graf neorientat), respectiv arcul (𝑥,𝑦) (pentru graf orientat).
Se vizitează în continuare primul vecin nevizitat al primului vecin al vârfului de start, şi aşa
mai departe, mergând în adâncime până când ajungem într-un vârf care nu mai are vecini nevizitaţi.
Când ajungem într-un astfel de vârf, revenim la vârful său părinte (vârful din care acest nod a
fost vizitat). Dacă acest vârf mai are vecini nevizitaţi, alegem primul vecin nevizitat al său şi
continuăm parcurgerea în acelaşi mod. Dacă nici acest vârf nu mai are vecini nevizitaţi, revenim în
vârful său părinte şi continuăm în acelaşi mod, până când toate vârfurile accesibile din vârful de start
sunt vizitate.
Exemplu:

Parcurgând graful din figură în adâncime considerând drept vârf de start vârful 3 putem obține
următoarea ordinea de vizitare a vârfurilor accesibile din nodul de start: 3,4,1,2,6,7,10,5,9
(Pentru această succesiune, ordinea de vizitare a vecinilor unui vârf este ordinea crescătoare a
numerelor lor)
Implementare:
Graful va fi reprezentat prin liste de adiacență (în una dintre cele trei variante). Pentru a reţine
care vârfuri au fost deja vizitate în timpul parcurgerii vom utiliza un vector 𝑣𝑖𝑧, cu 𝑛 componente din
mulţimea {0,1}, cu semnificaţia
𝑣𝑖𝑧[𝑖] = 1 dacă vârful 𝑖 a fost deja vizitat, respectiv 0, în caz contrar.
Observând că ordinea de parcurgere completă a vecinilor unui nod este ordinea inversă a
“atingerii” lor, abordarea cea mai simplă folosită pentru parcurgerea efectivă este cea recursivă.

#include <bits/stdc++.h>
using namespace std;
ifstream fin("dfs.in");
ofstream fout("dfs.out");
const int N=101;
bitset <N> viz;
vector <int> G[N];
inline void dfs(int x)
{
viz[x]=1;
fout<<x<<" ";
for(vector <int>::iterator it=G[x].begin();it!=G[x].end();it++)
{
if(viz[*it]==0)
Lectia 1. Grafuri. Parcurgeri. Conexitate

{
dfs(*it);
}
}
}
int main()
{
int n,m,i,X,x,y;
fin>>n>>m>>X;
for(i=1;i<=m;i++)
{
fin>>x>>y;
G[x].push_back(y);
G[y].push_back(x);
}
for(i=1;i<=n;i++) sort(nod[i].begin(),nod[i].end());
dfs(X);
}

Observații:
Complexitatea parcurgerii în adâncime (DFS) în cazul reprezentării prin liste de adiacență este
𝑂(𝑛+𝑚) (în cazul reprezentării prin matrice de adiacență complexitatea este 𝑂(𝑛2)).

Implementare cu matrici de adiacenta


#include <fstream>
#define N 101
using namespace std;
ifstream fin("dfs.in");
ofstream fout("dfs.out");
int A[N][N],n,m,k,viz[N];

void citirem()
{
int x,y;
fin>>n>>m>>k;
while(fin>>x>>y)
A[x][y]=A[y][x]=1;
///A[x][y]=1; pt orientate
}

void dfs(int nod)


{
fout<<nod<<" ";
viz[nod]=1;
for(int k=1;k<=n;k++)
if(A[nod][k]==1 && viz[k]==0) dfs(k);
}

int main()
{
citirem();
dfs(k);
return 0;
}

S-ar putea să vă placă și