Sunteți pe pagina 1din 19

Algoritmica grafurilor Cursul 2

Concepte de bază ı̂n teoria grafurilor.


Parcurgerea grafurilor

October 3, 2017

Cuprins
1 Concepte de bază ı̂n teoria grafurilor 2

2 Parcurgerea grafurilor 5
2.1 Parcurgerea ı̂n lăţime (Breadth First) . . . . . . . . . . . . . 5
2.2 Parcurgerea ı̂n adâncime (Depth First) . . . . . . . . . . . . . 8

3 Seminar 11

4 Anexe 15
4.1 Anexa 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.2 Anexa 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

1
1 Concepte de bază ı̂n teoria grafurilor
Vom introduce şi exemplifica ı̂n continuare câteva noţiuni necesare ı̂n dez-
voltarea capitolelor următoare.
Definiţia 1.1. i) Se numeşte semigraf interior al unui nod xk mulţimea
arcelor
Ux−k = {(xj , xk ) | (xj , xk ) ∈ U }
care sunt incidente spre interior nodului xk .
ii) Se numeşte semigraf exterior al unui nod xk mulţimea arcelor

Ux+k = {(xk , xj ) | (xk , xj ) ∈ U }

care sunt incidente spre exterior nodului xk .


Exemplul 1.1. Pentru graful reprezentat astfel:

avem semigraful interior vârfului x2 dat de Ux−2 = {(x1 , x2 ), (x3 , x2 ), (x5 , x2 )},
iar semigraful exterior aceluiaşi vârf este Ux+2 = {(x2 , x3 )(x2 , x4 )(x2 , x6 )}.
Definiţia 1.2. Semigradul interior (exterior) al unui nod xk este numă-
rul arcelor care sunt incidente spre interior (exterior) nodului xk şi se notează
cu δ − +
xk (respectiv δ xk ). Gradul unui nod xk , notat cu δ xk este suma semi-
gradelor nodului xk , adică

δ xk = δ − +
xk + δ xk .

Observaţia 1.1. i) Evident avem:

δ− − + +
 
xk = card Uxk şi δ xk = card Uxk ;

ii) Un nod izolat are gradul egal cu 0;


iii) Dacă un nod are gradul 1, atunci ı̂l vom numi nod suspendat.

2
Exemplul 1.2. Pentru graful din exemplul 1.1 avem δ − +
x2 = 3, δ x2 = 2,
aşadar gradul vârfului x2 este δ xk = 5. Graful nu are vârfuri izolate sau
suspendate.

Definiţia 1.3. Într-un graf G, prin drum vom ı̂ntelege o mulţime ordonată
de noduri ale grafului

d(x1 , xk ) = (x1 , x2 , ..., xk ),

cu proprietatea că există ı̂n graf toate arcele de forma (xi , xi+1 ), unde i =
1, ..., k − 1. Lungimea unui drum, notată l(d(x1 , xk )), este numărul
arcelor care ı̂l formează.
Un drum elementar este un drum ı̂n care fiecare nod apare o singură
dată. Un drum simplu este un drum ı̂n care fiecare arc apare o singură
dată, altfel se numeşte drum compus.

Observaţia 1.2. Un drum elementar este simplu.

Definiţia 1.4. Numărul de noduri la care se poate ajunge din xi ∈ X se


numeşte putere de atingere a unui nod xi ı̂n graful G.

Vom nota puterea de atingere cu p(xi ), 1 ≤ i ≤ n şi evident avem

p(xi ) = δ +
xi .

Definiţia 1.5. Se numeşte drum hamiltonian un drum elementar care


trece prin toate nodurile grafului, iar prin drum eulerian ı̂nţelegem un
drum simplu care conţine toate arcele grafului.

Definiţia 1.6. Vom numi circuit un drum ı̂n care nodul iniţial coincide cu
cel final.
Un circuit elementar este un drum ı̂n care fiecare nod apare o singură
dată, cu excepţia celui final, care coincide cu cel iniţial.
Un circuit simplu este un circuit ı̂n care fiecare arc apare o singură
dată, iar un circuit hamiltonian este un circuit care trece prin toate
nodurile grafului.

