46
Grafuri neorientate 1.Terminologie Graf neorientat= o pereche de multimi =(V, E)unde V este o mulţime finită nevida de elemente numite noduri iar E o multime de perechi neordonate din V, numite muchii. Notăm graful cu G=(V, E). Intr-un un graf G=(V, E) neorientat relaţia binară este simetrică: (v,w)ÎE atunci (w,v) ÎE. Nod = element al mulţimii V, unde G=(V, E) este un graf neorientat. Muchie = element al mulţimii E ce descrie o relaţie existentă între două noduri din V, unde G=(V, E) este un graf neorientat; In figura alaturata: V={1,2,3,4,5,6} sunt noduri E={[1,2], [1,4], [2,3],[3,4],[3,5]} sunt muchii G=(V, E)=({1,2,3,4,5,6}, {[1,2], [1,4], [2,3],[3,4],[3,5]}) Adiacenta: Într-un graf neorientat existenţa muchiei (v,w) presupune că w este adiacent cu v şi v adiacent cu w. În exemplul din figura de mai sus vârful 1 este adiacent cu 4 dar 1 şi 3 nu reprezintă o pereche de vârfuri 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 număr natural ce reprezintă numărul 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 5 1 2 4 3 6

Grafuri neorientatemmj

Embed Size (px)

DESCRIPTION

jmm

Citation preview

Page 1: Grafuri neorientatemmj

Grafuri neorientate

1.Terminologie

Graf neorientat= o pereche de multimi =(V, E)unde V este o mulţime finită nevida de elemente numite noduri iar E o multime de perechi neordonate din V, numite muchii. Notăm graful cu G=(V, E).

Intr-un un graf G=(V, E) neorientat relaţia binară este simetrică: (v,w)ÎE atunci (w,v) ÎE.

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

Muchie = element al mulţimii E ce descrie o relaţie existentă între două noduri din V, unde G=(V, E) este un graf neorientat;

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

Adiacenta: Într-un graf neorientat existenţa muchiei (v,w) presupune că w este adiacent cu v şi v adiacent cu w. În exemplul din figura de mai sus vârful 1 este adiacent cu 4 dar 1 şi 3 nu reprezintă o pereche de vârfuri 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 număr natural ce reprezintă numărul 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

5

1

2

43

6 Nodul 5 este terminal (gradul 1).

Nodul 6 este izolat (gradul 0)

Nodurile 1, 2, 4 au gradele egale cu 2.

Daca un graf neorientat are m muchii atunci suma gradelor tuturor nodurilor este 2m

5

1

2

4 3

6

Page 2: Grafuri neorientatemmj

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 1£i<n.

Lungimea unui lanţ = numărul de muchii din care este format.

Lanţ simplu = lanţul care conţine numai muchii distincte

Lanţ compus= lanţul care nu este format numai din muchii distincte

Lanţ elementar = lanţul care conţine numai noduri distincte

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

Ciclul este elementar dacă este format doar din noduri distincte, excepţie făcând primul şi ultimul. Lungimea unui ciclu nu poate fi 2.

5

1

2

4 3

6

Succesiunea de vârfuri 2, 3, 5, 6 reprezintă un lanţ simplu şi elementar de lungime 3.Lanţul 5 3 4 5 6 este simplu dar nu este elementar.Lanţul 5 3 4 5 3 2 este compus şi nu este elementar.Lanţul 3 4 5 3 reprezintă un ciclu elementar

Graf partial = Dacă dintr-un graf G=(V,E) se suprimă cel puţin o muchie atunci noul graf G’=(V,E’), E’Ì E se numeşte graf parţial al lui G (are aceleasi noduri si o parte din muchii).

G G1 este graf partial al lui G

Subgraf = Dacă dintr-un graf G=(V,E) se suprimă cel puţin un nod împreună cu muchiile incidente lui, atunci noul graf G’=(V’,E’), E’Ì E si V’ÌV se numeşte subgraf al lui G.

5 2

3

6

5

1

2

4 3

6

5

1

2

4 3

6

5

1

2

4 3

6

Page 3: Grafuri neorientatemmj

G G1 este subgraf al lui G

Graf regulat = graf neorientat în care toate nodurile au acelaşi grad;

5

1

2

4 3

6

Graf complet = graf neorientat G=(V,E) în care există muchie între oricare două noduri. Numărul de muchii ale unui graf complet este: nr*(nr-1)/2.Unde nr este numarul de noduri

1

2

4 3 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 uneşte.

5

1

2

43

