Sunteți pe pagina 1din 9

Ministerul Educaţiei, Culturii și Cercetării

al Republicii Moldova
Universitatea Tehnică a Moldovei
Facultatea”Calculatoare, Informatică și Microelectronică”
Departamentul Ingineria Software și Automatică

RAPORT
Lucrarea de laborator nr.2
la disciplina: Matematici speciale
Tema: Parcurgerea grafului în adâncime și lărgime

A efectuat: Onisim Ariadna


St. gr.TI-191 F/R

A verificat: Lisnic Inga


dr., conf.univ.

Chişinău, 2020
Lucrarea de laborator nr.2
Tema: Parcurgerea grafului în adâncime și lărgime
Scopul lucrării: Studierea procedurii parcurgerii grafului în adâncime și lărgime.Studierea
algoritmilor de căutare în graf şi a diferitor forme de păstrare şi prelucrare a datelor.

Note teoretice :
Parcurgerea grafurilor presupune examinarea în vederea prelucrării tuturor vârfurilor
acelui graf într-o anumită ordine, ordine care să permită prelucrarea optimă a informaţiilor
ataşate grafului. În acest scop s-au dezvoltat două tehnici fundamentale de traversare a grafurilor,
una bazată pe căutarea în adâncime, cealaltă bazată pe căutarea prin cuprindere. Ambele tehnici
constituie nuclee de bază pornind de la care se pot dezvolta numeroşi algoritmi eficienţi de
prelucrare a grafurilor.
Parcurgerea în lăţime a fost descoperită de către Moore în contextul căutării de drumuri
în labirinturi. Lee a descoperit, în mod independent, acelaşi algoritm în contextul stabilirii firelor
de pe plăcile de circuite. Hopcroft şi Tarjan au argumentat folosirea reprezentării prin liste de
adiacenţă în defavoarea reprezentării prin matrice de adiacenţă, pentru grafurile rare, şi au fost
primii care au recunoscut importanţa algoritmică a parcurgerii în adâncime. Parcurgerea în
adâncime a fost folosită pe scară largă începând cu anul sfârşitul anului 1950, în special în
programele din domeniul inteligenţei artificiale. Tarjan este cel care a elaborat un algoritm liniar
pentru determinarea componentelor tare conexe, iar Knuth a fost primul care a dat un algoritm
liniar pentru sortarea topologică.
Căutarea prin cuprindere sau traversarea grafurilor în lăţime este unul dintre cei mai simpli
algoritmi de căutare într-un graf şi arhetipul pentru mulţi algoritmi de grafuri importanţi.
Algoritmul lui Dijkstra pentru determinarea drumurilor minime de la un nod sursă la toate
celelalte şi algoritmul lui Prim pentru determinarea arborelui parţial de cost minim folosesc idei
similare din algoritmul de căutare în lăţime
Această metodă se bazează pe următoarea tehnică:
- fie un graf G = (X,U) cu n noduri şi un nod de plecare ns numit şi nod sursă
- căutarea în lăţime explorează sistematic muchiile grafului G pentru a "descoperi" fiecare nod
accesibil din ns. Algoritmul calculează distanţa (cel mai mic număr de muchii) de la ns la toate
vârfurile accesibile lui. El produce un "arbore de lăţime" cu rădăcina în ns, care conţine toate
nodurile accesibile. Pentru fiecare nod v accesibil din ns, calea din arborele de lăţime de la ns la
v corespunde "celui mai scurt drum" de la ns la v, adică conţine un număr minim de muchii.
Traversarea grafurilor în lăţime sau Breadth-First este numită astfel pentru că lărgeşte, uniform,
frontiera dintre nodurile descoperite şi cele nedescoperite, pe lăţimea frontierei. Aceasta

1
înseamnă că algoritmul descoperă toate vârfurile aflate la distanţa k faţă de ns înainte de a
descoperi vreun vârf la distanţa k+1. Cu alte cuvinte traversarea în lăţime a grafurilor presupune
faptul că după vizitarea unui anumit nod v, sunt parcurşi toţi vecinii nevizitaţi ai acestuia, apoi
toţi vecinii nevizitaţi ai acestora din urmă până la vizitarea tuturor nodurilor grafului(spunem că
două noduri sunt vecine dacă sunt adiacente).
Implementarea acestei metode se face folosind o structură de date de tip coadă. Cozile sunt
structuri de date în care elementele sunt inserate la un capăt (sfârşitul cozii) şi sunt suprimate de
la celălalt capăt (începutul cozii). Ele implementează politica "primul venit - primul servit".
Asupra unei cozi acţionează operatori specifici cum ar fi: iniţializare coadă, test de coadă vidă,
adăugă un element la sfârşitul cozii, scoate un element de la începutul cozii. Cozile pot fi
implementate static(cu variabile de tip tablou unidimensional) sau dinamic.

Algoritm de parcurgere in latime a unei singure componente conexe:


1. citirea datelor de intrare(număr de noduri si muchiile grafului) şi construirea matricei de
adiacenţă
2. afisarea pe ecran a matricei de adiacenta
3. citirea/determinarea unui nod de start
4. marcarea nodului de start ca fiind vizitat: v[ns]=1
5. afisarea nodului de start
6. adaug la coadă în prima pozitie nodul de start:
 prim=1; //poziţia primului nod din coadă
 ultim=1; // poziţia ultimul nod aşezat la coada
 c[ultim]=ns; //adăugarea nodului de start la coada