Definiţia 1.7. Prin lanţ ı̂nţelegem un drum ı̂n care arcele nu au neapărat
acelaşi sens de parcurgere. Un ciclu este un circuit ı̂n care arcele nu au
neapărat acelaşi sens de parcurgere. (Aceste noţiuni sunt utilizate pentru
grafurile neorientate)

3
Prin analogie cu proprietăţile circuitelor vom numi ciclu elementar un
ciclu ı̂n care fiecare nod apare o singură dată, cu excepţia celui final, care
coincide cu cel iniţial, iar un ciclu simplu un ciclu ı̂n care fiecare arc apare
o singură dată.

Observaţia 1.3. Într-un graf neorientat noţiunile de drum şi lanţ sunt
echivalente şi de asemenea cele de circuit şi ciclu.

Definiţia 1.8. Un graf parţial al unui graf G = (X, U ) este un graf G0 =


(X, U 0 ) cu U 0 ⊂ U .
Un subgraf al unui graf G = (X, Γ) este un graf G0 = (X 0 , Γ0 ) unde
X 0 ⊂ X şi Γ0 (xi ) = Γ(xi ) ∩ X 0 pentru orice xi ⊂ X 0 .

Exemplul 1.3. Pentru graful G = (X, U ) unde

X = {x1 , x2 , x3 , x4 }
U = {(x1 , x2 ), (x1 , x4 ), (x2 , x3 ), (x3 , x4 ), (x4 , x1 )}

avem un graf parţial G0 = (X, U 0 ) unde

U 0 = {(x1 , x2 ), (x2 , x3 ), (x4 , x1 )}

şi un subgraf G00 = (X 00 , U 00 ) unde

X 00 = {x1 , x3 , x4 }
U 00 = {(x1 , x4 ), (x3 , x4 ), (x4 , x1 )}.

Definiţia 1.9. Un graf simplu conex este un graf neorientat ı̂n care ı̂ntre
oricare două noduri există cel puţin un lanţ.
Un graf orientat G = (X, U ) se numeşte semitare conex dacă ı̂ntre
oricare două vârfuri ale sale există cel puţin un drum.
Un graf orientat G = (X, U ) se numeşte tare conex dacă pentru oricare
două vârfuri xi şi xj există cel puţin un drum de la xi la xj şi cel puţin un
drum de la xj la xi .

Observaţia 1.4. Pentru grafuri neorientate noţiunile de tare conex şi sim-
plu conex sunt echivalente, graful numindu-se doar conex.

Definiţia 1.10. Vom numi componentă tare conexă a unui graf G =


(X, U ) un subgraf al lui G care este tare conex şi nu este subgraful nici unui
alt subgraf tare conex al lui G (altfel spus, ı̂ntre oricare două noduri din
componentă există cel puţin un drum şi nu mai există nici un nod ı̂n afara
componentei legat printr-un drum de un nod al componentei).

4
Observaţia 1.5. Graful reprezentat geometric ı̂n figura 1 are două compo-
nente conexe.

Figura 1:

2 Parcurgerea grafurilor
Modalitatea de vizitare a tuturor vârfurilor grafului ı̂n care fiecare dintre
acestea este vizitat o singură dată se numeşte parcurgere sau traversare.
În acest capitol sunt prezentate metodele de parcurgere ı̂n lăţime (Breadth
First) şi ı̂n adâncime (Depth First).
Aceste metode de parcurgere sunt aplicate grafurilor neorientate şi pre-
supun selectarea unui vârf iniţial x1 şi identificarea acelor vârfuri xk ale
grafului cu proprietatea că există cel puţin un drum de la vârful iniţial către
xk . Grafurile cu proprietatea că oricare două vârfuri sunt conectate printr-
un drum se numesc grafuri conexe (vezi definiţia 1.9). Dacă graful este
tare conex, atunci prin aplicarea metodelor de parcurgere vor fi identificate
toate vârfurile grafului. Cele două modalităţi de parcurgere sunt prezentate
ı̂n continuare ı̂n cazul grafurilor neorientate, extinderea la grafuri orientate
fiind imediată.

