Sunteți pe pagina 1din 39

Grafuri neorientate

bm1logieeche de multimi =(V, E)unde V este o mulime finit nevida de


elemente numite noduri iar E o multime de perechi neordonate din V,
numite muchii. Notm graful cu G=(V, E).

Intr-un un graf G=(V, E) neorientat relaia binar este simetric:


(v,w)E atunci (w,v) E.

Nod = element al mulimii V, unde G=(V, E) este un graf neorientat.

Muchie = element al mulimii E ce descrie o relaie existent ntre dou


noduri din V, unde G=(V, E) este un graf neorientat;

1 6 In figura alaturata:
V={1,2,3,4,5,6} sunt noduri
5 E={[1,2], [1,4], [2,3],[3,4],[3,5]} sunt muchii
2 G=(V, E)=({1,2,3,4,5,6}, {[1,2], [1,4], [2,3],[3,4],[3,5]})
3 4

Adiacenta: ntr-un graf neorientat existena muchiei (v,w) presupune c w este


adiacent cu v i v adiacent cu w.

n exemplul din figura de mai sus vrful 1 este adiacent cu 4 dar 1 i 3 nu


reprezint o pereche de vrfuri adiacente.

Inciden = o muchie este incident cu un nod dac l are pe acesta ca


extremitate. Muchia (v,w) este incident n nodul v respectiv w.

Grad = Gradul unui nod v, dintr-un graf neorientat, este un numr natural
ce reprezint numrul de noduri adiacente cu acesta (sau numarul de
muchii incidente cu nodul respectiv)

Nod izolat = Un nod cu gradul 0.

Nod terminal= un nod cu gradul 1

1 6 Nodul 5 este terminal (gradul


1).
5
2
Nodul 6 este izolat (gradul 0)
3 4
Nodurile 1, 2, 4 au gradele
egale cu 2.

Daca un graf neorientat are m muchii atunci suma gradelor tuturor


nodurilor este 2m
In orice graf G exista un numar par de noduri de grad impar

Lan = este o secven de noduri ale unui graf neorientat G=(V,E), cu


proprietatea c oricare dou noduri consecutive din secventa lant sunt
adiacente:
L=[w1, w2, w3,. . ,wn] cu proprietatea c (wi, wi+1)E pentru 1i<n.

Lungimea unui lan = numrul de muchii din care este format.

Lan simplu = lanul care conine numai muchii distincte

Lan compus= lanul care nu este format numai din muchii distincte

Lan elementar = lanul care conine numai noduri distincte

Ciclu = Un lan n care primul nod coincide cu ultimul.

Ciclul este elementar dac este format doar din noduri distincte,
excepie fcnd primul i ultimul. Lungimea unui ciclu nu poate fi 2.

Succesiunea de vrfuri 2, 3, 5, 6
reprezint un lan simplu i
elementar de lungime 3.
Lanul 5 3 4 5 6 este simplu dar nu
este elementar.
Lanul 5 3 4 5 3 2 este compus i
nu este elementar.
Lanul 3 4 5 3 reprezint un ciclu
elementar

Graf partial = Dac dintr-un graf G=(V,E) se suprim cel puin o muchie
atunci noul graf G=(V,E), E E se numete graf parial al lui G (are
aceleasi noduri si o parte din muchii).

1 6 1 6

5 5
2 2

3 4 3 4

G G1 este graf partial al lui G

Subgraf = Dac dintr-un graf G=(V,E) se suprim cel puin un nod


mpreun cu muchiile incidente lui, atunci noul graf G=(V,E), E E si
VV se numete subgraf al lui G.
1 6 6

5 5
2 2

3 4 3

G G1 este subgraf al lui G

Graf regulat = graf neorientat n care toate nodurile au acelai grad;

1 6

5
2

3 4

Graf complet = graf neorientat G=(V,E) n care exist muchie ntre


oricare dou noduri.
Numrul de muchii ale unui graf complet este: nr*(nr-1)/2.Unde nr
este numarul de noduri

3 4
graf complet. Nr de muchii: 4x(4-1)/2 = 6

Graf conex = graf neorientat G=(V,E) n care pentru orice pereche de


noduri (v,w) exist un lan care le unete.

1 6

5
2

3 4
graf conex nu este graf conex

Component conex = subgraf al grafului de referin, maximal n


raport cu proprietatea de conexitate (ntre oricare dou vrfuri exist lan);

1 6

5
2

3 4
graful nu este conex. Are 2 componente conexe:
1, 2 si 3, 4, 5, 6
Lan hamiltonian = un lan elementar care conine toate nodurile unui
graf

1 6

5
2

3 4
L=[2 ,1, 6, 5, 4, 3] este lant hamiltonian
Ciclu hamiltonian = un ciclu elementar care conine toate nodurile
grafului

1 6

5
2

3 4
C=[1,2,3,4,5,6,1] este ciclu hamiltonian

Graf hamiltonian = un graf G care conine un ciclu hamiltonian


Graful anterior este graf Hamiltonian.

Daca G este un graf cu n>=3 noduri astfel incat gradul fiecarui nod este
mai mare sau egal decat n/2 atunci G este hamiltonian

Lan eulerian = un lan simplu care conine toate muchiile unui graf

Lantul: L=[1.2.3.4.5.3.6.2.5.6] este lant eulerian

Ciclu eulerian = un ciclu simplu care conine toate muchiile grafului


Ciclul: C=[1.2.3.4.5.3.6.2.5.6.1] este ciclu eulerian

Graf eulerian = un graf care conine un ciclu eulerian.

Condiie necesar i suficient: Un graf este eulerian dac i numai dac


oricare vrf al su are gradul par.

REPREZENTAREA GRAFURILOR NEORIENTATE


Fie G=(V, E) un graf neorientat.
Exista mai multe modalitati de reprezentare pentru un graf neorientat,
folosind diverse tipuri de structuri de date. Reprezentarile vor fi utilizate in diversi
algoritmi si in programele care implementeaza pe calculator acesti algoritmi.