6

graf conex

5

1

2

4 3

6

nu este graf conex

Componentă conexă = subgraf al grafului de referinţă, maximal în raport cu proprietatea de conexitate (între oricare două vârfuri există lanţ);

5

1

2

4 3

6

graful nu este conex. Are 2 componente conexe:1, 2 si 3, 4, 5, 6

Page 4: Grafuri neorientatemmj

Lanţ hamiltonian = un lanţ elementar care conţine toate nodurile unui graf

5

1

2

4 3

6

L=[2 ,1, 6, 5, 4, 3] este lant hamiltonianCiclu hamiltonian = un ciclu elementar care conţine toate nodurile grafului

5

1

2

4 3

6

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

Graf hamiltonian = un graf G care conţine 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 conţine toate muchiile unui graf

5

1

2

4 3

6

Lantul: L=[1.2.3.4.5.3.6.2.5.6] este lant eulerian

Ciclu eulerian = un ciclu simplu care conţine toate muchiile grafuluiCiclul: C=[1.2.3.4.5.3.6.2.5.6.1] este ciclu eulerian

Graf eulerian = un graf care conţine un ciclu eulerian.

Condiţie necesară şi suficientă: Un graf este eulerian dacă şi numai dacă oricare vârf al său are gradul par.

REPREZENTAREA GRAFURILOR NEORIENTATE

Page 5: Grafuri neorientatemmj

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) n x n 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:

0 1 0 1 0 0 nodul 1 are gradul 21 0 1 0 0 0 nodul 2 are gradul 2

A= 0 1 0 1 1 0 nodul 3 are gradul 31 0 1 0 0 0 nodul 4 are gradul 2

0 0 1 0 0 0 nodul 5 are gradul 1 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

5

1

2

4 3

6

Page 6: Grafuri neorientatemmj

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:

5

1

2

43

6

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

5

1

2

4 3

6

Nodul 6 nu are vecini (este izolat)Pentru intreg graful vom avea mai multe liste :

Nodul 1 :

Nodul 2 :

Nodul 3 :

Nodul 4 :

Nodul 5 :

2 4

1 3

2 4 5

1 3

3

Page 7: Grafuri neorientatemmj

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 fisierclrscr();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;

Page 8: Grafuri neorientatemmj

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)

5

1

2

4 3

6

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

Page 9: Grafuri neorientatemmj

5

1

2

4 3

6

se obtine 1,2,4

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

5

1

2

4 3

6

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 :

5

1

2

4 3

6

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 :

Page 10: Grafuri neorientatemmj

- 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 adiacentaf.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 adiacentafor( 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=";

Page 11: Grafuri neorientatemmj

cin>>nd; // nodul de la care se porneste parcurgereaviz[nd]=1;c[prim]=nd;bf_iterativ();for(i=1;i<=ultim;i++) //afisarea coziicout<<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 vizitateint 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 coziiwhile(p)

{nr=p->nd; if(viz[nr]==0) //numai daca nu a fost vizitat

Page 12: Grafuri neorientatemmj

{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);

Page 13: Grafuri neorientatemmj

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 coziiwhile(p)

{nr=p->nd; if(viz[nr]==0)//numai daca nu a fost vizitat {ultim++;//maresc coada

C[ultim]=nr;//il adaug in coadaviz[nr]=1;};//il marchez ca fiind vizitat

p=p->next;}prim++; //avansez la urmatorul nod din coadabfr_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 x1, se exploreaza primul nod adiacent lui fie acesta x2 , se trece apoi la primul nod adiacent cu x2 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)

Page 14: Grafuri neorientatemmj

5

1

2

4 3

6

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

5

1

2

4 3

6

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)

5

1

2

4 3

6

se obtine 1,2,3

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

5

1

2

4 3

6

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 .

Page 15: Grafuri neorientatemmj

5

1

2

4 3

6

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);

}

Page 16: Grafuri neorientatemmj

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) n x n cu:

1, daca exista lant de la i la j

Page 17: Grafuri neorientatemmj

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:

5

1

2

4 3

6 7 0 1 1 1 1 0 01 0 1 1 1 0 01 1 0 1 1 0 01 1 1 0 1 0 01 1 1 1 0 0 00 0 0 0 0 0 10 0 0 0 0 1 0

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 .

5

1

2

4 3

6 7 0 1 1 1 1 0 01 0 1 1 1 0 01 1 0 1 1 0 01 1 1 0 1 0 01 1 1 1 0 0 00 0 0 0 0 0 10 0 0 0 0 1 0

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)

