Sunteți pe pagina 1din 21

CAPITOLUL 9- Teoria grafurilor

A) GRAFURI NEORIENTATE

9.1) NOŢIUNI INTRODUCTIVE:

Definiţie: Graful neorientat este o pereche ordonată G=(V, E). unde:


• V= { v1, v2,…,vn} este o mulţime finită şi nevidă. Elementele mulţimii V se numesc noduri
(vârfuri);
• E este o mulţime finită de perechi neordonate de forma (vi, vj), unde i diferit de j, si vi, vj ∈ V.
Elementele mulţimii se numesc muchii. Semnificaţia unei muchii este aceea că uneşte două
noduri.

• exemplu de graf neorientat:


Avem:
G=(V,E)
1 6 V= {1,2,3,4,5,6}
E= { (1,2), (1,3), (1,5),(2,3), (3,4),(4,5)}

Observaţii:
2 5
Se notează cu n numarul de nodurin=6, iar cu m
3 numărul muchiilor  m=6
4 Muchia (1,2) este aceeaşi cu (2,1) pentru ca avem un
graf neorientat.
Nu există o muchie care uneşte un nod cu el insuşi.
Două noduri pot fi unite prin cel mult o muchie.

Definiţii:

1. În graful g=(V,E), nodurile distincte vi ,vj ∈ G sunt adiacente dacă există muchia (vi ,vj ) ∈
E. Vom spune că muchia (vi ,vj ) ∈ E este incidentă la nodurile vi şi vj . În exemplul nostru
nodurile 1 şi 5 sunt adiacente dar nodurile 2 şi 5 nu sunt adiacente. Muchia (4,5) este
incidentă la nodurile 4 şi 5.
2. Într-un graf neorientat, prin gradul unui nod v se înţelege numărul muchiilor incidente cu
nodul v şi se notează cu d(v). Un nod cu gradul 0 se numeşte nod izolat, iar unul cu gradul
1 se numeşte nod terminal. Exemplul nostru : d(2)=2, d(1)=3, d(6)=0 (nod izolat).
3. O relaţie utilă:
d1+d2+,....,+ dn =2*m, adică suma gradelor nodurilor grafului = 2* numărul muchiilor;

9.2) MEMORAREA GRAFURILOR

Memorarea se face prin mai multe metode care e aleg în funcţie de :


• algoritmul care prelucrează datele referitoare la graf;
• memoria internă pe care programul o are la dispoziţie;
• dacă graful conţine multe muchii sau nu.

1
Structuri de memorare a grafurilor:

1. Memorare grafului prin matricea de adiacenţă


a. Este o matrice An,n – o matrice pătratică, unde elementele ei, ai,j au semnificaţia:
1, pentru (i,j) ∈ E
i. Ai,j =
1. 0, pentru (i,j) ∉ E

Pentru exemplul de graf dat, matricea de adiacenţă este:

011010
101000
A6,6= 110100
001010
100100
000000
Observaţii:
 Elementele de pe diagonala principală sunt 0 ( pentru că am definit un graf la
care nu există muchii de la un nod la el insuşi)
 Matricea de adiacenţă este simetrică ai,j = aj,i ∀ i,j ∈ { 1,2,..,n}
 Suma elementelor de pe linia i reprezintă gradul nodului i, adică d(i)
 Suma tuturor elementelor matricei de adiacenţă = suma gradelor tuturor
nodurilor, adică dublul numărului de muchii (2m)
b) Crearea matricei prin citirea muchiilor grafului: Datele se citesc dintr-un fişier
care are pe primul rând numărul de noduri, iar pe fiecare din următoarele câte o muchie.
Se foloseşte funcţia următoare:
Fişierul ptr graful nostru:
void CitireN (char nume_fiş[20], int a[50][ 50], int &n)
{
6
int i,j;
12
fstream f(nume_fiş,ios :: in);
13
f>>n;
15
while (f>>i>>j)
23
a[i][j]=a[j][i]=1;
34
f.close();
45
}
Programul următor citeşte muchiile grafului şi afişează matricea de adiacenţă.
# include <iostream.h>
# include<conio.h>
int a[50][50], n;
void CitireN (char nume_fiş[20], int a[50][ 50], int &n)
{
int i,j;
fstream f(nume_fiş,ios :: in);
f>>n;
while (f>>i>>j)
a[i][j]=a[j][i]=1;
f.close();
}