Matricea de adiacent matricea boolean

Matricea de adiacent asociat unui graf neorientat cu n noduri se defineste


astfel: A = (ai j) nxn cu

1, daca [i,j]E

a[i,j]=

0, altfel

Observatii:

- Matricea de adiacenta asociat unui graf neorientat este o matrice simetric


- Suma elementelor de pe linia k reprezint gradul nodului k
- Suma elementelor de pe coloana k reprezint gradul nodului k

Fie graful din figura urmatoare:

Fie graful din figura urmatoare:

1 6
0 1 0 1 0 0 nodul 1 are gradul 2
1 0 1 0 0 0 nodul 2 are gradul 2
5
A= 0 1 0 1 1 0 nodul 3 are gradul 3
2 1 0 1 0 0 0 nodul 4 are gradul 2
0 0 1 0 0 0 nodul 5 are gradul 1
3 4
0 0 0 0 0 0 nodul 6 are gradul 0

Numarul de noduri este 6 si numarul de muchii este 5

Matricea este simetrica si patratica avand 6 linii si 6 coloane

Diagonala principala contine numai valori nule


Pentru a prelucra graful se citesc:

6- reprezentand n, numarul de noduri

5- reprezentand m, numarul de muchii

5 perechi x-y reprezentand extremitatile celor 5 muchii:

1-2 => a[1,2]=a[2,1]=1

1-4 => a[1,4]=a[4,1]=1

2-3 => a[2,3]=a[3,2]=1

3-4=> a[3,4]=a[4,3]=1

3-5 => a[3,5]=a[5,3]=1

Listele de adiacenta a nodurilor

Reprezentarea in calculator a unui graf se poate face utilizand listele de adiacenta a varfurilor,
adica pentru fiecare varf se alcatuieste lista varfurilor adiacente cu el.

Fie graful din figura urmatoare:


1 6

5
2

3 4

Lista vecinilor nodului 3: 2, 4, 5 (noduri adiacente)


1 6

5
2

3 4

Nodul 6 nu are vecini (este izolat)


Pentru intreg graful vom avea mai multe liste :

Nodul 1 : 2 4

Nodul 2 :
1 3
Nodul 3 : 2 4 5

Nodul 4 : 1 3

Nodul 5 : 3
Ordinea nodurilor in cadrul unei liste nu este importanta

Pentru a genera o astfel de lista vom defini tipul nod :

struct nod {int nd;


nod *next;};

Toate listele se vor memora utilizand un vector de liste :

nod *L[20];

Datele de intrare : numarul de noduri si muchiile se vor citi din fisier :

O solutie de implementare este urmatoarea :

#include<conio.h>
#include<fstream.h>
struct nod
{int nd;
nod *next;};

nod *L[20];

void afisare(int nr_nod) //afiseaza lista vecinilor nodului nr_nod


{nod *p=L[nr_nod];
if(p==0)
cout<<nr_nod<<" este izolat "<<endl;
else
{cout<<"lista vecinilor lui "<<nr_nod<<endl;
nod *c=p;
while(c)
{cout<<c->nd<<" ";
c=c->next;}
cout<<endl;}
}

void main()
{fstream f;int i,j,n;
nod *p,*q;

f.open("graf.txt",ios::in); //citirea datelor din fisier


clrscr();
f>>n;
while(f>>i>>j)
{p=new nod; //se adauga j in lista vecinilor lui i
p->nd=j;
p->next=L[i];
L[i]=p;
q=new nod; //se adauga i in lista vecinilor lui j
q->nd=i;
q->next=L[j];
L[j]=q;
}
f.close();

cout<<endl<<"listele de adiacente ";


for(i=1;i<=n;i++)
afisare(i);
getch();
}

Observatie : In exemplul anterior adaugarea unui nou element in lista se face inainte
celorlalte din lista corespunzatoare.

Aceste doua moduri de reprezentare (prin matrice de adiacenta si prin liste de vecini) se
folosesc dupa natura problemei. Adica, daca in problema se doreste un acces frecvent la
muchii, atunci se va folosi matricea de adiacenta; daca numarul de muchii care se reprezinta
este mult mai mic dect nxn, este de preferat sa se folosesca listele de adiacenta, pentru a se
face economie de memorie.

Parcurgerea

Rezolvarea multor probleme de grafuri, presupune parcurgerea lor de la un anumit nod.


Pentru explorarea grafurilor, exista doua tipuri de algoritmi: de explorarea in latime si de
explorare in adancime.

Explorarea grafurilor in latime

La explorarea in latime, dupa vizitarea nodului initial, se exploreaza toate nodurile adiacente
lui, se trece apoi la primul nod adiacent si se exploreaza toate nodurile adiacente acestuia si
neparcurse inca, s.a.m.d.

Fiecare nod se parcurge cel mult odata (daca graful nu este conex nu se vor putea parcurge
toate nodurile)

De exemplul pentru garful din figura de mai jos, se va proceda in felul urmator:

se porneste din nodul 1, (se poate incepe de la oricare alt nod)

1 6

5
2

3 4

se exploreaza in vontinuare vecinii acestuia : nodul 2 si apoi 4,


1 6

5
2

3 4
se obtine 1,2,4

dupa care din 2 se exploreaza nodul adiacent acestuia 3. Nodul 1 nu se mai viziteaza odata

1 6

5
2

3 4
se obtine 1,2,4,3

In continuare ar trebui parcursi vecinii lui 4 (1,2,4,3 ) dar acesta nu mai


are vecini nevizitati si se trece la vecinii lui 3 : 1,2,4,3 respectiv nodul 5 :

1 6

5
2

3 4
se obtine 1, 2, 4, 3, 5

Nodul 6 ramane neparcurs

Algoritmul