2.1 Parcurgerea ı̂n lăţime (Breadth First)


Parcurgerea ı̂n lăţime constă ı̂n a ”vizita” vârful iniţial, considerat ı̂n con-
tinuare a fi x1 , apoi vecinii acestuia, apoi vecinii nevizitaţi ai acestora şi aşa

5
mai departe până când au fost vizitate toate vârfurile conectate cu vârful
iniţial.
Astfel, ı̂n cadrul traversării BF, vârfurile grafului vor fi prelucrate ı̂n
ordinea crescătoare a distanţelor faţă de vârful iniţial, unde prin distanţa de
la xi la xj , notată δ(xi , xj ), vom considera ı̂n continuare numărul de muchii
ale unui cel mai scurt drum de la xi la xj .
Fie G = (X, U ) un graf cu n noduri. O variantă de implementare a
metodei BF este construită prin utilizarea următoarelor structuri de date:
• matricea A de adiacenţă a grafului (din care reiese care sunt vecinii
unui nod xi , i = 1, n);
• o structură de tip coadă, notată cu C, ı̂n care sunt introduse
vârfurile ce urmează a fi vizitate şi procesate (ı̂n sensul cercetării vecinilor
lor); (o coadă este o structură dinamică (un şir a cărui dimensiune este
variabilă) de tip FIFO - first in, first out, elementele părăsesc coada ı̂n
ordinea intrării lor ı̂n coadă, ca o coadă la pâine)
• un vector c cu n componente, unde n reprezintă numărul vârfurilor
grafului, (care ne va spune daca un nod a fost vizitat deja), unde
(
1, dacă nodul xi a fost adăugat ı̂n coadă;
ci =
0, altfel.

Componentele vectorului c sunt iniţializate cu valoarea 0.

În aceste condiţii parcurgerea BF poate fi descrisă astfel:

PASUL 1: Coada C este iniţializată cu vârful x1 .


PASUL 2: Este extras şi vizitat un vârf xi din coadă, apoi sunt introduşi
ı̂n coadă vecinii lui xi care nu au fost deja introduşi (acele vârfuri xk cu
proprietatea că ck = 0 şi aik = 1). Vârfurile xi ce au fost introduse ı̂n coadă
sunt marcate prin ci = 1.
PASUL 3: Cât timp C 6= ∅, se reia pasul 2. Algoritmul se ı̂ncheie
ı̂n momentul ı̂n care este efectuat un acces de preluare a unui nod şi se
constată că C este vidă.

6
Exemplul 2.1. Fie graful reprezentat geometric atfel:

Figura 2:

Pentru acesta, aplicarea metodei de traversare BF determină următoarea


evoluţie a vectorului c şi a cozii C (ieşirea din coadă se face prin stânga, iar
intrarea este prin dreapta):

c
t C
t x1 x2 x3 x4 x5 x6 x7 x8
1 x1
1 1 0 0 0 0 0 0 0
2 x2 x3 x4 x7
2 1 1 1 1 0 0 1 0
3 x3 x4 x7 x5
3 1 1 1 1 1 0 1 0
4 x4 x7 x5 x6
4 1 1 1 1 1 1 1 0
5 x7 x5 x6
5 1 1 1 1 1 1 1 0
6 x5 x6
6 1 1 1 1 1 1 1 0
7 x6 x8
7 1 1 1 1 1 1 1 1
8 x8
8 1 1 1 1 1 1 1 1
9
9 1 1 1 1 1 1 1 1

Aşadar ordinea ı̂n care au fost vizitate vârfurile grafului este x1 , x2 , x3 ,


x4 , x7 , x5 , x6 , x8 .
Observaţia 2.1. Deoarece graful din figura 2. este conex, traversarea BF
realizează vizitarea tuturor vârfurilor grafului. Cu alte cuvinte, metoda BF
aplicată unui graf determină vizitarea tuturor vârfurilor care sunt conectate
cu vârful iniţial selectat.
Sursa C pentru implementarea metodei BF este dată ı̂n anexa 1.

7
2.2 Parcurgerea ı̂n adâncime (Depth First)
Constă ı̂n a vizita vârful iniţial x1 şi a continua cu unul dintre vecinii săi
nevizitaţi xi . Tot timpul mergem ı̂n adâncime, cât este posibil, altfel ne
ı̂ntoarcem şi plecăm, dacă este posibil, spre un alt vecin nevizitat ı̂ncă.
Implementarea acestei metode poate fi realizată ı̂n mai multe moduri,
pentru menţinerea mulţimii vârfurilor vizitate până la momentul curent fi-
ind utilizată o structură de date S de tip stivă. (O stivă este o structură
dinamică (un şir a cărui dimensiune este variabilă) de tip LIFO - last in,
first out, elementele părăsesc stiva ı̂n ordinea inversă intrării lor ı̂n stivă, ca
o stivă de farfurii.)

Un algoritm pentru parcurgerea ı̂n adı̂ncime a unui graf este următorul:

PASUL 1: Vârful iniţial x1 este unicul component al lui S.


PASUL 2: Se preia, fără ştergere, ca vârf curent vârful stivei.
PASUL 3: Se introduce ı̂n stivă unul dintre vecinii vârfului curent ı̂ncă
nevizitat (Pentru gestiunea vârfurilor vizitate se utilizează acelaşi vector c
cu n componente din metoda anterioara). Un vârf se consideră a fi vizitat
dacă el a fost introdus ı̂n stiva S.
PASUL 4: Dacă vârful curent nu are vecini ı̂ncă nevizitaţi, atunci el este
eliminat din stivă.
PASUL 5: Se reia algoritmul de la pasul 2 până ı̂n momentul ı̂n care este
efectuat un acces de preluare a vârfului stivei ca vârf curent şi se constată
că S este vidă.

Este evident că nici ı̂n cazul acestei variante nu vor fi vizitate vârfurile
care nu sunt conectate cu vârful iniţial ales.

În anexa 2 este dată sursă C care implementează această variantă de


parcurgere DF.

8
Exemplul 2.2. Pentru graful din figura 2, prin aplicarea metodei descrise,
rezultă următoarea evoluţie:

- a stivei S (baza stivei este la stânga, iar vârful ei este la dreapta):

t S
1 x1
2 x1 x2
3 x1 x2 x5
4 x1 x2 x5 x4
5 x1 x2 x5 x4 x3
6 x1 x2 x5 x4 x3 x6
7 x1 x2 x5 x4 x3 x6 x7
8 x1 x2 x5 x4 x3 x6
9 x1 x2 x5 x4 x3 x6 x8
10 x1 x2 x5 x4 x3 x6
11 x1 x2 x5 x4 x3
12 x1 x2 x5 x4
13 x1 x2 x5
14 x1 x2
15 x1
16

- a vectorului c:

9
c
t x1 x2 x3 x4 x5 x6 x7 x8
1 1 0 0 0 0 0 0 0
2 1 1 0 0 0 0 0 0
3 1 1 0 1 1 0 0 0
4 1 1 1 1 1 0 0 0
5 1 1 1 1 1 1 0 0
6 1 1 1 1 1 1 1 0
7 1 1 1 1 1 1 1 1
8 1 1 1 1 1 1 1 1
9 1 1 1 1 1 1 1 1
10 1 1 1 1 1 1 1 1
11 1 1 1 1 1 1 1 1
12 1 1 1 1 1 1 1 1
13 1 1 1 1 1 1 1 1
14 1 1 1 1 1 1 1 1
15 1 1 1 1 1 1 1 1
16 1 1 1 1 1 1 1 1

Ordinea ı̂n care sunt vizitate vârfurile coincide cu ordinea introducerii


lor ı̂n stivă, adică: x1 , x2 , x5 , x4 , x3 , x6 , x7 , x8 .

10
3 Seminar
1. Să se parcurgă prin ambele metode prezentate următoarele grafuri, scriind
evoluţia acestor parcurgeri:

i)