2
void main( )
{

CitireN (“graf.txt”,a,n);
For (int i=1; i<=n;i++)
{
for (int j=1; j<=n;j++)
cout<<a[i][j]<<” ”;
cout<<endl;
}
getch();
}
2. Memorarea grafului prin liste de adiacenţă
a. Listele de adiacenţă reprezintă o altă formă de memorare a grafului, în care pentru
fiecare nod se indică lista nodurilor adiacente cu el.
b. Pentru exemplul nostru, vom avea listele:

1 2,3,5 c.
Se utilizeaza un
2 1,3
vector cu n
31,2,4
componente, pe
4 3,5 care il vom numi
51,4 Start şi o matrice
6 T cu 2 linii si 2m
coloane.
Semnificaţiile sunt;
i. Start – pentru fiecare nod i, start[i] specifica coloana din T unde incepe lista
nodurilor adiacente cu i. Daca reţine 0, inseamnă că nodul i nu are noduri
adiacente.
ii. T (0,i)- reprezintă indicele coloanei din T unde se gaseşte următorul element
din listă. Dacă reţine 0, atunci acesta este ultimul element din lista
succesorilor;
1 2 3 4 5 6 Nodul 6 nu are noduri
5 7 9 11 12 0 adiacente ptr ca Start
Start [6]= 0

1 2 3 4 5 6 7 8 9 10 11 12

T[0] 2 1 3 1 5 1 3 2 4 3 5 4

T[1] 0 0 1 0 3 0 2 4 8 0 10 6

Exemplu de utilizare: Vrem sa vedem nodurile adiacente ptr nodul 3.


• Ne uităm in StartStart[3]=9 in t[0]la coloana 9nodul 4 următorul se află la
coloana 8- avem nodul 2 următorul il gasim in coloana 4  nodul 1- rezultă ultimul
nod pentru că la T[1] urmează 0

3
• Rezultă ca nodurile adiacente ale lui 3 sunt 4, 2, 1 Stop am dat de 0

Subprogramul care construieşte listele de adiacenţă:

Void Citire_LA(char Nume_fis[20], int T[2][50], int Start[50], int& n)


{
int i, j, k=0 ; K++;
fstream f(Nume_fis,ios ::in) ; T[0][k]=I;
f>>n; T[1][k]=Start[j];
while (f>>i>>j) Start[j]=k;
{ }
k++ ;
T[0][k]=j; f.close();
T[1][k]=Start[i]; }
Start[i]=k;

Programul următor citeşte nr de varfuri şi muchiile unui graf şi afişează, pentru fiecare nod,
lista nodurilor adiacente.
Date de intrare
# include <iostream.h> graf1.txt
# include<conio.h> 6
int t[2][50], start[50], n, i, man; 1 2
1 3
void citire_muchii(char Nume_fis[20], int t[2][50], int start[50], int& n) 1 5
{ 2 3
int i, j, k=0 ; 3 4
fstream f(nume_fis,ios ::in) ; 4 5
f>>n;
while (f>>i>>j)
{
k++ ;
t[0][k]=j;
t[1][k]=start[i];
start[i]=k;
k++;
t[0][k]=i;
t[1][k]=start[j];
start[j]=k;
}

f.close();
}
Date de iesire
void main( )
{
1 2,3,5
citire_muchii( “graf1.txt”, t, start,n);
2 1,3
for ( i=1;i<=n;i++)
{ 31,2,4
4 3,5
4 51,4
6 -
cout<<”noduri adiacente cu “<<i<<endl;
man=start[i];
while ( man)
{
cout<<t[0] [man] <<” “;
man=t[1][man];
}
cout<<endl;
}
getch();
}
3. Memorarea grafurilor prin lista muchiilor

Se utilizează un vector cu m componente, unde m este numărul muchiilor. Fiecare componentă va


reţine cele două noduri la care muchia respectivă este incidentă şi , în anumite cazuri alte informaţii
referitoare la muchia respectivă.

Struct muchie
{
int x,y;
};
muchie v[50];

9.3 GRAF COMPLET


Definiţie: Un graf complet este un graf neorientat în care oricare două noduri sunt adiacente. El se
notează cu Kn, unde n este numărul de noduri ale grafului.
RELAŢII UTILE
1. Într-un graf complet gradul oricărui nod este n-1.
2. Într-un graf complet avem relaţia m= n(n-1) / 2 , unde m este numarul de muchii, iar n
numarul de noduri.
3. Avem 2n(n-1)/2 grafuri neorientate cu n noduri.