Se va folosi o coada in care se inscriu nodurile in forma in care sunt parcurse: nodul initial
varf (de la care se porneste), apoi nodurile a,b,..., adiacente lui varf, apoi cele adiacente lui a,
cele adiacente lui b,... ,s.a.m.d.

Coada este folosita astfel:


- se pune primul nod in coada;
- se afla toate varfurile adiacente cu primul nod si se introduc dupa primul nod
- se ia urmatorul nod si i se afla nodurile adiacente
- procesul se repeta pana cand se ajunge la sfarsitul cozii

-Graful se va memora utilizand matricea de adiacenta a[10][10]


-pentru memorarea succesiunii nodurilor parcurse se va folosi un vector c[20] care va
functiona ca o coada
-pentru a nu parcurge un nod de doua ori se va folosi un vector boolean viz[20] care va
retine :
- viz[k]=0 daca nodul k nu a fost vizitat inca
- viz[k]=1 daca nodul k a fost vizitat
-doua variabile : prim si ultim vor retine doua pozitii din vectorul c si anume :
- prim este indicele componentei pentru care se parcurg vecinii
(indexul componentelor marcate cu rosu in sirurile parcurse anterior ). Prin urmare
Varf=c[prim], este elementul pentru care se determina vecinii (nodurile adiacente)
-ultim este pozitia in vector pe care se va face o noua inserare in
vectorul c (evident, de fiecare data cand se realizeaza o noua inserare se mareste vectorul)
-vecinii nodului varf se cauta pe linia acestui varf : daca a[varf][k]=1 inseamna ca
nodurile varf si k sunt adiacente. Pentru ca nodul k sa fie adaugat in coada trebuie ca nodul sa
nu fi fost vizitat : viz[k]=0

#include<fstream.h>
#include<conio.h>

int a[10][10],c[20],viz[10];
int n,m,prim,ultim,varf;

void bf_iterativ() //parcurgerea in latime


{int k;
while(prim<=ultim)
{varf=c[prim];
for(k=1;k<=n;k++)
if(a[varf][k]==1&&viz[k]==0) //il adaug pe k in coada daca este vecin pt. varf si nu a fost
vizitat
{ultim++;
c[ultim]=k;
viz[k]=1;}
prim++;
}
}

void main()
{clrscr();
int x,y;
fstream f; //memorare graf in matrice de adiacenta
f.open("muchii.txt",ios::in);
f>>n>>m;
for(int i=1;i<=m;i++)
{f>>x>>y;
a[x][y]=a[y][x]=1;
}

cout<<"matricea de adiac "<<endl; // afisare matrice de adiacenta


for( i=1;i<=n;i++)
{for(int j=1;j<=n;j++)
cout<<a[i][j]<<" ";
cout<<endl;
}

int nd;
prim=ultim=1;
cout<<"nodul de inceput=";
cin>>nd; // nodul de la care se porneste parcurgerea
viz[nd]=1;
c[prim]=nd;
bf_iterativ();
for(i=1;i<=ultim;i++) //afisarea cozii
cout<<c[i]<<" ";
getch();
}

Varianta recursiva de parcurgere se obtine modificand functia de parcurgere iterativa


adaugand conditia necesara autoapelului:

void bf_recursiv() //parcurgerea in latime


{int k;
if(prim<=ultim)
{varf=c[prim];
for(k=1;k<=n;k++)
if(a[varf][k]==1&&viz[k]==0) //il adaug pe k in coada daca este vecin pt. varf si nu a fost
vizitat
{ultim++;
c[ultim]=k;
viz[k]=1;}
prim++;
bf_recursiv();
}
}

Parcurgerea in latime a grafurilor memorate prin liste este similara cu diferenta ca vecinii unui
nod adaugat in coada se cauta in lista corespunzatoare lui :

#include<conio.h>
#include<fstream.h>
struct nod
{int nd;
nod *next;};

nod *L[20];
int viz[100]; //marchez cu 1 nodurile vizitate
int m,n;
int prim,ultim,C[100];

void bfi_lis()
{int varf,nr;
nod *p;
while(prim<=ultim)
{varf=C[prim];
p=L[varf]; // se parcurge lista elementelor din varful cozii
while(p)
{nr=p->nd;
if(viz[nr]==0) //numai daca nu a fost vizitat
{ultim++; //maresc coada
C[ultim]=nr; //il adaug in coada
viz[nr]=1;}; //il marchez ca fiind vizitat
p=p->next;
}
prim++; //avansez la urmatorul nod din coada
}
}

void afisare(int nr_nod)


{nod *p=L[nr_nod];
if(p==0)
cout<<nr_nod<<" este izolat "<<endl;
else
{cout<<"lista vecinilor lui "<<nr_nod<<endl;
nod *c=p;
while(c)
{cout<<c->nd<<" ";
c=c->next;}
cout<<endl;}
}

void main()
{fstream f;
int i,j;
nod *p,*q;
f.open("muchii.txt",ios::in);
clrscr();
f>>n>>m;

while(f>>i>>j)
{p=new nod;
p->nd=j;
p->next=L[i];
L[i]=p;
q=new nod;
q->nd=i;
q->next=L[j];
L[j]=q;
}

f.close();

cout<<endl<<"listele de adiacente ";

for(i=1;i<=n;i++)
afisare(i);
int ndr;
cout<<endl<<"nodul de inceput ";
cin>>ndr;
viz[ndr]=1;
prim=ultim=1;
C[prim]=ndr;
cout<<endl<<"parcurgere in latime "<<endl;
bfi_lis();
for(i=1;i<=ultim;i++)
cout<<C[i]<<" ";
getch();
}