ii)

Soluţie:

i) În lăţime:

11
c
t C
t x1 x2 x3 x4 x5 x6 x7
1 x1
1 1 0 0 0 0 0 0
2 x2 x3 x4
2 1 1 1 1 0 0 0
3 x3 x4 x5
3 1 1 1 1 1 0 0
4 x4 x5 x6
4 1 1 1 1 1 1 0
5 x5 x6 x7
5 1 1 1 1 1 1 1
6 x6 x7
6 1 1 1 1 1 1 1
7 x7
7 1 1 1 1 1 1 1
8
8 1 1 1 1 1 1 1

Ordinea vizitării vârfurilor coincide cu introducerea lor ı̂n coadă: x1 , x2 ,


x3 , x4 , x5 , x6 , x7 .

12
În adı̂ncime:

t S
1 x1
2 x1 x2
3 x1 x2 x5
4 x1 x2 x5 x4
5 x1 x2 x5 x4 x3
6 x1 x2 x5 x4 x3 x6
7 x1 x2 x5 x4 x3 x6 x7
8 x1 x2 x5 x4 x3 x6
9 x1 x2 x5 x4 x3
10 x1 x2 x5 x4
11 x1 x2 x5
12 x1 x2
13 x1
14

t c
t x1 x2 x3 x4 x5 x6 x7
1 1 0 0 0 0 0 0
2 1 1 0 0 0 0 0
3 1 1 0 0 1 0 0
4 1 1 0 1 1 0 0
5 1 1 1 1 1 0 0
6 1 1 1 1 1 1 0
7 1 1 1 1 1 1 1
8 1 1 1 1 1 1 1
9 1 1 1 1 1 1 1
10 1 1 1 1 1 1 1
11 1 1 1 1 1 1 1
12 1 1 1 1 1 1 1
13 1 1 1 1 1 1 1
14 1 1 1 1 1 1 1