9.4 GRAF PARŢIAL, SUBGRAF


Definiţie:Un graf parţial al unui graf neorientat dat G=(V,E) este un graf G1= (V, E1), unde E1⊆ E.
Un graf parţial al unui graf dat, este el însuşi sau se obţine din G prin eliminarea unor muchii.

1 1

2 5 2 5

3 3
4 4

G=(V,E) G1= (V, E1),

5
Definiţie: Un subgraf al un ui graf neorientat G(V,E) este un graf G1=(V1, E1), unde V1⊂ V. E1⊂ E
iar muchiile din E2 sunt toate muchiile din E care sunt incidente numai cu nodurile din mulţimea V1.
Un subgraf al unui graf este el însuşi sau se obţine din G prin suprimarea anumitor noduri şi a
tuturor muchiilor incidente cu acestea.

1 1

2 5 5

3 3
4 4

G=(V,E) G1= (V1, E1),

9.5 PARCURGEREA GRAFURILOR NEORIENTATE

Parcurgerea grafurilor este o vizitare a nodurilor. Este importanta parcurgerea eficienta. Există două
modalităţi de parcurgere:
• Parcurgerea în lăţime BF- Breadth First
• Parcurgerea în adâncime DF

• Parcurgerea în lăţime BF- Breadth First


- se face incepând de la un nod i, pe care il considerăm vizitat.
- Vizităm apoi toate nodurile adiacentecu el- fie ele j1, j2, ..., jk
- Vizităm toate nodurile adiacentecu j1 apoi j2, ..., jk
- Parcurgerea continuă în acest mod până cand au fost vizitate toate nodurile accesibile
Algoritmul este următorul:
- parcurgerea se realizează prin utilizarea structurii coadă, având grijă ca fiecare nod să fie vizitat
o singură dată.
- Nodul de pornire este introdus în coadă şi este marcat cu vizitat
o Cât timp coada este nevidă
 Pentru nodul aflat la inceputul cozii:
• Se trec în coadă toate nodurile adiacente cu el, care nu au fost vizitate şi
se marchează ca vizitate
• Se afişează
• Se extrage din coadă
- Se folosesc notaţiile:
o i_c  indicele primei componente a cozii
o s_c  indicele ultimei componente a cozii
o coada  vectorul care memorează coada propriu-zisă
0, dacă nodul i a fost vizitat
o s  vector ce reţine nodurile vizitate: s[i]=
1, dacă nodul i a fost vizitat

6
Programul de parcurgere BF (lăţime) a unui graf memorat prin liste de
adiacenţă

#include <conio.h>
#include<iostream.h>

int n, coada[50], s[50], i_c=1, sf_c=1, t[2][50], start[50], p;


void citire_muchii (char nume_fis[20], int t[2][50], int start[50], int& n)
{
int i, j, k=0 ;
fstream f(nume_fis,ios ::in) ;
f>>n;
while (f>>i>>j)
{
k++ ;
t[0][k]=j;
t[1][k]=start[i];
start[i]=k;
k++;
t[0][k]=i;
t[1][k]=start[j];
start[j]=k;
}

f.close();
}

void bf(int nod)


{
coada[i_c]=nod;
s[nod]=1;
while (i_c<=sf_c)
{
p=start [coada[i_c]];
while (p)
{
if ( s [ t[0] [p] ]= =0)
{
sf_c++;
coada [sf_c]=t[0][p];
s [ t[0] [p] ]=1;
}
p=t[1][p];
}
cout<<coada[i_c] <<endl ;

7
}
}

void main( )
{
citire_muchii(“graf.txt”, t, start, n);
bf(6);
getch( );
}

Programul de parcurgere BF (lăţime) a unui graf memorat prin matricea de adiacenţă


#include <conio.h>
#include<iostream.h>
int n, coada[50], s[50], i_c=1, sf_c=1, A[50][50], i;

void CitireN (char nume_fiş[20], int a[50][ 50], int &n)


{
int i,j;
fstream f(nume_fiş,ios :: in);
f>>n;
while (f>>i>>j)
a[i][j]=a[j][i]=1;
f.close();
}

void bf (int nod)


