Documente Academic
Documente Profesional
Documente Cultură
A) GRAFURI NEORIENTATE
Observaţii:
2 5
Se notează cu n numarul de nodurin=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;
1
Structuri de memorare a grafurilor:
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
31,2,4
componente, pe
4 3,5 care il vom numi
51,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
3
• Rezultă ca nodurile adiacente ale lui 3 sunt 4, 2, 1 Stop am dat de 0
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++)
{ 31,2,4
4 3,5
4 51,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
Struct muchie
{
int x,y;
};
muchie v[50];
1 1
2 5 2 5
3 3
4 4
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
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
6
Programul de parcurgere BF (lăţime) a unui graf memorat prin liste de
adiacenţă
#include <conio.h>
#include<iostream.h>
f.close();
}
7
}
}
void main( )
{
citire_muchii(“graf.txt”, t, start, n);
bf(6);
getch( );
}
#include <conio.h>
#include<iostream.h>
f.close();
}
void main( )
{
Citire_LA(“graf.txt”, T, Start, n);
df(1);
getch( );
}
#include <conio.h>
#include<iostream.h>
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
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
#include <conio.h>
#include<iostream.h>
11
fstream f(nume_fiş,ios :: in);
f>>n;
while (f>>i>>j)
a[i][j]=a[j][i]=1;
f.close();
}
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( );
}
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
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>
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>
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 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( );
}
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
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
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
21