Functia recursiva :
void bfr_lis()
{int varf,nr;
nod *p;
if(prim<=ultim)
{varf=C[prim];
p=L[varf];// se parcurge lista elementelor din varful cozii
while(p)
{nr=p->nd;
if(viz[nr]==0)//numai daca nu a fost vizitat
{ultim++;//maresc coada
C[ultim]=nr;//il adaug in coada
viz[nr]=1;};//il marchez ca fiind vizitat
p=p->next;}
prim++; //avansez la urmatorul nod din coada
bfr_lis();
}
}

Parcurgerea grafurilor in adancime (depth first)

Parcurgerea unui graf in adancime se face prin utilizarea stivei (alocate implicit
prin subprograme recursive).

Pentru fiecare nod se parcurge primul dintre vecinii lui neparcursi inca

Dupa vizitarea nodului initial x 1, se exploreaza primul nod adiacent lui fie acesta
x2 , se trece apoi la primul nod adiacent cu x 2 si care nu a fost parcurs inca ,
s.a.m.d.

Fiecare nod se parcurge cel mult odata (daca graful nu este conex nu se vor
putea parcurge toate nodurile)

De exemplul pentru garful din figura de mai jos, se va proceda in felul urmator:

se porneste din nodul 1, (se poate incepe de la oricare alt nod)


1 6

5
2

3 4

se exploreaza in vontinuare primul vecin al acestuia acestuia : nodul 2,

1 6

5
2

3 4
se obtine 1,2

dupa care din 2 se exploreaza un nod adiacent cu acesta si care nu a fost vizitat :
3.( Nodul 1 nu se mai viziteaza odata)

1 6

5
2

3 4
se obtine 1,2,3

In continuare ar trebui sa se parcurga vecinul lui 3 nevizitat : 4

1 6

5
2

3 4
se obtine 1, 2, 3, 4

Pentru nodul 4 ar trebui sa se parcurga primul sau vecin neparcurs (nodul 1 dar
acesta a fost deja parcurs. Si nodul 3 a fost parcurs. Din 4 nu mai avem ce vizita
si se trece la nivelul anterior din stiva, la nodul 3 :

1, 2, 3, 4 Se parcurge vecinul sau nevizitat, nodul 5 .


1 6

5
2

3 4
SE obtine : 1, 2, 3, 4 , 5.

Nodul 3 nu mai are vecini nevizitati si se trece pe nivelul anterior din stiva, nodul
2 : 1, 2, 3, 4 , 5. Nici acesta nu mai are vecini nevizitati si se trece pe nivelul
anterior la nodul 1 : 1, 2, 3, 4 , 5. Cum nici acesta nu mai are vecini nevizitati se
incheie algoritmul.

Nodul 6 ramane nevizitat.

Algoritmul

-Graful se va memora utilizand matricea de adiacenta a[10][10]

-pentru a nu parcurge un nod de doua ori se va folosi un vector boolean vizcare


va retine :

- viz[k]=0 daca nodul k nu a fost vizitat inca

- viz[k]=1 daca nodul k a fost vizitat

-ca si la parcurgerea in latime vecinii unui nod se cauta pe linia acestui nod :
daca a[nod][k]=1 inseamna ca nodurile nod si k sunt adiacente. Pentru ca nodul
k sa fie fie parcurs trebuie ca nodul sa nu fi fost vizitat : viz[k]=0

#include<fstream.h>

#include<conio.h>

int a[20][20],n,m,viz[100],gasit;

void dfmr(int nod)

cout<<nod<<" ";

viz[nod]=1;

for(int k=1;k<=n;k++)

if(a[nod][k]==1&&viz[k]==0)

dfmr(k);
}

void main()

{int x,y,j;

fstream f;

clrscr();

f.open("graf.txt",ios::in);

if(f)

cout<<"ok!"<<endl;

else

cout<<"eroare la deschidere de fisier!";

f>>n>>m;

for(int i=1;i<=m;i++)

{f>>x>>y;

a[x][y]=a[y][x]=1;}

cout<<endl<<"matricea de adiacente"<<endl;

for(i=1;i<=n;i++)

{for(j=1;j<=n;j++)

cout<<a[i][j]<<" ";

cout<<endl;}

cout<<endl<<"parcurgere in adancime incepand de la varful 1"<<endl;

dfmr(1)

getch();}

Matricea lanturilor

Fie G=(V, E) un graf neorientat, unde V are n elemente (n noduri) si E are m elemente (m
muchii). Lui G i se asociaza matricea D numita matricea lanturilor, cu n linii si n coloane.

Astfel: D = (di j) nxn cu:

1, daca exista lant de la i la j


d[i,j]=
0, altfel

Matricea lanturilor ca si matricea de adiacenta, in cazul grafurilor neorientate , este o matrice


simetrica (daca exista lant de la i la j exista si lant de la j la i).

Grafului de mai jos i se asociaza urmatoarea matrice a lanturilor:

1 0111100
6 7
1011100
5 1101100
2 1110100
3 4 1111000
0000001
0000010

Linia k (sau coloana k) a matricei lanturilor ne arata nodurile j pentru care exista lant de k la j
( si de la j la k). Spre exemplu, pentru nodul 3 .

1 0111100
6 7
1011100
5 1101100
2 1110100
3 4 1111000
0000001
0000010

Nu se poate ajunge de la nodul 3 la nodurile 6 sau 7.

Problema:
Fiind dat un graf neorientat, memorat prin intermediul matricei de adiacenta, se cere sa se
creeze si sa se tipareasca matricea lanturilor.

Se observa ca parcurgand in adancime (sau in latime) graful pornind de la nodul nod=3 spre
exemplu vectorul de vizitati este:
1 1 1 1 1 0 0. Daca se inlocuieste viz[nod]=viz[3]=0 se obtine: 1 1 0 1 1 0 0 si se poate incarca
linia 3 din matricea lanturilor cu continultul vectorului de vizitati. Prin urmare se parcurge df
graful pornind de la fiecare nod in parte si se incarca de fiecare data continutul vectorului viz
in matricea lanturilor in linia corespunzatoare. Iata o solutie:

#include<fstream.h>
#include<conio.h>
int a[20][20],n,m,viz[100],gasit,drum[20][20];

void dfmr(int nod)


{ viz[nod]=1;
for(int k=1;k<=n;k++)
if(a[nod][k]==1&&viz[k]==0)
dfmr(k);
}

void main()
{int x,y,j;
fstream f;
clrscr();
f.open("muchii.txt",ios::in); //citire matrice de adiacenta din fisier
if(f)
cout<<"ok!"<<endl;
else
cout<<"eroare la deschidere de fisier!";
f>>n>>m;
for(int i=1;i<=m;i++)
{f>>x>>y;
a[x][y]=a[y][x]=1;}
cout<<endl<<"matricea de adiacente"<<endl;
for(i=1;i<=n;i++)
{for(j=1;j<=n;j++)
cout<<a[i][j]<<" ";
cout<<endl;}
int nod;
for(nod=1;nod<=n;nod++) //parcurgere graf incepand de la fiecare nod
{for(j=1;j<=n;j++)
viz[j]=0; //pentru fiecare nod se face vectorul viz in prealabil 0
dfmr(nod);
viz[nod]=0;
for(j=1;j<=n;j++) //incarcare vector viz in matrice drum
drum[nod][j]=viz[j];
}
cout<<endl<<"matricea drumurilor "<<endl;
for(i=1;i<=n;i++)
{for(j=1;j<=n;j++)
cout<<drum[i][j]<<" ";
cout<<endl;}
getch();}

Componente conexe

Fie G=(V, E) un graf neorientat, unde V are n elemente (n noduri) si E are m


elemente (m muchii).

Definitie: G1=(V1, E1)este o componenta conexa daca:

- pentru orice pereche x,y de noduri din V 1 exista un lant de


la x la y (implicit si de la y la x)
- nu exista alt subgraf al lui G, G 2=(V2, E2) care sa
indeplineasca prima conditie si care sa-l contina pe G1
1 Graful alaturat are doua componente
6 7
conexe:
5
2 - subgraful care contine
nodurile:
3 4 12345

- subgraful care contine


nodurile:
67

Observatie: subgraful 1, 2, 3, 4 nu este componenta conexa pentru ( chiar daca


pentru orice pereche x,y de noduri exista un lant de la x la y) deoarece exista
subgraful 1, 2, 3, 4, 5 care il contine si indeplineste aceeasi conditie.

Definitie: Un graf G=(V, E)este conex daca pentru orice pereche x,y de noduri
din V exista un lant de la x la y (implicit si de la y la x).

Observatii:

- Un graf este conex daca admite o singura componenta conexa.


- Graful anterior nu este conex pentru ca admite doua componente
conexe
Graful urmator este conex:

1
6 7

5
2

3 4

Probleme:

1. Fiind dat un graf memorat prin intermediul matricei de adiacenta sa se


determine daca graful este conex.
2. Fiind dat un graf memorat prin intermediul matricei de adiacenta si un
nod k sa se determine componenta conexa careia ii apartine nodul k
3. Sa se afiseze toate componentele conexe ale unui graf neorientat
Indicatii :
-In vectorul viz se incarca numarul componentei conexe astfel pentru graful
urmator, vectorul viz va retine:

viz=[1,1,1,1,2,1,2].
-Numarul de componente conexe este 2.
-Se afiseaza componentele cu numarul componentei conexe egal cu 1: 1,2,3,4,6
-Se afiseaza componentele cu numarul componentei conexe egal cu 2: 5, 7

1
5 7

6
2

3 4

-Incarcarea in viz se realizeaza prin parcurgere df pornind de la fiecare nod

-se porneste de la 1, se parcurge df si se incarca in viz valoarea 1 pt nodurile 1, 2,


3, 4, 6. Viz devine:

1,1,1,1,0,1,0

-se porneste in continuare de la primul nod nevizitat, adica 5 si se incarca


numarul celei de a doa componente, adica 2

Viz devine: 1,1,1,1,2,1,2

-Nu mai sunt noduri nevizitate si algoritmul se incheie.


Iata o solutie:

#include<fstream.h>

#include<conio.h>

int a[20][20],n,m,viz[100],gasit;

int nrc; //pastreaza numarul componentei conexe

void dfmr(int nod)

{ viz[nod]=nrc; //se incarca numarul componentei

for(int k=1;k<=n;k++)

if(a[nod][k]==1&&viz[k]==0)

dfmr(k);

}
void main()