Aşadar ordinea ı̂n care sunt vizitate vârfurile corespunzător acestei vari-
ante este x1 , x2 , x5 , x4 , x3 , x6 , x7 .

13
ii) În lăţime:

c
t C
t x1 x2 x3 x4 x5 x6 x7
1 x1
1 1 0 0 0 0 0 0
2 x2 x3 x5 x7
2 1 1 1 1 0 0 0
3 x3 x5 x7 x4
3 1 1 1 1 1 0 0
4 x5 x7 x4 x6
4 1 1 1 1 1 1 0
5 x7 x4 x6
5 1 1 1 1 1 1 1
6 x4 x6
6 1 1 1 1 1 1 1
7 x4
7 1 1 1 1 1 1 1
8
8 1 1 1 1 1 1 1

Ordinea vizitării vârfurilor coincide cu introducerea lor ı̂n coadă: x1 , x2 ,


x3 , x5 , x7 , x4 , x6 . Este evident că sunt vizitate doar acele vârfuri care sunt
ı̂n aceeaşi componentă conexă cu vârful de pornire, ı̂n acest caz x1 .

În adı̂ncime:

t S
1 x1
2 x1 x2
3 x1 x2 x4
4 x1 x2 x4 x5
5 x1 x2 x4
6 x1 x2 x4 x3
7 x1 x2 x4 x3 x6
8 x1 x2 x4 x3 x6 x7
9 x1 x2 x4 x3 x6
10 x1 x2 x4 x3
11 x1 x2 x4
12 x1 x2
13 x1
14

14
t c
t x1 x2 x3 x4 x5 x6 x7
1 1 0 0 0 0 0 0
2 1 1 0 0 0 0 0
3 1 1 0 1 0 0 0
4 1 1 0 1 1 0 0
5 1 1 0 1 1 0 0
6 1 1 1 1 1 0 0
7 1 1 1 1 1 1 0
8 1 1 1 1 1 1 1
9 1 1 1 1 1 1 1
10 1 1 1 1 1 1 1
11 1 1 1 1 1 1 1
12 1 1 1 1 1 1 1
13 1 1 1 1 1 1 1
14 1 1 1 1 1 1 1

Aşadar ordinea ı̂n care sunt vizitate vârfurile corespunzător acestei vari-
ante este x1 , x2 , x4 , x5 , x3 , x6 , x7 .