{
coada[i_c]=nod;
s[nod]=1;
while (i_c<=sf_c)
{
i=1;
while (i<=n)
{
if (A[coada[i_c]][i]= =1 && s[i]= =0 )
{
sf_c++;
coada[sf_c]=i;
s[i]=1;
}
i++;
}
cout<<coada[i_c]<<endl;
i_c++;
}
}
void main( )
{
CitireN( “graf.txt”, A, n);
8
Bf(1);
getch();
}
• Parcurgerea în adâncime DF- Depth First

- Parcurgerea în adâncime se face de la un nod i care se consideră vizitat.


- După vizitarea unui nod, se vizitează primul dintre nodurile adiacente, nevizitate încă, apoi
următorul nod adiacent, până când au fost vizitate toate nodurile adiacente cu el.
- ………
- Parcurgerea continuă în acest mod până când au fost vizitate toate nodurile accesibile.

Programul de parcurgere DF (adâncime) a unui graf memorat prin liste de adiacenţă

#include <conio.h>
#include<iostream.h>

int s[50], n, T[2][50], Start[50] ;


Void Citire_LA (char Nume_fis[20], int T[2][50], int Start[50], int& n)
{
int i, j, k=0 ;
fstream f(Nume_fis,ios ::in) ;
f>>n;
while (f>>i>>j)
{
k++ ;
T[0][k]=j;
T[1][k]=Start[i];
Start[i]=k;
K++;
T[0][k]=i;
T[1][k]=Start[j];
Start[j]=k;
}

f.close();
}

void df (int nod)


{
int p;
cout<< nod <<” “;
p=Start [nod];
s [nod]=1;
while (p)
{
if ( s [ T[0] [p] ]= =0)
df (T [0][p] );
p= T[1] [p];
9
}
}

void main( )
{
Citire_LA(“graf.txt”, T, Start, n);
df(1);
getch( );
}

Programul de parcurgere DF (adâncime) a unui graf memorat prin matricea de adiacenţă

#include <conio.h>
#include<iostream.h>

int s[50], n, A[50][50] ;


void CitireN (char nume_fiş[20], int a[50][ 50], int &n)
{
int i,j;
fstream f(nume_fiş,ios :: in);
f>>n;
while (f>>i>>j)
a[i][j]=a[j][i]=1;
f.close();
}

void df_r (int nod)


{
int k;
cout<< nod <<” “;
s [nod]=1;
for (k=1; k<=n; k++)
if ( A[nod] [k] = =1 && s[k]= = 0)
df_r(k);
}
void main( )
{
CitireN (“graf.txt”, A, n);
df_r(1);
getch( );
}
Estimarea timpului necesar parcurgerii grafurilor

- Parcurgerea BF sau DF a grafurilor pornind de la matricea de adiacenţă, se face în O(n2).


Pornind de la un nod i , se caută pe linia i a matricei toate nodurile adiacente cu el.
- Parcurgerea BF sau DF a grafurilor pornind de la listele de adiacenţă se face în O( m).
Pornind de la un nod i , se caută toate nodurile adiacente cu el. Dar acestea se găsesc deja

10
grupate în lista asociată nodului respectiv şi numărul lor corespunde numărului de muchii
incidente acestuia.

9.6 LANŢURI

Definiţie:
Pentru graful neorintat G=( V,E), un lanţ L= [v1,v2,....,vp] este o succesiune de noduri cu proprie-
tatea a oricare două noduri vecine sunt adiacente, adică (v1,v2) ∈ E, (v2,v3) ∈ E, ....(vp-i,vp) ∈ E. Un
lanţ poate fi definit ca o succesiune de muchii (v1,v2) ∈ E, (v2,v3) ∈ E, ....(vp-i,vp) ∈ E.
• Vârfurile v1 , vp se numesc extremităţile lanţului
• Numărul p-1 se numeşte lungimea lanţului. Acesta este dat de numărul de muchii ce
unesc nodurile lanţului.

Definiţie:
Se numeşte lanţ elementar un lanţ care conţine numai noduri distincte.

1
Lanţ elementar- [ 1,2,3,4] cu lungimea 3

Lanţ neelementar  [1,2,3,1,5] cu lungimea 4


2 5

3
4

APLICAŢIE
Fiind dat un graf şi două noduri ale sale a şi b, să se scrie un program care decide dacă între
ele există un lanţ sau nu, iar în caz că acest lanţ există, se cere să se afişeze lanţul.