{int x,y,j;

fstream f;

clrscr();

f.open("muchii.txt",ios::in); //memorare matrice de adiacenta

if(f)

cout<<"ok!"<<endl;

else

cout<<"eroare la deschidere de fisier!";

f>>n>>m;

for(int i=1;i<=m;i++)

{f>>x>>y;

a[x][y]=a[y][x]=1;}

cout<<endl<<"matricea de adiacente"<<endl;

for(i=1;i<=n;i++)
{for(j=1;j<=n;j++)

cout<<a[i][j]<<" ";

cout<<endl;}

for(int nod=1;nod<=n;nod++)

if(viz[nod]==0) //se incearca parcurgerea numai daca un nod nu a fost deja


parcurs

{nrc++;

dfmr(nod);}

cout<<endl<<"vectorul viz "<<endl;

for(i=1;i<=n;i++)

cout<<viz[i]<<" ";
cout<<endl<<"Componentele conexe :"<<endl;

for(i=1;i<=nrc;i++)

{cout<<endl<<"componenta "<<i<<endl;

for(j=1;j<=n;j++)

if(viz[j]==i)

cout<<j<<" ";

getch();}

Matricea ponderilor

Fie G=(V, E) un graf neorientat, unde V are n elemente (n noduri) si E are m elemente (m
muchii). Atasam fiecarei muchii o pondere (sau cost) ci,j. Spre exemplu, ne putem imagina
nodurile unui graf ca fiind niste repere pe o harta si costurile distante intre repere adiacente.
Matricea ponderilor asociata grafului este o matrice simetrica cu nxn elemente

Astfel: p= (pi j) nxn cu:

0, daca i=j
p[i,j]= ci,j, daca [i,j]E
altfel

1 025
2 5 206
2
5 6018
8
6 510
3 1 4
80

Datele care se vor citi pentru generarea matricii ponderilor sunt:

Numarul de noduri, numarul de muchii si m triplete de forma extremitati muchie si cost


asociat:

5
5
122
145
236
341
358
Problema:
Sa se genereze matricea ponderilor pentru un graf citit (n noduri, m muchii si m triplete).
Indicatii:
- pentru infinit se va folosi o valoare mai mare decat oricare dintre valorile posibile
pentru costuri. De obicei intervalul de valori pentru costuri este precizat.
- Initial se incarca in matrice infinit apoi se suprascrie matricea pentru fiecare triplet
citit:
Ex: cin>>x>>y>>cost
p[x][y]=p[y][x]=cost

Algoritmul lui Roy-Floyd

Fie G=(V, E) un graf neorientat, unde V are n elemente (n noduri) si E are m


elemente (m muchii) memorat prin matricea ponderilor. Se cere ca pentru doua
noduri x,y citite sa se determine lungimea minima a lantului de la x la y.

Astfel:

1
2 10
5
2 8
3
3 1 4

Initial matricea ponderilor pentru nodurile 1 si 4 va retine 10. Se observa ca


lantul 1,2,3,4 determina o suma a costurilor mai mica: 2+3+1=6. Lungime
minima a lantului de la 1 la 4 este 6.

Algoritmul:

-se genereaza matricea ponderilor:

1 0 2 pinf 10 pinf
2 10
2 0 3 pinf pinf
5
2 8
3 pinf 3 0 1 8
3 1 4
10 pinf 1 0 pinf

pinf pinf 8 pinf 0

Unde pinf reprezinta plus infinit


-se incearca pentru oricare pereche de noduri i,j sa se obtina drumuri mai
scurte prin noduri intermediare k (k1n).

Acest lucru se determina comparand lungimea lantului a[i,j] cu lungimea


lantului care trece prin k si daca:

a[i,j] > a[i,k]+a[k,j] atunci se atribuie: a[i,j] a[i,k]+a[k,j]

Astfel generarea matricii drumurilor optime se realizeaza cu urmatoarea


secventa:

for(int k=1;k<=n;k++)

for(int i=1;i<=n;i++)

for(int j=1;j<=n;j++)

if(a[i][j]>a[i][k]+a[k][j])

a[i][j]=a[i][k]+a[k][j];

Se obtine:

1 0 2 5 6 13
2 10
2 0 3 4 11
5
2 8
3 53018
3 1 4
64109

13 11 8 9 0

In continuare, dupa determinarea matricii lanturilor optime, pentru doua noduri


citite x, y se cere sa se reconstituie un lant optim de la x la y (pot fi mai multe
solutii).

Solutie:
-se determina daca exista un lant de la x la y (ar putea sa nu existe un astfel de
lant daca graful nu este conex):

cand a[x,y]

-se descompune drumul de la x la y prin k atunci cand: a[x][y]=a[x][k]+a[k][y];

- pentru un astfel de algoritm se utilizeaza strategia Divide et Impera

Iata o solutie:

#include<fstream.h>

#include<conio.h>

const pinf=1000; //pentru plus infinit

int a[20][20],n,m;

void citire_cost()

{fstream f;

int i,j,x,y,c;

f.open("roy.in",ios::in);

if(f)

cout<<"deschiderea a reusit";

else

cout<<"eroare la deschidere!";

cout<<endl;

f>>n>>m;

//initializare matrice

for(i=1;i<=n;i++)

for(j=1;j<=n;j++)

if(i==j)
a[i][j]=0;

else

a[i][j]=pinf;

for(i=1;i<=m;i++)

{f>>x>>y>>c;

a[x][y]=a[y][x]=c;}

void afisare_mat()

{for(int i=1;i<=n;i++)

{for(int j=1;j<=n;j++)

if(a[i][j]==pinf)

cout<<"pinf ";

else

cout<<a[i][j]<<" ";

cout<<endl;}

void genarare_matrice_drumuri_optime()

{for(int k=1;k<=n;k++)

for(int i=1;i<=n;i++)

for(int j=1;j<=n;j++)

if(a[i][j]>a[i][k]+a[k][j])

a[i][j]=a[i][k]+a[k][j];

void descompun_drum(int i,int j) //realizeaza descompunerea portiunii de la i la j


prin k

{int g=0,k=1;
while(k<=n&&!g)

{if(i!=k&&j!=k)

if(a[i][j]==a[i][k]+a[k][j])

{descompun_drum(i,k);

descompun_drum(k,j);

g=1;} //g marcheaza daca se poate realiza descompunerea

k++;

if(!g)

cout<<j<<" "; //cand drumul nu mai poate fi descompus afisez extremitatea


finala

void scriu_drum(int nodini,int nodfin) // functia primeste ca parametri cele doua


noduri pt care se determina optimul

{if(a[nodini][nodfin]<pinf)

{cout<<"lantul de la "<<nodini<<" la "<<nodfin<<" are lungimea "<<a[nodini]


[nodfin];

cout<<endl<<"un drum optim este: "<<endl;

cout<<nodini<<" ";

descompun_drum(nodini,nodfin); // apeleaza functia care afiseaza efectiv lantul

else

cout<<"nu exista drum de la "<<nodini<<" la "<<nodfin;

void main()

{clrscr();int x,y;
citire_cost();

cout<<endl<<"matricea ponderilor "<<endl;

afisare_mat();

genarare_matrice_drumuri_optime();

cout<<endl<<"matricea drumurilor optime "<<endl;

afisare_mat();

cout<<endl<<"Se determina drumul minim intre varfurile x si y "<<endl;

cout<<"x=";

cin>>x;

cout<<"y=";

cin>>y;

scriu_drum(x,y);

getch();

Graf hamiltonian

Fie G=(V, E) un graf neorientat, unde V are n elemente (n noduri) si E are m


elemente (m muchii).

Lan hamiltonian = un lan elementar care conine toate nodurile unui graf

1 6

5
2

3 4
L=[2 ,1, 6, 5, 4, 3] este lant hamiltonian

Ciclu hamiltonian = un ciclu elementar care conine toate nodurile grafului


1 6

5
2

3 4
C=[1,2,3,4,5,6,1] este ciclu hamiltonian

Graf hamiltonian = un graf G care conine un ciclu hamiltonian

Graful anterior este graf Hamiltonian.

Fiind dat un graf neorientat memorat prin matricea de adiacente sa se determine


daca graful este Hamiltonian . In caz afirmativ sa se afiseze ul ciclu Hamiltonian
altfel se va afisa un mesaj.

Pentru a rezolva problema vom utiliza tehnica backtracking. Vom incarca in stiva
noduri distincte si adiacente, astfel incat pornind de la problema clasica de
backtracking a permutarilor vom testa valoarea de pe nivelul k astfel:

Sa fie un nod adiacent cu precedentul adaugat. E necesar ca: a[st[k-


1]][st[k]]=1, in caz contrar se returneaza 0
Nodul adaugat sa nu se regaseasca pe nivelurile anterioare . Trebuie
ca st[k]st[i] unde i{1,2k-1}, in caz contrar se returneaza 0
Pentru a se incheia ciclul este necesar ca primul si ultimul nod sa fie
adiacente. Adica:
a[st[1]][st[n]]=1, in caz contrar se returneaza 0

#include<fstream.h>

#include<conio.h>

#include<stdio.h>

int st[100],n,m,k,a[20][20];

int ns;

//k este este nivelul din stiva (indexul - vetorul solutie),curent

int e_valid()

{if(k>1)

if(!a[st[k-1]][st[k]])
return 0;

else

for(int i=1;i<=k-1;i++)//parcurg nivelurile anterioarenivelului curent

if(st[i]==st[k])

return 0;

if(k==n)

if(!a[st[1]][st[k]])

return 0;

return 1;

void afisare()

{for(int i=1;i<=n;i++)

cout<<st[i]<<" ";

cout<<st[1];

k=0; //determina iesirea la prima solutie

ns++;

}void back()

{k=1; //pe primul nivel initial

while(k>0)//cand k va descreste la 0 algoritmul se incheie

if(st[k]<n)

{st[k]++;

if(e_valid())//daca elementul incarcat este valid

if(k==n)//verific daca am ajuns la solutia completa.

afisare();
else //daca nu am solutia completa urc in stiva (maresc vectorul,
adica pe k)

{k++;

st[k]=0;}

else

k--;

void main()

{clrscr();

fstream f;

f.open("ham.in",ios::in);

int u,v;

if(f)

cout<<"ok!";

else

cout<<"eroare";

cout<<endl;

f>>n>>m;

for(int i=1;i<=m;i++)

{f>>u>>v;

a[u][v]=a[v][u]=1;
}

cout<<"matricea de adiac "<<endl; // afisare matrice de adiacenta

for( i=1;i<=n;i++)

{for(int j=1;j<=n;j++)

cout<<a[i][j]<<" ";

cout<<endl;

back();

if(ns==0)

cout<<nu exista solutii;

getch();

Grafuri euleriene

Fie G=(V, E) un graf neorientat, unde V are n elemente (n noduri) si E are


m elemente (m muchii).

Lan eulerian = un lan simplu care conine toate muchiile unui graf

Lantul: L=[1.2.3.4.5.3.6.2.5.6] este lant eulerian

Ciclu eulerian = un ciclu simplu care conine toate muchiile grafului


Ciclul: C=[1.2.3.4.5.3.6.2.5.6.1] este ciclu eulerian

Graf eulerian = un graf care conine un ciclu eulerian.


Condiie necesar i suficient: Un graf este eulerian dac i numai dac
oricare vrf al su are gradul par.
Observatie: graful poate fi eulerian si daca contine noduri izolate.

Problema: fiind dat un graf fara noduri izolate sa se determine daca este
eulerian. In caz afirmativ se vor afisa toate ciclurile euleriene care incep cu
un nod nd citit.
- vom determina daca graful este conex
- vom determina daca fiecare nod are grad par
- vom genera toate ciclurile euleriene utilizand tehnica backtracking.
Astfel:
o primul nod va trebui sa fie nd
o un nou x=st[k], adaugat in stiva trebuie sa fie adiacent cu
anteriorul (y=st[k-1])
o muchia x-y nu trebuie sa mai fi fost adaugata inca odata
o ultimul nod, care incheie ciclul, trebuie sa fie incident cu
primul

O solutie:

#include<fstream.h>
#include<conio.h>
int st[100];
int k,nd;

int a[10][10],viz[10],n,m;

void df_r(int nod) //parcurgere in adancime


{int k;
cout<<nod<<" ";
viz[nod]=1;
for(k=1;k<=n;k++)
if(a[nod][k]&&!viz[k])
df_r(k);
}

int e_valid()
{int x,y;
if(k==1)
if(st[k]!=nd)
return 0;
if(k>1) //sa existe muchie cu precedentul
{x=st[k];
y=st[k-1];
if(a[x][y]==0)
return 0;
}
for(int i=1;i<=k-2;i++)
if((st[i]==x && st[i+1]==y) || (st[i]==y && st[i+1]==x))
return 0; //muchia a mai fost luata odata

//ultima muchie sa fie incidenta cu prima


if(k==m)
if(a[st[m]][st[1]]==0)
return 0;
return 1;}
void tipar()
{for(int i=1;i<=m;i++)
cout<<st[i]<<" ";
cout<<st[1];
cout<<endl;

void back()
{ k=1;
while(k>0)
{if(st[k]<n)
{st[k]++;
if(e_valid())
if(k==m)
tipar();
else{k++;
st[k]=0;
}
}
else
k--;}
}
void main()
{clrscr();int x,y;
fstream f;//int a[10][10];// citire matrice din fisier
f.open("matsim.txt",ios::in);
if(f)
cout<<"ok";
else
cout<<"error";
f>>n>>m;
for(int i=1;i<=m;i++)
{f>>x>>y;
a[x][y]=a[y][x]=1;
}
cout<<"matricea de adiac "<<endl; // afisare matrice
for( i=1;i<=n;i++)
{for(int j=1;j<=m;j++)
cout<<a[i][j]<<" ";
cout<<endl;
}

cout<<"nd="; //nodul de la care se porneste


cin>>nd;
df_r(nd);
int s=0;
for(i=1;i<=n;i++)
s+=viz[i]; //pentru a verifica daca graful este conex

if(s!=n)
cout<<"graful nu e conex ";
else
{int gasit=0;
cout<<endl<<"graful e conex!"<<endl;
for(i=1;i<=n;i++) //determin daca toate nodurile au gradele pare
{s=0;
for (int j=1;j<=n;j++)
s+=a[i][j];
if(s%2!=0)
gasit=1;}
if(gasit)
cout<<"am noduri fara grade pare";
else
cout<<"toate nodurile au gradele pare deci graful e eulerian";
}
back();
getch();
}

O varianta mai eficienta de determinare a unui ciclu eulerian este


urmatoarea:

Fie graful din figura urmatoare:

-se determina daca graful contine un ciclu eulerian (toate nodurile au grad
par si este conex)
-se retin gradele tuturor nodurilor in vectorul g. Acesta va retine:
g=[2,4,4,4,4,2]
-ciclul se genereaza pas cu pas retinand nodurile intr-o lista gestionata de
variabilele p si u (prima si ultima componenta)
-se genereaza un ciclu pornind de la nodul 1 care se adauga in lista p
(acesta va fi si u la inceput). In continuare se adauga noduri adiacente cu
informatia retinuta de u si se continua astfel pana se ajunge iar la nodul 1.
De fiecare data cand se adauga o muchie (u->info, i) la ciclu se
micsoreaza gradul lui u->info si lui i iar muchiile (u->info, i) si (i,u->info)
se elimina.
-acest prim ciclu se poate genera parcurgand nodurile grafului
-dupa prima secventa se determina ciclul :

- lista va retine : p={1, 2, 3, 1} iar g devine : g=[0, 2, 2, 4, 4, 2]


- in continuare se cauta un nou ciclu care sa porneasca de la un nod x
din p si pt care g[x]>0. Primul astfel de nod gasit este : x=2. Acest
nou ciclu este memorat de o noua lista gestinata de p1 si u1. Ciclul
nou se cauta dupa acelasi principiu. Se obtine :
p1={2, 4, 3, 5, 2} iar g devine : g=[0,0,0,1,1,1]

Noul ciclu se insereaza dupa x gasit (x=2, se insereaza lista p1 in lista p)


si se obtine : p={1,2,4,3,5,2,3,1}

-mai departe pe acelasi principiu se cauta x (x=4)iar urmatorul ciclu este


p1={4, 5,6,4} si g ajunge la g={0,0,0,0,0,0}. Acesta se insereaza dupa 4
:
Se obtine : p={1,2,4,5,6,4,3,5,2,3,1}
O variabila k retine numarul muchiilor adaugate la ciclu si algoritmul
continua pana k ajunge la m (numarul de muchii).

O solutie de implementare este :

#include<fstream.h>
#include<conio.h>
struct nod{int info;
nod *next;};

int a[20][20],viz[20];
int g[20];
int n,m,k;

void df(int nod)


{viz[nod]=1;
for(int k=1;k<=n;k++)
if(a[nod][k]==1&&viz[k]==0)
df(k);
}

void citire()
{ int x,y;
fstream f; //memorare graf in matrice de adiacenta
f.open("euler.txt",ios::in);
if(f)
cout<<"ok";
else
cout<<"eroare";
cout<<endl;
f>>n>>m;
for(int i=1;i<=m;i++)
{f>>x>>y;
g[x]++; g[y]++;
a[x][y]=a[y][x]=1;
}
}
int verific()
{ for(int i=1;i<=n;i++)
if(g[i]%2==1)
return 0;
df(1);
for(i=1;i<=n;i++)
if(viz[i]==0)
return 0;
return 1;
}
void generezc1(nod*&p,nod*&u,int x)
{nod *q;
q=new nod;
q->info=x;
p=u=q;

do
{ int gas=0;
for(int i=1;i<=n&&!gas;i++)
if(a[i][u->info]==1)
{g[u->info]--;
g[i]--;
k++;
a[i][u->info]=a[u->info][i]=0;
q=new nod;
q->info=i;
u->next=q;
u=q;
gas=1;}
}
while(p->info!=u->info);
u->next=0;
}
void afisare(nod *q)
{while(q)
{cout<<q->info<<" ";
q=q->next;}
}

int cauta(nod *&q)


{
while(q)
{if(g[q->info])
return q->info;
q=q->next;
}
return 0;
}

void main()
{clrscr();
citire();
if(verific()==0)
cout<<"gf nu este eulerian!";
else
{ cout<<"este eulerian!";
nod *p=0,*u;
cout<<endl;
generezc1(p,u,1);
cout<<endl;
nod *p1=0,*u1;
while(k<m)
{nod *q=p;
int x=cauta(q);
generezc1(p1,u1,x);
u1->next=q->next;
q->next=p1->next;
}
afisare(p); }
getch();
}

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