15
4 Anexe
4.1 Anexa 1

Implementarea metodei de parcurgere ı̂n lăţime a grafurilor [1].

#include <stdio.h>
#include <conio.h>
#include <alloc.h>

typedef struct nn
{ int inf;
struct nn *leg;
} nod,* pnod;

int insereaza coada(pnod *head,pnod *tail,int info)


{
pnod nou;
if(nou=(pnod)malloc(sizeof(nod))){
nou->inf=info;
nou->leg=NULL;
if(*head==NULL) *head=nou;
else (*tail)->leg=nou;
*tail=nou;
return 1;
}
else return 0;
}

int extrage coada(pnod *head,pnod *tail, int *info)


{
if(*head){
pnod aux=*head;
*info=(*head)->inf;
(*head)=(*head)->leg;
free(aux);
if(*head==NULL)*head=*tail=NULL;
return 1;
}

16
else return 0;}
void breadth first(int v0,int a[10][10],int n)
{
pnod head=NULL;
pnod tail=NULL;
int c[10];
for(int i=0;i<n;c[i++]=0);
int r=insereaza coada(&head,&tail,v0);
c[v0]=1;
while(head){
r=extrage coada(&head,&tail,&i);
printf(’’\n%i’’,i+1);
for(int k=0;k<n;k++)
if((a[i][k]==1)&&(c[k]==0)){
r=insereaza coada(&head,&tail,k);
c[k]=1;
}
}
}

void main()
{
int n,v0,a[10][10];
clrscr();
printf(’’Numarul de varfuri:’’);
scanf(’’%i’’,&n);
printf(’’\nMatricea de adiacenta\n’’);
for(int i=0;i<n;i++)
for(int j=0;j<i;j++){
scanf(’’%i’’,&v0);
a[j][i]=a[i][j]=v0;
}
for(i=0;i<n;i++)a[i][i]=0;
printf(’’\nVarful initial ’’);
scanf(’’%i’’,&v0);
printf(’’\nParcurgerea BF a grafului este’’);
breadth first(v0,a,n);
}

17
4.2 Anexa 2

Implementarea metodei de parcurgere ı̂n adâncime a grafurilor [1].

#include <stdio.h>
#include <conio.h>
#include <alloc.h>
typedef struct nn{
int inf;
struct nn *leg;
}nod,* pnod;

int insereaza stiva(pnod *head,int info)


{
pnod nou;
if(nou=(pnod)malloc(sizeof(nod))){
nou->inf=info;
nou->leg=*head;
*head=nou;
return 1;
}

else return 0;
}
int extrage stiva(pnod *head,int *info)
{
if(head){
pnod aux=*head;
*info=(*head)->inf;
(*head)=(*head)->leg;
free(aux);
return 1;
}
else return 0;}

void depth first(int v0,int a[10][10],int n)


{
pnod head=NULL;
int c[10];

18
for(int i=0;i<n;c[i++]=0);
int r=insereaza stiva(&head,v0);
c[v0]=1;
printf(’’\n%i’’,v0+1);
while(head){
r=extrage stiva(&head,&i);
for(int k=0;k<n;k++)
if((a[i][k]==1)&&(c[k]==0)){
r=insereaza stiva(&head,k);
c[k]=1;printf(’’\n%i’’,k+1);
}
}
}

void main()
{
int n,v0,a[10][10];
clrscr();
printf(’’Numarul de varfuri:’’);scanf(’’%i’’,&n);
printf(’’\nMatricea de adiacenta\n’’);
for(int i=0;i<n;i++)
for(int j=0;j<i;j++){
scanf(’’%i’’,&v0); a[j][i]=a[i][j]=v0;
}
for(i=0;i<n;i++)a[i][i]=0;
printf(’’\nVarful initial ’’);scanf(’’%i’’,&v0);
printf(’’\nParcurgerea DF a grafului este’’);
depth first(v0,a,n);
}

References
[1] I. Roşca, Gh. (coord.), Programarea calculatoarelor. Algoritmi ı̂n progra-
mare, Ed. ASE, Bucureşti, 2007.

19

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