Notă: intre a si b există lanţ doar dacă putem parcurge lanţul din a şi ajungem în b. Ne vom folosi
de un vector T care are forma

j, dacă i este descendent al lui j


T[j]=
0, dacă i nu a fost selectat

Programul afişează drumul, pornind de la matricea de adiacenţă a grafului.

#include <conio.h>
#include<iostream.h>

int s[50], A [50] [50], n, T[50] , a, b ;


void CitireN (char nume_fiş[20], int a[50][ 50], int &n)
{
int i,j;

11
fstream f(nume_fiş,ios :: in);
f>>n;
while (f>>i>>j)
a[i][j]=a[j][i]=1;
f.close();
}

void refac( int nod)


{
if (nod!=0)
{
refac (T[nod]);
cout<<nod<<’ “;
}
}

void df_r (int nod)


{
int k;
s [nod]=1;
for (k=1; k<=n; k++)
if ( A[nod] [k] = =1 && s[k]= = 0)
{
T[k]=nod;
df_r(k);
}
}

void main( )
{
CitireN (“graf.txt”, A, n);
cout<<”a=”;
cin>>a;
cout<<”b=”;
cin>>b;
df_r(a);
if (T[b] != 0)
refac (b);
getch( );
}

Programul afişează lanţul de lungime minimă aflat între nodurile a şi b


( parcurgere în lăţime DF )
Fişierul text ptr
graful nostru:
#include <conio.h>
#include<iostream.h>
6
12
int n, coada[50] , s[50], i_c=1, sf_c=1, A[50][50], I, T[50], a, b ;
13
15
12 23
34
45
void CitireN (char nume_fiş[20], int a[50][ 50], int &n)
{
int i,j;
fstream f(nume_fiş,ios :: in);
f>>n;
while (f>>i>>j)
a[i][j]=a[j][i]=1;
f.close();
}

void refac( int nod)


{
if (nod!=0)
{
refac (T[nod]);
cout<<nod<<’ “;
}
}

void bf (int nod)


{
coada[i_c]=nod;
s[nod]=1;
while (i_c<=sf_c)
{
i=1;
while (i<=n)
{
if (A[coada[i_c][i]= =1 && s[i]= =0 )
{
sf_c++;
coada[sf_c]=i;
s[i]=1;
T[i]=coada[i_c];
}
i++;
}
i_c++;
}
}
void main( )
{
CitireN (“graf.txt”, A, n);
cout<<”a=”; cin>>a;
cout<<”b=”; cin>>b;
df_r(a);
if (T[b] != 0)
refac (b);
getch( );
13
}

9.7 GRAF CONEX

Definiţie:

Un graf neorientat G=(V, E) este conex, dacă pentru orice pereche de noduri x, y ∈ V, există un lanţ
în care extremitatea iniţială este x şi extremitatea finală este y.
Un graf cu un singur nod este conex.

1 1 6

2 5 2
5
3 3
4

Graf CONEX Graful nu este CONEX


( nu există lanţ între nodul 1 şi 5)

9.9 COMPONENTE CONEXE

Definiţie:
Fir G=(V, E) un graf neorientat şi G1= ( V1,E1) un subgraf al său. Atunci G1= ( V1,E1) este o
componentă conexă a grafului G=(V, E) dacă sunt îndeplinte condiţiile:
• Oricare ar fi x, y ∈ V1, există un lanţ de la x la y;
• Nu există un alt subgraf al lui G, G2= ( V2,E2) cu V1 ⊂ V2 care îndeplineşte condiţia a).
Un graf cu n noduri poate avea un nr. de componente conexe cuprins între 1 şi n.
Exemple:

1 1 6
5

2 2
5
4
3 3
4

14
Graful are 2 componente conexe Graful are 3 componente conexe
(1,2,3), (5,6) (1,2,3,4),5, 6

APLICAŢIE

Fiind dat un graf neorientat, se cere să se afişeze nodurile fiecărei componente conexe.

NOTĂ: parcurgem graful…de câte ori il parcurgem atâtea componente conexe avem.

#include <conio.h>
#include<iostream.h>

int s[50], A [50] [50], n, i;