7. cât timp coada nu este vidă execute- determină TOATE nodurile adiacente cu primul nod
din coadă şi nevizitate, iar pentru fiecare nod astfel găsit efectuează următoarele operaţii:

 marchează-l vizitat
 afişează-l
 adaugă-l la coada
 elimină primul nod din coada

Parcurgerea in adâncime. Începe cu un vârf inițial, denumit varf de start. Se viziteaza


mai intai varful de start. Se viziteaza apoi primul vecin nevizitat al varfului de start. Varful y este
considerat vecin al varfului x daca exista muchia [x,y].  Se viziteaza in continuare primul vecin
nevizitat al primului vecin al varfului de start, si asa mai departe, mergand in adancime pana
cand ajungem intr-un varf care nu mai are vecini nevizitati. Cand ajungem intr-un astfel de varf,
revenim la varful sau parinte (varful din care acest nod a fost vizitat). Daca acest varf nu mai are
vecini nevizitati, alegem primul vecin nevizitat al sau si continuam parcurgerea in acelasi mod.

2
Daca nici acest varf nu mai are vecini nevizitati, revenim in varful sau parinte si continuam in
acelasi mod, pana cand toate varfurile accesibile din varful de start sunt vizitate.

Reprezentarea informatiilor:

1. Graful va fi reprezentat prin matricea de adiacenta;


2. Pentru a retine care varfuri au fost deja vizitate in timpul parcurgerii vom utiliza un
vector viz cu n componente din multimea {0,1}, cu semnificatia:

 viz[i]=1 daca varful i a fost deja vizitat


 viz[i]=0 in caz contrar.

    Consideram ca variabilele n (numarul de varfuri din graf), a (ma

tricea de adiacenta) si vectorul viz sunt globale.

Sarcina de bază :

 De executat un arbore;
 De calculate parcurgerea în lățime.

GRAF-UL:

PL=1,3,4,6,2,5,7,8
PA=2,1,6,5,7,8,3,4

Listingul programului :
1) Parcurgerea în lățime
#include<stdio.h>
#include<stdlib.h>
#define MAX 100
#define initial 1
#define waiting 2
#define visited 3
int n;
int adj[MAX][MAX];
int state[MAX];

3
void cream_graph();
void BF_lungime();
void BFS(int v);
int queue[MAX], front = -1,rear = -1;
void insert_queue(int vertex);
int delete_queue();
int isEmpty_queue();

int main()
{
cream_graph();
BF_lungime();
return 0;
}
void BF_lungime()
{
int v;

for(v=0; v<n; v++)


state[v] = initial;

printf("Introduce-ți pornirea: \n");


scanf("%d", &v);
BFS(v);
}
void BFS(int v)
{
int i;

insert_queue(v);
state[v] = waiting;

while(!isEmpty_queue())
{
v = delete_queue( );
printf("%d ",v);
state[v] = visited;

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


{
if(adj[v][i] == 1 && state[i] == initial)
{
insert_queue(i);
state[i] = waiting;
}
}
}
printf("\n");
}void insert_queue(int vertex)
{
if(rear == MAX-1)
printf("Queue Overflow\n");
else
4
{
if(front == -1)
front = 0;
rear = rear+1;
queue[rear] = vertex ;
}
}

int isEmpty_queue()
{
if(front == -1 || front > rear)
return 1;
else
return 0;
}
int delete_queue()
{
int delete_item;
if(front == -1 || front > rear)
{
printf("Queue Underflow\n");
exit(1);
}

delete_item = queue[front];
front = front+1;
return delete_item;
}

void cream_graph()
{
int count,max_edge,origin,destin;

printf("Scriem numarul de varfuri : ");


scanf("%d",&n);
max_edge = n*(n-1);

for(count=1; count<=max_edge; count++)


{
printf("Intra pe margine %d( -1 -1 renunta ) : ",count);
scanf("%d %d",&origin,&destin);

if((origin == -1) && (destin == -1))


break;

if(origin>=n || destin>=n || origin<0 || destin<0)


{
printf("Invalid edge!\n");
count--;
}
else
{
adj[origin][destin] = 1;
}
5
}}
Rezultatul :

2) Parcurgerea în adâncime:

#include<stdio.h>

void DFS(int);
int G[9][9],visited[9],n;

void main()
{
int i,j;
printf("Introduceți numarul de vârfuri:");

6
scanf("%d",&n);

// Citește matricea de adiacență


printf("\nIntroduceși matricea de adiacență:");

for(i=0;i<n;i++)
for(j=0;j<n;j++)
scanf("%d",&G[i][j]);

for(i=0;i<n;i++)
visited[i]=0;

DFS(0);
}

void DFS(int i)
{
int j;
printf("\n%d",i);
visited[i]=1;
for(j=0;j<n;j++)
if(!visited[j]&&G[i][j]==1)
DFS(j);
}

Rezultatul:

7
Concluzie:
În această lucrare am studiat procedura de parcurgere al grafului în adâncime și lărgime,
am studiat algoritmii de căutare în graf şi a diferitor forme de păstrare şi prelucrare a datelor. Am
alcătuit un graf și listingul programului prin intermediul limbaului de programare C , pentru a
afla parcurgerea în adâncime și în lățime.

Referințe bibliografice:
1. ”Matematica Discretă” V. Beșliu
2. ”Matematica Discretă în inginerie”
3. ”Teoria probabilității și Informații în sisteme de prelegeri ”
4. Conspectul prelegerilor cursului Matematica discretă pentru studenți gr. TI-191 F/R. Lector:
dr., conf. univ. Ceban Gh., mun. Chișinău, UTM, 2019-2020.
5. https://www.onlinegdb.com/online_c_compiler

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