Page 18: Grafuri neorientatemmj

dfmr(k); }

void main(){int x,y,j;fstream f;clrscr();f.open("muchii.txt",ios::in); //citire matrice de adiacenta din fisierif(f)cout<<"ok!"<<endl;elsecout<<"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 0dfmr(nod);viz[nod]=0;for(j=1;j<=n;j++) //incarcare vector viz in matrice drumdrum[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 V1 exista un lant de la x la y (implicit si de la y la x)

- nu exista alt subgraf al lui G, G2=(V2, E2) care sa indeplineasca prima conditie si care sa-l contina pe G1

Page 19: Grafuri neorientatemmj

5

1

2

4 3

6 7 Graful alaturat are doua componente conexe:

- subgraful care contine nodurile:1 2 3 4 5

- subgraful care contine nodurile:6 7

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 conexeGraful urmator este conex:

5

1

2

4 3

6 7

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 neorientatIndicatii :

-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

Page 20: Grafuri neorientatemmj

-Se afiseaza componentele cu numarul componentei conexe egal cu 2: 5, 7

6

1

2

4 3

5 7

-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;

Page 21: Grafuri neorientatemmj

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++)

Page 22: Grafuri neorientatemmj

{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) n x n cu:

0, daca i=jp[i,j]= ci,j, daca [i,j]ÎE¥ altfel

2 5

8 6 1

5

1

2

4 3

0 2 ¥ 5 ¥2 0 6 ¥ ¥¥ 6 0 1 85 ¥ 1 0 ¥¥ ¥ 8 ¥ 0

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:

551 2 21 4 52 3 63 4 13 5 8

Problema: Sa se genereze matricea ponderilor pentru un graf citit (n noduri, m muchii si m triplete). Indicatii:

Page 23: Grafuri neorientatemmj

- 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:

2 10

8 3 1

5

1

2

4 3

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:

2 10

8 3 1

5

1

2

4 3

0 2 pinf 10 pinf

2 0 3 pinf pinf

pinf 3 0 1 8

10 pinf 1 0 pinf

pinf pinf 8 pinf 0

Unde pinf reprezinta plus infinit

Page 24: Grafuri neorientatemmj

-se incearca pentru oricare pereche de noduri i,j sa se obtina “drumuri” mai scurte prin noduri intermediare k (kÎ1…n).

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:

2 10

8 3 1

5

1

2

4 3

0 2 5 6 13

2 0 3 4 11

5 3 0 1 8

6 4 1 0 9

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):

Page 25: Grafuri neorientatemmj

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;

Page 26: Grafuri neorientatemmj

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;

Page 27: Grafuri neorientatemmj

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;

Page 28: Grafuri neorientatemmj

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 conţine toate nodurile unui graf

5

1

2

4 3

6

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

Ciclu hamiltonian = un ciclu elementar care conţine toate nodurile grafului

Page 29: Grafuri neorientatemmj

5

1

2

4 3

6

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

Graf hamiltonian = un graf G care conţine 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,2…k-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;

Page 30: Grafuri neorientatemmj

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;}

Page 31: Grafuri neorientatemmj

}

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;

Page 32: Grafuri neorientatemmj

}

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 conţine toate muchiile unui graf

5

1

2

4 3

6

Lantul: L=[1.2.3.4.5.3.6.2.5.6] este lant eulerian

Ciclu eulerian = un ciclu simplu care conţine toate muchiile grafuluiCiclul: C=[1.2.3.4.5.3.6.2.5.6.1] este ciclu eulerian

Graf eulerian = un graf care conţine un ciclu eulerian.

Condiţie necesară şi suficientă: Un graf este eulerian dacă şi numai dacă oricare vârf al său 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

Page 33: Grafuri neorientatemmj

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 odatao 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 primaif(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];

Page 34: Grafuri neorientatemmj

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 fisierf.open("matsim.txt",ios::in);if(f)cout<<"ok";elsecout<<"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 matricefor( 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 pornestecin>>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

Page 35: Grafuri neorientatemmj

{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 :

4

1

3

6 5

2

4

1

3

6 5

2

Page 36: Grafuri neorientatemmj

- 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()

4

1

3

6 5

2

4

1

3

6 5

2

Page 37: Grafuri neorientatemmj

{ int x,y;fstream f; //memorare graf in matrice de adiacentaf.open("euler.txt",ios::in);if(f)cout<<"ok";elsecout<<"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;}

Page 38: Grafuri neorientatemmj

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();}