void CitireN (char nume_fiş[20], int a[50][ 50], int &n)
{ Fişierul text ptr
int i,j; graful nostru:
fstream f(nume_fiş,ios :: in);
f>>n; 6
while (f>>i>>j) 12
a[i][j]=a[j][i]=1; 13
f.close(); 15
} 23
34
45
void df_r (int nod)
{
int k;
cout<<nod<<endl;
s [nod]=1;
for (k=1; k<=n; k++)
if ( A[nod] [k] = =1 && s[k]= = 0)
df_r(k);

void main( )
{
CitireN (“graf.txt”, A, n);
for ( i=1; i<=n; i++)
if (s[i]= =0)
{
cout<<”componeneta conexa “<<endl;
df_r(i);
cout<<endl;
}
getch( );
}
15
9.9 CICLURI

Definiţie;

Un lanţ L care conţine numai muchii distincte şi pentru care nodul iniţial coincide cu nodul final
se numeşte ciclu. Dacă cu excepţia ultimului nod, care coincide cu primul, lanţul este elementar,
atunci ciclul este elementar ( adică, cu excepţia ultimului nod, care coincide cu primul, conţine
noduri distincte.

1 4
[1,2,3,1] ciclu elementar
[1,2,1,] nu este ciclu pentru ca (1,2) şi (2,1)
reprezintă aceaşi muchie, deci nu conţine
muchii distincte

2 3 5

APLICAŢIE:
Fiind dat un graf conex, G=(V,E), să se scrie un program care decide dacă graful conţine cel
puţin un ciclu.

NOTĂ: Graful conţine cel puţin un ciclu dacă, în timpul parcurgerii DF, algoritmul va ajunge în
situaţia de a vizita un nod de două ori.

#include <conio.h>
#include<iostream.h>

int s[50], A [50] [50], n, gasit;


void CitireN (char nume_fiş[20], int a[50][ 50], int &n)
{ Fişierul text ptr
int i,j; graful nostru:
fstream f(nume_fiş,ios :: in);
f>>n; 6
while (f>>i>>j) 12
a[i][j]=a[j][i]=1; 13
f.close(); 15
} 23
34
45

16
void df (int nod)
{
int k;
s [nod]=1;
for (k=1; k<=n; k++)
if ( A[nod] [k] = =1 )
{
A [k][nod]=0;
if (s[k]= = 0)
df (k);
else
gasit=1;
}
}

void main( )
{
CitireN (“graf.txt”, A, n);
df (1);
if (gasit)
cout<<” da”;
else
cout<<” NU”;
getch( );
}

Pentru a verifica matematic dacă există cel puţin un ciclu se face verificarea, m>n-1. Dacă este
verificată înseamnă ca există cel puţin un ciclu.
Dacă m=n-1  nu există cicluri
Dacă m<n-1  graful nu este conex, ceea ce ar contrazice cerinţa.

9.10 ARBORI

Definiţie:
Se numeşte arbore un graf neorientat care este conex şi nu conţine cicluri.
Exemplu un arbore cu 5 noduri.
1 2

3
5

Ex.: este conex pentru ca exista un lant intre orice doua varfuri x si y din V si nu are cicluri.

TEOREMĂ 1:

17
Fie G un graf neorientat cu n noduri. G este arbore dacă şi numai dacă are n-1 muchii şi nu
conţine cicluri.

Obs 1 : Orice arbore cu n varfuri are n-1 muchii.

Obs 2 : Orice arbore cu n>=2 varfuri contine cel putin 2 varfuri terminale.
TEOREMA2 :
Fie G=(V,M) un graf neorientat . Următoarele afirmaţii sunt adevărate:
-G este arbore
-G este un graf conex ,minimal in raport cu aceasta proprietate(daca se elimina o muchie oarecare
din G se obtine un graf neconex)
-G este fara cicluri ,maximal in raport cu aceasta proprietate (daca se adauga o muchie ,se va obtine
cel putin un ciclu in graf)

Obs. In cazul arborilor in loc de varfuri si muchii se folosesc cu precadere termenii de noduri si
arce.

APLICAŢIE:
Se citeşte un graf. Să se scrie un program care verifică dacă este arbore.
#include <conio.h>
#include<iostream.h>
int s[50], A [50] [50], n, gasit, i, suma;
void CitireN (char nume_fiş[20], int a[50][ 50], int &n)
{
int i,j;
fstream f(nume_fiş,ios :: in);
f>>n;
while (f>>i>>j) Fişierul text ptr
a[i][j]=a[j][i]=1; graful nostru:
f.close();
} 6
12
void df (int nod) 13
{ 15
int k; 23
s [nod]=1; 34
for (k=1; k<=n; k++) 45
if ( A[nod] [k] = =1 )
{
A [k][nod]=0;
if (s[k]= = 0)
df (k);
else
gasit=1;
}
}

void main()
{
18
CitireN (“graf.txt”, A, n);
df (1);
suma=0;
for (i=1;i<=n; i++)
suma+=s[i];
if (suma !=n)
cout<<” nu este conex”;
else
if (gasit)
cout<<”are ciclu ”;
else
cout<< “arbore “;
getch( );
}

Notiuni caracteristice arborilor:

1)Nodul in care nu intra nici un arc sau nodul initial reprezinta radacina arborelui
2) Cu exceptia nodului radacina , fiecare nod are proprietatea ca in el intra un singur arc . Acest arc
leaga nodul respectiv de un alt nod numit nod predecesor sau parinte.
3)Dintr-un nod pot iesi mai multe arce.Fiecare astfel de arc leaga nodul respectiv de un alt nod
numit succesor sau fiu al nodului sau descendent.
4)Nodurile sunt organizate pe nivele, primul nivel este ocupat de nodul radacina .Nodurile de pe
ultimul nivel se carcterizeaza prin faptul ca din ele nu mai iese nici un arc ,ele sn noduri terminale
sau frunze.
5)Inaltimea unui arbore este data de nr nivelurilor sale -1 sau lungimea celui mai lung lant
elementar

Ex 1 N1 1- radacina arborelui
Frunze : 3 4 5 7 8
Descendentii directi ai lui 2 : 5 si 6
2 3 4
N2 Descendenti lui 2: 5 6 7 8
H=3

5 6 N3

N4
Reprezentarea arborilor :
7 8

Arborii fiind cazuri particulare de garfuri ,au aceleasi metode de reprezentare ca si grafurile.
Exista insa o forma de reprezentare specifica arborilor prin vectorul „tatilor” astfel:

Se alcatuieste un tabel cu doua linii si coloane in numar egal cu numar de noduri respectand
urmatoarele reguli:
-pe prima linie se trec modurile
-pe a doua linie ,pt fiecare nod se trece tatal nodului respectiv

19
Mai clar :
Pentru graf neorientat cu n varfuri avem:
T=(t[1],t[2],…t[n]) unde t[i] i 1..n reprezinta tatal nodului i

Ex . Fiind dat vectorul de tati al unui arbore(6,0,2,2,3,3,2,7,7,9) sa se determine:


- nodul radacina
- frunzele grafului
- reprezentarea grafica.

Def. Se numeste arbore partial al unui graf neorientat G=(V,M) un graf partial al sau care in
plus este si arbore.

Ex
1 Daca eliminam [1,3] si [2,4] se obtine un arbore partial
2 4

Def . Se numeste arbore binar ,o multime finita de noduri care este:


-fie vida
- fie un arbore ordonat in care fiecare nod are cel mult doi descendenti directi

Notiuni : succesor stang ,drept


Subarbore stang ,drept

ARBORE PARŢIAL:
Se obţine dintr-un graf prin eliminarea unui număr de muchii, păstrându-se un număr minim
de muchii astfel incât graful rămas să fie conex şi fără cicluri.
Exemplu:
1
1

3
3 2
2

4 5
4 5

Graf conex G=(V, E) Arbore parţial


Graf.txt
Program pentru determinarea unui arbore parţial pornind de la un graf conex
6 G =(V, E).
12
#include <conio.h> 13
#include<iostream.h> 15
20 23
34
45
int s[50], A [50] [50], n ;
void CitireN (char nume_fiş[20], int a[50][ 50], int &n)
{
int i,j;
fstream f(nume_fiş,ios :: in);
f>>n;
while (f>>i>>j)
a[i][j]=a[j][i]=1;
f.close();
}

void df_r (int nod)


{
int k;
s [nod]=1;
for (k=1; k<=n; k++)
if ( A[nod] [k] = =1 && s[k]= = 0 )
{
cout<<nod<<” “<< k<< endl;
df_r(k);
}
}
void main( )
{
CitireN (“graf.txt”, A, n);
df_r (1);
getch();
}

21