Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul...

199
Introducere ˆ In informatic˘ a, datele reprezint˘ a modalit˘ at ¸i de a reprezenta ˆ ın memoria calculatorului sau pe suport extern a informat ¸iilor furnizate de problema supus˘ a rezolv˘ arii. Dup˘ a modul ˆ ın care sunt organizate, datele se ˆ ımpart ˆ ın: date elementare – simple, indivizibile, cum sunt datele numerice sau pointerii; date structurate - grupate, cum sunt tablourile, listele, fi¸ sierele. Gruparea unor date sub un singur nume a fost necesar˘ ınc˘ a de la ˆ ınceputurile program˘ arii calculatoa- relor pentru a facilita lucrul cu acestea (sort˘ ari, c˘ aut˘ ari ˆ ın colect ¸ii de date). Studiul structurilor de date se ocup˘ a cu identificarea modalit˘ at ¸ilor de memorare ¸ si de accesare a datelor, astfel ˆ ıncˆ at acestea s˘ a fie folosite ˆ ın mod eficient. Datele structurate sau structurile de date cont ¸in mai multe date de acela¸ si tip sau de tipuri diferite, grupate sub un singur nume. A¸ sadar, o structur˘ a de date poate fi omogen˘a, dac˘ a toate elementele sale componenete au acela¸ si tip (tablourile); neomogen˘a, dac˘ a elementele componenete ale structurii au tipuri diferite (structuri, ˆ ınregistr˘ ari). O structur˘ a de date poate fi privit˘ a din punctul de vedere al definit ¸iei acesteia – elementele posibile, leg˘ aturile dintre acestea ¸ si operat ¸iile posibile sunt specificate f˘ ar˘ a detalii de reprezentare/implementare (structur˘alogic˘a ); din punctul de vedere al stoc˘ arii acesteia ˆ ın memorie (structur˘afizic˘a ). Dup˘ a num˘ arul elementelor unei structuri de date, aceasta poate fi static˘a, atunci cˆ and num˘ arul de componente este fixat la compilare; dinamic˘a, atunci cˆ and num˘ arul de componente este variabil (poate fi modificat ˆ ın timpul execut ¸iei programului). a observ˘ am faptul c˘ a o structur˘ a de date logic˘ a ar putea fi implementat˘ a atˆ at ca structur˘ a static˘ a, cˆ at ¸ si ca structur˘ a dinamic˘ a (stive, cozi etc.). De asemenea, o structur˘ a de date poate fi: explicit˘a, dac˘ a pe lˆ ang˘ a componentele structurii se memoreaz˘ si alte date suplimentare prin care se specific˘ a relat ¸ia de ordine a componentelor; implicit˘aın caz contrar. De exemplu, structura de date de tip tablou este o structur˘ a implicit˘ a de date, iar structura de date de tip list˘ a este o structur˘ a explicit˘ a de date. Dup˘ a modul cum se realizeaz˘ a accesul la elementele unei structuri, aceasta poate fi: 1

Transcript of Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul...

Page 1: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Introducere

In informatica, datele reprezinta modalitati de a reprezenta ın memoria calculatorului sau pe suportextern a informatiilor furnizate de problema supusa rezolvarii. Dupa modul ın care sunt organizate,datele se ımpart ın:

• date elementare – simple, indivizibile, cum sunt datele numerice sau pointerii;

• date structurate - grupate, cum sunt tablourile, listele, fisierele.

Gruparea unor date sub un singur nume a fost necesara ınca de la ınceputurile programarii calculatoa-relor pentru a facilita lucrul cu acestea (sortari, cautari ın colectii de date). Studiul structurilor de datese ocupa cu identificarea modalitatilor de memorare si de accesare a datelor, astfel ıncat acestea sa fiefolosite ın mod eficient.

Datele structurate sau structurile de date contin mai multe date de acelasi tip sau de tipuri diferite,grupate sub un singur nume. Asadar, o structura de date poate fi

• omogena, daca toate elementele sale componenete au acelasi tip (tablourile);

• neomogena, daca elementele componenete ale structurii au tipuri diferite (structuri, ınregistrari).

O structura de date poate fi privita

• din punctul de vedere al definitiei acesteia – elementele posibile, legaturile dintre acestea sioperatiile posibile sunt specificate fara detalii de reprezentare/implementare (structura logica);

• din punctul de vedere al stocarii acesteia ın memorie (structura fizica).

Dupa numarul elementelor unei structuri de date, aceasta poate fi

• statica, atunci cand numarul de componente este fixat la compilare;

• dinamica, atunci cand numarul de componente este variabil (poate fi modificat ın timpul executieiprogramului).

Sa observam faptul ca o structura de date logica ar putea fi implementata atat ca structura statica, catsi ca structura dinamica (stive, cozi etc.).De asemenea, o structura de date poate fi:

• explicita, daca pe langa componentele structurii se memoreaza si alte date suplimentare prin carese specifica relatia de ordine a componentelor;

• implicita, ın caz contrar.

De exemplu, structura de date de tip tablou este o structura implicita de date, iar structura de date detip lista este o structura explicita de date.Dupa modul cum se realizeaza accesul la elementele unei structuri, aceasta poate fi:

1

Page 2: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

• cu acces direct, daca accesul la o anumita componenta a acesteia se poate face fara sa tinem seamade celelalte componente;

• cu acces secvential, daca accesul la o componenta a structurii de date se poate face numai tinandcont de alte campuri ale structurii, printr-un proces de parcurgere a elementelor componente.

Dupa relatiile existente ıntre elementele unei structuri de date, aceasta poate fi

• liniara – fiecare element, diferit de ultimul, are un singur succesor si fiecare element, diferit deprimul, un singur predecesor (tablouri, liste liniare);

• neliniara – un element poate avea mai multi succesori si mai multi predecesori (grafuri) sau un ele-ment poate avea mai multi succesori, dar un singur predecesor (arbori) (structura arborescenta)).

Principalele operatii ce se pot efectua asupra unei structuri de date sunt: crearea structurii de date,consultarea acesteia (consta ın accesarea elementelor structurii ın vederea prelucrarii acestora – parcur-gere, cautare, sortare), actualizarea structurii de date (consta ın adaugarea de noi elemente, eliminareaunor elemente care nu mai sunt necesare sau modificarea valorilor unor componente ale structurii),interclasare, concatenare, copiere.

Structurile de date de interes pentru acest curs sunt tablourile, listele liniare (cazuri particulare: stive,cozi) si cele circulare, grafurile, arborii si tabelele de dispersie.

2

Page 3: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Capitolul 1

Alocarea dinamica a memoriei. Tipurispecifice

1.1 Operatorul adresa (“&”)

In limbajele C/C++ (si nu numai), orice variabila simpla, tablou, functie sau alte obiecte declarate,ocupa o zona de memorie numita si locatie de memorie. Adresa unui obiect este adresa de ınceput a zoneide memorie alocata obiectului la declarare. Operatorul unar adresa (“&”) ıntoarce adresa operanduluisau (operandul fiind un obiect deja declarat). In exemplul de mai jos, i este o variabila de tip int,careia, la declarare, i s-a alocat un spatiu de memorie capabil sa memoreze o data de acest tip. Expresia&i va returna adresa primului octet al zonei alocate.

1 #include <iostream >

2 using namespace std;

3 int main()

4 {

5 int i;

6 cout <<"Adresa lui i, &i: "<<&i<<endl;

7 return 0;

8 }

Operatorul adresa functioneaza la fel si pentru celelalte tipuri de date predefinite sau pentru date definitede utilizator.

1.2 Pointeri. Operatorul tinta (“*”)

1.2.1 Declararea pointerilor

Un pointer este o variabila care retine adresa unei zone de memorie. Pointerii permit accesarea indirectaa altor obiecte, adica accesarea acestora prin intermediul adreselor lor. O variabila de tip pointer este ovariabila simpla, spatiul ocupat de aceasta fiind doar cel necesar pentru memorarea unei adrese. Trebuiesa facem distinctie ıntre:

– adresa variabilei de tip pointer (pointerul fiind o variabila ca oricare alta, la declarare i se alocao zona de memorie capabila sa pastreze o adresa);

– adresa continuta de pointer (aceasta este valoarea pointerului);– continutul de la adresa retinuta de pointer (valoarea variabilei (obiectului) a carei adresa este

retinuta de pointer).

3

Page 4: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a caror adresa o contin),chiar daca valoarea tuturor pointerilor este de acelasi tip (o adresa). Variabila la care face referire unpointer mai poarta numele de tinta. Sintaxa prin care se declara un pointer este

1 tip *nume_pointer;

dar si urmatoarele sintaxe sunt la fel de corecte, pozitia simbolului “*” fiind variabila,

1 tip * nume_pointer;

sau

1 tip* nume_pointer;

unde nume pointer este numele variabilei de tip pointer, tip este tipul tintei, iar simbolul “*” indicafaptul ca este declarat un pointer. In acest caz vom spune ca am declarat un “pointer catre tip”. Candse declara mai multi pointeri de acelasi tip ıntr-o aceeasi declaratie se foloseste sintaxa

1 tip *nume_pointer1 , *nume_pointer2 , *nume_pointer3;

Printr-o declaratie de tipul

1 tip *nume_pointer1 , nume_pointer2 , nume_pointer3;

nu vor fi declarati trei pointeri, ci doar unul si anume nume pointer1, celelalte doua variabile fiind detipul tip (nume pointer2 si nume pointer3). Declararea unui pointer catre un tip compus (tablou,functie) sau catre un tip definit de utilizator se face la fel ca ın cazul pointerilor catre tipurile simple:se declara tipul tintei, tinand cont de prioritatea operatorilor utilizati (de exemplu, operatorii “.” –operatorul de selectie, “ ( )” – operatorul de apelare a unei functii si “[ ]” – operatorul de indexare,au nivelul de prioritate mai ınalt decat operatorul unar “*”) si se precizeaza ca este vorba despre ovariabila de tip pointer folosind simbolul corespunzator. De exemplu, un pointer catre un tablou de 10ıntregi se declara astfel

1 int (*p)[10];

deoarece o declaratie de forma

1 int *p[10];

are ca efect declararea unui tablou de 10 pointeri catre tipul int.

1.2.2 Initializarea pointerilor

Dupa ce au fost declarati, pointerii nu pot fi utilizati fara a fi initializati. Utilizarea unui pointerdeclarat, dar neinitializat conduce la erori de executie. Initializarea unui pointer se poate face prin unadintre urmatoarele trei metode:

1. prin atribuirea adresei unei variabile deja definite. De exemplu, daca x este o variabila de tip int,adresa sa o vom retine ıntr-un pointer catre int.

1 #include <iostream >

2 using namespace std;

3 int main()

4 {

5 int x;

6 int *px;// variabila px este un pointer catre int

7 px = &x;

4

Page 5: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

8 //sau int *px = &x;

9 x = 5;

10 cout <<"Valoarea lui x: "<<x<<endl; // 5

11 cout <<"Adresa lui px , &px: "<<&px <<endl;

12 cout <<"Valoarea lui px: "<<px <<endl;

13 cout <<"Adresa lui x, &x: "<<&x<<endl;

14 //&x trebuie sa coincida cu valoarea lui px

15 return 0;

16 }

2. prin atribuirea valorii unui pointer deja initializat. Completam exemplul precedent cu urmatoarelelinii de cod:

1 int *qx;

2 qx = px;

3 cout <<"Valoarea lui px: "<<px <<endl;

4 cout <<"Valoarea lui qx: "<<qx <<endl;

Sunt permise initializari cu pointerul null. Acesta este un caz special prin care aratam faptulca pointerul nu indica spre niciun obiect si ca valoarea sa este adresa 0, aceasta nefiind adresaniciunei locatii de memorie. Modalitatea de initializare cu pointerul null este

1 tip *q;

2 // variabila q este un pointer catre un anumit tip de data

3 q = NULL; //in C (de evitat)

4 // echivalenta cu initializarea

5 q = 0;

6 //de preferat este initializarea cu pointerul nullptr

7 q = nullptr;

3. prin atribuirea unei adrese de memorie rezultata ca urmare a folosirii functiilor de alocare dinamicaa memoriei sau a operatorului new (pentru detalii, a se vedea sectiunea 1.3).

Operatorul unar tinta (“*”), numit si operator de dereferentiere, aplicat unui pointer, returneazacontinutul pastrat la adresa memorata de pointer. Reluam exemplul precedent

1 #include <iostream >

2 using namespace std;

3 int main()

4 {

5 int x = 5;

6 int *px = &x;

7 cout <<"Valoarea lui x: "<<x<<" "<<*px <<endl; // 5 5

8 cout <<"Adresa lui x, &x: "<<&x<<" "<<px <<endl;

9 cout <<"Adresa lui px , &px: "<<&px <<endl;

10 return 0;

11 }

Un pointer poate fi reinitializat (cu adresa altui obiect), putand fi astfel refolosit. In acest sens trebuierespectat tipul pointerului. Completam codul anterior:

1 int y = 10;

2 px = &y;

5

Page 6: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

3 cout <<"Valoarea lui y: "<<y<<" "<<*px <<endl; // 10 10

4 cout <<"Adresa lui y, &y: "<<&y<<" "<<px <<endl;

5 cout <<"Adresa lui px , &px: "<<&px <<endl;

6 // adresa lui px este aceeasi de mai sus

Pentru ca un pointer este o variabila careia i se aloca un spatiu de memorie la declarare, adresa deınceput a acestei zone de memorie poate fi atribuita unui pointer. Vom declara ın acest caz un pointer“dublu” prin sintaxa:

1 tip ** nume_pointer;

unde tip* este tipul tintei, adica a pointerului a carui adresa este retinuta de pointerul dublu. Opera-torul de dereferentiere poate fi aplicat ın mod repetat, spre deosebire de operatorul adresa.

1 #include <iostream >

2 using namespace std;

3 int main()

4 {

5 int x = 5;

6 int *px = &x;

7 int **ppx = &px;

8 cout <<"Valoarea lui x: "<<x<<" "<<*px <<" " <<**ppx <<endl;

9 // 5 5 5

10 cout <<"Adresa lui x, &x: "<<&x<<" "<<px <<endl;

11 cout <<"Adresa lui px , &px: "<<&px <<" "<<ppx <<endl;

12 cout <<"Adresa lui ppx , &ppx: "<<&ppx <<endl;

13 return 0;

14 }

Reamintim ca ın C/C++, prin definirea unei structuri (folosind cuvantul cheie struct) se grupeaza maimulte date de tipuri diferite sub acelasi nume. Astfel, programatorul defineste noi tipuri de date, pecare apoi le utilizeaza ca pe orice alt tip predefinit. Referirea la un element (membru) al unei structuridefinite se realizeaza folosind operatorul de selectie “.” ın conjunctie cu variabila corespunzatoare.Daca variabila de tip nou definit este de tip pointer catre structura, se utilizeaza de regula operatorulde accesare indirecta, “− >”.

1 #include <iostream >

2 using namespace std;

3 struct COMPLEX

4 {

5 double re , im;

6 };

7 int main()

8 {

9 COMPLEX c = {5, 6};

10 COMPLEX *pc;

11 pc = &c;

12 cout <<c.re<<endl; //5

13 cout <<pc->re<<endl; //5

14 // echivalent cu (*pc).re

15 // parantezele () sunt obligatorii

16 return 0;

6

Page 7: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

17 }

1.2.3 Aritmetica pointerilor

Consideram pointerul p care tinteste catre o variabila de tipul tip – predefinit sau definit de utilizator.Operatiile permise ıntre pointeri sunt:

– incrementare/decrementare: ++p, p++, --p, p--. Aceste operatii au aceeasi interpretare caın cazul variabilelor cu valori numerice, pasul incrementarii sau al decrementarii fiind egal cusizeof(tip);

1 #include <iostream >

2 using namespace std;

3 int main()

4 {

5 double x = 5.5;

6 double *p = &x;

7 cout <<"p: "<<p<<endl;//0096 FDD0

8 p++;

9 cout <<"p: "<<p<<endl;//0096 FDD8

10 return 0;

11 }

– suma/diferenta dintre un pointer si o variabila de tip int. Daca i este o variabila de tip int,expresiile p + i, i + p determina marirea valorii lui p cu i * sizeof(tip), iar p - i are carezultat micsorarea valorii lui p cu i * sizeof(tip);

– diferenta a doi pointeri. Scazand doi pointeri de acelasi tip, rezultatul este de tip int, reprezentandnumarul de elemente de tipul tip ce pot fi pastrate ın memorie ıntre cele doua adrese;

– compararea a doi pointeri. Intre doi pointeri de acelasi tip pot fi folositi operatorii relationali<,<=, >,>=,==, ! =.

Nu sunt permise atribuiri ıntre pointeri de tipuri diferite. Pot fi fortate astfel de atribuiri, conversiileıntre pointeri fiind permise doar folosind operatorul cast. De exemplu

1 #include <iostream >

2 using namespace std;

3 int main()

4 {

5 int x = 5;

6 // double *px = &x; // eroare de conversie

7 double *px = (double *)&x;

8 cout <<"Continutul de la adresa retinuta in px: " <<*(int*)px <<endl;

9 return 0;

10 }

Se observa ca pointerului px, un pointer catre tipul double, i-a fost atribuita adresa unei date ıntregix printr-o expresie de tip cast. Pentru a accesa zona de memorie referita de px se realizeaza tot oconversie explicita catre tipul int*, aplicandu-se apoi ın mod obisnuit operatorul tinta. Pointerii catretipul void reprezinta exceptii, ın sensul ca li se pot atribui adresele unor date de orice tip. Astfel, numai sunt necesare conversii explicite la initializare, folosind operatorul cast, dar sunt obligatorii pentruaccesul la obiectul referit.

7

Page 8: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

1 #include <iostream >

2 using namespace std;

3 int main()

4 {

5 int x = 5;

6 void *px = &x;

7 cout <<"Continutul de la adresa retinuta in px: " <<*(int*)px <<endl;

8 return 0;

9 }

In mod obisnuit nu sunt acceptate atribuiri ıntre pointeri si variabile de tipuri numerice. Insa, un tipde conversie permis folosind operatorul cast este conversia ıntre pointeri si ıntregi. Singura atribuirepermisa ın acest caz fara conversie explicita este

1 tip* p = 0;

(a se vedea initializarea pointerilor cu pointerul null, sectiunea 1.2.2).

1.2.4 Pointeri si modificatorul const

Reamintim ca modificatorul const este folosit pentru ca valoarea unei variabile sa nu poata fi modificataın program. Astfel, la declarea variabilei utilizam cuvantul cheie const ın dreapta tipului variabilei. Incazul variabilelor numerice, modificatorul const poate fi plasat si ınaintea tipului de date. Initializareaunei astfel de variabile se face obligatoriu la declarare.

1 //int const x;

2 //x = 5; // eroare

3 int const x = 5;

4 const int y = 6;

In ceea ce priveste pointerii, trebuie facuta distinctia ıntre un pointer catre un tip constant (la carevaloarea obiectului referit nu poate fi modificata), un pointer constant (care nu poate sa-si modificevaloarea, adica nu poate referi catre alt obiect) sau un pointer constant catre un tip constant.

1 #include <iostream >

2 using namespace std;

3 int main()

4 {

5 int const x = 5, y = 6;

6 int const * px = &x; // pointer catre tip constant

7 cout <<*px<<endl; //5

8 // *px = 7;

9 // eroare: valoarea tintei nu poate fi modificata

10 px = &y; // px isi poate modifica valoarea

11 cout <<*px<<endl; //6

12 int a = 7, b = 8;

13 int * const pc = &a; // pointer constant

14 cout <<*pc<<endl; //7

15 //pc = &b ;

16 // eroare: valoarea pointerului nu poate fi modificata

17 *pc = 9; // valoarea tintei poate fi modificata

18 cout <<a<<endl; //9

8

Page 9: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

19 // pointer constant catre tip constant

20 int const * const pcx = &x;

21 cout <<*pcx <<endl; //5

22 // pcx = &y;

23 // eroare: valoarea pointerului nu poate fi modificata

24 // *pcx = 10;

25 // eroare: valoarea tintei nu poate fi modificatat

26 return 0;

27 }

1.2.5 Pointeri si tablouri

Operatiile cu pointeri sunt utilizate ın special pentru lucrul cu tablouri (sau alte structuri de date).Numele unui tablou este o constanta de tip pointer care are ca valoare adresa primului element altabloului.Daca se declara un tablou unidimensional tab de tipul tip si de o anumita dimensiune maxima, atuncivaloarea lui tab este &tab[0]. Folosind aritmetica pointerilor, tab+1 este &tab[1] s.a.m.d., tab+i este&tab[i]. Daca dimensiunea efectiva a tabloului este n, atunci tab+n-1 este &tab[n-1], ultimul elemental tabloului. Aplicand operatorul tinta, *tab este chiar primul element al tabloului, tab[0], *(tab+1)este tab[1], . . . , *(tab+i) este tab[i], . . . , *(tab+n-1) este ultimul element al tabloului, tab[n-1].

Exemplul 1. Parcurgerea tablourilor unidimensionale (vectorilor).

1 #include <iostream >

2 using namespace std;

3 #define dim 30

4 // directiva de preprocesor ce defineste constanta dim

5 int main()

6 {

7 int n, tab[dim];// alocare statica a tabloului

8 cout <<"Dimensiunea tabloului (de la 1 la dim), n: ";

9 cin >>n;

10 while(n <= 0 || n > dim)

11 {

12 cout <<"n: ";

13 cin >>n;

14 }

15 cout <<"Citirea elementelor tabloului."<<endl;

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

17 {

18 cout <<"tab["<<i<<"] = ";

19 cin >>tab[i];

20 }

21 cout <<"Afisarea elementelor tabloului. "<<endl;

22 cout <<"Varianta 1: ";

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

24 cout <<*(tab+i)<<" "; //tab[i]

25 cout <<endl;

26 cout <<"Varianta 2: ";

27 int* const ptc = tab;

9

Page 10: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

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

29 cout <<*(ptc+i)<<" "; //ptc[i]

30 cout <<endl;

31 cout <<"Varianta 3: ";

32 int *pt = tab;

33 // pentru a putea folosi operatorul ++,

34 //pt nu trebuie sa fie constant

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

36 cout <<*pt++<<" ";

37 cout <<endl;

38 cout <<"Varianta 4: ";

39 //cea mai rapida varianta de parcurgere

40 int *p = tab , *endp = tab+n-1;

41 // pointerul p retine adresa primului elem. al tabloului

42 // pointerul endp retine adresa ultimului element al tabloului

43 for(; p <= endp; p++)

44 cout <<*p<<" ";

45 cout <<endl;

46 return 0;

47 }

Declaram un tablou bidimensional mat de tipul tip, precizand cate o valoare maxima pentru fie-care dimensiune (dim1 si dim2). Un tablou bidimensional este un tablou unidimensional de compo-nente tablouri unidimensionale. Asadar, mat este un tablou cu dim1 componente: mat[0], mat[1],

..., mat[dim1-1], fiecare dintre aceste componente fiind un tablou cu dim2 componente (de exem-plu, tabloul mat[i] are componentele mat[i][0], mat[i][1],..., mat[i][dim2-1]). Atunci mat[i]contine adresa lui mat[i][0]. Folosind aritmetica pointerilor, mat[i]+1 contine adresa lui mat[i][1]s.a.m.d., mat[i]+j retine adresa lui mat[i][j]. Daca dimensiunile efective ale tabloului sunt m si n,atunci mat[i]+n-1 contine adresa lui mat[i][n-1], ultimul element al liniei i. Pe de alta parte, matretine adresa tabloului mat[0], mat+1 contine adresa tabloului mat[1] s.a.m.d., mat+i retine adresa luimat[i], mat+m-1 contine adresa lui mat[m-1], ultima linie a tabloului bidimensional. Folosind operato-rul tinta, elementul de pe linia i si coloana j, mat[i][j], se poate accesa prin oricare dintre urmatoareleconstructii (*(mat+i))[j], *(mat[i]+j), *(*(mat+i)+j).

Exemplul 2. Parcurgerea tablourilor bidimensionale (matricelor).

1 #include <iostream >

2 using namespace std;

3 const int dim1 = 30, dim2 = 35;

4 int main()

5 {

6 int m, n, mat[dim1][dim2];// alocare statica

7 cout <<"Dimensiunile tabloului."<<endl;

8 cout <<"Numarul de linii (de la 1 la dim1), m: ";

9 cin >>m;

10 while(m <= 0 || m > dim1)

11 {

12 cout <<"m: ";

13 cin >>m;

14 }

10

Page 11: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

15 cout <<"Numarul de coloane (de la 1 la dim2), n: ";

16 cin >>n;

17 while(n <= 0 || n > dim2)

18 {

19 cout <<"n: ";

20 cin >>n;

21 }

22 cout <<"Citirea elementelor tabloului."<<endl;

23 for(int i = 0; i < m; i++)

24 for(int j = 0; j < n; j++)

25 {

26 cout <<"mat["<<i<<","<<j<<"] = ";

27 cin >>mat[i][j];

28 //sau (*( mat+i))[j], *(mat[i]+j)

29 //sau *(*( mat+i)+j)

30 }

31 cout <<"Afisarea elementelor tabloului. "<<endl;

32 cout <<"Varianta 1: "<<endl;

33 for(int i = 0; i < m; i++)

34 {

35 for(int j = 0; j < n; j++)

36 cout <<(*(mat+i))[j]<<" ";

37 cout <<endl;

38 }

39 cout <<endl;

40 cout <<"Varianta 2: "<<endl;

41 for(int i = 0; i < m; i++)

42 {

43 int *p = mat[i], *endp = mat[i] + n - 1;

44 //p retine adresa lui mat[i][0]

45 //endp retine adresa lui mat[i][n-1]

46 for( ;p <= endp; p++)

47 cout <<*p<<" ";

48 cout <<endl;

49 }

50 cout <<endl;

51 cout <<"Varianta 3: "<<endl;

52 int (*pl)[dim2], (* endpl)[dim2];

53 //pl si endpl sunt pointeri catre tablouri cu dim2 intregi

54 pl = mat; //pl retine adresa lui mat [0]

55 endpl = mat + m - 1; // endpl retine adresa lui mat[m-1]

56 for( ; pl <= endpl; pl++)

57 {

58 for(int j = 0; j < n; j++)

59 cout <<(*pl)[j]<<" ";

60 cout <<endl;

61 }

62 cout <<endl;

63 cout <<"Varianta 4: "<<endl;

11

Page 12: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

64 int* pp[dim1];// tablou de dim1 pointeri catre int

65 for(int i = 0; i < m; i++)

66 pp[i] = mat[i];

67 int ** matrice;// pointer catre int*

68 matrice = pp;

69 for(int i = 0; i < m; i++)

70 {

71 for(int j = 0; j < n; j++)

72 cout <<*(*( matrice+i)+j)<<" ";

73 cout <<endl;

74 }

75 cout <<endl;

76 return 0;

77 }

1.3 Alocarea dinamica a memoriei

Scopul alocarii dinamice a memoriei, adica a alocarii ın timpul rularii programului, este de a evitaeventuala risipa de memorie care ar aparea ın urma alocarii statice a memoriei, la compilare. Deexemplu, atunci cand se declara un tablou static, trebuie sa se precizeze ınca de la ınceput o dimensiunemaxima a acestuia, fara a sti cate dintre locatiile de memorie alocate vor fi efectiv folosite. Acest neajunseste ınlaturat prin utilizarea tablourilor cu dimensiune variabila, alocate dinamic, la rulare (tablouridinamice).In limbajul C se pot aloca zone de memorie ın memoria HEAP (memorie libera) folosind functii dinbiblioteca <stdlib.h>, cum ar fi: malloc(), calloc(). Pe langa aceste functii, limbajul C++ permitealocari ın zona HEAP a memoriei prin intermediul operatorului unar new. Operatorul new ıntoarce ıntr-un pointer adresa de ınceput a zonei de memorie alocata ın memoria HEAP, daca alocarea s-a realizatcu succes. In cazul ın care nu exista suficienta memorie pentru a fi alocata, implemetarile moderne C++“arunca” o exceptie numita std::bad alloc pe care programatorul o poate “prinde” si trata utilizandun bloc try...catch.

Atunci cand dorim alocarea dinamica a unui singur obiect, operandul lui new este tipul obiectului, tipce poate fi predefinit sau definit de utilizator. De exemplu,

1 int *p; // declararea pointerului

2 try

3 {

4 p = new int; // initialiarea pointerului

5 }

6 catch(bad_alloc)

7 {

8 cout <<"Spatiu de memorie insuficient!"<<endl;

9 exit (1);

10 }

11 *p = 10; // incarcarea zonei de memorie

Alternativ, ın caz de alocare esuata, utilizand argumentul nothrow, operatorul new va ıntoarce pointerulnull ın loc sa arunce o exceptie.

1 int *p;

2 p = new (nothrow) int;

12

Page 13: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

3 if(p == nullptr)

4 {

5 cout <<"Spatiu de memorie insuficient !!!"<<endl;

6 exit (1);

7 }

8 *p = 10;

In ambele situatii, se ıncearca alocarea ın zona HEAP a memoriei, a unei locatii de memorie capabilasa pastreze o data de tip int. Daca alocarea se realizeaza cu succes, atunci adresa de ınceput a locatieide memorie este atribuita pointerului p, un pointer catre int, apoi se plaseaza ıntregul 10 ın zonarespectiva.De asemenea, initializarea pointerului poate fi realizata la declarare:

1 int *p = new int;

2 *p = 10;

In zonele de memorie alocate cu operatorul new, pot fi plasate date de tip corespunzator, la alocare.

1 double *q;

2 q = new double (9.52);

3 // initializarea lui q si a zonei a carei adresa este retinuta de q

4 //sau echivalent

5 double *q = new double (9.52);

Prin aceasta expresie se realizeaza urmatoarele: se aloca ın memoria HEAP o zona de memorie ın carese va pastra valoarea 9.52 ın flotanta dubla precizie. Adresa de ınceput a acestei zone de memorie seatribuie pointerului q.

Atunci cand dorim alocarea unui tablou cu un numar variabil de obiecte (numar ce trebuie totusicunoscut ın momentul rularii), operandul lui new (cu sau fara argumentul nothrow) este numele tipuluielementelor tabloului (tip ce poate fi predefinit sau definit de utilizator), urmat, ıntre paranteze patrate,de numarul de obiecte ale tabloului.Alocarea unui tablou dinamic de nr elemente de tip double:

1 int nr = 10;

2 double *ptab;

3 ptab = new (nothrow) double[nr];

4 if(ptab == nullptr)

5 {

6 cout <<"Spatiu de memorie insuficient!"<<endl;

7 exit (1);

8 }

9 // initializare

10 for(int i = 0; i < nr; i++)

11 *(ptab+i) = sqrt(i); //ptab[i] = sqrt(i);

Daca alocarea se realizeaza cu succes, se rezerva ın memoria HEAP o zona de nr * sizeof(double)

octeti. Adresa de ınceput a acestei zone de memorie este atribuita pointerului ptab. Acesta nu este detip pointer catre tablou, ci de tip pointer catre tipul elementelor tabloului, adica un pointer catre tipuldouble. Initializarea elementelor tabloului dinamic se realizeaza apoi ın mod obisnuit.

Eliberarea locatiilor de memorie alocate dinamic care nu mai sunt necesare este doar sarcina progra-matorului (dealocarea se realizeaza tot ın momentul rularii). In limbajul C, se pot dealoca zonele de

13

Page 14: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

memorie alocate dinamic ın memoria HEAP folosind functia free() din biblioteca <stdlib.h>. Inlimbajul C++, o zona de memorie alocata cu operatorul new se elibereaza cu operatorul delete. Ope-randul lui delete este adresa primului octet al spatiului alocat cu operatorul new. Sintaxa prin care sedealoca un singur obiect este

1 delete p;

iar pentru dealocarea unui tablou dinamic

1 delete [] ptab;

unde ptab este un pointer catre tipul locatiilor alocate cu operatorul new.

Exemplul 3. Alocarea unui vector dinamic.

1 #include <iostream >

2 using namespace std;

3 int main()

4 {

5 int n;

6 do

7 {

8 cout <<"Dimensiunea vectorului (de la 1 la 1000), n: ";

9 cin >>n;

10 }while(n <= 0 || n > 1000);

11 // alocare dinamica

12 int *ptab = new (nothrow) int[n];

13 if(ptab == nullptr)

14 {

15 cout <<"Alocare esuata."<<endl;

16 exit (1);

17 }

18 // initializare

19 cout <<"Citirea elementelor vectorului."<<endl;

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

21 {

22 cout <<i<<" : ";

23 cin >>ptab[i];

24 }

25 cout <<"Afisarea elementelor vectorului."<<endl;

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

27 cout <<ptab[i]<<" ";

28 cout <<endl;

29 //cout <<&ptab <<endl;

30 delete [] ptab; // dealocare

31 //cout <<&ptab <<endl;

32 /*for(int i = 0; i < n; i++)

33 cout <<ptab[i]<<" ";

34 cout <<endl;*/

35 return 0;

36 }

14

Page 15: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Sa observam ca operatorul delete nu sterge din memorie operandul sau, ci elibereaza zona de memorietintita de acesta, pointerul putand fi refolosit ulterior.

Exemplul 4. Alocarea unei matrice cu numar variabil de linii. Va trebui sa alocam dinamic un tabloude tablouri alocate static (fiecare linie este un tablou cu un numar fix de elemente).

1 #include <iostream >

2 const int dim2 = 30;

3 using namespace std;

4 int main()

5 {

6 int m, n, i, j;

7 do

8 {

9 cout <<"Numarul de linii (de la 1 la 1000), m: ";

10 cin >>m;

11 }while(m <= 0 || m > 1000);

12 do

13 {

14 cout <<"Numarul de coloane (de la 1 la dim2), n: ";

15 cin >>n;

16 }while(n <= 0 || n > dim2);

17 // alocare dinamica

18 int (*mat)[dim2];

19 //mat este pointer catre un tablou cu dim2 intregi

20 mat = new (nothrow) int[m][dim2];

21 /* sau

22 typedef int Linie[dim2];

23 Linie *mat;

24 mat = new (nothrow) Linie[m];

25 */

26 if(mat == nullptr)

27 {

28 cout <<"Alocare esuata."<<endl;

29 exit (1);

30 }

31 cout <<"Citirea elementelor matricei."<<endl;

32 for(i = 0; i < m; i++)

33 for(j = 0; j < n; j++)

34 {

35 cout <<"["<<i<<","<<j<<"] : ";

36 cin >>mat[i][j];

37 }

38 cout <<"Afisarea elementelor matricei."<<endl;

39 for(i = 0; i < m; i++)

40 {

41 for(j = 0; j < n; j++)

42 cout <<mat[i][j]<<" ";

43 cout <<endl;

44 }

15

Page 16: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

45 // dealocare

46 delete [] mat;

47 return 0;

48 }

Exemplul 5. Alocarea unei matrice cu numar variabil de coloane. Va trebui sa alocam static un tabloude tablouri alocate dinamic (liniile sunt alocate dinamic).

1 #include <iostream >

2 const int dim1 = 30;

3 using namespace std;

4 int main()

5 {

6 int m, n, i, j;

7 do

8 {

9 cout <<"Numarul de linii (de la 1 la dim1), m: ";

10 cin >>m;

11 }while(m <= 0 || m > dim1);

12 do

13 {

14 cout <<"Numarul de coloane (de la 1 la 1000), n: ";

15 cin >>n;

16 }while(n <= 0 || n > 1000);

17 // alocare dinamica

18 //mat este un tablou static de dim1 pointeri catre int

19 int* mat[dim1];

20 // fiecare linie este alocata dinamic

21 for(i = 0; i < m; i++)

22 {

23 mat[i] = new (nothrow) int[n];

24 if(mat[i] == nullptr)

25 {

26 cout <<"Alocare esuata."<<endl;

27 exit (1);

28 }

29 }

30 cout <<"Citirea elementelor matricei."<<endl;

31 for(i = 0; i < m; i++)

32 for(j = 0; j < n; j++)

33 {

34 cout <<"["<<i<<","<<j<<"] : ";

35 cin >>mat[i][j];

36 }

37 cout <<"Afisarea elementelor matricei."<<endl;

38 for(i = 0; i < m; i++)

39 {

40 for(j = 0; j < n; j++)

41 cout <<mat[i][j]<<" ";

42 cout <<endl;

16

Page 17: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

43 }

44 // dealocare

45 for(i = 0; i < m; i++)

46 delete [] mat[i];

47 return 0;

48 }

Exemplul 6. Alocarea unei matrice cu numar variabil de linii si coloane. Va trebui sa alocam untablou dinamic de tablouri alocate dinamic.

1 #include <iostream >

2 using namespace std ;

3 int main()

4 {

5 int m, n, i, j;

6 do

7 {

8 cout <<"Numarul de linii (de la 1 la 1000), m: ";

9 cin >>m;

10 }while(m <= 0 || m > 1000);

11 do

12 {

13 cout <<"Numarul de coloane (de la 1 la 1000), n: ";

14 cin >>n;

15 }while(n <= 0 || n > 1000);

16 // alocare dinamica

17 //mat este un pointer catre int*, deci pointer dublu

18 int** mat;

19 //mat este un tablou dinamic de m pointeri catre int*

20 mat = new (nothrow) int*[m];

21 if(mat == nullptr)

22 {

23 cout <<"Alocare esuata pe linii."<<endl;

24 exit (1);

25 }

26 // fiecare linie este alocata dinamic

27 for(i = 0; i < m; i++)

28 {

29 mat[i] = new (nothrow) int[n];

30 if(mat[i] == nullptr)

31 {

32 cout <<"Alocare esuata pe coloane."<<endl;

33 exit (1);

34 }

35 }

36 cout <<"Citirea elementelor matricei."<<endl;

37 for(i = 0; i < m; i++)

38 for(j = 0; j < n; j++)

39 {

40 cout <<"["<<i<<","<<j<<"] : ";

17

Page 18: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

41 cin >>mat[i][j];

42 }

43 cout <<"Afisarea elementelor matricei."<<endl;

44 for(i = 0; i < m; i++)

45 {

46 for(j = 0; j < n; j++)

47 cout <<mat[i][j]<<" ";

48 cout <<endl;

49 }

50 //dealocare , in ordine inversa alocarii

51 // dealocare linii

52 for(i = 0; i < m; i++)

53 delete [] mat[i];

54 delete [] mat;

55 return 0;

56 }

1.4 Referinte

Deoarece lucrul cu pointeri necesita experienta, utilizarea acestora ın mod necorespunator conduce laerori grave de executie. Astfel, pentru a facilita rezolvarea unor anumite probleme ce ar trebui rezolvatecu ajutorul pointerilor, ın limbajul C++ a fost introdus un nou tip numit tipul referinta.

In limbajul C++ simbolul operatorului adresa este utilizat si pentru a introduce tipul referinta. Acestlucru se realizeaza printr-o constructie de tipul tip&, unde tip este un tip de data (predefinit saudefinit de utilizator). Tipul referinta creaza sinonime pentru obiecte deja definite, permitand accesareaindirecta a acestora. Sintaxa prin care se declara o referinta este

1 tip var;

2 tip& nume_ref = var;

dar si urmatoarele sintaxe sunt la fel de corecte, pozitia simbolului “&” fiind variabila,

1 tip & nume_ref = var;

sau

1 tip &nume_ref = var;

unde nume ref este numele variabilei de tipul referinta, iar var este numele variabilei referite. Cand sedeclara mai multe referinte de acelasi tip ın cadrul aceleiasi declaratii, trebuie ca simbolul “&” sa fiescris langa fiecare nume de referinta.

O variabila de tip referinta nu poate fi utilizata fara a fi initializata. Initializarea se realizeaza la decla-rarea referintei doar prin numele unei alte variabile deja declarate (pentru care adresa de ınceput a zoneide memorie alocata sa fie diferita de NULL). Din acel moment referinta este legata de variabila referitasi nu mai poate fi modificata. Astfel, prin utilizarea referintei se va accesa aceeasi zona de memorieocupata de variabila prin care a fost initializata referinta. De altfel, o referinta nu are alocat spatiude memorie propriu. Din acest motiv, spunem ca variabila de tip referinta este sinonima cu variabilaprin care a fost initializata. Toate modificarile asupra referintei se vor reflecta asupra variabilei referitesi invers. Sintaxa pentru utilizarea referintei este aceeasi cu cea utilizata pentru folosirea variabilei decare este legata, neexistand operatii specifice doar pe referinte.

18

Page 19: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

1 #include <iostream >

2 using namespace std;

3 int main()

4 {

5 int i;

6 int &refi = i, &refi2 = i;

7 cout <<"Adresa lui i, &i: "<<&i<<endl;

8 cout <<"Adresa lui refi , &refi: "<<&refi <<endl;

9 cout <<"Adresa lui refi2 , &refi2: "<<&refi2 <<endl;

10 i = 5;

11 cout <<"i: "<<i<<endl; //5

12 /* variabilele refi si refi2 sunt sinonime cu variabila i; cele doua

referinte reprezinta alte nume cu ajutorul carora avem acces la

intregul pastrat in zona de memorie alocata lui i*/

13 cout <<"refi: "<<refi <<endl; // 5

14 refi = 10;

15 cout <<"i: "<<i<<endl; // 10

16 cout <<"refi: "<<refi <<endl; // 10

17 i = i * 10 + 5;

18 cout <<"i: "<<i<<endl; // 105

19 cout <<"refi: "<<refi <<endl; // 105

20 refi += refi2;

21 cout <<"i: "<<i<<endl; // 210

22 cout <<"refi: "<<refi <<endl; // 210

23 int j = 7;

24 refi = j;

25 //se modifica valoarea lui i, var. legata de refi

26 //refi ramane legat de i

27 cout <<"i: "<<i<<" j: "<<j<<" refi: "<<refi <<" refi2: "<<refi2 <<endl;

28 //7 7 7 7

29 return 0;

30 }

Datorita ıntrebuintarilor operatorului adresa, acesta mai poarta numele de operator de referentiere.

1.5 Pointeri, referinte si functii

Datele de tipurile pointer si referinta pot fi atat valori returnate ale functiilor, cat si parametri formaliai acestora. Reamintim ca transmiterea parametrilor unei functii (apelul acesteia) se poate face fie prinvaloare, fie prin referinta.

Apelul prin valoare al unei functii presupune ca, la apel, parametrii actuali ai functiei (cei cu carese apeleaza efectiv functia) sa fie copiati ın parametrii formali (cei din lista parametrilor din definitiafunctiei). Orice modificare a parametrilor formali ın timpul executiei functiei se pierde, acestia fiindvariabile locale. Deci, acest tip de apel nu va fi folosit atunci cand modificarile asupra parametrilorformali trebuie sa ramana vizibile si dupa terminarea executiei functiei.

Apelul prin referinta al unei functii se poate realiza fie prin intermediul pointerilor (C/C++), fie prinintermediul referintelor (C++). Daca se folosesc pointerii, atunci la apelul unei functii se copie adreseleparametrilor actuali ın parametrii formali care sunt de tip pointer. Asadar, modificarile aduse asupra

19

Page 20: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

parametrilor formali prin executia functiei se pastreaza si asupra parametrilor actuali, lucrandu-se peaceleasi adrese. Atunci cand un parametru actual al unei functii este un tablou, apelul functiei esteprin referinta, ıntrucat numele tabloului este o constanta de tip pointer care are ca valoare adresaprimului element al tabloului. Daca se folosesc referintele, atunci parametrii formali sunt sinonimepentru parametrii actuali corespunzatori, accesand aceleasi zone de memorie. Ca atare, orice modificareadusa asupra parametrilor formali prin executia functiei, ramane valabila si dupa terminarea executiei.

Exemplul 7. Un exemplu clasic pentru explicarea diferentei dintre apelul prin valoare al unei functiisi cel prin referinta este cel al functiei care interschimba (permuta) valorile a doua variabile.

1 #include <iostream >

2 using namespace std;

3

4 // prototipurile functiilor

5 void interschimba(int ,int);

6 void pinterschimba(int*,int*);

7 void rinterschimba(int&,int&);

8

9 int main()

10 {

11 int a, b;

12 cout <<"a: "; cin >>a;

13 cout <<"b: "; cin >>b;

14 cout <<"Afisare inainte de interschimbare."<<endl;

15 cout <<"a = "<<a<<", b = "<<b<<endl;

16 interschimba(a, b);//apel prin valoare

17 cout <<"Afisare dupa interschimbare."<<endl;

18 cout <<"a = "<<a<<", b = "<<b<<endl;

19 pinterschimba (&a, &b);//apel prin referinta

20 cout <<"Afisare dupa interschimbare."<<endl;

21 cout <<"a = "<<a<<", b = "<<b<<endl;

22 rinterschimba(a, b);//apel prin referinta

23 cout <<"Afisare dupa interschimbare."<<endl;

24 cout <<"a = "<<a<<", b = "<<b<<endl;

25 return 0;

26 }

27

28 // definirea functiilor

29 void interschimba(int x, int y)// antet

30 {

31 int aux;

32 aux = x; x = y; y = aux;

33 }

34

35 void pinterschimba(int* x, int* y)// antet

36 {

37 int aux;

38 aux = *x; *x = *y; *y = aux;

39 }

40

20

Page 21: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

41 void rinterschimba(int& x, int& y)// antet

42 {

43 int aux;

44 aux = x; x = y; y = aux;

45 }

Functia interschimba() se apeleaza prin valoare. La executia acesteia, se copie valorile parametriloractuali (se mai numesc parametri efectivi sau parametri de apel) a si b ın parametrii formali x si y.Functia interschimba valorile parametrilor formali x si y, dar aceasta permutare nu are niciun efectasupra parametrilor actuali ıntrucat, la iesirea din functie, se pierd modificarile asupra parametrilorformali (parametrii formali sunt variabile locale ale functiei).

Functia pinterschimba() se apeleaza prin referinta, folosind pointerii. La executia acesteia, se copieadresele parametrilor actuali a si b ın parametrii formali de tip pointer catre int, x si y. Functia in-terschimba valorile ıntregilor spre care tintesc parametrii formali x si y, care sunt tocmai valorile lui asi b.

Functia rinterschimba() se apeleaza prin referinta, folosind tipul referinta. La executia acesteia,se evita copiile, parametrii formali x si y fiind sinonime pentru parametrii actuali a si b. Functiainterschimba valorile referite de x si y, care sunt tocmai valorile a si b. In ceea ce priveste codul, singuradeosebire dintre functiile interschimba() si rinterschimba() este faptul ca, ın cazul primei functii,parametrii formali sunt de tip int, iar ın cazul celei de-a doua functii, parametrii formali sunt referintela date de tip int.

Parametrii unei functii pot fi de tip pointer catre functii. Un pointer catre o functie se defineste astfel

1 double (*pf)(int);

Deci pf este un pointer catre o functie care returneaza o variabila de tip double, iar ca parametruformal are o data ıntreaga. Daca am fi folosit sintaxa

1 double *pf(int);

atunci pf era numele unei functii care returneaza o variabila de tip double*, deci un pointer catredouble si are ca parametru formal o data de tip int.Sa presupunem ca este definita o functie f astfel

1 double f(int x)

2 {

3 return 2 * x;

4 }

iar pointerul pf, declarat mai sus, este initializat cu adresa de ınceput a functiei f:

1 pf = f;

Atunci, apelul functiei prin intermediul pointerului catre functie se realizeaza astfel

1 (*pf)(2);

De asemenea, ın limbajul C++ este permis si apelul direct

1 pf(2);

deoarece numele unei functii este de fapt un pointer constant ce contine adresa functiei.

21

Page 22: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Exemplul 8. Aproximarea numerica a valorii unei integrale definite folosind formula trapezului. Con-sideram o functie reala f , continua pe intervalul [a, b]. Daca se cunoaste o primitiva a sa, F (x), atunciintegrala definita a lui f(x) ıntre limitele [a, b] se poate calcula folosind formula lui Leibniz-Newton:∫ b

af(x) dx = F (b)− F (a).

Pentru o serie de functii a caror expresie analitica este cunoscuta, calculul integralei se poate efectuafolosind formule cunoscute. In practica, exista numeroase situatii ın care evaluarea unei integrale definitecu metode analitice cunoscute este foarte complicata sau chiar imposibil de realizat. De exemplu, dacaexpersia analitica a functiei f este necunoscuta, valorile acesteia fiind doar tabelate, nici nu se poatevorbi despre o primitiva a acesteia. De asemenea, exista situatii cand expresia analitica a functiei feste foarte complicata si determinarea unei primitive ar fi greoaie sau chiar nu poate fi determinata pecai elementare, analitice. Exista functii ale caror primitive, desi exista, nu pot fi exprimate prin functiielementare (e−x

2, xx etc.). In aceste situatii, calculul integralei definite nu mai poate fi realizat folosind

relatia anterioara si se recurge la metode numerice de aproximare.Folosind una dintre metodele de aproximare numita metoda trapezului si tehnica divizarii se poatecalcula valoarea unei integrale definite astfel: se ımparte intervalului [a, b] ın doua jumatati si se aplicaformula cunoscuta ∫ b

af(x) dx =

∫ c

af(x) dx+

∫ b

cf(x) dx,

unde c = (a+ b)/2 este mijlocul intervalului [a, b]. Procedeul se repeta, pana cand lungimea intervaluluidevine suficient de mica, b − a < ε, cu ε = 0.0001, de exemplu. In acest caz elementar, curba functieide integrat (pe intervalul [a, b], de lungime ε), se aproximeaza liniar prin dreapta ce trece prin punctele(a, f(a)) si (b, f(b)). Asadar, valoarea integralei definite a lui f ıntre limitele [a, b] se aproximeaza prinaria trapezului cu varfurile (a, 0), (b, 0) (a, f(a)) si (b, f(b)). Deci

∫ b

af(x) dx =

b− a

2(f(a) + f(b)), b− a < ε,∫ c

af(x) dx+

∫ b

cf(x) dx, c =

a+ b

2, b− a ≥ ε.

1 #include <iostream >

2 #include <cmath >

3 #define eps 0.0001

4 using namespace std;

5

6 double func1(double x)

7 {

8 return x;

9 }

10

11 double func2(double x)

12 {

13 return exp(- x * x);

14 }

15

16 double func3(double x)

17 {

18 return pow(x,x);

19 }

22

Page 23: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

20

21 double calculIntegrala(double a, double b, double (*f)(double))

22 {

23 if(b - a < eps)

24 return (b - a)*(f(a) + f(b))/2.0;

25 else

26 {

27 double c = (a + b)/2.0;

28 return (calculIntegrala(a, c, f) + calculIntegrala(c, b, f));

29 }

30 }

31

32 int main ()

33 {

34 double a, b, I, aux;

35 cout <<"Capatul stang al intervalului de integrare: ";

36 cin >>a;

37 cout <<"Capatul drept al intervalului de integrare: ";

38 cin >>b;

39 if(b < a)

40 {

41 aux = a;

42 a = b;

43 b = aux;

44 }

45 I = calculIntegrala(a, b, func3);//func2 , func1

46 cout <<"Valoarea integralei definite a lui f intre limitele ["

47 <<a<<","<<b<<"] este "<<I<<endl;

48 return 0;

49 }

Se pot defini tablouri de pointeri la functii. In aceasta situatie, apelul functiei de mai sus se realizeazaprin referirea la componentele tabloului, ca ın exemplul

1 double (* tabpf [5])(double) = {func1 , func2 , func3 , sin , exp};

2 I = calculIntegrala(a, b, tabpf [3]);

3 cout <<"Valoarea integralei definite a lui f intre limitele ["

4 <<a<<","<<b<<"] este "<<I<<endl;

Rezultatul returnat de o functie poate fi de tip referinta, caz ın care acesta poate fi plasat ın parteastanga a unei atribuiri. De exemplu,

1 #include <iostream >

2 using namespace std;

3 struct COMPLEX

4 {

5 double re , im;

6 };

7 double& getRe(COMPLEX &a)

8 {

23

Page 24: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

9 return a.re;

10 }

11 double& getIm(COMPLEX &a)

12 {

13 return a.im;

14 }

15 int main()

16 {

17 COMPLEX z = {0,0};

18 getRe(z) = 7;

19 getIm(z) = 9;

20 cout <<z.re<<" "<<z.im <<endl;

21 return 0;

22 }

In cazul returnarii unei referinte la o variabila locala a functiei apelate (cu durata de viata tempoarasi dealocata de pe stiva programului la terminarea apelului functiei), rezultatul nu va fi cel asteptat,fapt semnalat de compilator printr-un mesaj de avertisment (observati ce s-ar fi ıntamplat daca s-ar fifolosit apelul prin valoare al celor doua functii din exemplul precedent).

24

Page 25: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Capitolul 2

Tablouri

Prima structura de date folosita a fost structura de tablou unidimensional (vector). Un vector este ocolectie de date de acelasi tip (omogena), ın care datele sunt stocate ıntr-o zona contigua de memorie(o zona din memoria interna formata din locatii de memorie succesive). Aceasta organizare a datelorpermite accesul direct al elementelor vectorului prin intermediul operatorului de indexare [ ] (complexi-tate O(1)). Reamintim ca un tablou poate fi alocat static sau dinamic.

Principalele operatii pe vectori sunt: parcurgere, inserare, stergere, cautare, sortare, interclasare, actua-lizare. Atunci cand se doreste inserarea unui element nou ıntr-un tablou static trebuie testat daca acestlucru este posibil, adica daca mai sunt locatii libere alocate vectorului care nu au fost ınca ıncarcate.Presupunem ca n este numarul curent de elemente ale unui vector, iar dim este capacitatea maxima aacestuia. Atunci, daca n + 1 > dim, apare o depasire a spatiului alocat vectorului si, implicit, o eroare.O solutie ar fi redimensionarea spatiului de memorie alocat tabloului.

Exemplul 9. Vom defini tipurile de date PUNCT, CERC si POLIGON CONVEX. Tipul PUNCT este cores-punzator notiunii de punct din plan dat ıntr-un sistem de coordonate carteziene, avand ca membridoua numere reale reprezentand abscisa, respectiv ordonata punctului. Tipul CERC este corespunzatornotiunii de cerc de centru O(x, y) si raza R. Structura prin care va fi implementat noul tip de dateva avea ca membri un punct din plan reprezentand centrul cercului si un numar real reprezentandraza cercului. Tipul POLIGON CONVEX este corespunzator notiunii de poligon convex cu n varfuri, dateın sens trigonometric, ıncepand cu cel de ordonata cea mai mare. Structura ce va implementa tipulPOLIGON CONVEX va avea ca membri un numar natural reprezentand numarul de varfuri si un tablou depuncte ın care vor fi ınregistrate varfurile poligonului dupa regula mai sus mentionata. Vom implementafunctii C++ pentru:

i) a calcula aria si lungimea unui cerc, aria si perimetrul unui poligon convex.

ii) a determina pozitia unui punct fata de un cerc.

Vom considera apoi un sir de mai multe puncte din plan. Se doreste eliminarea din sir a punctelor deabscisa sau ordonata 0, apoi adaugarea ın sir a unui nou punct A(a, b) dupa urmatoarea regula: ınainteapunctelor de abscisa maxima si dupa punctele de ordonata minima. Pentru a memora sirul de puncte seva folosi un vector cu dimensiunea fixa (alocat static), stabilita la scrierea programului si care nu poatefi modificata la executie. Lungimea logica a vectorului se va mari prin adaugarea de elemente, asadartrebuie verificata conditia ca noua lungime logica sa fie mai mica sau cel mult egala cu lungimea fizicaa vectorului, altfel se vor pierde elemente.Observatie. Atentie deosebita se va acorda modului ın care se vor ıncarca varfurile poligonului ıntabloul corespunzator. Acestea trebuie sa formeze un poligon convex, adica, oricare ar fi o latura a

25

Page 26: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

sa, toate varfurile nesituate pe latura considerata se afla de aceeasi parte a dreptei suport a latureirespective.

1 // fisierul header PCP.h

2 #ifndef PCP_H

3 #define PCP_H

4 #include <iostream >

5 #define dim 100

6 using namespace std;

7

8 struct PUNCT;

9 struct CERC;

10 struct POLIGON_CONVEX;

11

12 void citire(PUNCT&);

13 void afisare(PUNCT);

14 double distanta(PUNCT , PUNCT);

15 double arieTriunghi(PUNCT , PUNCT , PUNCT);

16 void eliminarePuncte(PUNCT[], int&);

17 double maxAbscisa(PUNCT[], int);

18 double minOrdonata(PUNCT[], int);

19 void adaugarePuncte1(PUNCT[], int&, PUNCT);

20 void adaugarePuncte2(PUNCT[], int&, PUNCT);

21

22 void citire(CERC&);

23 void afisare(CERC);

24 double lungime(CERC);

25 double arie(CERC);

26 void test(PUNCT , CERC);

27

28 void citire(POLIGON_CONVEX &);

29 void afisare(POLIGON_CONVEX);

30 double perimetru(POLIGON_CONVEX);

31 double arie(POLIGON_CONVEX);

32

33 struct PUNCT

34 {

35 double x, y;

36 // coordonatele carteziene ale unui punct din plan

37 };

38

39 struct CERC

40 {

41 PUNCT O; // centrul cercului

42 double R; //raza cercului

43 };

44

45 struct POLIGON_CONVEX

46 {

47 int n;

26

Page 27: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

48 PUNCT P[dim];

49 // cele n varfuri ale poligonului convex

50 };

51 #endif

52

53 // fisierul sursa PCP.cpp

54 #include <cmath >

55 #include "PCP.h"

56 #define pi 3.14159265301

57

58 void citire(PUNCT &P)

59 {

60 cout <<"Introduceti abscisa punctului: ";

61 cin >>P.x;

62 cout <<"Introduceti ordonata punctului: ";

63 cin >>P.y;

64 }

65

66 void afisare(PUNCT P)

67 {

68 cout <<"("<<P.x<<","<<P.y<< ")";

69 }

70

71 double distanta(PUNCT P, PUNCT Q) // distanta euclidiana

72 {

73 return sqrt((P.x-Q.x)*(P.x-Q.x)+(P.y-Q.y)*(P.y-Q.y));

74 }

75

76 double arieTriunghi(PUNCT A, PUNCT B, PUNCT C)

77 // formula lui Heron

78 {

79 //A, B, C distincte si necoliniare

80 double AB , AC , BC , p;

81 AB = distanta(A, B);

82 AC = distanta(A, C);

83 BC = distanta(B, C);

84 p = (AB + AC + BC) / 2;

85 return sqrt(p * (p - AB) * (p - AC) * (p - BC));

86 }

87

88 void eliminarePuncte(PUNCT P[], int &n)

89 {

90 /* eliminarea din sir a punctelor de abscisa sau ordonata 0*/

91 int i = 0;

92 int j;

93 if(n == 0)

94 {

95 /*cerr - la fel ca cout , doar ca se foloseste pentru a afisa mesaje

de eroare */

27

Page 28: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

96 cerr <<"Underflow!"<<endl;

97 exit (1);

98 }

99 while(i < n)

100 {

101 if(P[i].x == 0 || P[i].y == 0)

102 {

103 for(j = i; j < n - 1; j++)

104 //se deplaseaza la stanga cu o pozitie P_(i+1) ,...,P_(n-1)

105 P[j] = P[j + 1];

106 n--;// numarul punctelor este decrementat

107 }

108 else

109 i++;

110 }

111 }

112

113 double maxAbscisa(PUNCT P[], int n)

114 {

115 double max = P[0].x;

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

117 if(P[i].x > max)

118 max = P[i].x;

119 return max;

120 }

121

122 double minOrdonata(PUNCT P[], int n)

123 {

124 double min = P[0].y;

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

126 if(P[i].y < min)

127 min = P[i].y;

128 return min;

129 }

130

131 void adaugarePuncte1(PUNCT P[], int& n, PUNCT A)

132 // complexitate in cel mai defavorabil caz O(n)

133 {

134 /* punctul A este adaugat in sirul de puncte P_i , inaintea punctelor

de abscisa maxima */

135 int i = 0;

136 int j;

137 if(n + 1 > dim)

138 {

139 cerr <<"Overflow!"<<endl;

140 exit (1);

141 }

142 double max = maxAbscisa(P,n);

143 while(i < n)

28

Page 29: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

144 {

145 if(P[i].x == max && n < dim)

146 {

147 for(j = n; j >= i + 1; j--)

148 //se deplaseaza la dreapta cu o pozitie P_(n-1) ,...,P_(i)

149 P[j] = P[j - 1];

150 P[i] = A;

151 i += 2; //se sare peste elementul nou introdus

152 n++; //se incrementeaza numarul punctelor

153 }

154 else

155 i++;

156 }

157 }

158

159 void adaugarePuncte2(PUNCT P[], int& n, PUNCT A)

160 {

161 /* punctul A este adaugat in sirul de puncte P_i , dupa punctele de

ordonata minima */

162 int i = 0;

163 int j;

164 if(n + 1 > dim)

165 {

166 cerr << "Overflow!" << endl;

167 exit (1);

168 }

169 double min = minOrdonata(P,n);

170 while(i < n)

171 {

172 if(P[i].y == min && n < dim)

173 //fara a depasi dim. maxima a vectorului

174 {

175 for(j = n; j >= i + 2; j--)

176 // se deplaseaza la dreapta cu o pozitie P_(n-1) ,...,P_(i+1)

177 P[j] = P[j - 1];

178 P[i+1] = A;

179 i += 2; //se sare peste elementul nou introdus

180 n++;

181 }

182 else

183 i++;

184 }

185 }

186

187 void citire(CERC &C)

188 {

189 cout <<"Centrul cercului. ";

190 citire(C.O);

191 cout <<"Introduceti raza cercului: ";

29

Page 30: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

192 cin >>C.R;

193 }

194

195 void afisare(CERC C)

196 {

197 cout <<"(";

198 afisare(C.O);

199 cout <<","<<C.R<<")";

200 }

201

202 double lungime(CERC C)

203 {

204 return 2 * pi * C.R;

205 }

206

207 double arie(CERC C)

208 {

209 return pi * C.R * C.R;

210 }

211

212 void test(PUNCT P, CERC C)

213 // pozitia punctului P fata de cercul C(O;R)

214 {

215 double t = distanta(P, C.O);

216 afisare(P);

217 if(t == C.R)

218 cout <<" se afla pe cercul ";

219 else

220 if(t < C.R)

221 cout <<" se afla in interiorul cercului ";

222 else

223 cout <<" se afla in exteriorul cercului ";

224 afisare(C);

225 cout <<endl;

226 }

227

228 void citire(POLIGON_CONVEX &Polig)

229 {

230 cout <<"Introduceti numarul de varfuri ale "<<"poligonului: ";

231 cin >>Polig.n;

232 cout <<"Introduceti coordonatele varfurilor "

233 <<"poligonului convex , in sens trigonometric. ";

234 for(int i = 0; i < Polig.n; i++)

235 citire(Polig.P[i]);

236 }

237

238 void afisare(POLIGON_CONVEX Polig)

239 {

240 cout <<"Cele "<<Polig.n<<" varfuri ale "

30

Page 31: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

241 <<"poligonului convex sunt: "<<endl;

242 for(int i = 0; i < Polig.n; i++)

243 {

244 afisare(Polig.P[i]);

245 cout <<endl;

246 }

247 }

248

249 double perimetru(POLIGON_CONVEX Polig)

250 {

251 double s = 0.0;

252 for(int i = 0; i < Polig.n - 1; i++)

253 s += distanta(Polig.P[i], Polig.P[i+1]);

254 s += distanta(Polig.P[Polig.n-1], Polig.P[0]);

255 return s;

256 }

257

258 double arie(POLIGON_CONVEX Polig)

259 {

260 double s = 0.0;

261 for(int i = 1; i < Polig.n - 1; i++)

262 s+= arieTriunghi(Polig.P[0],Polig.P[i],Polig.P[i+1]);

263 return s;

264 }

265

266 // fisierul sursa main.cpp

267 #include "PCP.h"

268

269 int main()

270 {

271 PUNCT P;

272 citire(P);

273

274 CERC C;

275 citire(C);

276 cout <<"Lungimea cercului ";

277 afisare(C);

278 cout <<" este "<<lungime(C)<<", iar aria este "<< arie(C)<<"."<< endl;

279 test(P, C);

280

281 POLIGON_CONVEX PC;

282 citire(PC);

283 afisare(PC);

284 cout <<"Perimetrul poligonului convex este "<<perimetru(PC)

285 <<", iar aria este "<<arie(PC)<< "."<<endl;

286

287 PUNCT Q[dim];

288 int nr_puncte;

289 cout <<"Introduceti numarul de puncte din sir: ";

31

Page 32: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

290 cin >>nr_puncte ;

291 for(int i = 0; i < nr_puncte; i++)

292 citire(Q[i]);

293 cout <<"Cele "<<nr_puncte <<" puncte sunt: "<<endl;

294 for(int i = 0; i < nr_puncte; i++)

295 {

296 afisare(Q[i]);

297 cout <<endl;

298 }

299 cout <<"Se elimina punctele care au abscisa "<< "sau ordonata 0."

300 << endl;

301 eliminarePuncte(Q, nr_puncte);

302 if(nr_puncte == 0)

303 cout <<"Nu mai este niciun punct in sir."<<endl;

304 else

305 if(nr_puncte == 1)

306 {

307 cout <<"Mai este un punct in sir. "<<"Acesta este: ";

308 afisare(Q[0]);

309 }

310 else

311 {

312 cout <<"Mai sunt "<<nr_puncte <<" puncte. Acestea sunt: "<<endl;

313 for(int i = 0; i < nr_puncte; i++)

314 {

315 afisare(Q[i]);

316 cout <<endl;

317 }

318 }

319

320 PUNCT F;

321 cout <<"Punctul de adaugat. "<<endl;

322 citire(F);

323 cout <<"Adaugare inainte."<<endl;

324 adaugarePuncte1(Q, nr_puncte , F);

325 cout <<"Cele "<<nr_puncte <<" puncte sunt: "<<endl;

326 for(int i = 0; i < nr_puncte; i++)

327 {

328 afisare(Q[i]);

329 cout <<endl;

330 }

331 cout <<"Adaugare dupa."<<endl;

332 adaugarePuncte2(Q, nr_puncte , F);

333 cout <<"Cele "<<nr_puncte <<" puncte sunt: "<<endl;

334 for(int i = 0; i < nr_puncte; i++)

335 {

336 afisare(Q[i]);

337 cout <<endl;

338 }

32

Page 33: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

339 return 0;

340 }

Dupa executarea programului se obtin urmatoarele rezultate:

Exemplul 10. Polinomul Lagrange de interpolare.Consideram o functie f : [a, b] −→ R pentru care cunoastem doar valorile ın n+ 1 puncte distincte dinintervalul [a, b]. Fie x0, x1, . . . , xn cele n+ 1 puncte, iar yi = f(xi), i = 0, n, valorile functiei ın acestepuncte. Ne propunem sa aproximam valoarea functiei ıntr-un punct x ∈ [a, b]. In acest scop vom folosi

33

Page 34: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

polinomul de interpolare Lagrange (de grad cel mult n), care are urmatoarea forma:

Ln(x) =

n∑i=0

yi

n∏j=0j 6=i

x− xjxi − xj

.

Asadar, ın ipotezele considerate, Ln(x) aproximeaza valoarea functiei f ın punctul x ∈ [a, b].

Vom folosi tipul PUNCT definit ın aplicatia precedenta. Vom considera cele n + 1 puncte asa ıncatabscisele acestora sa fie distincte si sa apartina intervalului [a, b]. Vom folosi un tablou dinamic depuncte. Asadar, vom declara un pointer catre PUNCT si vom folosi operatorul new pentru a alocamemorie suficienta pentru a ınregistra n + 1 date de tip PUNCT. Reamintim ca, ın cazul tablourilor,rezultatul alocarii nu este de tip pointer catre tablou, ci de tip pointer catre tipul elementelor tabloului(pointer catre PUNCT). Spatiul de memorie alocat folosind operatorul new trebuie eliberat cu operatoruldelete.

1 // Lagrange.h

2 #ifndef LAGRANGE_H

3 #define LAGRANGE_H

4 #include <iostream >

5 using namespace std;

6

7 struct PUNCT;

8

9 void citire(PUNCT&);

10 void afisare(PUNCT);

11 double lagrange(PUNCT*, int , double);

12

13 struct PUNCT

14 {

15 double x, y;

16 // coordonatele carteziene ale unui punct din plan

17 };

18 #endif

19

20 // Lagrange.cpp

21 #include "Lagrange.h"

22

23 void citire(PUNCT &P)

24 {

25 /*...*/

26 }

27

28 void afisare(PUNCT P)

29 {

30 /*...*/

31 }

32

33 double lagrange(PUNCT* P, int n, double x)

34 {

35 int i, j ;

34

Page 35: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

36 double p, s = 0.0;

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

38 {

39 p = 1;

40 for(j = 0; j <= n; j++)

41 if(j != i)

42 p *= (x-(*(P + j)).x)/((*(P + i)).x-(*(P + j)).x);

43 //p *= (x-P[j].x)/(P[i].x-P[j].x);

44 s += p * (*(P + i)).y;

45 //s += p * P[i].y;

46 }

47 return s;

48 }

49

50 //main.cpp

51 #include "Lagrange.h"

52

53 int main()

54 {

55 int n;

56 double x, a, b;

57 cout <<"Introduceti capetele intervalului de definitie al functiei.";

58 cin >>a>>b ;

59 cout <<"Introduceti n, n+1 reprezentand numarul "

60 <<"de puncte din intervalul ["<< a << "," << b << "]: ";

61 cin >>n;

62 PUNCT *P = new (nothrow) PUNCT[n + 1];

63 if(P == nullptr)

64 {

65 cout <<"Alocare esuata."<<endl;

66 exit (1);

67 }

68 cout <<"Introduceti perechile (xi ,f(xi)), cu xi distincte."<<endl;

69 cout <<"[" <<1<<"] : ";

70 do{

71 citire (*P) ; // citire(P[0]);

72 }while ((*P).x < a || (*P).x > b) ;

73 int find , i, j ;

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

75 {

76 do

77 {

78 find = 0;

79 cout <<"[" << i + 1 << "] : ";

80 citire (*(P + i)); // citire(P[i]);

81 for(j = 0; j < i; j++)

82 if(P[i].x == P[j].x || P[i].x < a || P[i].x > b)

83 {

84 find = 1;

35

Page 36: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

85 break;

86 }

87 }while(find);

88 }

89 cout <<"Datele citite corect."<<endl;

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

91 {

92 afisare(P[i]);

93 cout <<endl;

94 }

95 cout <<"Introduceti punctul in care doriti "

96 <<"sa aproximati valoarea functiei , " ;

97 cout <<"x din intervalul ["<<a<<","<<b<< "]: ";

98 cin >>x ;

99 while(x < a || x > b)

100 cin >>x;

101 cout <<"Aproximarea valorii functiei in punctul "

102 <<x<<" este "<<lagrange(P, n, x)<<endl;

103 delete [] P ;

104 return 0 ;

105 }

Vom folosi urmatoarele date pentru test:

x 0.0 0.1 0.3 0.5 0.7 1.0 1.25 1.3 1.45 1.7 2.0

f(x) 0.0 0.01 0.09 0.25 0.49 1.0 1.5625 ? 2.1025 2.89 4.0

Dupa executia programului se obtin rezultatele:

36

Page 37: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Tablorile bidimensionale numite si matrice sunt structuri omogene de date ce ocupa o zona contigua dememorie. Fiecare element al unui tablou bidimensional este bine determinat de doi indici: indicele delinie si indicele de coloana. Astfel, elementele unei matrice sunt stocate ın locatii succesive de memorieın ordinea obtinuta prin incrementarea ciclica a indicilor de la dreapta la stanga.

Exemplul 11. Metoda lui Gauss de rezolvare a sistemelor algebrice liniare.Ne propunem sa rezolvam un sistem algebric liniar ın care numarul de ecuatii este egal cu numarulnecunoscutelor, iar determinantul matricei sistemului este nenul:

a11x1 + a12x2 + . . . · · ·+ a1nxn = b1

a21x1 + a22x2 + . . . · · ·+ a2nxn = b2

· · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·aj1x1 + · · ·+ ajjxj + · · ·+ ajnxn = bj

· · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·an1x1 + an2x2 + . . . · · ·+ annxn = bn.

Sistemul poate fi scris sub formaAx = b,

unde matricea sistemului este A = (aij), i, j = 1, n, vectorul termenilor liberi este b = (bi)T , i = 1, n, iar

vectorul solutiei este x = (xi)T , i = 1, n. Daca matricea A este nesingulara, sistemul are solutie unica:

x = A−1b.

Deoarece de cele mai multe ori, ın practica, matricea A are un numar mare de linii si coloane, iar calcu-lul matricei A−1 este dificil si acumuleaza erori, se impun metode directe sau metode iterative eficientepentru rezolvarea acestor sisteme.Metoda lui Gauss este o metoda directa de rezolvare a sistemelor algebrice liniare care presupune trans-formarea sistemului Ax = b ıntr-un sistem triunghiular superior (elementele de sub diagonala principalaa matricei A vor fi 0) si apoi rezolvarea acestuia prin substitutie inversa. Constructia sistemului triun-ghiular superior se face astfel: la pasul j se elimina xj din ecuatiile j+ 1, . . . , n, prin ınmultirea ecuatieij cu

mij = −aijajj

(elementul ajj 6= 0 se numeste pivot) si adunarea acesteia la ecuatiile i(i > j). In functie de alegereapivotului, exista urmatoarele variante ale metodei lui Gauss de eliminare:

1. metoda lui Gauss clasica - ın care, la fiecare pas, pivotul este elementul ajj 6= 0, j = 1, n;

2. metoda lui Gauss cu pivotare partiala - ın care, la fiecare pas j, se alege ca pivot elementul akjmaxim ın valoare absoluta pe coloana, pentru k > j, permutandu-se linia j cu linia k;

3. metoda lui Gauss cu pivotare totala - ın care, la fiecare pas, se alege ca pivot elementul maximatat pe linie, cat si pe coloana, pentru k > i, k > j, permutandu-se linia i cu linia k si coloana jcu coloana k.

Exemplificam ın cele ce urmeaza utilizarea metoda lui Gauss de eliminare cu pivotare partiala. In acestfel, sistemul se reduce la forma triunghiulara superior

a11 a12 . . . a1,n−2 a1,n−1 a1,n0 a22 . . . a2,n−2 a2,n−1 a2,n· · · · · · · · · · · · · · · · · ·0 0 . . . 0 an−1,n−1 an−1,n0 0 . . . 0 0 an,n

x1x2· · ·xn−1xn

=

b1b2· · ·bn−1bn

.

37

Page 38: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Rezolvarea sistemului se face apoi prin substitutie inversa:

xn =bnan,n

,

xi =

bi − n∑j=i+1

aijxj

1

aii, i = n− 1, n− 2, . . . , 1.

Se vor defini doua tipuri noi de date corespunzatoare notiunilor de vector cu n componente reale si,respectiv, matrice patratica de ordin n cu elemente numere reale. Pentru ambele tipuri de date seva folosi alocarea dinamica. Structura VECTOR va avea ca membri o data de tip int, reprezentanddimensiunea vectorului si o data de tip pointer catre tipul double care va retine adresa de ınceput azonei de memorie alocate pentru memorarea vectorului (adresa primului element al tabloului). Indatace acest nou tip a fost definit de catre utilizator, poate fi folosit ca orice alt tip predefinit. Alocareadinamica a unei matrice cu n linii si n coloane consta ın alocarea dinamica a unui tablou cu n date detip VECTOR. Structura MATRICE va avea ca membri o data de tip int, reprezentand dimensiunea matriceipatratice si o data de tip pointer catre tipul VECTOR, fiecare linie a matricei fiind modelata printr-o datade tip VECTOR.

1 // gauss.h

2 #ifndef GAUSS_H

3 #define GAUSS_H

4 #include <iostream >

5 using namespace std;

6

7 struct VECTOR;

8 struct MATRICE;

9

10 void error(const char []);

11

12 void initializare(VECTOR&, int);

13 void copiere(VECTOR&, const VECTOR &);

14 void citire(VECTOR &);

15 void afisare(VECTOR &);

16 void dealocare(VECTOR &);

17 // interschimbarea a doua elemente ale unui vector

18 void interschimbare(VECTOR&, int , int);

19 double norma_l2(VECTOR &); // norma euclidiana

20 double produs_scalar(VECTOR&, VECTOR &);

21 VECTOR diferenta(VECTOR&, VECTOR &);

22

23 void initializare(MATRICE&, int);

24 void copiere(MATRICE&, const MATRICE &);

25 void citire(MATRICE &);

26 void afisare(MATRICE &);

27 void dealocare(MATRICE &);

28 // interschimbarea a doua linii ale unei matrice

29 void interschimbare(MATRICE&, int , int);

30 VECTOR produs(MATRICE&, VECTOR &);

31

32 void gauss(MATRICE&, VECTOR&, VECTOR &);

38

Page 39: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

33 void triang(MATRICE&, VECTOR &);

34 double pivot(MATRICE&, VECTOR&, int);

35 void backst(MATRICE&, VECTOR&, VECTOR &);

36 double dotprod(VECTOR&, VECTOR&, int , int);

37

38 struct VECTOR{

39 int size;

40 double *vect;

41 };

42

43 struct MATRICE{

44 int size;

45 VECTOR *mat;

46 };

47 #endif

48

49 // gauss.cpp

50 #include "gauss.h"

51 #define tol 1.0E-7

52 /*din cauza erorilor de calcul se prefera , in locul testelor cu 0,

53 testele cu o valoare foarte mica , tol = 10^{ -7}*/

54

55 void error(const char msg [])

56 {

57 cerr <<"Eroare de executie."<<endl;

58 cerr <<msg <<endl;

59 cerr <<"Programul se opreste."<<endl;

60 exit (1);

61 }

62

63 void initializare(VECTOR &v, int n)

64 {

65 v.size = n;

66 v.vect = new (nothrow) double[v.size];

67 if(v.vect == nullptr)

68 /*daca sistemul de operare nu gaseste spatiul de memorie necesar ,

operatorul new returneaza pointerul null*/

69 error("ALOCARE ESUATA in VECTOR , initializare (...)");

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

71 v.vect[i] = 0.0;

72 }

73

74 void copiere(VECTOR &v, const VECTOR &v2)

75 {

76 v.size = v2.size;

77 v.vect = new (nothrow) double[v.size];

78 if(v.vect == nullptr)

79 error ("ALOCARE ESUATA in VECTOR , copiere (...)");

80 for(int i = 0; i < v.size; i++)

39

Page 40: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

81 v.vect[i] = v2.vect[i];

82 }

83

84 void dealocare(VECTOR &v)

85 {

86 delete [] v.vect;

87 }

88

89 void interschimbare(VECTOR &v, int i, int j)

90 {

91 double temp;

92 temp = v.vect[i];

93 v.vect[i] = v.vect[j];

94 v.vect[j] = temp;

95 }

96

97 double norma_l2(VECTOR &v)

98 {

99 double sum = 0.0;

100 for(int i = 0; i < v.size; i++)

101 sum += v.vect[i] * v.vect[i];

102 return sqrt(sum);

103 }

104

105 void citire(VECTOR &v)

106 {

107 cout <<"VECTOR: "<<endl;

108 for(int i = 0; i < v.size; i++)

109 cin >>v.vect[i];

110 }

111

112 void afisare(VECTOR &v)

113 {

114 cout <<"VECTOR: ";

115 for(int i=0; i < v.size; i++)

116 cout <<v.vect[i]<<" ";

117 cout <<endl;

118 }

119

120 double produs_scalar(VECTOR &v, VECTOR &u)

121 {

122 if(v.size != u.size)

123 error("DIMENSIUNI DIFERITE in produs_scalar (...)");

124 double sum = 0.0;

125 for(int i = 0; i < v.size; i++)

126 sum += v.vect[i] * u.vect[i];

127 return sum;

128 }

129

40

Page 41: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

130 VECTOR diferenta(VECTOR &v, VECTOR &u)

131 {

132 if(v.size != u.size)

133 error("DIMENSIUNI DIFERITE in diferenta (...)");

134 VECTOR w;

135 initializare(w,v.size);

136 for(int i = 0; i < w.size; i++)

137 w.vect[i] = v.vect[i] - u.vect[i];

138 return w;

139 }

140

141 void initializare(MATRICE &m, int n)

142 {

143 m.size = n;

144 m.mat = new (nothrow) VECTOR[m.size];

145 if(m.mat == nullptr)

146 error("ALOCARE ESUATA pe linie in MATRICE , initializare (...)");

147 for(int i = 0; i < m.size; i++)

148 initializare(m.mat[i], m.size);

149 }

150

151 void copiere(MATRICE &m, const MATRICE &A)

152 {

153 m.size = A.size;

154 m.mat = new (nothrow) VECTOR[m.size];

155 if(m.mat == nullptr)

156 error("ALOCARE ESUATA pe linie in MATRICE , copiere (..)");

157 for(int i = 0; i < m.size; i++)

158 {

159 initializare(m.mat[i], m.size);

160 copiere(m.mat[i], A.mat[i]);

161 }

162 }

163

164 void citire(MATRICE &m)

165 {

166 int i;

167 cout <<"MATRICE :\n";

168 for(i = 0; i < m.size; i++)

169 citire(m.mat[i]);

170 }

171

172 void afisare(MATRICE &m)

173 {

174 int i;

175 cout <<"MATRICE :\n";

176 for(i = 0; i < m.size; i++)

177 afisare(m.mat[i]);

178 }

41

Page 42: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

179

180 void dealocare(MATRICE &m)

181 {

182 for(int i = 0; i < m.size; i++)

183 dealocare(m.mat[i]);

184 delete [] m.mat;

185 }

186

187 void interschimbare(MATRICE &m, int i, int j)

188 {

189 VECTOR temp;

190 copiere(temp , m.mat[i]);

191 copiere(m.mat[i], m.mat[j]);

192 copiere(m.mat[j], temp);

193 }

194

195 VECTOR produs(MATRICE &m, VECTOR &w)

196 {

197 if(m.size != w.size)

198 error("DIMENSIUNI INCORECTE in produs (...)");

199 VECTOR u;

200 initializare(u, m.size);

201 for(int i = 0; i < u.size; i++)

202 u.vect[i] = produs_scalar(m.mat[i],w);

203 return u;

204 }

205

206 double pivot(MATRICE &A, VECTOR &b, int j)

207 {

208 /* pivotul este elementul a_{kj} maxim in valoare absoluta pe coloana ,

pentru k > j, permutandu -se linia j cu linia k*/

209 int n = A.size;

210 int i = j;

211 double t = 0.0;

212 for(int k = j; k < n; k++)

213 {

214 double akj = abs(A.mat[k].vect[j]);

215 if(akj > t)

216 {

217 t = akj;

218 i = k;

219 }

220 }

221 if(i > j)

222 {

223 interschimbare(A,j,i);

224 interschimbare(b,j,i);

225 }

226 return A.mat[j].vect[j];

42

Page 43: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

227 }

228

229 void triang(MATRICE &A, VECTOR &b)

230 {

231 //aduce matricea sistemului la forma triunghiulara superior

232 int n = A.size;

233 for(int j = 0; j < n - 1; j++)

234 {

235 double d = pivot(A, b, j);

236 if(abs(d) < tol)

237 error("DETERMINANT ZERO");

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

239 {

240 double temp = A.mat[i].vect[j] / d;

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

242 A.mat[i].vect[k] -= temp * A.mat[j].vect[k];

243 b.vect[i] -= temp * b.vect[j];

244 }

245 }

246 }

247

248 double dotprod(VECTOR &u, VECTOR &v, int k1 , int k2)

249 {

250 // produs scalar "partial" u_i*v_i , i = k1 ,...,k2

251 double sum = 0.0;

252 for(int i = k1; i <= k2; i++)

253 sum += u.vect[i] * v.vect[i];

254 return sum;

255 }

256

257 void backst(MATRICE &A, VECTOR &b, VECTOR &x)

258 {

259 //se determina solutia sistemului prin substitutie inversa

260 int n = A.size;

261 double d = A.mat[n-1]. vect[n-1];

262 if(abs(d) < tol)

263 error("ELEMENT DIAGONAL PREA MIC , backst (...)");

264 x.vect[n-1] = b.vect[n-1] / d;

265 for (int i = n - 2; i >= 0; i--)

266 x.vect[i]=(b.vect[i]-dotprod(A.mat[i],x,i+1,n-1))

267 / A.mat[i].vect[i];

268 }

269

270 void gauss(MATRICE &A, VECTOR &b, VECTOR &x)

271 {

272 triang(A, b);

273 backst(A, b, x);

274 }

275

43

Page 44: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

276 //main.cpp

277 #include "gauss.h"

278

279 int main()

280 {

281 int n;

282 cout <<"Dimensiunea sistemului: ";

283 cin >>n;

284

285 MATRICE A;

286 initializare(A, n);

287 VECTOR b, x, w;

288 initializare(b, n);

289 initializare(x, n);

290 initializare(w, n);

291

292 // ....A si b ....

293 cout <<"Dati matricea sistemului :\n";

294 citire(A);

295 cout <<"Dati vectorul termenilor liberi :\n";

296 citire(b);

297

298 MATRICE A1;

299 copiere(A1 , A);

300 VECTOR b1;

301 copiere(b1 , b);

302

303 gauss(A, b, x);

304 cout <<"Solutia sistemului :\n";

305 afisare(x);

306

307 // Testam solutia

308 copiere(w, diferenta(produs(A1 , x), b1));

309 cout <<"\nReziduul calculat\n";

310 afisare(w);

311

312 double l2_res = norma_l2(w);

313 double rel_err = l2_res / norma_l2(b1);

314 cout <<"\nNorma reziduala = "<<l2_res <<endl;

315 cout <<"Eroarea relativa = "<<rel_err <<endl;

316

317 dealocare(A);

318 dealocare(A1);

319 dealocare(b);

320 dealocare(b1);

321 dealocare(x);

322 dealocare(w);

323

324 return 0;

44

Page 45: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

325 }

Folosind programul de mai sus, ne propunem sa rezolvam sistemulx1 − 2x2 + x3 = 0

2x1 + x2 − x3 = 1

−3x1 + x2 + x3 = 2.

Se obtine urmatoarea solutie:

45

Page 46: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Capitolul 3

Liste

O lista este o colectie de elemente de acelasi tip (o structura de date omogena), numite si noduri, aflateıntr-o anumita relatie de ordine. Un nod al listei contine o zona de informatie utila si una sau maimulte zone de legatura. Zona de informatie utila este compusa din una sau mai multe chei. Numarulde noduri din lista determina lungimea acesteia. Cel mai simplu tip de lista este lista liniara. O listaliniara are un prim element si un ultim element, iar toate elementele cu exceptia primului, au un singurpredecesor si toate elementele, cu exceptia ultimului, au un singur succesor. Un alt tip de lista este listacirculara. Intr-o astfel de lista dupa ultimul nod urmeaza primul nod, asadar fiecare nod are un singursuccesor si un singur predecesor.

Principalele operatii care se efectueaza asupra listelor sunt: crearea listei, parcurgerea listei si implicitaccesul la orice element al listei (pentru modificare sau pentru examinare), eliminarea unui nod al listei,inserarea unui element nou ın lista, schimbarea pozitiei unui element ın cadrul listei. Alte operatii potfi: determinarea lungimii unei liste, separarea unei liste ın doua sau mai multe subliste dupa anumitecriterii, selectia unor elemente dintr-o lista (elemente ce satisfac anumite conditii), crearea unei listeordonate crescator sau descrescator dupa valorile unei chei, combinarea a doua (sau mai multe) listeprin concatenare (alipire) sau prin interclasare.

Dupa modul ın care se aloca spatiul de memorie ocupat de o lista, aceasta poate fi secventiala sauınlantuita. Atat alocarea secventiala, cat si cea ınlantuita pot fi efectuate atat static cat si dinamic.In alocarea statica, nodurile sunt memorate cu ajutorul vectorilor alocati ın segmentul de date (vectoristatici). In cazul alocarii dinamice datele sunt alocate ın zona HEAP a memoriei. In alocarea secventiala,nodurile se plaseza ın locatii succesive de memorie, ın functie de ordinea lor din lista. Implementareasecventiala se realizeaza prin intermediul tablourilor. Avantajul acestui mod de implementare esteaccesul rapid la oricare nod al listei. Dezavantajele constau ın faptul ca inserarea si stergerea unuinod din lista sau schimbarea pozitiei unui nod ın cadrul listei sunt operatii greoaie pe vectori. Unalt dezavantaj este utilizarea incompleta a spatiului alocat listei, daca aceasta este alocata static. Inalocarea ınlantuita statica, zonele de informatie si cele de legatura asociate nodurilor sunt memoratecu ajutorul vectorilor alocati ın segmentul de date (vectori statici). In cele ce urmeaza ne vom ocupadoar de cazul alocarii ınlantuite efectuata dinamic. Exista doua moduri de alocare ınlantuita: simpluınlantuita si dublu ınlantuita. Alocarea simplu ınlantuita presupune ca fiecare nod sa aiba doua parti:o zona de informatie si o zona de legatura reprezentata de adresa nodului succesor. Zona de informatiecontine unul sau mai multe campuri (chei). Avantajul acestui tip de alocare este ca operatiile de inserarea unui nod nou ın lista si stergerea unui nod din lista sunt rapide. Dezavantajele constau ın faptul caoperatia de acces la un nod al listei necesita parcurgerea tuturor predecesorilor sai, ceea ce conduce laun consum mai mare de timp. In plus, exista un consum suplimentar de memorie pentru ca se stocheazalegatura catre nodul succesor. Alocarea dublu ınlantuita presupune folosirea a doua adrese ın loc de una.Astfel, un nod contine, pe langa informatia utila, adresa nodului succesor si adresa nodului predecesor.O lista dublu ınlantuita poate fi parcursa ın ambele sensuri: de la primul nod catre ultimul si invers,

46

Page 47: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

de la ultimul spre primul.

info1 adr2

adr1

info2 adr3

adr2

info3 adr4

adr3

. . . infoN nullptr

adrN

Figura 1. Lista liniara simplu ınlantuita

nullptr info1 adr2

adr1

adr1 info2 adr3

adr2

. . . adr(N-1) infoN nullptr

adrN

Figura 2. Lista liniara dublu ınlantuita

3.1 Liste liniare simplu ınlantuite

3.1.1 Stiva

O stiva este o lista liniara cu proprietatea ca operatiile de adaugare si eliminare a nodurilor se facprintr-un singur capat al listei, numit varful listei. Astfel, ultimul nod adaugat va fi primul sters. Dinacest motiv, stivele se mai numesc si liste LIFO (Last In First Out). De exemplu, ar putea fi modelateprin intermediul stivelor: un set de farfurii sau de carti asezate una peste cealalta pe o masa, nistediscuri plasate pe o tija, unul peste celalalt etc.

Operatiile posibile asupra unei stive sunt: crearea unei stive vide, inserarea unui element ın stiva, extra-gerea unui element din stiva, accesarea elementului din varful stivei pentru modificare sau vizualizare.Daca dorim sa accesam un element al stivei diferit de varful acesteia, ar trebui sa extragem succesivelemente din varful stivei pana ajungem la elementul dorit.

Implementarea unei stive ca o lista liniara simplu ınlantuita presupune definirea unei structuri cores-punzatoare fiecarui nod al stivei. Vom numi aceasta structura NOD. Fiecare element al stivei are douacomponente: una ın care se va pastra informatia utila a nodului si una care va memora adresa noduluiurmator din lista. Se va redenumi tipul NOD* cu numele STIVA, folosind cuvantul cheie typedef. Stivava fi gestionata prin intermediul variabilei vf care este un pointer catre NOD (si deci de tip STIVA) sicare retine adresa ultimului nod inserat, adica a varfului stivei.

Adaugarea unui nod nou ın stiva (“push”) se realizeaza astfel: se aloca memorie pentru nodul nou, seıncarca zona de informatie a acestuia, apoi se creaza legatura noua, ıncarcand ın zona de legatura anodului nou adresa varfului stivei, dupa care se sterge vechea legatura, vf tintind acum catre nodulnou, acesta devenind varful stivei.

infoN adr(N-1)

vf(2)

infoNOU vf

(1)info(N-1)adr(N-2) . . . info1 nullptr

Figura 3. Adaugarea unui element ın stiva

47

Page 48: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Stergerea unui nod din stiva (“pop”) se realizeaza astfel: vf va tinti catre nodul succesor varfului stivei,acesta devenind noul varf al listei. Se elibereaza zona de memorie ocupata de vechiul varf al stivei, prinutilizarea operatorului delete.

infoN adr(N-1)

(2)

vf(1)

info(N-1)adr(N-2) info(N-2)adr(N-3) . . . info1 nullptr

Figura 4. Stergerea unui element din stiva

Codul C++ corespunzator alocarii ınlantuite dinamice a unei stive este:

1 // stiva.h

2 #ifndef STIVA_H

3 #define STIVA_H

4 #include <iostream >

5 using namespace std;

6

7 struct NOD;

8 typedef NOD* STIVA;

9

10 void error(const char []);

11 void push(STIVA&, int);

12 void pop(STIVA&);

13 STIVA creareStiva ();

14

15 struct NOD

16 {

17 int info;

18 NOD *next;

19 };

20 #endif

21

22 // stiva.cpp

23 #include "stiva.h"

24

25 void error(const char msg [])

26 {

27 cerr <<"Eroare de executie."<<endl;

28 cerr <<msg <<endl;

29 cerr <<"Programul se opreste."<<endl;

30 exit (1);

31 }

32

33 void push(STIVA &vf , int nInfo)

34 {

35 STIVA nou = new (nothrow) NOD;

36 if(nou == nullptr)

37 error("Spatiu insuficient");

48

Page 49: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

38 nou ->info = nInfo;

39 nou ->next = vf;

40 vf = nou;

41 }

42

43 void pop(STIVA &vf)

44 {

45 STIVA p;

46 if(vf == nullptr)

47 error("Stiva vida");

48 p = vf;

49 cout <<"Se elimina "<<p->info <<endl;

50 vf = vf->next;

51 delete p;

52 }

53

54 STIVA creareStiva ()

55 {

56 int nr_noduri , info;

57 STIVA vf = nullptr; // stiva vida

58 cout <<"Numarul initial de noduri: ";

59 cin >>nr_noduri;

60 // crearea unei stive cu un numar initial (cunoscut) de noduri

61 for(int i = 1; i <= nr_noduri; i++)

62 {

63 cout <<"Informatia pt. nodul "<<i<<": ";

64 cin >>info;

65 push(vf ,info);

66 }

67 return vf;

68 }

69

70 //main.cpp

71 #include "stiva.h"

72

73 int main()

74 {

75 STIVA prim;

76 prim = creareStiva ();

77 char c;

78 int infon;

79 do

80 {

81 cout <<"Doriti adaugarea inca a unui element in stiva?[D/N]"<<endl;

82 cin >>c;

83 if(c == ’D’ || c == ’d’)

84 {

85 cout <<"Introduceti elementul de adaugat in stiva: ";

86 cin >>infon;

49

Page 50: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

87 push(prim , infon);

88 }

89 }while(c == ’D’ || c == ’d’);

90 cout <<"Eliminare elemente din stiva."<<endl;

91 do

92 {

93 cout <<"Doriti eliminarea unui element din stiva?[D/N]"<<endl;

94 cin >>c;

95 if(c == ’D’ || c == ’d’)

96 pop(prim);

97 }while((c == ’D’ || c == ’d’));

98 return 0;

99 }

Exemplul 12. Presupunem ca ıntr-o gara sunt 20 de vagoane de tren numerotate de la 1 la 20.Acestea se plaseaza ın depou ın ordine, astfel ca primul vagon sa fie cel numerotat cu 1 (se presupuneca acest lucru este posibil). Se doreste asamblarea lor pentru a forma un tren dupa urmatoarea regula:vagoanele cu numere pare, ın ordine crescatoare, la ınceputul trenului, iar cele cu numere impare, lasfarsitul trenului, ın ordine descrescatoare. Se presupune ca exista doar doua linii de manevra si ca peuna dintre acestea trebuie sa fie asezat trenul, ın componenta sa finala.

Pentru rezolvarea acestei probleme, vom construi 3 stive, corespunzatoare depoului si celor doua liniide manevra. Adaugam, mai ıntai, vagoanele ın depou. Apoi, se scoate cate un vagon din depou si seadauga fie primei stive, fie celei de-a doua, dupa cum vagonul este numerotat cu numar par sau impar.Pe prima linie avem acum vagoanele: 19, 17, ..., 3, 1, iar pe cea de-a doua linie avem vagoanele: 20,18, ..., 4, 2. Se scoate cate un vagon de pe linia a doua si se adauga primei stive. In acest moment peprima linie se afla garnitura de tren dorita.

1 int main()

2 {

3 STIVA vdepou = nullptr , vl1 = nullptr , vl2 = nullptr;

4 int elem;

5 for(int i = 20; i >= 1; i--)

6 push(vdepou , i);

7 for(int i = 1; i <= 10; i++)

8 {

9 elem = vdepou ->info;

10 pop(vdepou);

11 push(vl1 , elem);

12 elem = vdepou ->info;

13 pop(vdepou);

14 push(vl2 , elem);

15 }

16 for(int i = 1; i <= 10; i++)

17 {

18 elem = vl2 ->info;

19 pop(vl2);

20 push(vl1 , elem);

21 }

22 return 0;

23 }

50

Page 51: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

3.1.2 Coada

O coada este o lista liniara ın care adaugarea de noi noduri se face dupa ultimul element adaugat ınlista, iar stergerea unui nod se face din celalalt capat al listei, numit varful listei. Din acest motiv, cozilese mai numesc si liste FIFO (First In First Out). O astfel de structura de date se foloseste pentrua modela fenomenul cu acelasi nume din lumea reala (de exemplu, coada la un muzeu sau un set dedecizii puse ın asteptare etc).

Operatiile posibile pe o coada sunt: crearea unei cozi vide, inserarea unui element ın coada, extragereaunui element din coada, accesarea elementului din varful cozii pentru examinare sau modificare. Dacadorim sa aflam valoarea unui alt element al cozii, ar trebui sa extragem succesiv elemente pana laelementul dorit.

In cazul unei cozi implementate prin intermediul unei liste liniare simplu ınlantuite, aceasta va fi ges-tionata prin intermediul a doua variabile: vf – un pointer catre NOD, care retine adresa primului nodinserat, adica a varfului listei si sf – un pointer catre NOD, care retine adresa ultimului nod inserat.Vom redenumi tipul NOD* cu numele COADA.

Adaugarea unui nod nou ın coada se realizeaza astfel: se aloca memorie pentru nodul nou, se ıncarcazona de informatie a acestuia, apoi se initializeaza zona de legatura a nodului nou cu nullptr, se creeazanoua legatura, dupa care se sterge vechea legatura, sf tintind acum catre nodul nou, acesta devenindultimul nod al listei.

info1 adr2

vf

info2 adr3 . . . infoN nullptr

adrNOU

sf(2)

(1)

infoNOU nullptr

Figura 5. Adaugarea unui element ın coada

Stergerea unui nod din coada este identica cu stergerea unui nod din stiva.Codul C++ corespunzator alocarii ınlantuite si dinamice a unei cozi este:

1 // coada.h

2 #ifndef COADA_H

3 #define COADA_H

4 #include <iostream >

5 using namespace std;

6

7 struct NOD;

8 typedef NOD* COADA;

9

10 void error(const char []);

11 void adaugare(COADA&, COADA&, int);

12 void eliminare(COADA&);

13 COADA creareCoada(COADA &);

14

15 struct NOD

16 {

17 int info;

18 NOD *next;

51

Page 52: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

19 };

20 #endif

21

22 // coada.cpp

23 #include "coada.h"

24

25 void error(const char msg [])

26 {

27 cerr <<"Eroare de executie."<<endl;

28 cerr <<msg <<endl;

29 cerr <<"Programul se opreste."<<endl;

30 exit (1);

31 }

32

33 void adaugare(COADA &vf , COADA &sf , int nInfo)

34 {

35 COADA nou;

36 nou = new (nothrow) NOD;

37 if(nou == nullptr)

38 error("Spatiu insuficient!");

39 nou ->info = nInfo;

40 nou ->next = nullptr;

41 if(vf == nullptr) // coada este vida

42 {

43 vf = nou;

44 sf = nou;

45 }

46 else

47 {

48 sf->next = nou;

49 sf = nou;

50 }

51 }

52

53 void eliminare(COADA &vf)

54 {

55 COADA p;

56 if(vf == nullptr)

57 error("COADA ESTE VIDA!");

58 p = vf;

59 vf = vf->next;

60 cout <<"S-a eliminat "<<p->info <<endl;

61 delete p;

62 }

63

64 COADA creareCoada(COADA &sf)

65 {

66 int nr_noduri ,info;

67 COADA vf = nullptr;// creare coada vida

52

Page 53: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

68 cout <<"Nr. de noduri: ";

69 cin >>nr_noduri;

70 for(int i = 1; i <= nr_noduri; i++)

71 {

72 cout <<"Informatia pt. nodul "<<i<<": ";

73 cin >>info;

74 adaugare(vf, sf, info);

75 }

76 return vf;

77 }

78

79 //main.cpp

80 #include "coada.h"

81

82 int main()

83 {

84 COADA prim , ultim;

85 prim = creareCoada(ultim);

86 char c;

87 int infon;

88 do

89 {

90 cout <<"Doriti adaugarea inca a unui element in coada?[D/N]"<<endl;

91 cin >>c;

92 if(c == ’D’ || c == ’d’)

93 {

94 cout << "Introduceti elementul de adaugat in coada: ";

95 cin >> infon;

96 adaugare(prim , ultim , infon);

97 }

98 }while(c == ’D’ || c == ’d’);

99 cout <<"Eliminare elemente din coada."<<endl;

100 do

101 {

102 cout <<"Doriti eliminarea unui element din coada?[D/N]"<<endl;

103 cin >>c;

104 if(c == ’D’ || c == ’d’)

105 eliminare(prim);

106 }while(c == ’D’ || c == ’d’);

107 return 0;

108 }

Exemplul 13. Se citeste de la tastatura o succesiune de paranteze rotunde deschise si ınchise, pana laıntalnirea caracterului de oprire considerat a fi caracterul ’*’. Sa se verifice daca parantezele din sirulcitit se ınchid corect, adica pentru orice paranteza ’(’ exista ın sirul ce ıi urmeaza o paranteza ”pereche”’)’ si pentru orice paranteza ’)’ exista ın sirul ce o precede o paranteza ”pereche” ’(’. De exemplu,parantezele din sirul “()()(())” se ınchid corect, pe cand cele din sirul “(())())”, nu se ınchid corect.

Vom citi sirul de intrare, caracter cu caracter, pana la ıntalnirea caracterului de oprire. Pentru fiecarecaracter citit verificam daca este paranteza deschisa sau ınchisa. In cazul ın care caracterul citit este

53

Page 54: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

paranteza deschisa o inseram ıntr-o structura de tip coada, iar daca este paranteza ınchisa extragem unelement din coada (daca este posibil; daca nu, rezulta ca sirul nu este o expresie corecta). Dupa citireaıntregului sir, verificam daca mai sunt elemente ın coada. Daca raspunsul este afirmativ ınseamna casirul este o expresie gresita.Modificam programul precedent astfel ıncat coada sa pastreze date de tip char.

1 //...

2 struct NOD

3 {

4 char info;

5 NOD *next;

6 };

7

8 typedef NOD* COADA;

9

10 void error(const char msg [])

11 {

12 /*...*/

13 }

14

15 void adaugare(COADA &vf , COADA &sf , char nInfo)

16 {

17 /*...*/

18 }

19

20 void eliminare(COADA &vf)

21 {

22 /*...*/

23 }

24

25 int main()

26 {

27 int corect = 1;

28 COADA vf = nullptr , sf = nullptr;

29 char ch;

30 cout <<"Introduceti cate o paranteza sau "

31 <<"caracterul de terminare ’*’: "<<endl;

32 cin >>ch;

33 while(ch != ’*’)

34 {

35 if(ch == ’(’ )

36 adaugare(vf, sf, ch);

37 else

38 if(ch == ’)’)

39 {

40 if(vf != nullptr)

41 eliminare(vf);

42 else

43 {

44 corect = 0;

54

Page 55: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

45 break;

46 }

47 }

48 else

49 cout <<"Ati introdus un caracter diferit de ’(’ si ’)’. "

50 <<"Nu se efectueaza nicio operatie."<<endl;

51 cin >>ch;

52 }

53 if(corect == 1 && vf == nullptr)

54 cout <<"Parantezele se inchid corect."<<endl;

55 else

56 cout <<"Parantezele nu se inchid corect."<<endl;

57 return 0;

58 }

3.1.3 LLSI – cazul general

Conform definitiei unei liste liniare, aceasta are un prim element si un ultim element, iar toate elementelecu exceptia primului, au un predecesor si toate elementele, cu exceptia ultimului, au un succesor. Inabordarea simplu ınlantuita, fiecare nod al listei dinamice va avea doua parti: o zona de informatie si ozona de legatura care retine adresa nodului succesor. Vom defini o structura care sa descrie alcatuireafiecarui element al listei, structura pe care o vom numi NOD. Structura este una recursiva, ıntrucat unmembru al sau este un pointer spre structura care tocmai se defineste. Acest pointer stabileste o relatiede ordine ıntre noduri. Se va redenumi tipul NOD* cu numele LISTA, folosind cuvantul cheie typedef.Lista va fi gestionata prin intermediul variabilei vf care este un pointer catre NOD (deci de tip LISTA)si care retine adresa primului nod inserat ın lista, numit ın continuare varful listei.

Principalele operatii care se efectueaza asupra listelor sunt: crearea listei vide, inserarea unui elementnou ın lista, eliminarea unui nod al listei, parcurgerea listei si implicit accesul la orice element al listei(pentru modificare sau pentru examinare).

Inserarea unui nod nou o vom realiza la ınceputul listei, la sfarsitul listei, dupa un nod de informatiedata si ınaintea unui nod de informatie data. Etapele inserarii sunt: dupa ce se stabileste pozitia ıncare se va insera noul nod, se aloca memorie pentru nodul nou, se ıncarca zona de informatie a acestuia,apoi se creaza legatura noua si abia la sfarsit se sterge vechea legatura. Daca inserarea are loc ınainteaprimului nod al listei, atunci se modifica varful listei.

info1 adr2

vf

info2 adr3

adrNOU

info3 adr4 . . . infoN nullptr

infoNOU adr3

(1)(2)

Figura 6. Adaugarea unui nod nou dupa/ınaintea un/unui nod de informatie data ıntr-o lista liniarasimplu ınlantuita

Stergerea unui nod din lista se realizeaza astfel: dupa ce se stabileste pozitia nodului ce va fi sters, secreaza noua legatura. Se elibereaza apoi zona de memorie ocupata de nodul ce se doreste a fi sters, prinutilizarea operatorului delete. Daca elementul de sters este primul nod al listei, atunci se modificavarful listei.

55

Page 56: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

info1 adr2

vf

adr3

info2 adr3

(2)

info3 adr4 . . . infoN nullptr

(1)

Figura 7. Stergerea unui nod dintr-o lista liniara simplu ınlantuita

Operatiile pe LLSI pe care le vom implementa ın continuare sunt: adaugarea unui nod nou la sfarsitullistei, adaugarea unui nod nou la ınceput listei, crearea unei liste cu un numar initial de elemente prinadaugari succesive la sfarsitul sau la ınceputul listei, inserarea unui nod nou dupa un nod de informatiedata, inserarea unui nod nou ınaintea unui anumit nod de informatie data, stergerea unui element allistei (cautarea acestuia se realizeaza dupa informatia utila, fiind una secventiala), parcurgerea listei siafisarea informatiilor din noduri, cautarea unei anumite informatii ın lista. O ultima actiune asupralistei va fi inversarea legaturilor. Pentru a avea sens inversarea, lista trebuie sa aiba cel putin douanoduri. Vom utiliza doi pointeri care retin adresa a cate doua noduri succesive si vom parcurge listarealizand inversarea legaturilor. De asemenea, vom implementa un meniu din care utilizatorul va selectaoperatiile dorite.

1 //llsi.h

2 #ifndef LLSI_H

3 #define LLSI_H

4 #include <iostream >

5 using namespace std;

6

7 struct NOD;

8 typedef NOD* LISTA;

9

10 void error(const char []);

11 void adaugareSfarsit(LISTA&, int);

12 void adaugareInceput(LISTA&, int);

13 LISTA creareInceput ();

14 LISTA creareSfarsit ();

15 void adaugareDupa(LISTA , int , int);

16 void adaugareInainte(LISTA&, int , int);

17 void stergereNod(LISTA&, int);

18 void afisareLista(LISTA);

19 int cautareInfo(LISTA , int);

20 void rasturnareLista(LISTA&);

21

22 struct NOD

23 {

24 int info;

25 NOD* next;

26 };

27

28 #endif

29

30 //llsi.cpp

31 #include "llsi.h"

32

56

Page 57: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

33 void error(const char msg [])

34 {

35 cerr <<"Eroare de executie."<<endl;

36 cerr <<msg <<endl;

37 cerr <<"Programul se opreste!"<<endl;

38 exit (1);

39 }

40

41 void adaugareSfarsit(LISTA &vf , int infon)

42 {

43 LISTA nou = new (nothrow) NOD;

44 if(nou == nullptr)

45 error("Spatiu insuficient!");

46 nou ->info = infon;

47 nou ->next = nullptr;

48 if(vf == nullptr) //daca lista este vida

49 vf = nou;

50 else

51 {

52 LISTA p = vf;

53 while(p->next != nullptr) p = p->next;

54 p->next = nou;

55 }

56 }

57

58 void adaugareInceput(LISTA &vf , int infon)

59 {

60 LISTA nou = new (nothrow) NOD;

61 if (nou == nullptr)

62 error("Spatiu insuficient!");

63 nou ->info = infon;

64 nou ->next = vf;

65 vf = nou;

66 }

67

68 LISTA creareInceput ()

69 {

70 int nr_noduri , info;

71 LISTA vf = nullptr;

72 cout <<"Numarul initial de noduri: ";

73 cin >>nr_noduri;

74 for(int i = 1; i <= nr_noduri; i++)

75 {

76 cout <<"Informatia pentru nodul "<<i<<": ";

77 cin >>info;

78 adaugareInceput(vf, info);

79 }

80 return vf;

81 }

57

Page 58: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

82 LISTA creareSfarsit ()

83 {

84 int nr_noduri , info;

85 LISTA vf = nullptr;

86 cout <<"’Numarul initial de noduri: ";

87 cin >>nr_noduri;

88 for(int i = 1; i <= nr_noduri; i++)

89 {

90 cout <<"Informatia pentru nodul "<<i<<": ";

91 cin >>info;

92 adaugareSfarsit(vf, info);

93 }

94 return vf;

95 }

96

97 // adaugarea unui nod nou dupa nodul de informatie infoc

98 void adaugareDupa(LISTA vf , int infoc , int infon)

99 {

100 if(vf == nullptr)

101 {

102 cout <<"LISTA este vida!"<<endl;

103 return;

104 }

105 LISTA p = vf;

106 // cautarea nodului de informatie infoc

107 while(p != nullptr && p->info != infoc)

108 p = p->next;

109 if(p != nullptr)

110 {

111 LISTA nou = new (nothrow) NOD;

112 if(nou == nullptr)

113 error("Spatiu insuficient!");

114 nou ->info = infon;

115 nou ->next = p->next;

116 p->next = nou;

117 }

118 else

119 cout <<"Informatia cautata "<<infoc <<" nu este in LISTA! "

120 <<"Inserarea nu are loc."<<endl;

121 }

122

123 // adaugarea unui nod nou inaintea nodului de informatie infoc

124 void adaugareInainte(LISTA &vf , int infoc , int infon)

125 {

126 if(vf == nullptr)

127 {

128 cout <<"LISTA este vida!"<<endl;

129 return;

130 }

58

Page 59: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

131 if(vf->info == infoc)

132 adaugareInceput(vf, infon);

133 else

134 {

135 LISTA p = vf;

136 while(p->next != nullptr && p->next ->info != infoc)

137 p = p->next;

138 if(p->next != nullptr)

139 {

140 LISTA nou = new (nothrow) NOD ;

141 if(nou == nullptr)

142 error("Spatiu insuficient!");

143 nou ->info = infon;

144 nou ->next = p->next;

145 p->next = nou;

146 }

147 else

148 cout <<"Informatia cautata "<<infoc <<"nu este in LISTA! "

149 <<"Inserarea nu are loc."<<endl;

150 }

151 }

152

153 // stergerea prima aparitie a nodului de informatie infoc

154 void stergereNod(LISTA &vf , int infoc)

155 {

156 LISTA p, q;

157 if(vf == nullptr)

158 {

159 cout <<"LISTA este vida!"<<endl;

160 return;

161 }

162 if(vf->info == infoc)

163 {

164 q = vf;

165 vf = vf ->next;

166 delete q;

167 }

168 else

169 {

170 p = vf;

171 while(p->next != nullptr && p->next ->info != infoc)

172 p = p->next;

173 if(p->next != nullptr)

174 {

175 q = p->next;

176 p->next = q->next;

177 delete q;

178 }

179 else

59

Page 60: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

180 cout <<"Informatia cautata "<<infoc <<" nu este in LISTA! "

181 <<"Stergerea nu are loc."<<endl;

182 }

183 }

184

185 void afisareLista(LISTA vf)

186 {

187 LISTA p = vf;

188 if(p != nullptr)

189 {

190 cout <<"LISTA: ";

191 while(p != nullptr)

192 {

193 cout << p->info << " ";

194 p = p->next;

195 }

196 cout <<endl;

197 }

198 else

199 cout <<"LISTA este vida!"<<endl;

200 }

201

202 int cautareInfo(LISTA vf , int infoc)

203 {

204 int flag =0;

205 LISTA p = vf;

206 while(p != nullptr)

207 {

208 if(p->info == infoc)

209 flag ++;

210 p = p->next;

211 }

212 return flag;

213 }

214

215 void rasturnareLista(LISTA &vf)

216 {

217 LISTA p, q, r;

218 p = vf;

219 q = vf ->next;

220 vf ->next = nullptr;

221 while(q != nullptr)

222 {

223 r = q->next;

224 q->next = p;

225 p = q;

226 q = r;

227 }

228 vf = p;

60

Page 61: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

229 }

230

231 //main.cpp

232 #include "llsi.h"

233

234 int main()

235 {

236 LISTA vf = nullptr;

237 int optiune , infon , infoc , ct;

238 do

239 {

240 cout <<"Lista liniara simplu inlantuita."<<endl;

241 cout <<"1. Creare lista prin inserare la inceputul listei"<<endl;

242 cout <<"2. Creare lista prin inserare la sfarsitul listei"<<endl;

243 cout <<"3. Inserarea unui nod nou dupa un nod "

244 <<"cu o anumita informatie"<<endl;

245 cout <<"4. Inserarea unui nod nou inaintea unui nod "

246 <<"cu o anumita informatie"<<endl;

247 cout <<"5. Adaugarea unui nod nou la inceputul listei"<<endl;

248 cout <<"6. Adaugarea unui nod nou la sfarsitul listei"<<endl;

249 cout <<"7. Stergerea unui nod cu o anumita informatie"<<endl;

250 cout <<"8. Afisare lista"<<endl;

251 cout <<"9. Inversarea legaturilor in lista"<<endl;

252 cout <<"10. Cautarea unei informatii in lista"<<endl;

253 cout <<"11. Terminare"<<endl <<endl;

254

255 cout <<"Optiunea conform numarului de ordine: ";

256 cin >>optiune;

257 switch(optiune)

258 {

259 case 1:

260 vf = creareInceput ();

261 break;

262 case 2:

263 vf = creareSfarsit ();

264 break;

265 case 3:

266 cout <<"Informatia cautata , dupa care se insereaza nodul nou: ";

267 cin >>infoc;

268 cout <<"Informatia noua: ";

269 cin >>infon;

270 adaugareDupa(vf ,infoc ,infon);

271 break;

272 case 4:

273 cout <<"Informatia cautata , inaintea careia "

274 <<"se insereaza nodul nou: ";

275 cin >>infoc;

276 cout <<"Informatia noua: ";

277 cin >>infon;

61

Page 62: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

278 adaugareInainte(vf,infoc ,infon);

279 break;

280 case 5:

281 cout <<"Informatia noua , ce va fi adaugata "

282 <<"la inceputul listei: ";

283 cin >>infon;

284 adaugareInceput(vf,infon);

285 break;

286 case 6:

287 cout <<"Informatia noua , ce va fi adaugata "

288 <<"la sfarsitul listei: ";

289 cin >>infon;

290 adaugareSfarsit(vf,infon);

291 break;

292 case 7:

293 cout <<"Informatia de sters: ";

294 cin >>infoc;

295 stergereNod(vf,infoc);

296 break;

297 case 8:

298 afisareLista(vf);

299 break;

300 case 9:

301 if(!vf)

302 cout <<"Lista este vida!"<<endl;

303 else

304 rasturnareLista(vf);

305 break;

306 case 10:

307 cout <<"Informatia de cautat in lista: ";

308 cin >>infoc;

309 ct = cautareInfo(vf ,infoc);

310 if(ct)

311 cout <<infoc <<" este in lista de "<<ct <<" ori."<<endl;

312 else

313 cout <<infoc <<" nu este in lista."<<endl;

314 break;

315 case 11:

316 break;

317 default:

318 cout <<"Introduceti un numar valid de optiune (1 -> 11)!"<<endl;

319 }

320 }while(optiune !=11);

321 return 0;

322 }

Exemplul 14. Folosind liste liniare simplu inlantuite sa implementam functii de adunare si ınmultirea doua polinoame cu coeficienti reali. Un polinom va fi reprezentat printr-o lista, un nod al listei cores-punzand unui monom. Informatia utila a fiecarui nod al listei trebuie sa contina gradul monomului si

62

Page 63: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

coeficientul acestuia. Pentru a aduna doua polinoame, vom crea o noua lista obtinuta prin concatenarea(alipirea) listelor ce reprezinta cele doua polinoame. Daca doua noduri din lista suma au acelasi grad,coeficientul unuia dintre monoame va fi suma coeficientilor din cele doua noduri, iar coeficientul celuilaltva fi setat la valoarea 0. Cu functia de stergere din lista vom elimina nodurile care au coeficientul 0.Produsul polinoamelor va fi retinut ıntr-o noua lista, ın care fiecare nod este produsul a doua monoame,cate unul din fiecare polinom. Daca doua noduri din lista produs au acelasi grad, coeficientul unuiadintre monoame va deveni egal cu suma coeficientilor, iar coeficientul celuilalt va fi setat la valoarea 0.Apoi folosim functia de stergere din lista si vom elimina nodurile care au coeficientul 0. De asemenea,vom calcula valoarea polinomului ıntr-un punct x real, citit de la tastatura.

1 // polinom.h

2 #ifndef POLINOM_H

3 #define POLINOM_H

4 #include <iostream >

5 using namespace std;

6

7 struct NOD;

8 typedef NOD* LISTA;

9

10 void error(const char []);

11 void adaugareNod(LISTA&, int , double);

12 LISTA creare ();

13 void afisare(LISTA);

14 void canonic(LISTA);

15 LISTA concatenare(LISTA , LISTA);

16 void stergereNod(LISTA&, double);

17 LISTA produs(LISTA , LISTA);

18 LISTA suma(LISTA , LISTA);

19 double valoare(LISTA , double);

20

21 struct NOD

22 {

23 int grad;

24 double coeficient;

25 NOD* next ;

26 };

27 #endif

28

29 // polinom.cpp

30 #include "polinom.h"

31

32 void error(const char msg [])

33 {

34 /*...*/

35 }

36

37 void adaugareNod(LISTA &vf , int gr , double c)

38 {

39 LISTA nou = new (nothrow) NOD;

40 if(nou == nullptr)

63

Page 64: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

41 error("Spatiu insuficient!");

42 nou ->grad = gr;

43 nou ->coeficient = c;

44 nou ->next = vf;

45 vf = nou;

46 }

47

48 LISTA creare ()

49 {

50 LISTA vf = nullptr;

51 int gr;

52 double coef;

53 char c;

54 cout <<"Introduceti gradul ";

55 cin >>gr;

56 cout <<"Introduceti coeficientul ";

57 cin >>coef;

58 adaugareNod(vf, gr, coef);

59 cout <<"Doriti sa adaugati inca un nod?[D/N] ";

60 cin >>c;

61 while(c == ’d’ || c == ’D’)

62 {

63 cout <<"Introduceti gradul ";

64 cin >>gr;

65 cout <<"Introduceti coeficientul ";

66 cin >>coef;

67 adaugareNod(vf, gr, coef);

68 cout <<"Doriti sa adaugati inca un nod?[D/N] ";

69 cin >>c;

70 }

71 return vf;

72 }

73

74 void afisare(LISTA vf)

75 {

76 LISTA p = vf;

77 if(p == nullptr)

78 {

79 cout <<"Lista este vida!"<<endl;

80 return;

81 }

82 cout << p->coeficient << "X^" << p->grad;

83 for (p = p->next; p != nullptr; p = p->next)

84 if(p->coeficient < 0)

85 cout << p->coeficient << "X^" << p->grad;

86 else

87 cout << "+" << p->coeficient << "X^" << p->grad;

88 cout <<endl;

89 }

64

Page 65: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

90 LISTA concatenare(LISTA v1, LISTA v2)

91 {

92 LISTA q, vf = nullptr;

93 for(q = v1; q != nullptr; q = q->next)

94 adaugareNod(vf,q->grad ,q->coeficient);

95 for(q = v2; q != nullptr; q = q->next)

96 adaugareNod(vf,q->grad ,q->coeficient);

97 return vf;

98 }

99

100 void canonic(LISTA vf)

101 {

102 /*daca doua noduri din lista au acelasi grad , coeficientul unuia

dintre monoame va deveni egal cu suma coeficientilor , iar

coeficientul celuilalt va fi egal cu 0*/

103 LISTA p, q;

104 for(p = vf; p != nullptr; p = p->next)

105 for(q = p->next; q != nullptr; q = q->next)

106 if(p->grad == q->grad)

107 {

108 p->coeficient = p->coeficient + q->coeficient;

109 q->coeficient = 0;

110 }

111 }

112

113 void stergereNod(LISTA &vf , double info)

114 {

115 /* stergerea tuturor nodurilor din lista cu informatia (coeficientul)

info*/

116 LISTA q, r;

117 if(vf == nullptr)

118 {

119 cout <<"Lista este vida!"<<endl;

120 return;

121 }

122 while(vf != nullptr && vf ->coeficient == info)

123 {

124 q = vf;

125 vf = vf ->next;

126 delete q;

127 }

128 if(vf != nullptr)

129 {

130 q = vf;

131 while(q->next != nullptr)

132 {

133 if(q->next ->coeficient == info)

134 {

135 r = q->next;

65

Page 66: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

136 q->next = r->next;

137 delete r;

138 }

139 else

140 q = q->next;

141 }

142 }

143 }

144

145 LISTA produs(LISTA v1, LISTA v2)

146 {

147 LISTA vf = nullptr , p, q;

148 for(p = v1; p != nullptr; p = p->next)

149 for(q = v2; q != nullptr; q = q->next)

150 adaugareNod(vf,p->grad+q->grad , p->coeficient*q->coeficient);

151 return vf;

152 }

153

154 double valoare(LISTA vf , double x)

155 {

156 LISTA p;

157 double produs , suma = 0;

158 for(p = vf; p != nullptr; p = p->next)

159 {

160 produs = 1;

161 for(int i = 1; i <= p->grad; i++)

162 produs = produs * x;

163 suma += p->coeficient * produs;

164 }

165 return suma;

166 }

167

168 //main.cpp

169 #include "polinom.h"

170

171 int main()

172 {

173 LISTA p1 = nullptr , p2 = nullptr , prod = nullptr , suma = nullptr;

174 p1 = creare ();

175 cout <<"Primul polinom."<<endl;

176 afisare(p1);

177 p2 = creare ();

178 cout <<"Al doilea polinom."<<endl;

179 afisare(p2);

180 suma = concatenare(p1 , p2);

181 cout <<"Polinoamele concatenate"<<endl;

182 afisare(suma);

183 canonic(suma);

184 cout <<"Suma polinoamelor"<<endl;

66

Page 67: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

185 stergereNod(suma , 0);

186 afisare(suma);

187 cout <<"Produsul polinoamelor"<<endl;

188 prod = produs(p1 , p2);

189 canonic(prod);

190 stergereNod(prod , 0);

191 afisare(prod);

192 double x;

193 cout <<"Introduceti x : ";

194 cin >>x;

195 cout <<"Valoarea lui P1 in "<<x<<" este "<<valoare(p1 ,x)<<endl;

196 return 0;

197 }

3.2 Liste liniare dublu ınlantuite

O lista liniara dublu ınlantuita este o structura dinamica de date care are un prim element si un ultimelement, iar toate elementele cu exceptia primului, au un predecesor si toate elementele, cu exceptiaultimului, au un succesor. De asemenea, ıntre elementele unei liste dublu ınlantuite este definita odubla relatie de ordine. Fiecare nod are o zona de informatie utila si doua zone de legatura: una retineadresa nodului succesor, iar cealalta, adresa nodului predecesor. Astfel, o lista dublu ınlantuita poatefi parcursa ın ambele sensuri. Vom defini o structura care sa descrie alcatuirea fiecarui element al listei,structura pe care o vom numi NOD. Astfel, structura NOD va contine trei campuri: primul, info (de tipint) reprezinta informatia utila a nodului, al doilea camp, next (de tip pointer catre NOD) este pointerulcare pastreaza adresa nodului urmator, iar al treilea, back, este pointerul care retine adresa noduluianterior. Se va redenumi tipul NOD* cu numele LISTA, folosind cuvantul cheie typedef. Lista va figestionata prin intermediul variabilelor first si last care sunt pointeri catre NOD (deci de tip LISTA)ın care se pastreaza adresa primului nod inserat ın lista, respectiv adresa ultimului nod inserat ın lista.Utilizarea pointerului last este optionala, lista putand fi gestionata doar prin intermediul pointeruluifirst. Astfel, first->back = nullptr si last->next = nullptr.

Operatiile posibile pe liste liniare dublu ınlantuite sunt cele obisnuite pe liste liniare: crearea listeivide (necesita initializarile first = nullptr si last = nullptr), inserarea unui element nou ın lista,eliminarea unui nod din lista, parcurgerea listei si implicit accesarea oricarui element al listei ın vedereamanipularii acestuia.Inserarea unui nod nou o vom realiza ınaintea primului nod, dupa ultimul nod, dupa un nod de informatiedata si ınaintea unui nod de informatie data. Etapele inserarii sunt: dupa ce se stabileste pozitia ıncare se va insera noul nod, se aloca memorie pentru nodul nou, se ıncarca zona de informatie a acestuia,apoi se creaza legaturile noi si abia la sfarsit se sterg vechile legaturi. Daca inserarea are loc ınainteaprimului nod al listei, atunci se modifica first, iar daca are loc dupa ultimul nod, se modifica last.

nullptr info1

adrNOU adrNOU

adr2

adr1

adr1 infoNOU adr2

(1)(2)(2) (1)

adr1 info2 adr3 . . . adr(N-1) infoN nullptr

adrN

Figura 8. Inserarea unui nod nou intr-o lista liniara dublu ınlantuita

67

Page 68: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Stergerea unui nod din lista se realizeaza astfel: dupa ce se stabileste pozitia nodului ce va fi sters, secreaza noile legaturi. Apoi, se elibereaza zona de memorie ocupata de nodul ce se doreste a fi sters,prin utilizarea operatorului delete. Daca elementul de sters este primul nod al listei, atunci se modificafirst, iar daca este ultimul nod, se modifica last.

nullptr info1 adr2

adr1

adr1 info2 adr3

adr2

adr2 info3 adr4

adr3

. . . adr(N-1) infoN nullptr

adrN

(2)adr3 adr1

(1)

(1)

Figura 9. Stergerea unui nod dintr-o lista liniara dublu ınlantuita

1 //lldi.h

2 #ifndef LLDI_H

3 #define LLDI_H

4 #include <iostream >

5 using namespace std;

6

7 struct NOD;

8 typedef NOD* LISTA;

9

10 void error(const char []);

11 void adaugareSfarsit(LISTA&, LISTA&, int);

12 void adaugareInceput(LISTA&, LISTA&, int);

13 LISTA creareInceput(LISTA &);

14 LISTA creareSfarsit(LISTA &);

15 void adaugareDupa(LISTA , LISTA&, int , int);

16 void adaugareInainte(LISTA&, LISTA , int , int);

17 void stergereNod(LISTA&, LISTA&, int);

18 void afisareListaInainte(LISTA);

19 void afisareListaInapoi(LISTA);

20 int cautareInfo(LISTA , int);

21 void stergereLista(LISTA&, LISTA&);

22

23 struct NOD

24 {

25 int info;

26 LISTA next; // pointer catre succesor

27 LISTA back; // pointer catre predecesor

28 };

29 #endif

30

31

32 //lldi.cpp

33 #include "lldi.h"

34

68

Page 69: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

35 void error(const char msg [])

36 {

37 cerr <<"Eroare de executie."<<endl;

38 cerr <<msg << endl;

39 cerr <<"Programul se opreste!"<<endl;

40 exit (1);

41 }

42

43 void adaugareSfarsit(LISTA &v, LISTA &s, int infon)

44 {

45 LISTA nou = new (nothrow) NOD;

46 if(nou == nullptr)

47 error("Spatiu insuficient!");

48 nou ->info = infon;

49 nou ->next = nullptr;

50 if(v == nullptr) //lista este vida

51 {

52 //nou va fi singurul nod din lista

53 nou ->back = nullptr;

54 v = nou;

55 s = nou;

56 }

57 else

58 {

59 nou ->back = s;

60 s->next = nou;

61 s = nou;

62 }

63 }

64

65 void adaugareInceput(LISTA &v, LISTA &s, int infon)

66 {

67 LISTA nou = new (nothrow) NOD;

68 if(nou == nullptr)

69 error("Spatiu insuficient!");

70 nou ->info = infon;

71 nou ->back = nullptr;

72 if(v == nullptr) //lista este vida

73 {

74 nou ->next = nullptr;

75 v = nou;

76 s = nou;

77 }

78 else

79 {

80 nou ->next = v;

81 v->back = nou;

82 v = nou;

83 }

69

Page 70: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

84 }

85

86 LISTA creareInceput(LISTA &last)

87 {

88 int nr_noduri , info;

89 LISTA first = nullptr;

90 last = nullptr;

91 cout <<"Nr. initial de noduri: ";

92 cin >>nr_noduri;

93 for(int i = 1; i <= nr_noduri; i++)

94 {

95 cout <<"Informatia pt. nodul "<<i<<" : ";

96 cin >>info;

97 adaugareInceput(first , last , info);

98 }

99 return first;

100 }

101

102 LISTA creareSfarsit(LISTA &last)

103 {

104 int nr_noduri , info;

105 LISTA first = nullptr;

106 last = nullptr;

107 cout <<"Nr. initial de noduri: ";

108 cin >>nr_noduri;

109 for(int i = 1; i <= nr_noduri; i++)

110 {

111 cout <<"Dati informatia pt. nodul "<<i<< " : ";

112 cin >>info;

113 adaugareSfarsit(first , last , info);

114 }

115 return first;

116 }

117

118 void adaugareDupa(LISTA v, LISTA &s, int infoc , int infon)

119 {

120 //se cauta nodul de informatie infoc , adresa sa va fi retinuta in p

121 LISTA p = v;

122 while(p != nullptr && p->info != infoc)

123 p = p->next;

124 if(p != nullptr)

125 {

126 LISTA nou = new (nothrow) NOD;

127 if(nou == nullptr)

128 error("Spatiu insuficient!");

129 nou ->info = infon;

130 //se creaza legaturile noi

131 nou ->back = p;

132 nou ->next = p->next;

70

Page 71: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

133 //se sterg vechile legaturi

134 if(p->next != nullptr) p->next ->back = nou;

135 p->next = nou;

136 //daca se adauga dupa ultimul nod

137 if(s == p) s = nou;

138 }

139 else

140 cout <<"Informatia cautata "<<infoc <<" nu este in LISTA! "

141 <<"Inserarea nu are loc."<<endl;

142 }

143

144 void adaugareInainte(LISTA &v, LISTA s, int infoc , int infon)

145 {

146 //se cauta nodul de informatie infoc , adresa sa va fi retinuta in p

147 LISTA p = v;

148 while(p != nullptr && p->info != infoc)

149 p = p->next;

150 if(p != nullptr)

151 {

152 LISTA nou = new (nothrow) NOD;

153 if(nou == nullptr)

154 error("Spatiu insuficient!");

155 nou ->info = infon;

156 //se creaza legaturile noi

157 nou ->back = p->back;

158 nou ->next = p;

159 //se sterg vechile legaturi

160 if(p->back != nullptr) p->back ->next = nou;

161 p->back = nou;

162 //daca se adauga inaintea primului nod

163 if(p == v) v = nou;

164 }

165 else

166 cout <<"Informatia cautata "<<infoc <<" nu este in LISTA! "

167 <<"Inserarea nu are loc."<<endl;

168 }

169

170 void stergereNod(LISTA &v, LISTA &s, int infoc)

171 {

172 //se cauta nodul de informatie infoc , adresa sa va fi retinuta in p

173 LISTA p = v;

174 while(p != nullptr && p->info != infoc)

175 p = p->next;

176 if(p != nullptr)

177 {

178 if (v == p && s == p) // lista are un singur nod

179 {

180 //lista devine vida

181 v = nullptr;

71

Page 72: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

182 s = nullptr;

183 delete p; // stergere nod

184 }

185 else

186 if(p == v) //se sterge primul nod

187 {

188 v = v->next;

189 v->back = nullptr;

190 delete p;

191 }

192 else

193 if(p == s) // se sterge ultimul nod

194 {

195 s = s->back;

196 s->next = nullptr;

197 delete p;

198 }

199 else

200 // nodul de sters este diferit de capete

201 {

202 // legaturile noi

203 //p->back se leaga de p->next

204 p->next ->back = p->back;

205 p->back ->next = p->next;

206 delete p;

207 }

208 }

209 else

210 cout <<"Informatia cautata "<<infoc <<" nu este in LISTA! "

211 <<"Stergerea nu are loc."<<endl;

212 }

213

214 void afisareListaInainte(LISTA v)

215 {

216 // afisarea listei de la primul la ultimul nod

217 LISTA p = v;

218 if(p != nullptr)

219 {

220 cout << "LISTA: ";

221 for(; p != nullptr; p = p->next)

222 cout <<p->info <<" ";

223 cout <<endl;

224 }

225 else

226 cout <<"LISTA este vida!"<<endl;

227 }

228

229 void afisareListaInapoi(LISTA s)

230 {

72

Page 73: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

231 // afisarea listei de la ultimul la primul nod

232 LISTA p = s;

233 if(p != nullptr)

234 {

235 cout <<"LISTA: ";

236 for(; p != nullptr; p = p->back)

237 cout <<p->info <<" ";

238 cout <<endl;

239 }

240 else

241 cout <<"LISTA este vida!"<<endl;

242 }

243

244 int cautareInfo(LISTA v, int infoc)

245 {

246 // cautarea in lista a nodului de informatie infoc

247 LISTA p = v;

248 while(p != nullptr)

249 {

250 if(p->info == infoc)

251 return 1;

252 p = p->next;

253 }

254 return 0;

255 }

256

257 void stergereLista(LISTA &v, LISTA &s)

258 {

259 // eliberarea spatiului de memorie ocupat de intreaga lista

260 LISTA p;

261 while(v != nullptr)

262 {

263 p = v;

264 if(v == s)

265 {

266 v = nullptr;

267 s = nullptr;

268 }

269 else

270 {

271 v = v->next;

272 v->back = nullptr;

273 }

274 delete p;

275 }

276 }

277

278 //main.cpp

279 #include "lldi.h"

73

Page 74: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

280 int main()

281 {

282 LISTA first = nullptr , last = nullptr;

283 int optiune , infon , infoc , ok;

284 do

285 {

286 cout <<endl << "Lista liniara dublu inlantuita."<<endl;

287 cout <<"1. Creare lista prin adaugare la inceput."<<endl;

288 cout <<"2. Creare lista prin adaugare la sfarsit."<<endl;

289 cout <<"3. Inserarea unui nod nou dupa un nod "

290 << "cu o anumita informatie."<<endl;

291 cout <<"4. Inserarea unui nod nou inaintea "

292 << "unui nod cu o anumita informatie."<<endl;

293 cout <<"5. Adaugarea unui nod nou la inceputul listei."<<endl;

294 cout <<"6. Adaugarea unui nod nou la sfarsitul listei."<<endl;

295 cout <<"7. Stergerea unui nod cu o anumita informatie."<<endl;

296 cout <<"8. Afisare lista inainte."<<endl;

297 cout <<"9. Afisare lista inapoi."<<endl;

298 cout <<"10. Cautarea unei informatii in lista."<<endl;

299 cout <<"11. Stergerea completa a listei."<<endl;

300 cout <<"12. Terminare program."<<endl <<endl;

301

302 cout <<"Optiunea conform numarului de ordine: ";

303 cin >>optiune;

304 switch(optiune)

305 {

306 case 1:

307 first = creareInceput(last);

308 break;

309 case 2:

310 first = creareSfarsit(last);

311 break;

312 case 3:

313 if(first != nullptr)

314 {

315 cout <<"Informatia cautata , "

316 <<"dupa care se realizeaza inserarea: ";

317 cin >>infoc;

318 cout <<"Informatia noua: ";

319 cin >>infon;

320 adaugareDupa(first , last , infoc , infon);

321 }

322 else

323 cout <<"Lista este vida. Inserarea nu are loc."<<endl;

324 break;

325 case 4:

326 if(first != nullptr)

327 {

328 cout <<"Informatia cautata , "

74

Page 75: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

329 <<"inaintea careia se insereaza nodul nou: ";

330 cin >>infoc;

331 cout <<"Informatia noua: ";

332 cin >>infon;

333 adaugareInainte(first , last , infoc , infon);

334 }

335 else

336 cout <<"Lista este vida. Inserarea nu are loc."<<endl;

337 break;

338 case 5:

339 cout <<"Informatia noua , ce va fi adaugata la inceputul listei: ";

340 cin >>infon;

341 adaugareInceput(first , last , infon);

342 break;

343 case 6:

344 cout <<"Informatia noua , ce va fi adaugata la sfarsitul listei: ";

345 cin >>infon;

346 adaugareSfarsit(first , last , infon);

347 break;

348 case 7:

349 if(first != nullptr)

350 {

351 cout <<"Informatia de sters: ";

352 cin >>infoc;

353 stergereNod(first , last , infoc);

354 }

355 else

356 cout <<"Lista este vida. Stergerea nu are loc."<<endl;

357 break;

358 case 8:

359 afisareListaInainte(first);

360 break;

361 case 9:

362 afisareListaInapoi(last);

363 break;

364 case 10:

365 if(first != nullptr)

366 {

367 cout <<"Informatia de cautat in lista: ";

368 cin >>infoc;

369 ok = cautareInfo(first , infoc);

370 if(ok == 1)

371 cout <<infoc <<" este in lista."<<endl;

372 else

373 cout <<infoc <<" nu este in lista."<<endl;

374 }

375 else

376 cout <<"Lista este vida. Cautarea nu are loc."<<endl;

377 break;

75

Page 76: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

378 case 11:

379 if(first != nullptr)

380 stergereLista(first , last);

381 else

382 cout <<"Lista este vida. Stergerea nu are loc."<<endl;

383 break;

384 case 12:

385 break;

386 default:

387 cout <<"Introduceti un numar de optiune corect "

388 <<"(1 -- 12)!"<<endl;

389 }

390 }while(optiune != 12);

391 return 0;

392 }

Exemplul 15. Ne propunem sa implementam conceptul de matrice rara folosind liste liniare dubluınlantuite. O matrice rara este o matrice de dimensiune mare cu un numar foarte mare de elemente nule.Astfel, utilizarea structurii de date uzuale pentru manipularea matricelor si anume tabloul bidimensionalstatic, duce la ocuparea nejustificata a memoriei prin stocarea inutila a valorilor nule. Acestea nuinfluenteaza rezultatul operatiilor cu matrice (adunare, ınmultire etc.), pastrarea acestora ın memorieducand la cresterea duratei de executie a acestor operatii prin ocuparea procesorului cu adunari siınmultiri cu zero. Acest lucru se observa cu cat dimensiunea matricei este mai mare. Astfel, pentrumatrice de dimensiuni mari, s-au cautat modalitati de reprezentare a acestora, ın care sa se renunte lamemorarea elementelor nule. In mod evident, pentru fiecare element nenul memorat, trebuie retinutindexul de linie si cel de coloana al acestuia. Listele reprezinta una dintre modalitatile de reprezentarea matricelor rare. Exista si dezavantaje ale acestei abordari: daca numarul de valori nenule nu estemult mai mic decat numarul de valori nule, atunci spatiul de memorie necesar stocarii matricei rare estemult mai mare, iar implementare operatiilor pe matrice este mai dificila datorita faptului ca accesul laelementele matricei nu este unul direct. In practica, daca ponderea elementelor nenule ın cadrul matriceise afla in intervalul [0, 0015; 0, 03], atunci se opteaza pentru pastrarea doar a acestora ın memorie.

Definim un nou tip, ElementMR, corespunzator unui element nenul al unei matrice rare. Campurilestructurii care defineste noul tip sunt: indexul de linie al elementului nenul, indexul de coloana sivaloarea efectiva a acestuia. Fiecare nod al listei liniare dublu ınlantuite prin care implementam omatrice rara va contine o zona de informatie utila reprezentata de o data de tip ElementMR si douazone de legatura ce vor retine adresa succesorului, respectiv a predecesorului nodului. Crearea listei serealizeaza prin adaugare de noduri noi la sfarsitul listei.Se citesc elementele nenule ale unei matrice. Daca matricea satisface criteriile pentru a fi memorata camatrice rara, atunci elementele nenule vor fi adaugate ın lista, ımpreuna cu elementele de identificarea pozitiei acestora ın matrice. Ne propunem sa implementam operatiile de transpunere a unei matricerare si de adunare a doua matrice rare. Transpunerea se realizeaza prin inversarea indexului de liniecu cel de coloana, pentru fiecare nod. Pentru operatia de adunare, vom crea o noua lista obtinuta princoncatenareaa listelor ce memoreaza cele doua matrice. Daca doua noduri din lista rezultata au acelasiindice de linie si acelasi indice de coloana, valoarea pastrata ıntr-un nod va fi suma valorilor din celedoua noduri, iar valoarea pastrata ın celalalt va deveni 0. Cu o functie de stergere din lista vom eliminanodurile care au valoarea egala cu 0.

1 // matrice_rara.h

2 #ifndef MR_H

3 #define MR_H

76

Page 77: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

4 #include <iostream >

5 #define dim 100

6 using namespace std;

7

8 struct ElementMR;

9 struct NOD;

10 typedef NOD* LISTA;

11

12 void error(const char []);

13 void adaugareSfarsit(LISTA&, LISTA&, ElementMR);

14 void citireMR(double [][ dim], unsigned int , unsigned int);

15 void afisareMR(LISTA v, unsigned int , unsigned int);

16 LISTA creareSfarsit(LISTA&,double [][dim],

17 unsigned int ,unsigned int ,int&,unsigned int&);

18 void afisareListaInainte(LISTA);

19 void transpusaMR(LISTA , LISTA&, LISTA&);

20 void concatenareListe(LISTA , LISTA , LISTA&, LISTA&);

21 void canonic(LISTA);

22 void stergereNod(LISTA&, LISTA&);

23 void stergereLista(LISTA&, LISTA&);

24

25 struct ElementMR

26 {

27 //tip corespunzator fiecarui element nenul al matricei rare

28 unsigned int i, j;// indicii de linie si de coloana

29 double valoare; // valoarea nenula

30 };

31

32 struct NOD

33 {

34 ElementMR elem;

35 LISTA next; // pointer catre succesor

36 LISTA back; // pointer catre predecesor

37 };

38 #endif

39

40 // matrice_rara.cpp

41 #include "matrice_rara.h"

42

43 void error(const char msg [])

44 {

45 /* ... */

46 }

47

48 void adaugareSfarsit(LISTA &v, LISTA &s, ElementMR elemN)

49 {

50 LISTA nou = new (nothrow) NOD;

51 if(nou == nullptr)

52 error("Spatiu insuficient!");

77

Page 78: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

53 nou ->elem = elemN;

54 nou ->next = nullptr;

55 if(v == nullptr) //lista este vida

56 {

57 //nou va fi singurul nod din lista

58 nou ->back = nullptr;

59 v = nou;

60 s = nou;

61 }

62 else

63 {

64 nou ->back = s;

65 s -> next = nou;

66 s = nou;

67 }

68 }

69

70

71 // citire matrice rara

72 void citireMR(double A[][ dim],unsigned int m,unsigned int n)

73 {

74 char c;

75 double val;

76 unsigned int i, j;

77 for(i = 0; i < m; i++)

78 for(j = 0; j < n; j++)

79 A[i][j] = 0.0;

80 cout <<"Introduceti un prim element nenul al matricei? [D/N]: ";

81 cin >>c;

82 while(c == ’d’ || c == ’D’)

83 {

84 do

85 {

86 cout <<"Introduceti indexul de linie al "

87 <<"elementului nenul [1... dim]: ";

88 cin >>i;

89 }while(i > dim || i == 0);

90 do

91 {

92 cout <<"Introduceti indexul de coloana al "

93 <<"elementului nenul [1... dim]: ";

94 cin >>j;

95 }while(j > dim || j == 0);

96 cout <<"Introduceti valoarea elementului nenul: ";

97 cin >>val;

98 if(A[i-1][j-1] == 0)

99 A[i-1][j-1] = val;

100 else

101 {

78

Page 79: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

102 cout <<"Pozitia indicata contine un element "

103 <<"de valoare nenula! Rescrieti elementul ?[D/N]: ";

104 cin >>c;

105 if(c == ’d’ || c == ’D’)

106 A[i-1][j-1] = val;

107 }

108 cout <<"Mai introduceti un element nenul al matricei? [D/N]: ";

109 cin >>c;

110 }

111 }

112

113 /*se adauga in lista , la sfarsitul acesteia , doar elementele nenule ale

matricei rare*/

114 LISTA creareSfarsit(LISTA &last , double A[][dim],

115 unsigned int m,unsigned int n,int &ok,unsigned int &nrElem)

116 {

117 nrElem = 0;

118 unsigned int i,j;

119 LISTA first = nullptr;

120 last = nullptr;

121 for(i = 0; i < m; i++)

122 for(j = 0; j < n; j++)

123 if(A[i][j] != 0)

124 nrElem ++;

125 double pondere = nrElem/double(m*n);

126 //se verifica conditia ca o matrice sa fie rara

127 if(pondere >= 0.0015 && pondere <= 0.03)

128 {

129 ok = 1;

130 for(i = 0; i < m; i++)

131 for(j = 0; j < n; j++)

132 if(A[i][j] != 0)

133 {

134 ElementMR m;

135 m.i = i + 1;

136 m.j = j + 1;

137 m.valoare = A[i][j];

138 adaugareSfarsit(first , last , m);

139 }

140 }

141 else

142 ok = 0;

143 return first;

144 }

145

146

147 // afisare matrice rara

148 void afisareMR(LISTA v, unsigned int m, unsigned int n)

149 {

79

Page 80: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

150 unsigned int k, t;

151 for(k = 1; k <= m; k++)

152 {

153 for(t = 1; t <= n; t++)

154 {

155 int ok = 1;

156 for(LISTA p = v; p != nullptr; p = p->next)

157 if((p->elem.i == k) && (p->elem.j == t))

158 {

159 cout <<p->elem.valoare <<" ";

160 ok = 0;

161 }

162 if(ok == 1)

163 cout <<"0 ";

164 }

165 cout <<endl;

166 }

167 }

168

169 void afisareListaInainte(LISTA v)

170 {

171 // afisarea de la primul la ultimul nod

172 LISTA p = v;

173 if(p != nullptr)

174 for(; p != nullptr; p = p->next)

175 cout <<p->elem.i<<" "<<p->elem.j<<" "<<p->elem.valoare <<endl;

176 else

177 cout <<"LISTA este vida!"<<endl;

178 }

179

180 // transpusa unei matrice rare

181 void transpusaMR(LISTA v, LISTA &vt , LISTA &st)

182 {

183 LISTA p = v;

184 vt = nullptr;

185 st = nullptr;

186 while(p != nullptr)

187 {

188 ElementMR elemN;

189 elemN.i = p->elem.j;

190 elemN.j = p->elem.i;

191 elemN.valoare = p->elem.valoare;

192 adaugareSfarsit(vt, st, elemN);

193 p = p->next;

194 }

195 }

196 // concataenarea a doua liste

197 void concatenareListe(LISTA v1 , LISTA v2 , LISTA &vsum , LISTA &ssum)

198 {

80

Page 81: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

199 vsum = 0;

200 ssum = 0;

201 LISTA q;

202 for(q = v1; q != nullptr; q = q->next)

203 adaugareSfarsit(vsum , ssum , q->elem);

204 for(q = v2; q != nullptr; q = q->next)

205 adaugareSfarsit(vsum , ssum , q->elem);

206 }

207

208 /* aduna valorile pentru nodurile ce au acelasi index de linie si de

coloana si atribuie 0 valorii unuia dintre cele doua noduri */

209 void canonic(LISTA v)

210 {

211 LISTA p, q;

212 for (p = v; p != nullptr; p = p->next)

213 for (q = p->next; q != nullptr; q = q->next)

214 if(p->elem.i == q->elem.i && p->elem.j == q->elem.j)

215 {

216 p->elem.valoare = p->elem.valoare + q->elem.valoare;

217 q->elem.valoare = 0;

218 }

219 }

220

221 // sterge din lista toate nodurile de valoare nula

222 void stergereNod(LISTA &v, LISTA &s)

223 {

224 LISTA q, r;

225 if(v == nullptr)

226 {

227 cout <<"Lista este vida!"<<endl;

228 return;

229 }

230 while(v != nullptr && v->elem.valoare == 0)

231 {

232 q = v;

233 if(q != s)

234 {

235 v = v->next;

236 v->back = nullptr;

237 delete q;

238 }

239 else

240 {

241 v = nullptr;

242 s = nullptr;

243 delete q;

244 }

245 }

246 if(v != nullptr)

81

Page 82: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

247 {

248 q = v;

249 while(q->next != nullptr)

250 if(q->next ->elem.valoare == 0)

251 {

252 r = q->next;

253 if(r != s)

254 {

255 q->next = r->next;

256 r->next ->back = q;

257 }

258 else

259 {

260 s = s->back;

261 s->next = nullptr;

262 }

263 delete r;

264 }

265 else q = q->next;

266 }

267 }

268

269 void stergereLista(LISTA &v, LISTA &s)

270 {

271 // stergerea intregii liste

272 /* functia din aplicatia precedenta */

273 }

274

275 //main.cpp

276 #include "matrice_rara.h"

277

278 int main()

279 {

280 double A[dim][dim], B[dim][dim];

281 unsigned int mA, nA, mB, nB;

282 unsigned int nrElem;

283 int okA , okB;

284 LISTA firstA , lastA , firstB , lastB , firstT , lastT;

285 LISTA firstS , lastS;

286 cout <<"Numar de linii ale matricei A: ";

287 cin >>mA;

288 cout <<"Numar de coloane ale matricei A: ";

289 cin >>nA;

290 citireMR(A, mA, nA);

291 firstA = creareSfarsit(lastA , A, mA , nA , okA , nrElem);

292 cout <<"Numar de linii ale matricei B: ";

293 cin >>mB;

294 cout <<"Numar de coloane ale matricei B: ";

295 cin >>nB;

82

Page 83: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

296 citireMR(B, mB, nB);

297 firstB = creareSfarsit(lastB , B, mB , nB , okB , nrElem);

298 if(okA)

299 {

300 cout <<"Lista continand matricea rara A:"<<endl;

301 afisareListaInainte(firstA);

302 cout <<"Matricea A: "<<endl;

303 afisareMR(firstA , mA, nA);

304 //se determina transpusa lui A

305 transpusaMR(firstA , firstT , lastT);

306 cout <<"Lista continand matricea transpusa:"<<endl;

307 afisareListaInainte(firstT);

308 cout <<"Matricea transpusa: "<<endl;

309 afisareMR(firstT , mA, nA);

310 stergereLista(firstT , lastT);

311 }

312 else

313 cout <<"Tabloul bidimensional A nu indeplineste "

314 <<"criteriile de matrice rara!"<<endl;

315 if(okB)

316 {

317 cout <<"Lista continand matricea rara B:"<<endl;

318 afisareListaInainte(firstB);

319 cout <<"Matricea B: "<<endl;

320 afisareMR(firstB , mB, nB);

321 }

322 else

323 cout <<"Tabloul bidimensional B nu indeplineste "

324 <<"criteriile de matrice rara!"<<endl;

325 if(okA && okB)

326 {

327 if(mA == mB && nA == nB)

328 {

329 concatenareListe(firstA , firstB , firstS , lastS);

330 canonic(firstS);

331 //se sterg nodurile cu valoare = 0

332 stergereNod(firstS , lastS);

333 cout <<"Lista continand matricea suma:"<<endl;

334 afisareListaInainte(firstS);

335 cout <<"Matricea suma: "<<endl;

336 afisareMR(firstS , mA, nA);

337 // stergere liste

338 stergereLista(firstA , lastA);

339 stergereLista(firstB , lastB);

340 stergereLista(firstS , lastS);

341 }

342 else

343 cout <<"Matricele au dimensiuni diferite!"<<endl;

344 }

83

Page 84: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

345 else

346 cout <<"Tablourile (unul sau amandoua) nu "

347 <<"indeplinesc criteriile de matrice rara!"<<endl;

348 return 0;

349 }

3.3 Liste circulare simplu ınlantuite

O lista simplu ınlantuita este circulara daca succesorul ultimului element al listei este chiar primulelement adaugat ın lista. Asadar, ıntr-o lista circulara simplu ınlantuita fiecare nod are un succesorsi un predecesor si niciunul dintre nodurile listei nu va contine pointerul null ın zona de legatura. Incadrul listei circulare simplu ınlantuite nu exista capete. Pentru gestionarea ei se va folosi un pointercatre un nod oarecare al listei. Este totusi convenabil sa gestionam lista circulara simplu ınlantuitacu ajutorul unui pointer (crt) catre ultimul element adaugat ın lista, numit si nodul curent. Astfel,crt->next va fi adresa primului nod adaugat ın lista. Nodurile pot fi accesate secvential pornind de lanodul de adresa crt.

info1 adr2 info2 adr3 info3 adr4 . . . infoN adr1

Figura 10. Lista circulara simplu ınlantuita

Operatiile care se pot efectua asupra listelor circulare sunt aceleasi ca ın cazul listelor liniare simpluınlantuite: crearea listei vide (crt = nullptr), inserarea unui element nou ın lista, eliminarea unui nodal listei, parcurgerea listei pentru a realiza diverse operatii asupra informatiilor din noduri.Pentru inserarea unui nod nou sunt doua situatii: inserarea nodului nou ınaintea unui nod de informatiedata sau dupa un nod de informatie data. In ambele cazuri se identifica acest nod. Daca exista, adresasa se va retine ın pointerul q. Apoi se aloca memorie pentru nodul nou, se ıncarca zona de informatiea acestuia, se creaza legatura noua corespunzatoare si, ın final, se sterge legatura veche.Pentru a sterge un nod de informatie data, mai ıntai se cauta nodul ın lista. Daca acesta exista, se vaelibera zona de memorie ocupata de acesta, nu ınainte de a crea legatura noua. Daca nodul de sterseste nodul a carei adresa este retinuta ın pointerul crt, atunci crt va tinti spre nodul precedent.

Implementam alte trei operatii: cautarea ın lista a unui nod de informatie data cu returnarea adreseiacestuia (daca nodul se gaseste ın lista), stergerea completa a listei si realizarea unei copii a listei.

1 //lcsi.h

2 #ifndef LCSI_H

3 #define LCSI_H

4 #include <iostream >

5 using namespace std;

6

7 struct NOD;

8 typedef NOD* LISTA;

9

10 void error(const char []);

11 void adaugare(LISTA&, int);

84

Page 85: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

12 LISTA creareLista ();

13 void afisareLista(LISTA);

14 LISTA cautareNod(LISTA , int);

15 void inserareInainte(LISTA&, int , int);

16 void inserareDupa(LISTA&, int , int);

17 void stergereNod(LISTA&, int);

18 void stergereLista(LISTA&);

19 LISTA copieLista(LISTA);

20

21 struct NOD

22 {

23 int info;

24 NOD* next;

25 };

26 #endif

27

28 //lcsi.cpp

29 #include "lcsi.h"

30

31 void error(const char msg [])

32 {

33 cerr <<"Eroare de executie."<<endl;

34 cerr <<msg <<endl;

35 cerr <<"Programul se opreste!"<<endl;

36 exit (1);

37 }

38

39 void adaugare(LISTA &crt , int infon)

40 {

41 /* adaugarea unui nod nou dupa ultimul nod adugat in lista , adica

dupa nodul a carui adresa este retinuta de crt */

42 LISTA nou = new (nothrow) NOD;

43 if(nou == nullptr)

44 error("Spatiu insuficient!");

45 nou ->info = infon;

46 if(crt == nullptr) // lista este vida

47 {

48 crt = nou;

49 //crt contine adresa ultimului nod adaugat , nodul curent

50 crt ->next = nou;

51 //crt ->next este adresa primului nod adaugat

52 }

53 else

54 {

55 nou ->next = crt ->next;

56 crt ->next = nou;

57 crt = nou;

58 }

59 }

85

Page 86: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

60 LISTA creareLista ()

61 {

62 int nr_noduri , info;

63 LISTA crt = nullptr; // lista vida

64 cout <<"Numarul initial de noduri: ";

65 cin >>nr_noduri;

66 for(int i = 1; i <= nr_noduri; i++)

67 {

68 cout <<"Informatia pentru nodul "<<i<<" : ";

69 cin >>info;

70 adaugare(crt , info);

71 }

72 return crt;

73 }

74

75 void afisareLista(LISTA crt)

76 {

77 LISTA p = crt;

78 if(p != nullptr)

79 {

80 do

81 {

82 cout <<p->info <<" ";

83 p = p->next;

84 }while(p != crt);

85 cout << endl;

86 }

87 else

88 cout <<"Lista este vida!"<<endl;

89 }

90

91 LISTA cautareNod(LISTA crt , int infoc)

92 {

93 LISTA p = crt;

94 if(p != nullptr)

95 {

96 do

97 {

98 if(p->info == infoc)

99 return p;

100 //se returneaza adresa nodului de informatie infoc

101 p = p->next;

102 }while(p != crt);

103 }

104 return nullptr;

105 }

106

107 void inserareInainte(LISTA &crt , int infoc , int infon)

108 {

86

Page 87: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

109 // inserare inainte nodului de informatie infoc

110 if(crt == nullptr)

111 {

112 cout <<"LISTA este vida!"<<endl;

113 return;

114 }

115 //se cauta nodul de informatie infoc , adresa sa va fi retinuta in q

116 LISTA p, q;

117 q = crt;

118 do

119 {

120 p = q;

121 q = q->next;

122 if(q->info == infoc) break;

123 }while(q != crt);

124 //se insereaza nodul nou

125 if(q->info == infoc)

126 {

127 LISTA nou = new (nothrow) NOD;

128 if(nou == nullptr)

129 error("Spatiu insuficient!");

130 nou ->info = infon;

131 nou ->next = q;

132 p->next = nou;

133 if(crt == p)

134 crt = nou;

135 }

136 else

137 cout <<"Informatia cautata "<<infoc <<" nu este in LISTA!"

138 <<" Inserarea nu are loc."<<endl;

139 }

140

141 void inserareDupa(LISTA &crt , int infoc , int infon)

142 {

143 // inserare dupa nodului de informatie infoc

144 if(crt == nullptr)

145 {

146 cout <<"LISTA este vida!"<<endl;

147 return;

148 }

149 //se cauta nodul de informatie infoc

150 // adresa sa va fi retinuta in q

151 LISTA q;

152 q = crt;

153 do

154 {

155 if (q->info == infoc) break;

156 q = q->next;

157 }while(q != crt);

87

Page 88: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

158 //se insereaza nodul nou

159 if(q->info == infoc)

160 {

161 LISTA nou = new (nothrow) NOD;

162 if(nou == nullptr)

163 error("Spatiu insuficient!");

164 nou ->info = infon;

165 nou ->next = q->next;

166 q->next = nou;

167 if(crt == q)

168 crt = nou;

169 }

170 else

171 cout <<"Informatia cautata "<<infoc <<" nu este in LISTA!"

172 <<" Inserarea nu are loc."<<endl;

173 }

174

175 void stergereNod(LISTA &crt , int infoc)

176 {

177 if(crt == nullptr)

178 {

179 cout <<"LISTA este vida!"<<endl;

180 return;

181 }

182 LISTA p, q;

183 /*se cauta nodul de sters , de informatie infoc; adresa sa va fi

retinuta in q*/

184 q = crt;

185 do

186 {

187 p = q;

188 q = q->next;

189 if (q->info == infoc) break;

190 }while(q != crt);

191 /*se sterge nodul; daca se sterge nodul spre care tinteste crt ,

atunci crt va pointa spre nodul precedent , p*/

192 if(q->info == infoc)

193 {

194 if (q == q->next) crt = nullptr; // lista devine vida

195 else

196 {

197 p->next = q->next;

198 if (q == crt) crt = p;

199 }

200 delete q;

201 }

202 else

203 cout <<"Informatia cautata "<<infoc <<" nu este in LISTA!"

204 <<" Stergerea nu are loc."<<endl;

88

Page 89: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

205 }

206

207 void stergereLista(LISTA &crt)

208 {

209 if(crt == nullptr)

210 {

211 cout <<"LISTA este vida!"<<endl;

212 return;

213 }

214 LISTA p, q = crt;

215 do

216 {

217 p = q;

218 q = q->next;

219 delete p;

220 }while (q != crt);

221 crt = nullptr;

222 }

223

224 LISTA copieLista(LISTA crt)

225 {

226 if(crt == nullptr)

227 {

228 cout <<"LISTA este vida!"<<endl;

229 return 0;

230 }

231 LISTA crt_copy = nullptr , p = crt ->next;

232 /* incepem copierea cu primul nod inserat in lista , asa incat crt sa

reprezinte tot adresa ultimului element inserat */

233 do

234 {

235 adaugare(crt_copy , p->info);

236 p = p->next;

237 }while (p != crt ->next);

238 return crt_copy;

239 }

240

241 //main.cpp

242 #include "lcsi.h"

243

244 int main()

245 {

246 LISTA curent = creareLista ();

247 cout <<"Lista circulara initiala."<<endl;

248 afisareLista(curent);

249 int iC , iN;

250 cout <<"Informatia inaintea careia doriti inserarea nodului nou: ";

251 cin >>iC;

252 cout <<"Informatia nodului nou: ";

89

Page 90: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

253 cin >>iN;

254 inserareInainte(curent , iC, iN);

255 cout <<"Lista circulara dupa inserarea_inainte."<<endl;

256 afisareLista(curent);

257 cout <<"Informatia dupa care doriti inserarea nodului nou: ";

258 cin >>iC;

259 cout <<"Informatia nodului nou: ";

260 cin >>iN;

261 inserareDupa(curent , iC , iN);

262 cout <<"Lista circulara dupa inserarea_dupa."<<endl;

263 afisareLista(curent);

264 cout <<"Informatia nodului pe care doriti sa -l stergeti: ";

265 cin >>iC;

266 stergereNod(curent , iC);

267 cout <<"Lista circulara dupa stergere."<<endl;

268 afisareLista(curent);

269 cout <<"Copiem lista intr -o lista noua. Lista noua: ";

270 LISTA curent_copy = copieLista(curent);

271 afisareLista(curent_copy);

272 cout <<"Stergem intreaga lista."<<endl;

273 stergereLista(curent);

274 afisareLista(curent);

275 cout <<"Afisam copia listei originale: ";

276 afisareLista(curent_copy);

277 return 0;

278 }

Exemplul 16. Simulam urmatorul joc: un grup de n copii se aseaza ın cerc, fiind numerotati cu1, 2, . . . , n, ın sensul acelor de ceasornic. Incepand cu o anumita pozitie, se numara copiii ın sens orar.Jocul are doua variante:a) Fiecare al n-lea copil este eliminat din cerc;b) Fiecare al m-lea copil spune un numar de la 1 la 10. Se continua numaratul, iar copilul cu numarulprecizat iese din joc.Castigatorul jocului este declarat ultimul copil ramas ın joc. Sa afisam copilul castigator pentru celedoua variante de joc.

1 //joc.h

2 #ifndef JOC_H

3 #define JOC_H

4 #include <iostream >

5 using namespace std;

6

7 struct NOD;

8 typedef NOD* LISTA;

9

10 void error(const char []);

11 void adaugare(LISTA&, int);

12 LISTA creareLista(int&);

13 void afisareLista(LISTA);

14 LISTA cautareNod(LISTA , int);

90

Page 91: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

15 LISTA copieLista(LISTA);

16 void joc1(LISTA&, int , int&);

17 void joc2(LISTA&, int , int&, int&);

18

19 struct NOD

20 {

21 int info;

22 NOD* next;

23 };

24 #endif

25

26 //joc.cpp

27 #include "joc.h"

28

29 void error(const char msg [])

30 {

31 /* functia error de mai sus*/

32 }

33

34 void adaugare(LISTA &crt , int infon)

35 {

36 /* functia adaugare de mai sus*/

37 }

38

39 LISTA creareLista(int &n)

40 {

41 LISTA crt = nullptr;

42 do

43 {

44 cout <<"Numarul de copii (cel putin 2): ";

45 cin >>n;

46 }while(n < 2);

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

48 adaugare(crt , i);

49 return crt;

50 }

51

52 void afisareLista(LISTA crt)

53 {

54 // afisam lista incepand cu primul nod inserat

55 LISTA p = crt ->next;

56 if(p != nullptr)

57 {

58 do

59 {

60 cout <<p->info <<" ";

61 p = p->next;

62 }while(p != crt ->next);

63 cout << endl;

91

Page 92: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

64 }

65 else

66 cout <<"Lista este vida!"<<endl;

67 }

68

69 LISTA cautareNod(LISTA crt , int infoc)

70 {

71 /* functia cautareNod de mai sus*/

72 }

73

74 LISTA copieLista(LISTA crt)

75 {

76 /* functia copieLista de mai sus*/

77 }

78

79 void joc1(LISTA &crt , int n, int &poz)

80 {

81 do

82 {

83 cout <<"Pozitia de la care incepe numaratoarea: ";

84 cin >>poz;

85 }while(poz <= 0 || poz > n);

86 LISTA p = nullptr , q = cautareNod(crt , poz);

87 int j = n;

88 do

89 {

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

91 {

92 p = q;

93 q = q->next;

94 }

95 // nodul de sters are adresa pastrata in q, iar precedentul , in p

96 cout <<"Copilul eliminat este "<<q->info <<endl;

97 p->next = q->next; // legatura noua

98 if (q == crt) crt = p;

99 delete q; //se sterge nodul spre care tinteste q

100 // afisareLista(crt);

101 //se trece la urmatorul copil de dupa cel sters

102 q = p->next;

103 j--; //se decerementeaza numarul de copii

104 }while(j > 1);

105 }

106

107 void joc2(LISTA &crt , int n, int &poz , int &m)

108 {

109 int nr;

110 do

111 {

112 cout <<"Pozitia de la care incepe numaratoarea: ";

92

Page 93: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

113 cin >>poz;

114 }while(poz <= 0 || poz > n);

115 do

116 {

117 cout <<"m: ";

118 cin >>m;

119 }while(m < 2);

120 LISTA p = nullptr , q = cautareNod(crt , poz);

121 do

122 {

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

124 {

125 p = q;

126 q = q->next;

127 }

128 cout <<"Copilul care trebuie sa spuna un numar "

129 <<"de la 1 la 10 este "<<q->info <<endl;

130 do

131 {

132 cout <<"Numarul de eliminare (1--10): ";

133 cin >>nr;

134 }while(nr <= 0 || nr > 10);

135 for(int i = 1; i <= nr; i++)

136 {

137 p = q;

138 q = q->next;

139 }

140 cout <<"Copilul eliminat este "<<q->info <<endl;

141 p->next = q->next;

142 if (q == crt) crt = p;

143 delete q;

144 // afisareLista(crt);

145 q = p->next;

146 n--;

147 }while(n > 1);

148 }

149

150 //main.cpp

151 #include "joc.h"

152

153 int main()

154 {

155 int nr , poz , m;

156 LISTA curent = creareLista(nr);

157 // relizam o copie a listei initiale

158 LISTA curent_copy = copieLista(curent);

159 cout <<"Lista circulara initiala."<<endl;

160 afisareLista(curent);

161 joc1(curent , nr , poz);

93

Page 94: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

162 cout <<"Castigator al primului joc este copilul ";

163 afisareLista(curent);

164 joc2(curent_copy , nr , poz , m);

165 cout <<"Castigator al celui de -al doilea joc "<<"este copilul ";

166 afisareLista(curent_copy);

167 return 0;

168 }

3.4 Liste circulare dublu ınlantuite

O lista dublu ınlantuita este circulara daca succesorul ultimului element inserat ın lista este chiar primulelement adaugat ın lista, iar predecesorul primului element din lista este ultimul element adaugat. Olista circulara dublu ınlantuita se poate parcurge ın ambele sensuri, fiecare nod avand un succesor si unpredecesor. Niciunul dintre nodurile listei nu contine pointerul null ın zonele de legatura. In cadrul listeicirculare dublu ınlantuite nu exista capete. Pentru gestionarea acesteia se va folosi un pointer catreun nod oarecare al listei. Poate fi convenabil sa gestionam lista circulara dublu ınlantuita cu ajutorulunui pointer (crt) catre ultimul element adaugat ın lista, nodul curent. Astfel, crt->next este adresaprimului nod adaugat ın lista, iar crt si crt->next->back reprezinta adresa ultimului nod inserat ınlista. Toate elementele listei pot fi accesate secvential plecand de la nodul de adresa crt.

adrN info1 adr2 adr1 info2 adr3 . . . adr(N-1) infoN adr1

Figura 11. Lista circulara dublu ınlantuita

Operatiile pe care le vom implementa asupra listelor circulare dublu ınlantuite sunt cele obisnuite:crearea listei vide (crt = nullptr), inserarea unui element nou ın lista, eliminarea unui nod al listei,parcurgerea listei pentru a realiza diverse operatii asupra informatiilor din noduri.Pentru inserarea unui nod nou sunt doua situatii: inserarea nodului nou ınaintea unui nod de informatiedata sau dupa un nod de informatie data. In ambele cazuri, mai ıntai se cauta acest nod. Daca exista,adresa sa se va retine ın pointerul q. Apoi se aloca memorie pentru nodul nou, se ıncarca zona delegatura a acestuia si se fac legaturile corespunzatoare (mai ıntai se creaza legaturile noi, apoi se stergvechile legaturi). Pentru cazul inserarii unui nod nou ınaintea nodului q, acesta se insereaza ıntrenodurile q->back si q, iar ın cazul adaugarii unui nod nou dupa nodul q, acesta se insereaza ıntre q siq->next.Pentru a sterge un nod de informatie data, mai ıntai se cauta nodul ın lista. Daca acesta exista, se vorcrea legaturile noi si apoi se va elibera spatiul de memorie aferent. Daca q este adresa nodului ce va fisters, atunci q->back se va lega de q->next. Daca nodul de sters este cel tintit de crt, atunci crt vapointa spre nodul precedent.

1 //lcdi.h

2 #ifndef LCDI_H

3 #define LCDI_H

4 #include <iostream >

5 using namespace std;

6

94

Page 95: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

7 struct NOD;

8 typedef NOD* LISTA;

9

10 void error(const char []);

11 void adaugare(LISTA&, int);

12 LISTA creareLista ();

13 void afisareListaSensOrar(LISTA);

14 void afisareListaSensAntiorar(LISTA);

15 LISTA cautareNod(LISTA , int);

16 void inserareInainte(LISTA&, int , int);

17 void inserareDupa(LISTA&, int , int);

18 void stergereNod(LISTA&, int);

19

20 struct NOD

21 {

22 int info;

23 LISTA next; // pointer catre succesor

24 LISTA back; // pointer catre predecesor

25 };

26 #endif

27

28 //lcdi.cpp

29 #include "lcdi.h"

30

31 void error(const char msg [])

32 {

33 cerr <<"Eroare de executie."<<endl;

34 cerr <<msg <<endl;

35 cerr <<"Programul se opreste!"<<endl;

36 exit (1);

37 }

38

39 void adaugare(LISTA &crt , int infon)

40 {

41 // adaugarea unui nod nou dupa ultimul nod adugat in lista

42 LISTA nou = new (nothrow) NOD;

43 if(nou == nullptr)

44 error("Spatiu insuficient!");

45 nou ->info = infon;

46 if (crt == nullptr) // lista este vida

47 {

48 crt = nou;

49 //crt contine adresa ultimului nod adaugat , nodul curent

50 crt ->next = nou;

51 //crt ->next este adresa primului nod aduagat

52 crt ->back = nou;

53 }

54 else

55 {

95

Page 96: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

56 nou ->next = crt ->next;

57 nou ->back = crt;

58 crt ->next ->back = nou;

59 crt ->next = nou;

60 crt = nou;

61 }

62 }

63

64 LISTA creareLista ()

65 {

66 int nr_noduri , info;

67 LISTA crt = nullptr; // lista vida

68 cout <<"Numarul de noduri initiale: ";

69 cin >>nr_noduri;

70 for(int i = 1; i <= nr_noduri; i++)

71 {

72 cout <<"Informatia pentru nodul "<<i<<" : ";

73 cin >>info;

74 adaugare(crt , info);

75 }

76 return crt;

77 }

78

79 void afisareListaSensOrar(LISTA crt)

80 {

81 LISTA p = crt;

82 if(p != nullptr)

83 {

84 do

85 {

86 cout <<p->info <<" ";

87 p = p->next;

88 }while(p != crt);

89 cout << endl;

90 }

91 else

92 cout <<"Lista este vida!"<<endl;

93 }

94

95 void afisareListaSensAntiorar(LISTA crt)

96 {

97 LISTA p = crt;

98 if(p != nullptr)

99 {

100 do

101 {

102 cout <<p->info <<" ";

103 p = p->back;

104 }while(p != crt);

96

Page 97: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

105 cout << endl;

106 }

107 else

108 cout <<"Lista este vida!"<<endl;

109 }

110

111 LISTA cautareNod(LISTA crt , int infoc)

112 {

113 LISTA p = crt;

114 if(p != nullptr)

115 {

116 do

117 {

118 if(p->info == infoc)

119 return p;

120 //se returneaza adresa nodului de informatie infoc

121 p = p->next;

122 }while(p != crt);

123 }

124 return nullptr;

125 }

126

127 void inserareInainte(LISTA &crt , int infoc , int infon)

128 {

129 if(crt == nullptr)

130 {

131 cout <<"LISTA este vida!"<<endl;

132 return;

133 }

134 //se cauta nodul de informatie infoc (adresa sa va fi q)

135 LISTA q;

136 q = cautareNod(crt , infoc);

137 //se insereaza nodul nou

138 if(q != nullptr)

139 {

140 LISTA nou = new (nothrow) NOD;

141 if(nou == nullptr)

142 error("Spatiu insuficient!");

143 nou ->info = infon;

144 nou ->next = q;

145 nou ->back = q->back;

146 q->back ->next = nou;

147 q->back = nou;

148 if(q->back == crt)

149 crt = nou;

150 }

151 else

152 cout <<"Informatia cautata "<<infoc <<" nu este in LISTA! "

153 <<"Inserarea nu are loc."<<endl;

97

Page 98: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

154 }

155

156 void inserareDupa(LISTA &crt , int infoc , int infon)

157 {

158 if(crt == nullcrt)

159 {

160 cout <<"LISTA este vida!"<<endl;

161 return;

162 }

163 LISTA q;

164 q = cautareNod(crt , infoc);

165 //se insereaza nodul nou

166 if(q != nullptr)

167 {

168 LISTA nou = new NOD;

169 if(!nou)

170 error("Spatiu insuficient!");

171 nou ->info = infon ;

172 nou ->next = q->next;

173 nou ->back = q;

174 q->next ->back = nou;

175 q->next = nou;

176 if(crt == q)

177 crt = nou;

178 }

179 else

180 cout <<"Informatia cautata "<<infoc <<" nu este in LISTA! "

181 <<"Inserarea nu are loc."<<endl;

182 }

183

184 void stergereNod(LISTA &crt , int infoc)

185 {

186 if(crt == nullptr)

187 {

188 cout <<"LISTA este vida!"<<endl;

189 return;

190 }

191 LISTA q;

192 q = cautareNod(crt , infoc);

193 /* se sterge nodul; daca se sterge nodul de adresa crt ,

194 atunci crt va pointa spre nodul precedent , q->back */

195 if(q != nullptr)

196 {

197 if(q == q->next) crt = nullptr;

198 // lista devine vida

199 else

200 {

201 q->back ->next = q->next;

202 q->next ->back = q->back;

98

Page 99: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

203 if(q == crt) crt = q->back;

204 }

205 delete q;

206 }

207 else

208 cout <<"Informatia cautata "<<infoc <<" nu este in LISTA! "

209 <<"Stergerea nu are loc."<<endl;

210 }

211

212 //main.cpp

213 #include "lcdi.h"

214

215 int main()

216 {

217 LISTA crt = creareLista ();

218 cout <<"Lista circulara initiala."<<endl;

219 afisareListaSensOrar(crt);

220 afisareListaSensAntiorar(crt);

221 int iC , iN;

222 cout <<"Informatia inaintea careia doriti inserarea nodului nou: ";

223 cin >>iC;

224 cout <<"Informatia nodului nou: ";

225 cin >>iN;

226 inserareInainte(crt , iC, iN);

227 cout <<"Lista circulara dupa inserarea_inainte."<<endl;

228 afisareListaSensOrar(crt);

229 afisareListaSensAntiorar(crt);

230 cout <<"Informatia dupa care doriti inserarea nodului nou: ";

231 cin >>iC;

232 cout <<"Informatia nodului nou: ";

233 cin >>iN;

234 inserareDupa(crt , iC , iN);

235 cout <<"Lista circulara dupa inserarea_dupa."<<endl;

236 afisareListaSensOrar(crt);

237 afisareListaSensAntiorar(crt);

238 cout <<"Informatia nodului pe care doriti sa -l stergeti: ";

239 cin >>iC;

240 stergereNod(crt , iC);

241 cout <<"Lista circulara dupa stergere."<<endl;

242 afisareListaSensOrar(crt);

243 afisareListaSensAntiorar(crt);

244 return 0;

245 }

Exemplul 17. Codul necesar deschiderii unui anumit seif poate contine orice numar natural de la 1la n. Modalitatea de stabilire a codului este urmatoarea: fiecare mutare se face ori ın sens orar, ori ınsens antiorar, cu un anumit numar de rotatii. Numarul de mutari nu este cunoscut dinainte, ci sirulmutarilor se ıncheie la dorinta celui care genereaza codul. Sa se genereze si sa se afiseze un astfel de cod.

99

Page 100: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Vom plasa numerele 1, 2, . . . , n, ın aceasta ordine, ın zona de informatie a elementelor unei liste circularedublu ınlantuite. Vom citi perechi de numere (x, y), reprezentand numarul de rotatii si sensul de rotatiepentru fiecare mutare (y = 1 pentru sens orar si y = 2 pentru sens antiorar). Aceste perechi de numerevor reprezenta liniile unei matrice alocate la compilare. Generarea codului se va realiza prin parcurgereaın ambele sensuri a listei, ın functie de informatiile stocate ın matrice.De exemplu, daca n = 10 si perechile de numere (x, y), reprezentand numarul de rotatii si sensul derotatie pentru fiecare mutare, sunt: (3, 1), (8, 2), (4, 1), (7, 1), (8, 2), (5, 1), atunci codul rezultat este4 6 10 7 9 4.

1 // cifru.h

2 #ifndef CIFRU_H

3 #define CIFRU_H

4 #include <iostream >

5 using namespace std;

6 #define dim 20

7

8 struct NOD;

9 typedef NOD* LISTA;

10

11 void error(const char []);

12 void adaugare(LISTA&, int);

13 LISTA creareLista ();

14 void afisareListaSensOrar(LISTA);

15 LISTA cautareNod(LISTA , int);

16 void citireMutari(int [][2], int&);

17 void cifru(LISTA , int [][2], int);

18

19 struct NOD

20 {

21 int info;

22 LISTA next; // pointer catre succesor

23 LISTA back; // pointer catre predecesor

24 };

25 #endif

26

27 // cifru.cpp

28 #include "cifru.h"

29

30 void error(const char msg [])

31 {

32 /* functia error de la aplicatia precedenta */

33 }

34

35 void adaugare(LISTA &crt , int infon)

36 {

37 // adaugarea unui nod nou dupa ultimul nod adugat in lista

38 /* functia adaugare de la aplicatia precedenta */

39 }

40

41 LISTA creareLista ()

100

Page 101: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

42 {

43 int n;

44 LISTA crt = nullptr; // lista vida

45 cout <<"n (n >= 2): ";

46 cin >>n;

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

48 adaugare(crt , i);

49 return crt;

50 }

51

52 void afisareListaSensOrar(LISTA crt)

53 {

54 /* functia afisareListaSensOrar de la aplicatia precedenta */

55 }

56

57 void citireMutari(int A[dim][2], int &m)

58 {

59 int x, y;

60 char ch;

61 cout <<"Prima mutare."<<endl;

62 cout <<"Numarul de rotatii: ";

63 cin >>x;

64 do

65 {

66 cout <<"Sensul (1-sens orar , 2-sens antiorar): ";

67 cin >>y;

68 }while(y != 1 && y != 2);

69 m = 0;

70 A[m][0] = x;

71 A[m][1] = y;

72 cout <<"Mai introduceti o mutare? [D/N]: ";

73 cin >>ch;

74 while(ch == ’d’ || ch == ’D’)

75 {

76 m = m + 1;

77 cout <<"Mutarea "<<m + 1<<"."<<endl;

78 cout <<"Numarul de rotatii: ";

79 cin >>x;

80 do

81 {

82 cout <<"Sensul (1-sens orar , 2-sens antiorar): ";

83 cin >>y;

84 }while(y != 1 && y != 2);

85 A[m][0] = x;

86 A[m][1] = y;

87 if(m >= dim -1) break;

88 cout <<"Mai introduceti o mutare? [D/N]: ";

89 cin >>ch;

90 }

101

Page 102: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

91 }

92

93 void cifru(LISTA crt , int A[dim][2], int m)

94 {

95 LISTA q = crt ->next;

96 for (int i = 0; i <= m; i++)

97 {

98 if(A[i][1] == 1) // parcurgere in sens orar

99 for(int j = 1; j <= A[i][0]; j++)

100 q = q->next;

101 else // parcurgere in sens antiorar

102 for(int j = 1; j <= A[i][0]; j++)

103 q = q->back;

104 cout <<q->info <<" ";

105 }

106 cout <<endl;

107 }

108

109 //main.cpp

110 #include "cifru.h"

111

112 int main()

113 {

114 LISTA crt = creareLista ();

115 cout <<"Lista circulara dublu inlantuita."<<endl;

116 afisareListaSensOrar(crt ->next);

117 int M[dim][2], m;

118 citireMutari(M, m);

119 cifru(crt , M, m);

120 return 0;

121 }

3.5 Liste ordonate

Sunt situatii ın care este de dorit sa se genereze o lista ordonata (crescator sau descrecator) dupa anumiteinformatii (chei) pastrate ın nodurile listei. Generarea unei liste ordonate crescator se poate face directın functia de creare a acesteia, fiind posibile urmatoarele situatii: inserarea se face ınaintea primuluinod, informatia nodului nou fiind mai mica decat informatia oricarui nod din lista, inserarea se facedupa ultimul nod, informatia nodului nou fiind mai mare decat orice informatie a listei sau inserarease face ın interior. Exemplificam functia de creare a unei liste ordonate, atat pentru liste liniare simpluınlantuite, cat si pentru liste liniare dublu ınlantuite.

a) Liste liniare simplu ınlantuite

1 #include <iostream >

2 using namespace std;

3

4 struct NOD

5 {

102

Page 103: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

6 int info ;

7 NOD *next ;

8 };

9

10 typedef struct NOD* LISTA;

11

12 void error(const char msg [])

13 {

14 /* functia error din aplicatiile precedente */

15 }

16

17 LISTA creareListaOrdonata ()

18 {

19 int info;

20 LISTA vf , nou , p, pa;

21 char ch;

22 vf = nullptr;

23 cout <<"Introduceti un nod nou in lista? [D/N]: ";

24 cin >>ch;

25 while(ch == ’d’ || ch == ’D’)

26 {

27 cout <<"Informatia pentru nodul nou: ";

28 cin >>info;

29 nou = new (nothrow) NOD;

30 if(nou == nullptr)

31 error("Spatiu de memorie insuficient!");

32 nou ->info = info;

33 if(vf == nullptr)

34 {

35 // inserare in lista vida

36 vf = nou;

37 vf ->next = nullptr;

38 }

39 else

40 {

41 if(vf->info > info)

42 {

43 // inserarare inaintea primului nod

44 nou ->next = vf;

45 vf = nou;

46 }

47 else

48 {

49 pa = vf;

50 p = vf->next;

51 while(p != nullptr && p->info < info)

52 {

53 pa = p;

54 p = p->next;

103

Page 104: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

55 }

56 /* inserarea intre pa si p, fie in interior , fie la sfarsitul

listei */

57 nou ->next = p;

58 pa->next = nou;

59 }

60 }

61 cout <<"Introduceti un nod nou in lista? [D/N]: ";

62 cin >>ch;

63 }

64 return vf;

65 }

66

67 void afisareLista(LISTA v)

68 {

69 LISTA p = v;

70 if(p != nullptr)

71 {

72 while(p != nullptr)

73 {

74 cout << p->info << " ";

75 p = p->next;

76 }

77 cout << endl;

78 }

79 else

80 cout <<"LISTA este vida!"<<endl;

81 }

82

83 int main()

84 {

85 LISTA first;

86 first = creareListaOrdonata ();

87 cout << "LISTA ordonata crescator: ";

88 afisareLista(first);

89 return 0;

90 }

b) Liste liniare dublu ınlantuite

1 #include <iostream >

2 using namespace std;

3

4 struct NOD

5 {

6 int info;

7 NOD *next;

8 NOD *back;

9 };

104

Page 105: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

10

11 typedef struct NOD* LISTA;

12

13 void error(const char msg [])

14 {

15 /*...*/

16 }

17

18 LISTA creareListaOrdonata(LISTA &sf)

19 {

20 int info;

21 LISTA vf , nou , p;

22 char ch;

23 vf = nullptr;

24 sf = nullptr;

25 cout <<"Introduceti un nod nou in lista? [D/N]: ";

26 cin >>ch;

27 while(ch == ’d’ || ch == ’D’)

28 {

29 cout <<"Informatia pentru nodul nou: ";

30 cin >>info;

31 nou = new (nothrow) NOD;

32 if(nou == nullptr)

33 error("Spatiu de memorie insuficient!");

34 nou ->info = info;

35 if(vf == nullptr)

36 {

37 // inserare in lista vida

38 nou ->back = nullptr;

39 nou ->next = nullptr;

40 vf = nou;

41 sf = nou;

42 }

43 else

44 {

45 p = vf;

46 while (p != nullptr && p->info < info)

47 p = p->next;

48 /* inserare fie in interior , fie la inceputul listei , fie la

sfarsitul acesteia */

49 if(p != nullptr)

50 {

51 nou ->back = p->back;

52 nou ->next = p;

53 //se sterg vechile legaturi

54 if (p->back != 0) p->back ->next = nou;

55 p->back = nou;

56 //daca se adauga inaintea primului nod

57 if (p == vf) vf = nou;

105

Page 106: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

58 }

59 else

60 {

61 // adaugare la sfarsit

62 nou ->back = sf;

63 nou ->next = nullptr;

64 sf -> next = nou;

65 sf = nou;

66 }

67 }

68 cout <<"Introduceti un nod nou in lista? [D/N]: ";

69 cin >>ch;

70 }

71 return vf;

72 }

73

74 void afisareListaPrim(LISTA vf)

75 {

76 LISTA p = vf;

77 if(p != nullptr)

78 {

79 while(p != nullptr)

80 {

81 cout << p->info << " ";

82 p = p->next;

83 }

84 cout << endl;

85 }

86 else

87 cout <<"LISTA este vida!"<<endl;

88 }

89

90 void afisareListaUltim(LISTA sf)

91 {

92 LISTA p = sf;

93 if(p != nullptr)

94 {

95 while(p != nullptr)

96 {

97 cout <<p->info << " ";

98 p = p->back;

99 }

100 cout << endl;

101 }

102 else

103 cout <<"LISTA este vida!"<<endl;

104 }

105

106 int main()

106

Page 107: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

107 {

108 LISTA first , last;

109 first = creareListaOrdonata(last);

110 cout <<"LISTA , ordonata crescator: ";

111 afisareListaPrim(first);

112 cout <<"LISTA , ordonata descrescator: ";

113 afisareListaUltim(last);

114 return 0;

115 }

107

Page 108: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Capitolul 4

Grafuri

Desi, de regula, listele ınlantuite ofera o mai buna flexibilitate decat tablourile, sunt totusi dificil deutilizat pentru a memora si manipula colectii de obiecte organizate ierarhic (de exemplu, organigramaunei firme, un arbore genealogic, o retea de drumuri etc.). Desi stivele si cozile reflecta o anume ierarhie,acestea sunt limitate la cea liniara.In practica exista numeroase situatii cand trebuie realizate prelucrari asupra unei colectii de date alecarei elemente se afla ın diferite relatii, nu neaparat liniare (o harta plana etc.). Aceste relatii vor fimodelate prin utilizarea structurilor neliniare. Graful este o structura neliniara ın care orice elementpoate avea mai multi succesori si mai multi predecesori.

4.1 Grafuri neorientate

Un graf neorientat G este o pereche ordonata de multimi (V,E), unde V este o multime finita numitamultimea nodurilor grafului, iar E ⊂ V × V este o multime de perechi neordonate din V × V , numitamultimea muchiilor lui G. Asadar, fiecare muchie este o pereche neordonata de noduri (v, w) ∈ E, princare este stabilita o relatie de vecinatate ıntre nodurile v, w ∈ V .Daca G = (V,E) este un graf neorientat, pentru orice muchie (v, w) ∈ E, muchia (w, v) ∈ E, ıntrucatperechea este una neordonata. In cele ce urmeaza vom considera grafurile ın care o muchie uneste douanoduri distincte. Despre o muchie (v, w) ∈ E spunem ca este incidenta nodurilor v si w si ca nodurilev si w sunt adiacente. Gradul unui nod este dat de numarul muchiilor incidente acestuia. Daca un nodare gradul 0, acesta se numeste nod izolat, iar un nod care are gradul 1 poarta numele de nod terminal.Notam gradul unui nod v ∈ V cu d(v).

Un graf neorientat poate fi reprezentat ca ın figura urmatoare:

Figura 12. G1 – graf neorientat

undeG1 = (V,E), cu V = {0, 1, 2, 3, 4, 5, 6, 7}, iar E = {(0, 1), (0, 5), (1, 2), (1, 4), (2, 3), (2, 5), (3, 4), (4, 5)}.De exemplu, ın acest graf, muchia (1, 4) este incidenta nodurilor 1 si 4, nodurile 2 si 5 sunt adiacente,nodurile 3 si 5 nu sunt adiacente, iar d(2) = 3, d(6) = 0 sau d(0) = 2. Nodurile 6 si 7 sunt izolate.

108

Page 109: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Se poate demonstra ca, pentru un graf neorientat cu n noduri (etichetate cu numere de la 0 la n − 1)si m muchii, are loc relatia

d(0) + d(1) + · · ·+ d(n− 1) = 2m.

4.1.1 Reprezentarea ın memorie a grafurilor neorientate

Exista mai multe modalitati de stocare ın memorie a grafurilor. Algerea structurii de date prin care se vamemora un graf depinde de caracteristicile problemei de rezolvat si, implicit, de complexitatea grafuluirezultat. Pentru a memora un graf, este necesar mai ıntai sa furnizam datele de intrare: numarul denoduri, respectiv fiecare muchie ın parte ca o pereche de noduri.

Memorarea grafurilor folosind matricea de adiacenta

Memorarea unui graf G = (V,E), V = {0, 1, . . . , n−1}, folosind matricea de adiacenta atasata acestuia,presupune determinarea matricei patratice de ordin n, An×n, de componente

aij =

{1, daca (i, j) ∈ E0, daca (i, j) /∈ E

,

i, j = 0, 1, . . . , n− 1. Pentru graful G1 de mai sus, matricea de adiacenta este

A8×8 =

0 1 0 0 0 1 0 01 0 1 0 1 0 0 00 1 0 1 0 1 0 00 0 1 0 1 0 0 00 1 0 1 0 1 0 01 0 1 0 1 0 0 00 0 0 0 0 0 0 00 0 0 0 0 0 0 0

.

Observam ca elementele de pe diagonala principala sunt egale cu 0 deoarece nu exista muchii de laun nod la el ınsusi, iar matricea este una simetrica fata de diagonala principala (muchiile (i, j) si (j, i)coincid). De asemenea, d(i) = suma elementelor de pe linia i a matricei, iar d(j) = suma elementelorde pe coloana j, cu i, j = 0, 1, . . . , n − 1. Conform celor precizate anterior, suma elementelor matriceide adiacenta este egala cu 2m.

Sa presupunem ca datele de intrare pentru graful G1 din Figura 12 sunt citite dintr-un fisier text,”in.txt”, de forma

8

0 1

0 5

1 2

1 4

2 3

2 5

3 4

4 5

ın care pe prima linie se afla numarul de noduri, fiecare nod fiind etichetat cu numere de la 0 la n−1, iarpe urmatoarele linii, cate o muchie (i, j) a grafului. Construirea matricei de adiacenta atasata grafuluise realizeaza astfel:

109

Page 110: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

1 #include <iostream >

2 #include <fstream >

3 using namespace std;

4

5 void CitireDate(const char numeFisier [], int** &A, int &n)

6 {

7 ifstream fin(numeFisier);

8 int i, j;

9 fin >> n;

10 A = new (nothrow) int*[n];

11 if(A == nullptr)

12 {

13 cout <<"Alocare esuata."<<endl;

14 exit (1);

15 }

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

17 {

18 A[i] = new (nothrow) int[n];

19 if(A[i] == nullptr)

20 {

21 cout <<"Alocare esuata."<<endl;

22 exit (1);

23 }

24 for (j = 0; j < n; j++)

25 A[i][j] = 0;

26 }

27 while (fin >> i >> j)

28 {

29 A[i][j] = 1;

30 A[j][i] = A[i][j];

31 }

32 fin.close();

33 }

34

35 int main()

36 {

37 int n, i, j;

38 int **A;

39 CitireDate("in.txt", A, n);

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

41 {

42 for (j = 0; j < n; j++)

43 cout << A[i][j] << " ";

44 cout << endl;

45 }

46 return 0;

47 }

110

Page 111: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Aceasta modalitate de stocare a unui graf este utila ın special atunci cand se testeaza daca exista saunu o muchie ınre doua noduri date sau atunci cand se calculeaza gradul unui nod, datorita accesuluirapid la informatii. Pe de alta parte ınsa, aceasta modalitate de reprezentare a grafurilor este ineficientapentru grafuri cu putine muchii (m� n), deoarece matricea de adiacenta atasata grafului va avea multecomponente egale cu 0 si stocarea acestora ar conduce la o risipa de memorie.

Memorarea grafurilor folosind listele de adiacenta

Pentru fiecare nod se construieste lista nodurilor adiacente cu acesta. Pentru graful G1 de mai sus,obtinem listele

0: 1, 5

1: 0, 2, 4

2: 1, 3, 5

3: 2, 4

4: 1, 3, 5

5: 0, 2, 4

6:

7:

Daca nodul este izolat, lista sa de adiacenta este vida. Observam ca d(i) = numarul de elemente dinlista de adiacenta a nodului i, i = 0, 1, . . . , n− 1.

Listele de adiacenta pot fi implementate atat static, cat si dinamic, folosind fie structuri secventiale, fieınlantuite. De exemplu, se poate utiliza o matrice AD cu 2 linii si n+2m coloane. Prima linie a matriceiAD contine etichetele nodurilor pe primele n coloane (AD[0][j] = j, j = 0, . . . , n − 1) si cele n liste deadiacenta, cate una pentru fiecare nod, pe urmatoarele 2m coloane. A doua linie a matricei AD contineinformatiile necesare pentru identificarea listei de adiacenta a fiecarui nod ın prima linie a matricei:primele n coloane contin, ın ordine, indicele coloanei din prima linie de unde ıncepe lista de adiacentaa fiecarui nod (AD[1][j] = k, unde k este indicele coloanei de unde ıncepe lista de adiacenta a noduluij, j = 0, . . . , n− 1. Daca nodul j este izolat, atunci AD[1][j] = −1, cu semnificatia ca respectivul nodnu are lista de adiacenta), urmatoarele 2m coloane contin, ın ordine, informatii despre modul ın care sesucced elementele din lista de adiacenta. Daca nodul AD[0][j] este interior listei de adiacenta, atunciAD[1][j] = j + 1, iar daca este ultimul nod al listei, atunci AD[1][j] = −1, cu semnificatia ca lista deadiacenta a nodului j s-a terminat. Matricea AD pentru garful G1 din Figura 12 are urmatoarea forma

AD[0]: 0 1 2 3 4 5 6 7 1 5 0 2 4 1 3 5 2 4 1 3 5 0 2 4

AD[1]: 8 10 13 16 18 21 -1 -1 9 -1 11 12 -1 14 15 -1 17 -1 19 20 -1 22 23 -1

j: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

O alta varianta este sa se foloseasca liste liniare simplu ınlantuite pentru a pastra listele de adiacenta.Astfel vom considera un vector cu n componente ce va retine adresele de ınceput ale celor n liste liniaresimplu ınlantuite, corespunzatoare listelor de adiacenta ale celor n noduri ale grafului. Structura unuinod al listei este cea obisnuita: informatia din nod si adresa succesorului ın lista. Pentru usurinta,adaugarea unui nod ın lista se realizeaza la ınceputul acesteia, ordinea nodurilor ın lista nefiind impor-tanta.

1 #include <iostream >

2 #include <fstream >

3 using namespace std;

4

5 struct NOD

6 {

111

Page 112: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

7 int info;

8 NOD* next;

9 };

10

11 typedef NOD* LISTA;

12

13 void CitireListeAdiacenta(const char numeFisier [], LISTA* &l, int &n)

14 {

15 ifstream fin(numeFisier);

16 LISTA p;

17 int i, j;

18 fin >> n;

19 l = new (nothrow) LISTA[n];

20 if(l == nullptr)

21 {

22 cout <<"Alocare esuata."<<endl;

23 exit (1);

24 }

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

26 l[i] = nullptr; // initial listele de adiacenta sunt vide

27 while (fin >> i >> j)

28 {

29 /* daca nodul j este adaugat in lista de adiacenta a nodului i,

atunci si i este adaugat in lista de adiacenta a nodului j */

30 p = new (nothrow) NOD;

31 if(p == nullptr)

32 {

33 cout <<"Alocare esuata."<<endl;

34 exit (1);

35 }

36 p->info = j;

37 p->next = l[i]; // adaugare la inceput

38 l[i] = p;

39 p = new (nothrow) NOD;

40 if(p == nullptr)

41 {

42 cout <<"Alocare esuata."<<endl;

43 exit (1);

44 }

45 p->info = i;

46 p->next = l[j]; // adaugare la inceput

47 l[j] = p;

48 }

49 fin.close();

50 }

51

52 int main()

53 {

54 LISTA* l;

112

Page 113: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

55 int n;

56 CitireListeAdiacenta("in.txt", l, n);

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

58 {

59 cout << "Lista de adiacenta pentru nodul " << i << endl;

60 // parcurgem lista corespunzatoare

61 LISTA p = l[i];

62 while(p != nullptr)

63 {

64 cout << p->info << " ";

65 p = p->next;

66 }

67 cout << endl;

68 }

69 return 0;

70 }

Varianta de stocare a grafurilor prin liste de adiacenta foloseste mai eficient memoria, dar operatia deidentificare a unei muchii este mai costisitoare.

Memorarea grafurilor folosind lista muchiilor

Se va defini o structura de date cu doua campuri corespunzatoare celor doua noduri la care este incidentao muchie. Apoi se declara un vector cu m componente de tipul nou definit (sunt m muchii) si se citescdatele referitoare la graf.Aceasta modalitate de stocare este utila pentru problemele ın care se prelucreaza muchiile ın modrepetat. De asemenea, aceasta reprezentare permite adaugarea la structura ce defineste o muchie si aaltor campuri corespunzatoare unor caracteristici ce pot fi asociate muchiilor (ponderi).

4.1.2 Graf complet, graf partial si subgraf

Un graf complet este un graf neorientat ın care oricare doua noduri sunt adiacente. Astfel, ıntr-un grafcomplet cu n noduri, gradul oricarui nod este n− 1, iar numarul muchiilor este egal cu m = n(n− 1)/2.Apoi, cum acesta este numarul maxim de muchii pe care le poate avea un graf neorientat, ınseamna canumarul de grafuri neorientate cu n noduri este egal cu 2n(n−1)/2. Desigur, graful din Figura 12 nu esteun graf complet, dar cel din figura urmatoare este.

Figura 13. G2 – graf complet

Un graf partial al unui graf neorientat G = (V,E) este un graf G′ = (V,E′), cu E′ ⊆ E. Astfel, G′ seobtine din G prin suprimarea unor muchii ale acestuia. Un graf cu n noduri si m muchii are 2m grafuripartiale.

113

Page 114: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Figura 14. G′ – graf partial al lui G2

Un subgraf al unui graf neorientat G = (V,E) este un graf S = (V1, E1), unde V1 ⊂ V si E1 ⊂ E, iarmuchiile din E1 sunt muchiile din E care sunt incidente doar la noduri din V1. Astfel, S se obtine dinG prin suprimarea anumitor noduri si a muchiilor incidente lor. Numarul de subgrafuri ale unui grafcu n noduri este egal cu 2n − 1.

Figura 15. S - subgraf al grafului G2

4.1.3 Grafuri conexe si componente conexe

Un lant al grafului neorientat G = (V,E) este o succesiune de noduri din V , L = [v1, v2, . . . , vl],cu proprietatea ca muchiile (v1, v2), (v2, v3), . . . , (vl−1, vl) ∈ E (oricare doua noduri consecutive dinsuccesiune sunt adiacente). Nodurile v1 si vl se numesc extremitatile lantului, iar lungimea lantului estedata de numarul de muchii continute. De exemplu, L = [0, 1, 2, 3, 4, 5, 2, 1, 4] este un lant al grafuluiG1 din Figura 12. Un lant elementar este un lant ın care nodurile sunt distincte doua cate doua. Deexemplu, L = [0, 1, 2, 3, 4] este un lant elementar al grafului G1.Programul urmator genereaza, pentru doua noduri date v1 si vl, toate lanturile elementare L =[v1, . . . , vl]. In acest scop se va folosi metoda backtracking.

1 #include <iostream >

2 #include <fstream >

3 #define dim 20

4 using namespace std;

5

6 int st[dim]; // stiva solutiei; contine nodurile unui lant elementar

7 int A[dim][dim];

8 int n, v1 , vl;

9 bool sol = false;

10

11 void CitireDate(const char numeFisier [])

12 {

13 ifstream fin(numeFisier);

114

Page 115: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

14 int i, j;

15 fin >> n;

16 while (fin >> i >> j)

17 {

18 A[i][j] = 1;

19 A[j][i] = A[i][j];

20 }

21 fin.close();

22 }

23

24 int valid(int k)

25 {

26 int i;

27 if (k >= 1) //doua noduri succesive sunt adiacente

28 if (A[st[k - 1]][st[k]] == 0) return 0;

29 for (i = 0; i < k; i++) //lant elementar

30 if (st[k] == st[i]) return 0;

31 return 1;

32 }

33

34 void scrie_solutie(int k)

35 {

36 sol = true;

37 for (int i = 0; i <= k; i++)

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

39 cout << endl;

40 }

41

42 void btr()

43 {

44 int k = 1;

45 st[k] = -1;

46 while(k > 0)

47 {

48 if(st[k] < n - 1)

49 {

50 st[k] = st[k] + 1;

51 if(valid(k))

52 {

53 if(st[k] == vl)

54 scrie_solutie(k);

55 else

56 {

57 k = k + 1;

58 st[k] = -1;

59 }

60 }

61 }

62 else

115

Page 116: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

63 k = k - 1;

64 }

65 }

66

67 int main()

68 {

69 CitireDate("in.txt");

70 cout << "Extremitatea stanga a lantului: ";

71 cin >> v1;

72 cout << "Extremitatea dreapta a lantului: ";

73 cin >> vl;

74 st[0] = v1;

75 btr();

76 if (sol == false)

77 cout << "Nu exista lant elementar intre nodurile " << v1 << " si "

78 << vl << endl;

79 return 0;

80 }

Plecand de la algoritmul precedent se pot afisa toate lanturile elementare dintr-un graf.

Un lant ın care extremitatile coincid si toate muchiile corespunzatoare acestuia sunt distincte doua catedoua, se numeste ciclu. Daca lantul este elementar (cu exceptia extremitatilor, care coincid) atunciciclul se numeste ciclu elementar. Un graf neorientat fara cicluri se numeste aciclic. Un ciclu elementarın graful G1 este L = [0, 1, 2, 3, 4, 5, 0].

Un graf neorientat G = (V,E) este conex daca, pentru orice pereche de noduri v, w ∈ V , exista un lantde la v la w. Un graf cu un singur nod este considerat conex. Desigur, graful G1 din Figura 12 nu esteconex, deoarece, de exemplu, nu exista un lant de la nodul 0 la nodul 6. Un exemplu de graf conex:

Figura 16. Graf conex

Consideram un graf neorientat G = (V,E) si un subgraf al acestuia, C = (V1, E1). Atunci C este ocomponenta conexa a grafului G daca C este conex si nu exista un alt subgraf al lui G, C ′ = (V2, E2)cu V1 ⊂ V2 care sa fie conex (C este un subgraf conex al lui G, maximal cu aceasta proprietate).Asadar, o componenta conexa este alcatuita din toate nodurile ıntre care exista un lant elementar.Sunt 3 componente conexe ale grafului G1 din Figura 12: acestea au multimea nodurilor formata din{0, 1, 2, 3, 4, 5}, {6} si respectiv {7}.Intr-un graf neorientat conex cu n noduri si m muchii este ındeplinita relatia m ≥ n− 1. Daca un grafcu n noduri are p componente conexe, atunci numarul minim de muchii care trebuie adaugate pentru cagraful sa devina conex este p−1. Un graf neorientat conex, cu n noduri si m = n−1 muchii, este aciclicsi maximal cu aceasta proprietate. Pentru a obtine dintr-un graf neorientat conex, doua componenteconexe, se ınlatura un numar minim de muchii egal cu gradul minim din graf.

116

Page 117: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Programul urmator genereaza componentele conexe ale unui graf neorientat. Deoarece un nod nu poatefi decat ıntr-o componenta conexa, vom considera un tablou marcat cu n componente, prin intermediulcaruia vom marca nodurile care au fost introduse ıntr-o componenta conexa. Initial, toate elementeletabloului marcat sunt 0, cu semnificatia ca nici un nod nu a fost ınca marcat. Atunci cand un nod vva fi introdus ıntr-o componenta conexa, marcat[v] va deveni 1.

1 #include <iostream >

2 #include <fstream >

3 #define dim 20

4 using namespace std;

5

6 int st[dim];

7 int A[dim][dim];

8 int n, v, w;

9 int marcat[dim]; // folosit pentru marcarea nodurilor

10 bool sol = false;

11

12 void CitireDate(const char numeFisier [])

13 {

14 /* aceeasi functie din programul precedent */

15 }

16

17 int valid(int k)

18 {

19 /* aceeasi functie din programul precedent */

20 }

21

22 void scrie_solutie ()

23 {

24 sol = true;

25 }

26

27 void btr()

28 {

29 int k = 1;

30 st[k] = -1;

31 while(k > 0)

32 {

33 if(st[k] < n - 1)

34 {

35 st[k] = st[k] + 1;

36 if(valid(k))

37 {

38 if(st[k] == w)

39 scrie_solutie ();

40 else

41 {

42 k = k + 1;

43 st[k] = -1;

44 }

117

Page 118: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

45 }

46 }

47 else

48 k = k - 1;

49 }

50 }

51

52 void comp_conexe ()

53 {

54 int nr = 0; // numarul de componente conexe

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

56 if (marcat[v] == 0)

57 {

58 nr++;

59 st[0] = v;

60 marcat[v] = 1;

61 cout << "Componenta conexa " << nr << ": " << v << " ";

62 for (w = 0; w < n; w++)

63 if (v != w)

64 {

65 sol = false;

66 btr();

67 if (sol == true)

68 {

69 marcat[w] = 1;

70 cout << w << " ";

71 }

72 }

73 cout << endl;

74 }

75 }

76

77 int main()

78 {

79 CitireDate("in.txt");

80 comp_conexe ();

81 return 0;

82 }

4.1.4 Grafuri euleriene, grafuri bipartite si grafuri hamiltoniene

Un lant eulerian al unui graf neorientat G = (V,E) este un lant care viziteaza fiecare muchie o singuradata. Daca extremitatile lantului coincid si lantul este eulerian atunci ciclul respectiv se numete ciclueulerian.Un graf care contine un ciclu eulerian se numeste graf eulerian. Un graf fara noduri izolate este euleriandaca si numai daca este conex si gradele tuturor nodurilor sale sunt numere pare.

118

Page 119: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Figura 17. Graf eulerian

Un graf neorientat se numeste bipartit daca exista o partitie a multimii nodurilor ın doua submultimiastfel ıncat oricare doua noduri din aceeasi submultime sa nu fie adiacente.Graful din Figura 17 este bipartit, deoarce exista o partitie a multimii nodurilor ın doua submultimi,A = {0, 3, 6} si B = {1, 2, 4, 5}, ın care oricare doua noduri ale aceleiasi submultimi nu sunt adiacente.

Intr-un graf neorientat, un lant hamiltonian este un lant elementar care contine toate nodurile grafului.Un ciclu hamiltonian este un ciclu elementar care contine toate nodurile grafului. Asadar, un cicluhamiltonian este un ciclu care trece o singura data prin toate nodurile grafului (cu exceptia noduluide start). Un graf care admite un ciclu hamiltonian se numeste graf hamiltonian. De exemplu, dacastergem nodurile izolate din graful G1, acesta devine un graf hamiltonian. De asemenea, graful G2 dinFigura 13 este unul hamiltonian.In cele ce urmeaza vom genera toate ciclurile hamiltoniene ale unui graf neorientat.

1 #include <iostream >

2 #include <fstream >

3 #define dim 20

4 using namespace std;

5

6 int st[dim];

7 int A[dim][dim];

8 int n;

9 bool sol = false;

10

11 void CitireDate(const char numeFisier [])

12 {

13 /* functia de mai sus */

14 }

15

16 int valid(int k)

17 {

18 if (A[st[k - 1]][st[k]] == 0)

19 return 0;

20 if (k == n - 1 && A[st[0]][ st[k]] == 0)

21 return 0;

22 for (int i = 0; i < k; i++)

23 if (st[i] == st[k]) return 0;

24 return 1;

25 }

26

27 void scrie_solutie ()

119

Page 120: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

28 {

29 sol = true;

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

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

32 cout << st[0] << endl;

33 }

34

35 void btrRec(int k)

36 {

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

38 {

39 st[k] = i;

40 if (valid(k))

41 if (k == n - 1)

42 {

43 sol = true;

44 scrie_solutie ();

45 }

46 else

47 btrRec(k + 1);

48 }

49 }

50

51 int main()

52 {

53 CitireDate("in.txt");

54 st[0] = 0;

55 btrRec (1);

56 if (sol == false)

57 cout <<"Nu exista ciclu hamiltonian in graf."<<endl;

58 return 0;

59 }

Sa remarcam faptul ca algoritmul precedent rezolva problema comisului voiajor (despre care stim ca nupoate fi rezolvata printr-un algoritm polinomial).

4.2 Grafuri orientate

Relatiile existente ıntre elementele unei colectii de date nu pot fi exprimate ıntotdeauna prin grafurineorienate. Pentru a identifica tipul de graf pe care ıl vom folosi pentru a reprezenta datele furnizate deo problema concreta, vom defini mai ıntai relatia dintre nodurile grafului si vom verifica daca aceastaare proprietatea de simetrie. Daca nodul v aflat ın relatie cu nodul w implica si ca nodul w este ınrelatie cu nodul v, atunci graful este neorientat. In caz contrar, graful este orientat.Un graf orientat (digraf) G este o pereche ordonata de multimi (V,A), unde V este o multime finitanumita multimea varfurilor grafului, iar A ⊂ V ×V este o multime de perechi ordonate (v, w) ∈ V ×V ,numita multimea arcelor lui G. Arcul (v, w) ∈ A este reprezentat grafic printr-o sageata de la varful vla varful w cu semnificatia ca exista o relatie de la v la w. Vom considera ın cele ce urmeaza ca un arcuneste doua varfuri distincte.Intr-un graf orientat G = (V,A), varful v este adiacent cu varful w, cu v, w ∈ V, v 6= w, daca exista

120

Page 121: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

macar un arc care le uneste (poate exista fie arcul (v, w), fie arcul (w, v), fie amandoua). Fiecare dintrecele doua varfuri este varf incident cu arcul (v, w) sau cu arcul (w, v). Pentru un arc (v, w) ∈ A, varfulv se numeste extremitatea initiala a arcului, iar varful w se numeste extremitatea finala a arcului. Dealtfel, daca exista arcul (v, w) ∈ A, spunem ca acesta este incident spre exterior cu v si spre interior cuw (arcul pleaca din v spre w). Daca exista arcul (w, v) ∈ A, spunem ca acesta este incident spre interiorcu v si spre exterior cu w (arcul pleaca din w spre v). Gradul exterior al unui varf este numarul arcelorcare ies din varful respectiv (numarul arcelor incidente spre exterior). Gradul interior al unui varf estenumarul arcelor care intra ın varful respectiv (numarul arcelor incidente spre interior). Notam gradulexterior al unui varf v ∈ V , cu d+(v), iar gradul interior, cu d−(v). Un varf al grafului este izolat dacad+(v) = d−(v) = 0. Multimea succesorilor unui varf v ∈ V este {w ∈ V ; (v, w) ∈ A}, iar multimeapredecesorilor lui v este {w ∈ V ; (w, v) ∈ A}.Un graf orientat G3 = (V,A), cu multimea varfurilor V = {0, 1, 2, 3, 4} si multimea arcelor A ={(0, 1), (0, 3), (1, 4), (2, 1), (2, 4), (3, 1), (3, 2), (4, 2)} poate fi reprezentat grafic ca ın figura

Figura 18. G3 – graf orientat

De exemplu, ın acest graf, arcul (1, 4) este incident exterior nodului 1 si interior nodului 4, nodurile 1si 2 sunt adiacente, nodurile 0 si 2 nu sunt adiacente, iar d+(2) = 2, d−(2) = 2.Pentru un graf orientat cu n varfuri (etichetate cu numere de la 0 la n− 1) si m arce, are loc relatia

d+(0) + d+(1) + · · ·+ d+(n− 1) = d−(0) + d−(1) + · · ·+ d−(n− 1) = m.

Sunt 4n(n−1)/2 grafuri orientate cu n varfuri.

4.2.1 Reprezentarea ın memorie a grafurilor orientate

Memorarea grafurilor orientate se face la fel ca ın cazul grafurilor neorientate: folosind matricea deadiacenta atasata grafului, listele de adiacenta ale varfurilor grafului sau folosind lista arcelor.

In cazul grafurilor orientate, matricea de adiacenta nu mai este ın mod obligatoriu simetrica fata dediagonala principala. Suma elementelor de pe linia i a matricei de adiacenta, i = 0, 1, . . . , n − 1,reprezinta d+(i), iar suma elementelor de pe coloana j, j = 0, 1, . . . , n − 1, este d−(j). Astfel, sumatuturor elementelor matricei de adiacenta este egala cu m.

Sa presupunem ca datele de intrare pentru graful G3 din Figura 18 sunt citite dintr-un fisier text,”in.txt”, de forma

5

0 1

0 3

1 4

2 1

2 4

121

Page 122: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

3 1

3 2

4 2

ın care pe prima linie se afla numarul de varfuri (fiecare varf este etichetat cu numere de la 0 la 4), iarpe urmatoarele linii se regaseste cate un arc (i, j).

Prezentam functia care ıncarca datele de intrare referitoare la un graf orientat ın matricea de adiacentaatasata acestuia:

1 void CitireDate(const char numeFisier [], int** &A, int &n)

2 {

3 ifstream fin(numeFisier);

4 int i, j;

5 fin >> n;

6 A = new (nothrow) int*[n];

7 if(A == nullptr)

8 {

9 cout <<"Alocare esuata."<<endl;

10 exit (1);

11 }

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

13 {

14 A[i] = new (nothrow) int[n];

15 if(A[i] == nullptr)

16 {

17 cout <<"Alocare esuata."<<endl;

18 exit (1);

19 }

20 for (j = 0; j < n; j++)

21 A[i][j] = 0;

22 }

23 while (fin >> i >> j) A[i][j] = 1;

24 fin.close();

25 }

In cazul memorarii unui graf orientat folosind listele de adiacenta ale varfurilor acestuia, singura modi-ficare ın functia care creeaza listele de adiacenta este data de faptul ca arcul (i, j) este diferit de arcul(j, i). De fapt, lista de adiacenta a unui varf al grafului contine lista succesorilor acestuia.

1 struct NOD

2 {

3 int info;

4 NOD* next;

5 };

6

7 typedef NOD* LISTA;

8

9 void CitireListeAdiacenta(const char numeFisier [], LISTA* &l, int &n)

10 {

11 ifstream fin(numeFisier);

12 LISTA p;

122

Page 123: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

13 int i, j;

14 fin >> n;

15 l = new (nothrow) LISTA[n];

16 if(l == nullptr)

17 {

18 cout <<"Alocare esuata."<<endl;

19 exit (1);

20 }

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

22 l[i] = nullptr; // initial listele de adiacenta sunt vide

23 while (fin >> i >> j)

24 {

25 p = new (nothrow) NOD;

26 if(p == nullptr)

27 {

28 cout <<"Alocare esuata."<<endl;

29 exit (1);

30 }

31 p->info = j;

32 p->next = l[i]; // adaugare la inceput

33 l[i] = p;

34 }

35 fin.close();

36 }

Stocarea grafului folosind lista arcelor acestuia se realizeaza astfel: vom defini o structura cores-punzatoare unui arc al grafului orienatat, adica celor doua varfuri la care arcul este incident exterior,respectiv interior si vom declara un vector cu m componente de tipul nou definit. Pentru usurinta,vom adauga pe prima linie a fisierului din care citim datele de intrare, numarul m al arcelor, ın cazulexemplului de mai sus, 8.

1 #include <iostream >

2 #include <fstream >

3 using namespace std;

4

5 struct ARC

6 {

7 int v, w;

8 };

9

10 void CitireDate(const char numeFisier [], ARC* &tab , int &n, int &m)

11 {

12 ifstream fin(numeFisier);

13 int i, j;

14 fin >> n >> m;

15 tab = new (nothrow) ARC[m];

16 if(tab == nullptr)

17 {

18 cout <<"Alocare esuata."<<endl;

19 exit (1);

123

Page 124: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

20 }

21 for (int k = 0; k < m; k++)

22 tab[k] = { -1, -1 };

23 for (int k = 0; k < m; k++)

24 {

25 fin >> i >> j;

26 tab[k].v = i;

27 tab[k].w = j;

28 }

29 fin.close();

30 }

31

32 int main()

33 {

34 int n, m, i;

35 ARC* a;

36 CitireDate("in.txt", a, n, m);

37 cout << "Arcele garfului orientat sunt: " << endl;

38 for (i = 0; i < m; i++)

39 cout << a[i].v << " " << a[i].w << endl;

40 return 0;

41 }

4.2.2 Graf partial, subgraf, lant

Notiunile de graf partial si subgraf introduse pentru grafuri neorientate, raman valabile si pentru garfuriorientate. Un lant al grafului orientat G = (V,A) este o succesiune de varfuri din V , L = [v1, v2, . . . , vl],cu proprietatea ca ıntre oricare doua varfuri succesive din lant, vi si vi+1, exista fie arcul (vi, vi+1), fiearcul (vi+1, vi). La definirea unui lant, nu conteaza orientarea arcelor, ci numai faptul ca doua varfurisunt legate printr-un arc. De exemplu, L = [0, 1, 2, 3, 1, 4, 2] este un lant ın G3. Un lant este elementardaca contine doar varfuri distincte.

4.2.3 Drumuri si circuite

Un drum al grafului orientat G = (V,A) este o succesiune de varfuri din V , D = [v1, v2, . . . , vd],cu proprietatea ca arcele (v1, v2), (v2, v3), . . . , (vd−1, vd) ∈ A. Varfurile v1 si vd se numesc extremitatiledrumului, iar lungimea drumului este data de numarul de arce ce unesc nodurile din drum. De exemplu,D = [0, 1, 4, 2, 4] este un drum ın graful orientat G3 din Figura 18. Un drum elementar este un drumın care toate varfurile sunt distincte. De exemplu, D = [0, 1, 4, 2] este un drum elementar ın grafulG3. Un circuit ıntr-un graf orientat este un drum ın care extremitatile coincid si care contine numaiarce distincte. Circuitul elementar este circuitul ın care toate nodurile, cu exceptia extremitatilor, suntdistincte doua cate doua.

4.2.4 Graf complet si graf turneu

Un graf orientat este complet daca oricare doua varfuri distincte sunt adiacente. Sunt deci 3n(n−1)/2

grafuri complete cu n varfuri.

Un graf orientat este turneu, daca oricare ar fi doua varfuri distincte exista un singur arc ıntre acestea.Orice graf turneu este asadar graf complet si sunt 2n(n−1)/2 grafuri turneu cu n varfuri.

124

Page 125: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

4.2.5 Graf tare conex si componente tare conexe

Pentru grafurile orientate exista doua notiuni asociate cu notiunea generala de conexitate. Un graforientat G = (V,A) este slab conex daca, transformandu-l ıntr-un graf neorientat, ınlocuind toate arcelegrafului cu muchii, graful neorientat astfel obtinut este conex.Un graf orientat G = (V,A) este tare conex daca, oricare ar fi doua varfuri distincte ale acestuia v siw, exista drum si de la v la w, si de la w la v.Consideram un graf orientat G = (V,A) si un subgraf al acestuia, C = (V1, A1). Atunci C esteo componenta tare conexa a grafului G, daca este tare conex si nu exista un alt subgraf al lui G,C ′ = (V2, A2) cu V1 ⊂ V2 care sa fie tare conex (C este un subgraf tare conex al lui G, maximal cuaceasta proprietate). Daca vom considera urmatorul exemplu de graf orientat

Figura 19. Componente tare conexe

observam ca acesta nu este tare conex (de exemplu, nu este drum de la varful 1 la varful 6), dar are 5componente tare conexe: subgraful cu varfurile {0, 1, 2, 3}, subgraful cu varfurile {7, 8} si 3 subgrafurice contin cate un singur varf: {4}, {5}, respectiv {6}.

4.3 Parcurgerea grafurilor

Prin parcurgerea unui graf neorientat/orientat se ıntelege o modalitate de vizitare sistematica a nodu-rilor/varfurilor acestuia ın scopul vizualizarii sau prelucrarii lor. Exista doua modalitati de parcurgerea unui graf: ın latime (BFS – Breadth First Search) si ın adancime (DFS – Depth First Search).

4.3.1 Parcurgerea ın latime

Aceasta presupune ınceperea parcurgerii de la un anumit nod/varf, numit nod/varf de start, apoivizitarea tuturor nodurilor/varfurilor accesibile din acesta: i1, i2, . . . , ik. Se viziteaza apoi ın ordinetoate nodurile/varfurile accesibile din i1, nevizitate ınca, apoi din i2, . . . , apoi din ik, s.a.m.d, pana lavizitarea tuturor nodurilor/varfurilor accesibile pornind de la nodul/varful de start.Parcurgerea ın latime a grafului neorientat G1 din Figura 12, plecand de la diferite noduri de start:

0: 0 1 5 2 4 3

1: 1 0 2 4 5 3

2: 2 1 3 5 0 4

3: 3 2 4 1 5 0

4: 4 1 3 5 0 2

5: 5 0 2 4 1 3

6: 6

7: 7

Parcurgerea ın latime a grafului orientat G3 din Figura 18, pornind cu diferite varfuri de start:

125

Page 126: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

0: 0 1 3 4 2

1: 1 4 2

2: 2 1 4

3: 3 1 2 4

4: 4 2 1

Vom utiliza pentru parcurgerea BFS a unui graf o coada implementata dinamic si ınlantuit. Atunci candun nod/varf este introdus ın coada, acesta va fi marcat ca fiind vizitat, deoarece un nod/varf trebuievizitat o singura data. Vom folosi un tablou vizitat, ın care vizitat[i] este 0, daca nodul/varful i nua fost vizitat si 1, daca a fost vizitat. Astfel, la ınceputul algoritmului BFS, nodul/varful de start esteadaugat ın coada si marcat ca vizitat. Apoi, cat timp coada este nevida, pentru nodul/varful aflat laınceputul cozii: se adauga ın coada toate nodurile/varfurile accesibile din acesta ce nu au fost vizitate siapoi se marcheaza ca vizitate, apoi se afiseaza sau se prelucreaza informatia din nod/varf si se eliminadin coada. Daca graful a fost reprezentat folosind matricea de adiacenta atasata, atunci algoritmul BFSeste:

1 #include <iostream >

2 #include <fstream >

3 using namespace std;

4

5 struct NOD

6 {

7 int info;

8 NOD *next;

9 };

10

11 typedef NOD* COADA;

12

13 void adaugare(COADA &vf , COADA &sf , int nInfo) // adaugare in coada

14 {

15 COADA nou;

16 nou = new (nothrow) NOD;

17 if(nou == nullptr)

18 {

19 cout <<"Alocare esuata."<<endl;

20 exit (1);

21 }

22 nou ->info = nInfo;

23 nou ->next = nullptr;

24 if (vf == nullptr) //daca coada este vida

25 {

26 vf = nou;

27 sf = nou;

28 }

29 else

30 {

31 sf->next = nou;

32 sf = nou;

33 }

34 }

126

Page 127: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

35 void eliminare(COADA &vf)// eliminare din coada

36 {

37 COADA p;

38 if (vf == nullptr)

39 {

40 cout << "Coada vida." << endl;

41 return;

42 }

43 else

44 {

45 p = vf;

46 vf = vf ->next;

47 delete p;

48 }

49 }

50

51 void CitireDate(const char numeFisier [], int** &A, int &n)

52 {

53 /* citirea datelor de intrare in matricea de adiacenta pentru un graf

orientat sau neorientat */

54 }

55

56 void parcurgereBFS(int n, int start , int** A)

57 {

58 int* vizitat = new (nothrow) int[n];

59 if(vizitat == nullptr)

60 {

61 cout <<"Alocare esuata."<<endl;

62 exit (1);

63 }

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

65 vizitat[i] = 0; //nod nevizitat

66 COADA vf = nullptr , sf = nullptr;

67 adaugare(vf, sf, start);

68 vizitat[start] = 1;

69 while(vf != nullptr)

70 {

71 int i = vf ->info;

72 for(int j = 0; j < n; j++)

73 {

74 if (A[i][j] == 1 && vizitat[j] == 0)

75 {

76 adaugare(vf, sf, j);

77 vizitat[j] = 1;

78 }

79 }

80 cout << vf ->info << " ";

81 eliminare(vf);

82 }

127

Page 128: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

83 cout << endl;

84 }

85

86 int main()

87 {

88 int n;

89 int **A;

90 CitireDate("in.txt", A, n);

91 int start;

92 cout <<"Nodul de start: ";

93 cin >>start;

94 parcurgereBFS(n, start , A);

95 return 0;

96 }

Daca graful a fost memorat prin liste de adiacenta, algoritmul BFS devine:

1 #include <iostream >

2 #include <fstream >

3 using namespace std;

4

5 struct NOD

6 {

7 int info;

8 NOD *next;

9 };

10

11 typedef NOD* COADA;

12

13 void adaugare(COADA &vf , COADA &sf , int nInfo) // adaugare in coada

14 {

15 /* ... */

16 }

17

18 void eliminare(COADA &vf)// eliminare din coada

19 {

20 /* ... */

21 }

22

23 typedef NOD* LISTA;

24

25 void CitireListeAdiacenta(const char numeFisier [], LISTA* &l, int &n)

26 {

27 /* citirea datelor de intrare in liste de adiacenta , pentru un graf

orientat sau neorientat */

28 }

29

30 void parcurgereBFS(int n, int start , LISTA* l)

31 {

32 int* vizitat = new (nothrow) int[n];

128

Page 129: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

33 if(vizitat == nullptr)

34 {

35 cout <<"Alocare esuata."<<endl;

36 exit (1);

37 }

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

39 vizitat[i] = 0; //nod nevizitat

40 COADA vf = nullptr , sf = nullptr;

41 adaugare(vf, sf, start);

42 vizitat[start] = 1;

43 while (vf != nullptr)

44 {

45 int i = vf ->info;

46 LISTA p = l[i];

47 while (p != nullptr)

48 {

49 int j = p->info;

50 if (vizitat[j] == 0)

51 {

52 adaugare(vf, sf, j);

53 vizitat[j] = 1;

54 }

55 p = p->next;

56 }

57 cout << vf ->info << " ";

58 eliminare(vf);

59 }

60 cout << endl;

61 }

62

63 int main()

64 {

65 int n;

66 LISTA* l;

67 CitireListeAdiacenta("in.txt", l, n);

68 int start;

69 cout <<"Nodul de start: ";

70 cin >>start;

71 parcurgereBFS(n, start , l);

72 return 0;

73 }

Daca adaugarea de elemente ın listele de adiacenta s-ar fi realizat la sfarsit, atunci rezultatele (afisarile)celor doua parcurgeri ar fi fost identice.

4.3.2 Parcurgerea ın adancime

Aceasta presupune ınceperea parcurgerii de la un nod/varf de start si marcarea acestuia ca fiind vizitat.Apoi se continua cu vizitarea primului nod/varf accesibil din acesta, ce nu a fost ınca vizitat (sa ıl notamcu i). Se viziteaza apoi primul nod/varf accesibil din i s.a.m.d. Atunci cand se ıntalneste un nod/varf

129

Page 130: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

pentru care s-au vizitat toate nodurile/varfurile accesibile din acesta, se revine la nodul/varful imediatanterior din care s-a ajuns la acesta din urma. Parcurgerea continua pana se revine la nodul/varful destart si acesta nu mai are noduri/varfuri accesibile din acesta si nevizitate.Parcurgerea ın adancime a grafului neorientat din Figura 12, pornind cu diferite noduri de start:

0: 0 1 2 3 4 5

1: 1 0 5 2 3 4

2: 2 1 0 5 4 3

3: 3 2 1 0 5 4

4: 4 1 0 5 2 3

5: 5 0 1 2 3 4

6: 6

7: 7

Parcurgerea ın adancime a grafului orientat G3 din Figura 18, cu diferite varfuri de start:

0: 0 1 4 2 3

1: 1 4 2

2: 2 1 4

3: 3 1 4 2

4: 4 2 1

Aceasta modalitate de vizitare a nodurilor/varfurilor este una recursiva si poate fi implementata caatare, adica printr-o functie recursiva sau iterativ, caz ın care vom folosi o stiva implementata dinamicsi ınlantuit. Atunci cand un nod/varf este introdus ın stiva, acesta va fi marcat ca fiind vizitat. Astfel,la ınceputul algoritmului DFS, nodul/varful de start este adaugat ın stiva si marcat ca vizitat. Apoi, cattimp stiva este nevida, pentru nodul/varful aflat la ınceputul stivei: daca exista un urmator nod/varfaccesibil din acesta care este nevizitat, se adauga ın stiva si se marcheaza ca vizitat, apoi se afiseaza sause prelucreaza informatia din nod/varf, altfel se elimina din stiva nodul/varful de la ınceputul stivei.Daca graful a fost reprezentat folosind matricea de adiacenta atasata, atunci algoritmul DFS este:

1 #include <iostream >

2 #include <fstream >

3 using namespace std;

4

5 struct NOD

6 {

7 int info;

8 NOD *next;

9 };

10

11 typedef NOD* STIVA;

12

13 void push(STIVA &vf , int nInfo)

14 {

15 STIVA nou = new (nothrow) NOD;

16 if(nou == nullptr)

17 {

18 cout <<"Alocare esuata."<<endl;

19 exit (1);

20 }

21 nou ->info = nInfo;

130

Page 131: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

22 nou ->next = vf;

23 vf = nou;

24 }

25

26 void pop(STIVA &vf)

27 {

28 STIVA p;

29 if (vf == nullptr)

30 {

31 cout << "Stiva vida." << endl;

32 return;

33 }

34 else

35 {

36 p = vf;

37 vf = vf ->next;

38 delete p;

39 }

40 }

41

42 void CitireDate(const char numeFisier [], int** &A, int &n)

43 {

44 /*... graf orienat sau neorientat ... */

45 }

46

47 void parcurgereDFS(int n, int start , int** A)

48 {

49 int* vizitat = new (nothrow) int[n];

50 if(vizitat == nullptr)

51 {

52 cout <<"Alocare esuata."<<endl;

53 exit (1);

54 }

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

56 vizitat[i] = 0; //nod nevizitat

57 STIVA vf = nullptr;

58 push(vf , start);

59 vizitat[start] = 1;

60 cout << vf ->info << " ";

61 while (vf != nullptr)

62 {

63 int i = vf ->info;

64 int j = 0;

65 while(j < n && (A[i][j] == 0 || (A[i][j] == 1 && vizitat[j] == 1)))

66 j++;

67 if (j == n) pop(vf);

68 else

69 {

70 cout << j << " ";

131

Page 132: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

71 push(vf , j);

72 vizitat[j] = 1;

73 }

74 }

75 cout << endl;

76 }

77

78 int main()

79 {

80 int n;

81 int **A;

82 CitireDate("in.txt", A, n);

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

84 parcurgereDFS(n, i, A);

85 return 0;

86 }

Implementarea recursiva a functiei de parcurgere ın adancime:

1 int *vizitat , n, **A; // variabile globale

2

3 void CitireDate(const char numeFisier [])

4 {

5 /*... graf orienat sau neorientat ... */

6 }

7

8 void DFS_rec(int i)

9 {

10 cout << i << " ";

11 vizitat[i] = 1;

12 for (int j = 0; j < n; j++)

13 if (A[i][j] == 1 && vizitat[j] == 0)

14 DFS_rec(j);

15 }

16

17 int main()

18 {

19 CitireDate("in.txt");

20 vizitat = new (nothrow) int[n];

21 if(vizitat == nullptr)

22 {

23 cout <<"Alocare esuata."<<endl;

24 exit (1);

25 }

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

27 vizitat[i] = 0; //nod nevizitat

28 int start;

29 cout <<"Nodul de start: ";

30 cin >>start;

31 DFS_rec(start);

132

Page 133: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

32 return 0;

33 }

Prezentam ın cele ce urmeaza implementarea parcurgerii DFS ın cazul ın care graful este reprezentatprin liste de adiacenta.

1 #include <iostream >

2 #include <fstream >

3 using namespace std;

4

5 struct NOD

6 {

7 int info;

8 NOD *next;

9 };

10

11 typedef NOD* STIVA;

12

13 void push(STIVA &vf , int nInfo)

14 {

15 /*...*/

16 }

17

18 void pop(STIVA &vf)

19 {

20 /*...*/

21 }

22

23 typedef NOD* LISTA;

24

25 void CitireListeAdiacenta(const char numeFisier [], LISTA* &l, int &n)

26 {

27 /*... grafuri neorientate sau orientate ...*/

28 }

29

30 void parcurgereDFS(int n, int start , LISTA* l)

31 {

32 int* vizitat = new (nothrow) int[n];

33 if(vizitat == nullptr)

34 {

35 cout <<"Alocare esuata."<<endl;

36 exit (1);

37 }

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

39 vizitat[i] = 0; //nod nevizitat

40 STIVA vf = 0;

41 push(vf , start);

42 vizitat[start] = 1;

43 cout << vf ->info << " ";

44 while (vf != nullptr)

133

Page 134: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

45 {

46 int i = vf ->info;

47 LISTA p = l[i];

48 while(p != nullptr && vizitat[p->info] == 1) p = p->next;

49 if (p == nullptr) pop(vf);

50 else

51 {

52 cout << p->info << " ";

53 push(vf , p->info);

54 vizitat[p->info] = 1;

55 }

56 }

57 cout << endl;

58 }

59

60 int main()

61 {

62 int n;

63 LISTA* l;

64 CitireListeAdiacenta("in.txt", l, n);

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

66 {

67 parcurgereDFS(n, i, l);

68 cout << endl;

69 }

70 return 0;

71 }

Implementarea recursiva a pargurgerii DFS pentru reprezentarea grafului prin liste de adiacenta:

1 typedef NOD* LISTA;

2 int *vizitat , n;

3 LISTA* l;

4

5 void CitireListeAdiacenta(const char numeFisier [])

6 {

7 /*... graf orienat sau neorientat ... */

8 }

9

10 void DFS_rec2(int i)

11 {

12 cout << i << " ";

13 vizitat[i] = 1;

14 for(LISTA p = l[i]; p != nullptr; p = p->next)

15 {

16 int j = p->info;

17 if (vizitat[j] == 0)

18 DFS_rec2(j);

19 }

20 }

134

Page 135: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

21 int main()

22 {

23 CitireListeAdiacenta("in.txt");

24 vizitat = new (nothrow) int[n];

25 if(vizitat == nullptr)

26 {

27 cout <<"Alocare esuata."<<endl;

28 exit (1);

29 }

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

31 vizitat[i] = 0; //nod nevizitat

32 int start;

33 cout <<"Nodul de start: ";

34 DFS_rec2(start);

35 return 0;

36 }

Complexitatea operatiei de parcurgere (BFS sau DFS) a unui graf reprezentat prin matricea de adiacentaeste O(n2). Pentru un nod/varf de start se cauta pe linia corespunzatoare a matricei de adiacenta toatenodurile/varfurile accesibile din el si pentru toate nodurile/varfurile gasite se procedeaza la fel. Fiecarenod/varf este vizitat o singura data. Timpul necesar pentru a gasi toate nodurile/varfurile accesibiledintr-un nod/varf este O(n) deoarece trebuie inspectate toate cele n coloane ale liniei corespunzatoarenodului/varfului considerat. Sumand dupa toate cele n noduri/varfuri, rezulta un timp O(n2).Parcurgerea BFS sau DFS a grafurilor reprezentate prin listele de adiacenta are complexitatea O(n+m).Fiecare nod/varf este vizitat o singura data. Toate nodurile accesibile din nodul/varful de start se gasescın lista sa de adiacenta. Numarul de elemente din lista de adiacenta a unui nod/varf este egal cu numarulde muchii incidente acestuia ın cazul grafurilor neorientate, respectiv numarul de arce incidente spreexterior, pentru grafuri orientate.

4.3.3 Aplicatii

Utilizarea parcurgerii ın latime pentru determinarea lanturilor/drumurilor de lungimeminima ıntr-un graf neorientat/orientat

Parcurgerea ın latime are o proprietate importanta pe care o vom folosi ın cele ce urmeaza: fiecarenod/varf este vizitat pe cel mai scurt lant/drum, ıncepand din nodul/varful de start. Parcurgerea BFSselecteaza nodurile/varfurile ın ordinea departarii lor fata de nodul/varful de start: la ınceput se vizi-teaza nodul/varful de start, apoi nodurile/varfurile pentru care lungimea lantului de la nodul/varful destart la acestea este 1, apoi nodurile/varfurile pentru care lungimea lantului/drumului de la nodul/varfulde start la acestea este 2 s.a.m.d.Programul urmator afiseaza un lant de lungime minima avand ca extremitati nodurile x si y ale unuigraf neorientat. Se va folosi un tablou declarat global, numit predecesor, initializat cu −1 (o valoareadiferita de etichetele posibile ale nodurilor), ın care se va pastra predecesorul fiecarui nod obtinut folosindparcurgerea BFS. Astfel, lantul va putea fi reconstituit folosind tabloul predecesor, astfel: se afiseazay, apoi predecesor[y], apoi predecesor[predecesor[y]] si tot asa pana cand se obtine valoarea −1(predecesorul nodului de start, x, ramane setat la valoarea −1). Vom implementa o functie recursivapentru a afisa lantul de lungime minima de la x la y.

1 #include <iostream >

2 #include <fstream >

3 using namespace std;

135

Page 136: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

4 struct NOD

5 {

6 int info;

7 NOD *next;

8 };

9

10 typedef NOD* COADA;

11 int n, **A, *vizitat , *predecesor;

12

13 void adaugare(COADA &vf , COADA &sf , int nInfo)

14 {

15 /* ... */

16 }

17

18 void eliminare(COADA &vf)

19 {

20 /* ... */

21 }

22

23 void CitireDate(const char numeFisier [])

24 {

25 /* ... */

26 }

27

28 void afisare(int i)

29 {

30 if (i != -1)

31 {

32 afisare(predecesor[i]);

33 cout << i << " ";

34 }

35 }

36

37 void BFS(int start)

38 {

39 COADA vf = nullptr , sf = nullptr;

40 vizitat = new (nothrow) int[n];

41 if(vizitat == nullptr)

42 {

43 cout <<"Alocare esuata."<<endl;

44 exit (1);

45 }

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

47 vizitat[i] = 0; //nod nevizitat

48 predecesor = new (nothrow) int[n];

49 if(predecesor == nullptr)

50 {

51 cout <<"Alocare esuata."<<endl;

52 exit (1);

136

Page 137: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

53 }

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

55 predecesor[i] = -1; // predecesorul lui i

56 adaugare(vf, sf, start);

57 vizitat[start] = 1;

58 predecesor[start] = -1;

59 while (vf != nullptr)

60 {

61 int i = vf ->info;

62 for (int j = 0; j < n; j++)

63 {

64 if (A[i][j] == 1 && vizitat[j] == 0)

65 {

66 adaugare(vf, sf, j);

67 vizitat[j] = 1;

68 predecesor[j] = i;

69 }

70 }

71 eliminare(vf);

72 }

73 }

74

75 int main()

76 {

77 CitireDate("in.txt");

78 int x, y;

79 cout << "Extremitatea stanga: ";

80 cin >> x;

81 cout << "Extremitatea dreapta: ";

82 cin >> y;

83 BFS(x);

84 cout << "Lantul de lungime minima de la " << x << " la " << y<< endl;

85 if (predecesor[y] != -1) afisare(y);

86 else cout << "Nu exista lant de la " << x << " la " << y << endl;

87 cout << endl;

88 return 0;

89 }

Algoritmul ramane acelasi pentru determinarea unui drum de lungime minima ıntre doua varfuri aleunui graf orientat.

Utilizarea parcurgerii ın adancime sau ın latime pentru determinarea componentelor co-nexe ale unui graf neorientat

O parcurgere ın adancime sau ın latime a unui graf neorientat ıncepand cu un anumit nod de start,viziteaza toate nodurile componentei conexe care ıl contine. Daca, dupa o parcurgere, mai sunt nodurinevizitate, parcurgerea se reia pornind de la primul nod nevizitat pentru a determina o noua componentaconexa. Numarul componentelor conexe ale unui graf este egal cu numarul de parcurgeri necesare pentrua fi vizitate toate nodurile grafului. Daca graful este conex, se va afisa o singura componenta conexa,care va contine toate nodurile grafului.

137

Page 138: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

1 #include <iostream >

2 #include <fstream >

3 using namespace std;

4

5 int n, *vizitat , **A;

6

7 void CitireDate(const char numeFisier [])

8 {

9 /* ... */

10 }

11

12 void DFS_rec(int i)

13 {

14 cout << i << " ";

15 vizitat[i] = 1;

16 for (int j = 0; j < n; j++)

17 if (A[i][j] == 1 && vizitat[j] == 0)

18 DFS_rec(j);

19 }

20

21 int main()

22 {

23 CitireDate("in.txt");

24 vizitat = new (nothrow) int[n];

25 if(vizitat == nullptr)

26 {

27 cout <<"Alocare esuata."<<endl;

28 exit (1);

29 }

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

31 vizitat[i] = 0; //nod nevizitat

32 int nr = 0; //nr. copmonentelor conexe

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

34 if (vizitat[i] == 0)

35 {

36 cout << "Componenta conexa " << ++nr << ": ";

37 DFS_rec(i);

38 cout << endl;

39 }

40 return 0;

41 }

Utilizarea parcurgerii ın adancime pentru determinarea componentelor tare conexe aleunui graf orientat

Varful de start x se cauta printre varfurile ce nu au fost adaugate unei componente tare conexe. Seidentifica: multimea succesorilor lui x (varfurile y pentru care exista drum de la x la y), la care seadauga si varful x si apoi, multimea predecesorilor lui x (varfurile y pentru care exista drum de la y la

138

Page 139: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

x), la care se adauga si varful x. Prin intersectia celor doua multimi de varfuri se obtine componentatare conexa. Identificarea celor doua multimi de varfuri se face parcurgand graful ın adancime. Un varfse marcheaza ca vizitat daca a fost adaugat la o componenta tare conexa. Daca graful este tare conex,se va afisa o singura componenta tare conexa, care va contine toate varfurile grafului.

1 #include <iostream >

2 #include <fstream >

3 using namespace std;

4

5 int n, **A, *succesor , *predecesor , nr;

6

7 void CitireDate(const char numeFisier [])

8 {

9 //...

10 }

11

12 void DFS_rec1(int i)

13 {

14 succesor[i] = nr;

15 for (int j = 0; j < n; j++)

16 if (A[i][j] == 1 && succesor[j] == 0)

17 DFS_rec1(j);

18 }

19

20 void DFS_rec2(int i)

21 {

22 predecesor[i] = nr;

23 for (int j = 0; j < n; j++)

24 if (A[j][i] == 1 && predecesor[j] == 0)

25 DFS_rec2(j);

26 }

27

28 int main()

29 {

30 CitireDate("in.txt");

31 succesor = new (nothrow) int[n];

32 if(succesor == nullptr)

33 {

34 cout <<"Alocare esuata."<<endl;

35 exit (1);

36 }

37 predecesor = new (nothrow) int[n];

38 if(predecesor == nullptr)

39 {

40 cout <<"Alocare esuata."<<endl;

41 exit (1);

42 }

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

44 {

45 succesor[i] = 0;

139

Page 140: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

46 predecesor[i] = 0;

47 }

48 nr = 1;

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

50 if (succesor[i] == 0)

51 {

52 DFS_rec1(i); DFS_rec2(i);

53 for (int j = 0; j < n; j++)

54 if (succesor[j] != predecesor[j])

55 succesor[j] = predecesor[j] = 0;

56 nr++;

57 }

58 for (int i = 1; i < nr; i++)

59 {

60 cout << "Componenta tare conexa " << i << ": ";

61 for (int j = 0; j < n; j++)

62 if (succesor[j] == i) cout << j << " ";

63 cout << endl;

64 }

65 return 0;

66 }

Variabila nr (initializata cu 1) retine numarul curent al componentei tare conexe care urmeaza sa fieidentificata. Vectorii succesor si predecesor corespunzatori multimilor succesorilor, respectiv prede-cesorilor, sunt initializati cu 0. Pentru fiecare varf i, daca acesta nu a fost adaugat unei componente tareconexe (adica daca succesor[i] = 0), toti succesorii lui i, inclusiv i, vor fi marcati cu valoarea nr, lafel si toti predecesorii lui i, inclusiv i. Daca succesor[i] 6= predecesor[i], atunci ınseamna ca varfulcorespunzator nu va face parte din componenta tare conexa curenta si atunci are loc reinitializareasuccesor[i] = predecesor[i] = 0.

4.4 Grafuri ponderate

Consideram un graf orientat G = (V,A) si o functie f : A −→ R+ care asociaza fiecarui arc un numarreal pozitiv (avand semnificatia de cost, distanta, timp, durata etc.), numit costul arcului. Functa f senumeste functia cost. Un graf orientat G = (V,A) pentru care s-a definit o functie cost se numeste graforientat ponderat. De exemplu,

Figura 20. Graf orientat ponderat

Daca pentru garful G = (V,A) exista un cost (pondere) atasat fiecarui arc, atunci si aceasta informatietrebuie stocata ın memorie. Astfel, graful poate fi stocat ın memorie prin intermediul matricei costurilor

140

Page 141: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

(ponderilor), adica a matricei An×n, de componente

aij =

cij , daca i 6= j si (i, j) ∈ A∞ sau −∞, daca i 6= j si (i, j) /∈ A0, daca i = j,

,

unde i, j = 0, 1, . . . , n− 1, iar cij este costul atasat arcului (i, j). Matricea ponderilor memoreaza doarcosturile drumurilor directe ıntre doua varfuri (prin drum direct ıntre varfurile v si w se ıntelege undrum ıntre aceste doua varfuri care nu trece printr-un alt varf). Deoarece +∞ sau −∞ nu pot fireprezentate ın memoria calculatorului, pentru arce inexistente se stocheaza ın matrice o valoare foartemare, respectiv foarte mica.

Desigur ca, o alta varianta de reprezentare a unui graf ponderat este prin intermediul listei arcelor. Inacest caz, structura definita pentru memorarea fiecarui arc va contine si un camp corespunzator costuluiarcului.

Notiunile tocmai prezentate sunt identice si pentru grafuri neorientate.

4.4.1 Drumuri de cost minim

Se considera G = (V,A) un graf orientat ponderat. Costul unui drum ıntre doua varfuri v, w ∈ V estesuma costurilor arcelor componente. Se cere sa se determine drumul de cost minim de la v la w, pentruorice v, w ∈ V . Vom studia doi algoritmi clasici de rezolvare a acestei probleme: algoritmul Roy-Floydsi algoritmul Dijkstra. Ambii algoritmi au la baza teorema lui Bellman care afirma ca: daca drumulde cost minim (optim) dintre doua varfuri oarecare v si w trece printr-un nod u, atunci si drumurilede la v la u si de la u la w sunt optime. Cei doi algoritmi se disting prin modul ın care se identificanodurile intermediare u. Algoritmul Roy-Floyd determina drumul de cost minim ıntre oricare douavarfuri ale grafului G, iar algoritmul Dijkstra determina drumurile de cost minim de la un varf fixat latoate celelalte varfuri ale grafului.

Algoritmul Roy-Floyd

Matricea costurilor este supusa unor transformari care determina ca, ın final, fiecare componenta aij samemoreze costul drumului minim dintre varfurile i si j. Pentru orice varf k al grafului, k = 0, . . . , n− 1si pentru orice pereche de varfuri i si j, cu i, j = 0, . . . , n − 1 , daca aik + akj < aij (suma dintrecostul drumului de la i la k si costul drumului de la k la j este mai mica decat costul drumului directde la i la j, atunci se ınlocuieste ın matricea costurilor, costul drumului direct de la i la j, cu costuldrumului care trece prin nodul k. Matricea costurilor astfel obtinuta ofera informatii ce se pot folosipentru a verifica daca exista drum de cost minim ıntre doua varfuri ale grafului. In caz afirmativ, poatefi determinat costul acestui drum. Algoritmul prin care se reconstituie drumul de cost minim, folosindmatricea costurilor transformata prin algoritmul Roy-Floyd, foloseste metoda divizarii. Acest lucru esteposibil deoarece problema de determinare a varfurilor care compun drumul de cost minim de la i la jse descompune ın doua subprobleme: determinarea drumului minim de la varful i la varful k (k 6= i) sideterminarea drumului minim de la varful k la varful j (k 6= j). Daca aik + akj = aij , atunci varful kface parte din drumul minim de la i la j.

1 #include <iostream >

2 #include <fstream >

3 using namespace std;

4

5 double **A;

6 int n;

141

Page 142: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

7 const double MaxInf = 1.e20; // 10^{20}

8

9 void Citire_Date(const char numeFisier [])

10 {

11 int i, j;

12 double c;

13 ifstream fin(numeFisier);

14 fin >> n;

15 A = new (nothrow) double *[n];

16 if(A == nullptr)

17 {

18 cout <<"Alocare esuata."<<endl;

19 exit (1);

20 }

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

22 {

23 A[i] = new (nothrow) double[n];

24 if(A[i] == nullptr)

25 {

26 cout <<"Alocare esuata."<<endl;

27 exit (1);

28 }

29 for (j = 0; j < n; j++)

30 if (i == j)

31 A[i][j] = 0.0;

32 else

33 A[i][j] = MaxInf;

34 //se cere determinarea drumului de cost minim

35 }

36 // pentru fiecare arc (i,j) se citeste si costul asociat

37 while (fin >> i >> j >> c)

38 A[i][j] = c;

39 fin.close();

40 }

41

42 void Roy_Floyd ()

43 {

44 for (int k = 0; k < n; k++)

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

46 for (int j = 0; j < n; j++)

47 if (A[i][j] > A[i][k] + A[k][j])

48 A[i][j] = A[i][k] + A[k][j];

49 }

50

51 void DEI(int i, int j)

52 {

53 int k, find = 0;

54 for (k = 0; k < n && !find; k++)

55 if (i != k && j != k && A[i][j] == A[i][k] + A[k][j])

142

Page 143: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

56 {

57 DEI(i, k);

58 DEI(k, j);

59 find = 1;

60 }

61 if (!find) cout << j << " ";

62 }

63

64 void Afisare(int i, int j)

65 {

66 if (A[i][j] < MaxInf)

67 {

68 cout << "Drumul de la " << i << " la " << j

69 << " are lungimea " << A[i][j] << endl;

70 cout << "Acesta este: "<< i << " ";

71 DEI(i, j);

72 }

73 else

74 cout << "Nu exista drum de la " << i << " la " << j;

75 }

76

77 int main()

78 {

79 int x, y;

80 Citire_Date("in.txt");

81 Roy_Floyd ();

82 cout <<"Extremitatea stanga: ";

83 cin >>x;

84 cout <<"Extremitatea dreapta: ";

85 cin >>y;

86 Afisare(x, y);

87 cout <<endl;

88 return 0;

89 }

Acest algoritm combina metoda programarii dinamice cu metoda divizarii. Algoritmul de transformarea matricei costurilor are ordinul de complexitate O(n3), iar algoritmul de determinare a drumurilor decost minim are complexitatea O(n log n). Deci ordinul algoritmului este O(n3). Algoritmul poate fiadaptat pentru a determina drumurile de cost maxim dintr-un graf aciclic (se considera costul muchiilorinexistente egal cu −∞).

Algoritmul Dijkstra

Fiind dat un graf orientat ponderat G = (V,A), se cere sa se determine pentru un varf fixat v ∈ V ,drumurile de cost minim de la v la orice varf w ∈ V . Algoritmul abordeaza o strategie Greedy:pornind de la varful v, la fiecare pas se selecteaza succesiv varfurile grafului, ın ordinea crescatoare acosturilor drumurilor de la varful v la acestea. Se vor folosi tablourile dmin, predecesor si selectat, cuurmatoarele semnificatii: dmin este vectorul costurilor drumurilor minime gasite, la un moment dat, dela varful v la toate celelalte varfuri ale grafului, predecesor indica drumurile de la varful v la celelaltevarfuri ale grafului. Pentru fiecare varf i al grafului, se retine predecesorul j al acestuia pe drumul de la

143

Page 144: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

v la i. Pentru varful de start v se seteaza predecesor[v] = -1. Vectorul selectat semnifica multimeavarfurilor selectate: selectat[i] = 0, daca varful i este neselectat, iar selectat[i] = 1, daca varfuli este selectat.Algoritmul este urmatorul: initial, se adauga v la vectorul solutiei, adica selectat[v] = 1. Costuriledrumurilor de la v la fiecare varf i al grafului se copie de pe linia v a matricei costurilor ın vectoruldmin. Daca varfurile i au un cost finit al drumului de la v la acestea, atunci predecesor[i] = v.Apoi se executa de n − 1 ori secventa (pentru toate varfurile, mai putin pentru v): mai ıntai, printrevarfurile neselectate se cauta cel aflat la distanta minima fata de v si se selecteaza. Fie acesta w; atunciselectat[w] = 1. Pentru varfurile neselectate j, se actualizeaza ın vectorul dmin costul drumurilor dela v la acestea, utilizand ca varf intermediar varful w, astfel: se compara dmin[j] (costul existent ınvectorul dmin pentru j) cu dmin[w] + A[w][j] (suma dintre costul existent ın dmin pentru varful w sicostul drumului de la w la varful j). Daca suma este mai mica, atunci dmin[j] = dmin[w] + A[w][j],iar predecesor[j] = w (drumul trece prin acest varf). In final, pentru fiecare varf al grafului, cuexceptia lui v, se indica drumul de la v la acesta.

1 #include <iostream >

2 #include <fstream >

3 using namespace std;

4

5 double **A;

6 int n;

7 int *predecesor;

8 const double MaxInf = 1.e20;

9

10 void Citire_Date(const char numeFisier [])

11 {

12 //...

13 }

14

15 void afisare(int i)

16 {

17 if (predecesor[i] != -1)

18 afisare(predecesor[i]);

19 cout << i << " ";

20 }

21

22 int main()

23 {

24 Citire_Date("in.txt");

25 predecesor = new (nothrow) int[n];

26 if(predecesor == nullptr)

27 {

28 cout <<"Alocare esuata."<<endl;

29 exit (1);

30 }

31 int *selectat = new (nothrow) int[n];

32 if(selectat == nullptr)

33 {

34 cout <<"Alocare esuata."<<endl;

35 exit (1);

144

Page 145: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

36 }

37 double *dmin = new (nothrow) double[n], min;

38 if(dmin == nullptr)

39 {

40 cout <<"Alocare esuata."<<endl;

41 exit (1);

42 }

43 int v, w = -1;

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

45 {

46 selectat[i] = 0;

47 dmin[i] = 0.0;

48 predecesor[i] = -1;

49 }

50 cout << "v: ";

51 cin >> v;

52 selectat[v] = 1;

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

54 {

55 dmin[i] = A[v][i];

56 if (i != v)

57 if (dmin[i] < MaxInf) predecesor[i] = v;

58 }

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

60 {

61 min = MaxInf;

62 for (int j = 0; j < n; j++)

63 if (selectat[j] == 0)

64 if (dmin[j] < min)

65 {

66 min = dmin[j];

67 w = j;

68 }

69 if (w != -1)

70 {

71 selectat[w] = 1;

72 for (int j = 0; j < n; j++)

73 if (selectat[j] == 0)

74 if (dmin[j] > dmin[w] + A[w][j])

75 {

76 dmin[j] = dmin[w] + A[w][j];

77 predecesor[j] = w;

78 }

79 }

80 }

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

82 if (i != v)

83 if (predecesor[i] != -1)

84 {

145

Page 146: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

85 cout << "Costul drumului de la " << v << " la " << i

86 << " este " << dmin[i] << endl;

87 afisare(i);

88 cout << endl;

89 }

90 else

91 cout << "Nu exista drum de la " << v << " la " << i << endl;

92 return 0;

93 }

Determinarea costului minim al drumului de la varful v la oricare celelalte varfuri ale grafului arecomplexitatea O(n2).

146

Page 147: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Capitolul 5

Arbori

Din punctul de vedere al definitiei matematice, un arbore este un graf neorientat conex si aciclic.Asadar, un graf neorientat cu n noduri este arbore daca si numai daca are n − 1 muchii si nu continecicluri. De exemplu, graful de mai jos este arbore.

Figura 21. Arbore

In informatica este utilizata notiunea de arbore cu radacina si din acest motiv termenul arbore esteasociat (ın informatica) celui de arbore cu radacina. Daca stabilim nodul 0 ca radacina a arborelui demai sus, acesta poate fi reprezentat astfel

Figura 22. Arbore cu radacina

Putem defini (recursiv) un arbore cu radacina ca o multime finita A cu 0 sau mai multe elementenumite noduri, cu urmatoarele proprietati: daca A = ∅, atunci arborele este vid, iar daca A 6= ∅,atunci exista un singur nod numit radacina arborelui (R), iar celelalte noduri sunt partitionate ın k ≥ 0arbori disjuncti, T1, T2, . . . , Tk, nodul R fiind legat de radacina fiecarui arbore Ti, 1 ≤ i ≤ k, printr-o

147

Page 148: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

muchie. Arborii T1, T2, . . . , Tk se numesc subarborii (fiii) lui R, iar R se numeste parintele subarborilorTi, 1 ≤ i ≤ k.

Cateva exemple de arbori:

Figura 23. Arbori cu radacina

In figura de mai sus, observam ca arborele (a) este vid, iar arborele (b) are un singur nod.

Introducem cateva elemente de terminologie specifica arborilor.

Exista un lant unic, adica o succesiune unica de noduri (muchii), de la radacina unui arbore la fiecarenod al acestuia. Numarul de muchii ale lantului desemneaza, dupa cum stim deja, lungimea acestuia.Nivelul unui nod ın arbore este lungimea lantului de la radacina la acel nod. Ca urmare, radacina unuiarbore nevid este singurul nod ce are nivelul 0. Sa observam ca arborele din Figura 22 si arborii (d), (e)si (f) din Figura 23 sunt structurati pe 4 nivele (nivelele 0, 1, 2 si 3). De exemplu, ın cazul arborelui (f),nodul radacina A formeaza nivelul 0, nodurile B,C si D sunt pe nivelul 1, nodurile E, . . . , P formeazanivelul 2, iar R si S sunt pe nivelul 3. Daca nodurile i si j sunt adiacente, atunci nodul aflat pe un nivelmai mic se numeste ascendent (parinte), iar cel aflat pe nivelul urmator se numeste descendent (fiu).De exemplu, pentru arborele din Figura 22, nodul 3 este descendentul (fiul) nodului 2, iar nodul 2 esteascendentul (parintele) nodului 3. Fiecare nod poate sa aiba zero, unul sau mai multi descendenti, ınsapoate avea un singur nod parinte. Un nod fara descendenti se numete nod terminal sau nod frunza.Descendentii directi ai aceluiasi nod parinte se numesc noduri frate. Exista un singur nod fara parinte,acesta fiind radacina arborelui.

Inaltimea unui nod ın arbore este lungimea celui mai lung lant de la respectivul nod la un nod terminal.Astfel, ınaltimea nodului B (Figura 23) este 2, iar a nodului C este 1. Inaltimea unui arbore nevid esteınaltimea radacinii arborelui, adica nivelul maxim al nodurilor din arbore. Inaltimea arborilor (d), (e)si (f) este egala cu 3, a arborelui (c) este 1, iar a arborelui (b) este 0.

Ordinul unui nod este reprezentat de numarul de fii (descendenti directi) ai nodului. De exemplu,ordinul nodului G al arborelui (f) este 2. Orice nod terminal are ordinul 0, iar un nod de ordin diferitde 0 se numeste nod intern.

148

Page 149: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

5.1 Reprezentarea ın memorie a arborilor cu radacina si parcurgereaacestora

Memorarea unui arbore cu radacina se poate face folosind matricea de adiacenta, listele de adiacenta,referintele descendente sau referintele ascendente, ultimele doua modalitati de reprezentare fiind specificearborilor si eficiente pentru acestia.

Memorarea arborilor cu radacina folosind referintele descendente

Pornind de la definitia recursiva a arborelui cu radacina, se va memora pentru nodul radacina: informatiautila si adresele tuturor celor k subarbori ai sai. Astfel, se va preciza pentru fiecare nod: informatia utila,numarul descendentilor si un tablou ce contine adresele fiilor. Un nod va avea urmatoarea structura:

1 struct NODA //nod arbore

2 {

3 int info; // nodurile pot contine mai multe informatii utile

4 int nr; //nr. de fii

5 NODA* *adrFii; // tablou dinamic cu adresele fiilor

6 };

Parcurgerea unui arbore presupune vizitarea nodurilor acestuia ıntr-o anumita ordine ın scopul pre-lucrarii informatiilor utile ale acestora. Un arbore cu radacina A poate fi parcurs ın adancime (ınpreordine, inordine sau postordine) sau ın latime (pe nivele). Cele trei modalitati de parcugere ınadancime se pot defini plecand de la definitia recursiva a arborelui A:

• daca arborele A este vid, atunci parcurgerea lui A ın preordine, inordine sau postordine se reducela lista vida;

• daca A are un singur nod, atunci parcurgerea lui A ın preordine, inordine sau postordine se reducela vizitarea singurului nod;

• altfel, daca arborele A are radacina R si subarborii T1, T2, . . . , Tk,

– parcurgerea ın preordine a arborelui A presupune: vizitarea radacinii R, parcurgerea ınpreordine a lui T1, apoi parcurgerea ın preordine a lui T2, T3, . . . , Tk.

– parcurgerea ın inordine a arborelui A presupune: parcurgerea ın inordine a subarborelui T1,vizitarea radacinii R, parcurgerile ın inordine ale subarborilor T2, T3, . . . , Tk.

– parcurgerea ın postordine a arborelui A presupune: parcurgerea ın postordine a subarborilorT1, T2, . . . , Tk, apoi vizitarea radacinii R.

Exemplificam cele enuntate mai sus pentru arborele (f) din Figura 23. Astfel, obtinem urmatoarelerezultate daca acesta este parcurs ın

a) preordineA,B,E, F,G,R, S,C,H, I, J,K,D,L,M,N,O, P

b) inordineE,B, F,R,G, S,A,H,C, I, J,K,L,D,M,N,O, P

c) postordineE,F,R, S,G,B,H, I, J,K,C, L,M,N,O, P,D,A

149

Page 150: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

d) latime sau pe nivele (se parcurg ın ordine nodurile de pe fiecare nivel de la stanga la dreapta,ıncepand de la nivelul 0 pana la ultimul nivel)

A,B,C,D,E, F,G,H, I, J,K,L,M,N,O, P,R, S.

Parcurgerile ın adancime pot fi implementate recursiv sau iterativ (folosind o stiva).

Pentru fiecare nod al arborelui vom citi urmatoarele date: numarul descendentilor si informatia utila aacestuia. Citirea informatiilor din noduri o vom realiza ın postordine: mai ıntai fiii si apoi nodul parinte.Adresele nodurilor le vom plasa, pe rand, ıntr-o stiva pana cand apare nodul parinte al acestora. Atuncivom scoate din stiva adresele fiilor, plasandu-le ın nodul parinte. Apoi adaugam ın stiva adresa noduluiparinte. In final, cand arborele este construit, singura adresa din stiva este cea a radacinii arborelui.In ceea ce priveste traversarea arborelui, aceasta o vom realiza pe latime, pe fiecare nivel ın parte. Inacest sens vom utiliza, dupa cum stim deja, o coada. Coada va contine adresele nodurilor ce urmeazaa fi traversate. Cat timp coada nu este vida, se extrage din coada adresa nodului curent, iar apoi seintroduc ın coada adresele fiilor acestuia. Se repeta procedeul pana coada devine vida.

1 #include <iostream >

2 using namespace std;

3

4 struct NODA //nod arbore

5 {

6 int info;

7 int nr;

8 NODA* *adrFii;

9 };

10

11 struct NODL //nod lista (stiva/coada)

12 {

13 NODA* adr; // adresa nod arbore (informatia utila)

14 NODL* next;

15 };

16

17 NODA* R;// radacina arbore

18 NODL *vf , *sf;// primul si ultimul nod din lista

19

20 void push(NODA* a) // adaugare in stiva

21 {

22 NODL* nou = new (nothrow) NODL;

23 if (nou == nullptr)

24 {

25 cout << "Eroare de alocare." << endl;

26 exit (1);

27 }

28 nou ->adr = a;

29 nou ->next = vf;

30 vf = nou;

31 }

32

33 NODA* pop() // eliminare din stiva

34 {

150

Page 151: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

35 NODA* a;

36 NODL* l;

37 if (vf == nullptr) return nullptr;

38 l = vf;

39 vf = vf->next;

40 a = l->adr;

41 delete l;

42 return a;

43 }

44

45 NODA* creare_arbore ()

46 {

47 NODA* a;

48 int nr_noduri;

49 cout << "Numarul de noduri ale arborelui: ";

50 cin >> nr_noduri;

51 vf = nullptr;

52 for (int i = 1; i <= nr_noduri; i++)

53 {

54 a = new (nothrow) NODA;

55 if (a == nullptr)

56 {

57 cout << "Eroare de alocare." << endl;

58 exit (1);

59 }

60 cout << "Informatia din nod: ";

61 cin >> a->info;

62 cout << "Numarul de fii: ";

63 cin >> a->nr;

64 if (a->nr == 0)

65 a->adrFii = nullptr;

66 else

67 {

68 a->adrFii = new (nothrow) NODA*[a->nr];

69 if (a->adrFii == nullptr)

70 {

71 cout << "Eroare de alocare." << endl;

72 exit (1);

73 }

74 }

75 for (int j = a->nr - 1; j >= 0; j--)

76 a->adrFii[j] = pop();

77 push(a);

78 }

79 return pop(); // returneaza adresa radacinii

80 }

81

82 void adaugare(NODA* a) // adaugare in coada

83 {

151

Page 152: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

84 NODL* nou;

85 nou = new (nothrow) NODL;

86 if (nou == nullptr)

87 {

88 cout << "Eroare de alocare." << endl;

89 exit (1);

90 }

91 nou ->adr = a;

92 nou ->next = nullptr;

93 if (vf == nullptr)

94 {

95 vf = nou;

96 sf = nou;

97 }

98 else

99 {

100 sf->next = nou;

101 sf = nou;

102 }

103 }

104

105 NODA* stergere () // stergere din coada

106 {

107 NODA* a;

108 NODL* l;

109 if (vf == nullptr) return nullptr;

110 l = vf;

111 vf = vf->next;

112 if (vf == nullptr) sf = nullptr;

113 a = l->adr;

114 delete l;

115 return a;

116 }

117

118 void parcurgere(NODA* rad) // parcurgere pe nivele

119 {

120 NODA* a;

121 vf = nullptr;

122 sf = nullptr;

123 adaugare(rad);

124 do {

125 a = stergere ();

126 if (a != nullptr)

127 {

128 cout << a->info << " ";

129 for (int i = 0; i < a->nr; i++)

130 adaugare(a->adrFii[i]);

131 }

132 }while (a != nullptr);

152

Page 153: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

133 cout << endl;

134 }

135

136 int main()

137 {

138 R = creare_arbore ();

139 parcurgere(R);

140 return 0;

141 }

Memorarea arborilor cu radacina folosind referintele ascendente

Pentru fiecare nod se precizeaza informatia utila si referinta catre nodul parinte. In acest scop, se vautiliza un tablou numit parinte, ın care parinte[i] = -1, daca i este radacina arborelui si p[i] =

j, daca j este ascendentul (parintele) lui i. De exemplu, pentru arborele cu radacina din Figura 22,tabloul ascendentilor este parinte[ ] = {-1, 0, 0, 2, 3, 0, 3}.

Memorarea arborilor cu radacina folosind listele sau matricea de adiacenta necesita precizarea radacinii.

5.2 Arbori partiali de cost minim

Un arbore partial al unui graf G este un graf partial al lui G care este arbore. Fie G = (V,E) un grafneorientat, conex si o functie f : E −→ R+ care asociaza fiecarei muchii (i, j) ∈ E un cost cij ≥ 0 (Geste un graf neorientat ponderat). Se cere sa se determine un arbore partial al lui G, astfel ıncat sumacosturilor muchiilor sale sa fie minima. Un astfel de arbore se numete arbore partial de cost minim.Pentru rezolvarea problemei vom prezenta doi algoritmi clasici: algoritmul lui Kruskal si algoritmul luiPrim ce au la baza tehnica Greedy de proiectare.

Algoritmul lui Kruskal

Presupunem ca G = (V,E) este un graf neorientat conex cu n noduri. Initial, fiecare nod constituieun arbore (graful partial al grafului G ce nu contine nici o muchie si deci are doar noduri izolate esteformat din n arbori partiali disjuncti). Apoi, se executa de n− 1 ori secventa: se cauta si se selecteazamuchia de cost minim ce uneste doua noduri care fac parte din doi arbori diferiti. Algoritmul asigurafaptul ca, graful obtinut la un moment dat nu contine cicluri deoarece, la fiecare pas, se selecteaza omuchie care uneste doi arbori diferiti.Pentru reprezentarea grafului vom folosi lista muchiilor. Pentru a usura identificarea muchiei de costminim, lista muchiilor va fi sortata, dupa cost. De asemenea, se va folosi un tablou l ın care fiecarecomponenta l[i] va memora numarul de ordine al arborelui partial din care face parte nodul i. Initial,l[i] = i, cu i = 0, 1, . . . , n − 1. O muchie (x, y) este selectata astfel ıncat sa aiba costul minim siextremitatile sale sa faca parte din arbori diferiti (l[x] 6= l[y]). Dupa ce s-a identificat o muchie (x, y) cesatisface cerintele, unificarea arborilor se face astfel: daca l[x] < l[y], se adauga arborele partial din careface parte nodul y la arborele partial din care face parte nodul x, iar daca daca l[x] > l[y], se adaugaarborele partial din care face parte nodul x la arborele partial din care face parte nodul y.

153

Page 154: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Figura 24. Arbore partial de cost minim

1 #include <iostream >

2 #include <fstream >

3 using namespace std;

4

5 struct MUCHIE

6 {

7 int v, w; // noduri

8 double cost; //cost

9 };

10

11 int n, m;

12 int* l;

13 MUCHIE *lm; // lista muchiilor

14

15 void Citire_Date(const char numeFisier [])

16 {

17 int i, j;

18 double c;

19 ifstream fin(numeFisier);

20 fin >> n >> m;

21 l = new (nothrow) int[n];

22 if(l == nullptr)

23 {

24 cout <<"Eroare la alocare."<<endl;

25 exit (1);

26 }

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

28 l[i] = i;

29 lm = new (nothrow) MUCHIE[m];

30 if(lm == nullptr)

31 {

32 cout <<"Eroare la alocare."<<endl;

33 exit (1);

154

Page 155: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

34 }

35 for (int k = 0; k < m; k++)

36 {

37 fin >> i >> j >> c;

38 lm[k].v = i;

39 lm[k].w = j;

40 lm[k].cost = c;

41 }

42 fin.close();

43 }

44

45 void sortare ()

46 {

47 MUCHIE aux;

48 for (int i = 0; i < m - 1; i++)

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

50 if (lm[i].cost > lm[j].cost)

51 {

52 aux = lm[i];

53 lm[i] = lm[j];

54 lm[j] = aux;

55 }

56 }

57

58 int main()

59 {

60 double c = 0; // costul total

61 Citire_Date("in.txt");

62 sortare ();

63 cout << "Arborele partial de cost minim : " << endl;

64 int i = 0;

65 int k = 0;

66 while(k < n - 1)

67 {

68 int x = lm[i].v, y = lm[i].w;

69 if (l[x] != l[y])

70 {

71 k++;

72 c += lm[i].cost;

73 //se alege muchia (x,y) pentru a unifica doi arbori

74 cout << "(" << x << "," << y << ") ";

75 if (l[x] > l[y])

76 {

77 for (int j = 0; j < n; j++)

78 if (l[j] == l[x])

79 l[j] = l[y];

80 }

81 else

82 {

155

Page 156: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

83 for (int j = 0; j < n; j++)

84 if (l[j] == l[y])

85 l[j] = l[x];

86 }

87 }

88 i++;

89 }

90 cout << endl << "Costul: " << c <<endl;

91 return 0;

92 }

Complexitatea algoritmului lui Kruskal este O(m logm), daca algoritmului de sortare utilizat are com-plexitatea O(m logm).

Algoritmul lui Prim

Se porneste de la un nod al grafului, numit nod radacina. La fiecare pas al algoritmului din cei n− 1,se adauga arborelui obtinut la pasul anterior o muchie de cost minim. Aceasta muchie are o singuraextremitate ın arborele obtinut la pasul anterior. Graful va fi memorat prin matricea costurilor.Dupa ce a fost ales nodul radacina, se considera ca toate nodurile sunt adiacente cu nodul de pornire.Daca un nod nu este de fapt adiacent cu nodul radacina, ıl vom presupune adiacent cu o muchie de cost+∞. Initial, este selectat nodul radacina, iar prin alegerea unei muchii se va selecta un nou nod. Unnod neselectat se considera adiacent la unul dintre nodurile selectate, mai exact la acel nod pentru carecostul muchiei este minim. Se va folosi un tablou s, ın care s[i] = −1 daca nodul i apartine arboreluiconstruit pana la acel moment si s[i] = k, daca nodul i nu apartine arborelui construit pana atunci, iarmuchia de cost minim care uneste pe i cu unul dintre nodurile arborelui deja construit este (i, k) (decimuchia de cost minim este (i, s[i]), costul acesteia fiind A[i][s[i]]). Dupa ce, la un anumit pas, a fostselectat nodului j, fiecare nod neselectat i, va fi legat de nodul j, daca muchia (i, j) are costul mai micdecat costul muchiei care-l lega la unul dintre nodurile selectate (A[i][s[i]] > A[i][j]).

1 #include <iostream >

2 #include <fstream >

3 using namespace std;

4

5 double **A;

6 int n;

7 const double MaxInf = 1.e20;

8

9 void Citire_Date(const char numeFisier [])

10 {

11 int i, j;

12 double c;

13 ifstream fin(numeFisier);

14 fin >> n;

15 A = new (nothrow) double *[n];

16 if(A == nullptr)

17 {

18 cout <<"Eroare la alocare."<<endl;

19 exit (1);

20 }

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

156

Page 157: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

22 {

23 A[i] = new (nothrow) double[n];

24 if(A[i] == nullptr)

25 {

26 cout <<"Eroare la alocare."<<endl;

27 exit (1);

28 }

29 for (j = 0; j < n; j++)

30 if (i == j)

31 A[i][j] = 0.0;

32 else

33 A[i][j] = MaxInf;

34 }

35 while (fin >> i >> j >> c)

36 {

37 A[i][j] = c;

38 A[j][i] = A[i][j];

39 }

40 fin.close();

41 }

42

43 int main()

44 {

45 Citire_Date("in.txt");

46 int *s = new (nothrow) int[n], j;

47 if(s == nullptr)

48 {

49 cout <<"Eroare la alocare."<<endl;

50 exit (1);

51 }

52 double c = 0;//se calculeaza costul total

53 double min;

54 s[0] = -1; // nodul 0 este radacina si este selectata in arbore

55 /*toate nodurile grafului sunt considerate adiacente cu nodul

radacina */

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

57 s[i] = 0;

58 cout << "Arborele partial de cost minim : " << endl;

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

60 {

61 min = MaxInf;

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

63 if (s[i] != -1)

64 if (min > A[i][s[i]])

65 {

66 min = A[i][s[i]];

67 j = i;

68 }

69 //se adauga la costul total costul aferent muchiei (j, s[j])

157

Page 158: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

70 c += A[j][s[j]];

71 cout << "(" << j << "," << s[j] << ") ";

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

73 if (s[i] !=-1 && A[i][s[i]] > A[i][j])

74 s[i] = j;

75 s[j] = -1;

76 }

77 cout << endl << "Costul: " << c << endl;

78 return 0;

79 }

Algoritmul lui Prim are complexitatea O(n2). In functie de numarul muchiilor din graf se prefera unalgoritm sau altul pentru determinarea arborelui partial de cost minim al unui graf ponderat: dacanumarul muchiilor este mic, se prefera algoritmul lui Kruskal, iar daca acesta este mare, atunci seprefera algoritmul lui Prim (numarul muchiilor se apropie de n2).

5.3 Arbori binari

Un arbore se numeste ordonat daca pentru fiecare nod, subarborii sai sunt considerati ıntr-o anumitaordine. Un arbore binar este un arbore (cu radacina) ordonat ın care fiecare nod are cel mult doisubarbori: subarborele stang si subarborele drept. Asadar, pentru ca un nod al unui arbore binar sa fiebine determinat trebuie sa se cunoasca nodul parinte al sau si faptul daca este fiu stang sau fiu drept,deoarece ıntr-un arbore binar se face distinctie clara ıntre cei doi subarbori. Un exemplu de arborebinar:

Figura 25. Arbore binar

Pentru un arbore binar, pe nivelul s exista cel mult 2s noduri. Un arbore binar de ınaltime h are celmult 2h noduri pe ultimul nivel si numarul total de noduri este cel mult 20 + 21 + · · ·+ 2h = 2h+1 − 1.Inaltimea unui arbore binar satisface relatia log2(n + 1) − 1 ≤ h ≤ n− 1, deoarece n ≤ 2h+1 − 1 si, ınacelasi timp, h ≤ n− 1 (ınaltimea maxima se obtine pentru arborele degenerat).

5.3.1 Reprezentarea ın memorie si traversarea arborilor binari

Un arbore binar poate fi reprezentat ın memorie folosind doi vectori, stang si drept, unde stang[i]memoreaza eticheta fiului stang al nodului i, iar drept[i], eticheta fiului drept al nodului i, cu i =0, 1, . . . , n− 1. Daca nu exista fiu, se va memora valoarea −1. Pentru arborele din Figura 25,

nod: 0 1 2 3 4 5 6 7 8 9 10

stang[]: 1 3 5 -1 8 -1 -1 -1 -1 -1 -1

drept[]: 2 4 6 7 9 -1 10 -1 -1 -1 -1

158

Page 159: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

O alta modalitate de memorare (folosind referintele descendente) presupune precizarea pentru fiecarenod al arborelui binar a informatiei utile a acestuia si a adreselor fiului stang si fiului drept.

La fel ca oricare arbore cu radacina, un arbore binar poate fi parcurs:

a) ın inordine – mai ıntai se parcurge subarborele stang, apoi se viziteaza radacina, apoi se parcurgesubarborele drept.

b) ın preordine – mai ıntai se viziteaza radacina, apoi se parcurge subarborele stang, apoi subarboreledrept.

c) ın postordine – mai ıntai se parcurge subarborele stang, apoi subarborele drept, apoi se viziteazaradacina.

d) ın latime sau pe nivele – se parcurg nodurile ıncepand cu cel de pe nivelul 0 (radacina), continuandapoi cu nodurile de pe nivelurile superioare, ın ordine, fiecare nivel fiind parcurs de la stanga ladreapta.

Parcurgerea arborelui binar din Figura 25:

inordine: 3 7 1 8 4 9 0 5 2 6 10

preordine: 0 1 3 7 4 8 9 2 5 6 10

postordine: 7 3 8 9 4 1 5 10 6 2 0

latime: 0 1 2 3 4 5 6 7 8 9 10

Datele de intrare le vom citi din fisierul text “in.txt” care are pe prima linie numarul de noduri alearborelui binar, pe urmatoarea linie, ın ordine, fiii stangi ai fiecarui nod, iar pe ultima linie, ın ordine,succesorii drepti ai fiecarui nod. Pentru exemplul din Figura 25, fisierul text este

11

1 3 5 -1 8 -1 -1 -1 -1 -1 -1

2 4 6 7 9 -1 10 -1 -1 -1 -1

Vom parcurge arborele ın inordine, preordine, postordine si ın latime.

1 #include <iostream >

2 #include <fstream >

3 using namespace std;

4

5 struct NODL

6 {

7 int info;

8 NODL* next;

9 };

10

11 NODL *vf , *sf;

12 int n, *stang , *drept;

13

14 void CitireDate(const char numeFisier [])

15 {

16 int i;

17 ifstream fin(numeFisier);

18 fin >> n;

159

Page 160: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

19 stang = new int[n];

20 drept = new int[n];

21 for (i = 0; i < n; i++) fin >> stang[i];

22 for (i = 0; i < n; i++) fin >> drept[i];

23 fin.close();

24 }

25

26 void adaugare(int info) // adaugare in coada

27 {

28 NODL* nou;

29 nou = new (nothrow) NODL;

30 if (nou == nullptr)

31 {

32 cout << "Eroare la alocare." << endl;

33 exit (1);

34 }

35 nou ->info = info;

36 nou ->next = nullptr;

37 if (vf == nullptr)

38 {

39 vf = nou;

40 sf = nou;

41 }

42 else

43 {

44 sf->next = nou;

45 sf = nou;

46 }

47 }

48

49 int sterg() // stergere din coada

50 {

51 int info;

52 NODL* l;

53 if (vf == nullptr) return -1;

54 l = vf;

55 vf = vf->next;

56 if (vf == nullptr) sf = nullptr;

57 info = l->info;

58 delete l;

59 return info;

60 }

61

62 void parcurgere(int i) // parcurgere in latime

63 {

64 int info;

65 vf = nullptr;

66 sf = nullptr;

67 adaugare(i);

160

Page 161: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

68 do

69 {

70 info = sterg();

71 if (info != -1)

72 {

73 cout << info << " ";

74 if (stang[info] != -1) adaugare(stang[info]);

75 if (drept[info] != -1) adaugare(drept[info]);

76 }

77 }while(vf != nullptr);

78 }

79

80 void inordine(int i)

81 {

82 if (i != -1)

83 {

84 inordine(stang[i]);

85 cout << i << " ";

86 inordine(drept[i]);

87 }

88 }

89

90 void preordine(int i)

91 {

92 if (i != -1)

93 {

94 cout << i << " ";

95 preordine(stang[i]);

96 preordine(drept[i]);

97 }

98 }

99

100 void postordine(int i)

101 {

102 if (i != -1)

103 {

104 postordine(stang[i]);

105 postordine(drept[i]);

106 cout << i << " ";

107 }

108 }

109

110 void afisare(int i, int n) // afisare pe nivele identate , n = nivelul

111 {

112 if (i != -1)

113 {

114 afisare(drept[i], n + 1);

115 for (int j = 0; j < n; j++)

116 cout << " ";

161

Page 162: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

117 cout << i << endl;

118 afisare(stang[i], n + 1);

119 }

120 }

121

122 int main()

123 {

124 CitireDate("in.txt");

125 int R;

126 do

127 {

128 cout << "Radacina: ";

129 cin >> R;

130 } while (R < 0 || R >= n);

131 cout << "Parcurgerea arborelui binar in inordine: ";

132 inordine(R);

133 cout << endl;

134 cout << "Parcurgerea arborelui binar in preordine: ";

135 preordine(R);

136 cout << endl;

137 cout << "Parcurgerea arborelui binar in postordine: ";

138 postordine(R);

139 cout << endl;

140 cout << "Parcurgerea arborelui binar in latime: ";

141 parcurgere(R);

142 cout << endl;

143 cout << "Afisarea pe nivele identate: " << endl;

144 afisare(R, 0);

145 return 0;

146 }

Multi dintre algoritmii care prelucreaza arborii binari sunt recursivi si folosesc tehnica divizarii, avandla baza definitia recursiva a acestora.Rescriem programul folosind referintele descendente pentru a stoca arborele binar. In plus, imple-mentam o functie de stergere ın ıntregime a arborelui binar, parcurgandu-l ın postordine si stergand perand fiecare nod.

1 #include <iostream >

2 #include <fstream >

3 using namespace std;

4

5 struct NOD

6 {

7 int info;

8 NOD *s, *d; // adresele fiului stang si fiului drept

9 };

10

11 struct NODL

12 {

13 NOD* adr;

162

Page 163: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

14 NODL* next;

15 };

16

17 NODL *vf , *sf;

18 int n, *stang , *drept;

19

20 void CitireDate(const char numeFisier [])

21 {

22 //...

23 }

24

25 void adaugare(NOD* a) // adaugare in coada

26 {

27 NODL* nou;

28 nou = new (nothrow) NODL;

29 if (nou == nullptr)

30 {

31 cout << "Eroare la alocare." << endl;

32 exit (1);

33 }

34 nou ->adr = a;

35 nou ->next = nullptr;

36 if (vf == nullptr)

37 {

38 vf = nou;

39 sf = nou;

40 }

41 else

42 {

43 sf->next = nou;

44 sf = nou;

45 }

46 }

47

48 NOD* sterg() // stergere din coada

49 {

50 NOD* a;

51 NODL* l;

52 if (vf == nullptr) return nullptr;

53 l = vf;

54 vf = vf->next;

55 if (vf == nullptr) sf = nullptr;

56 a = l->adr;

57 delete l;

58 return a;

59 }

60

61

62 void parcurgere(NOD* rad) // parcurgere in latime

163

Page 164: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

63 {

64 NOD* a;

65 vf = nullptr;

66 sf = nullptr;

67 adaugare(rad);

68 do {

69 a = sterg ();

70 if (a != nullptr)

71 {

72 cout << a->info << " ";

73 if (a->s != nullptr) adaugare(a->s);

74 if (a->d != nullptr) adaugare(a->d);

75 }

76 }while(a != nullptr);

77 }

78

79 NOD* CreareArbore(int i)

80 {

81 if (i == -1) return nullptr;

82 else

83 {

84 NOD * p = new (nothrow) NOD;

85 if(p == nullptr)

86 {

87 cout <<"Eroare la alocare."<<endl;

88 exit (1);

89 }

90 p->info = i;

91 p->s = CreareArbore(stang[i]);

92 p->d = CreareArbore(drept[i]);

93 return p;

94 }

95 }

96

97 void inordine(NOD* p)

98 {

99 if (p != nullptr)

100 {

101 inordine(p->s);

102 cout << p->info << " ";

103 inordine(p->d);

104 }

105 }

106

107 void preordine(NOD* p)

108 {

109 if (p != nullptr)

110 {

111 cout << p->info << " ";

164

Page 165: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

112 preordine(p->s);

113 preordine(p->d);

114 }

115 }

116

117 void postordine(NOD* p)

118 {

119 if (p != nullptr)

120 {

121 postordine(p->s);

122 postordine(p->d);

123 cout << p->info << " ";

124 }

125 }

126

127 void StergereArbore(NOD* p)

128 {

129 if (p != nullptr)

130 {

131 StergereArbore(p->s);

132 StergereArbore(p->d);

133 delete p;

134 }

135 }

136

137 void afisare(NOD* p, int n) // afisare pe nivele identate , n = nivelul

138 {

139 if (p != nullptr)

140 {

141 afisare(p->d, n + 1);

142 for (int j = 0; j < n; j++)

143 cout << " ";

144 cout << p->info << endl;

145 afisare(p->s, n + 1);

146 }

147 }

148

149 int main()

150 {

151 CitireDate("in.txt");

152 int R;

153 do

154 {

155 cout << "Radacina: ";

156 cin >> R;

157 } while (R < 0 || R >= n);

158 NOD* r = CreareArbore(R);

159 cout << "Parcurgerea arborelui binar in inordine: ";

160 inordine(r);

165

Page 166: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

161 cout << endl;

162 cout << "Parcurgerea arborelui binar in preordine: ";

163 preordine(r);

164 cout << endl;

165 cout << "Parcurgerea arborelui binar in postordine: ";

166 postordine(r);

167 cout << endl;

168 cout << "Parcurgerea arborelui binar in latime: ";

169 parcurgere(r);

170 cout << endl;

171 cout << "Afisarea pe nivele identate: " << endl;

172 afisare(r, 0);

173 StergereArbore(r);

174 return 0;

175 }

Desigur, pot fi utilizate si celelalte modalitati cunoscute de reprezentare ın memorie a arborilor curadacina. De exemplu, daca am memora un arbore binar folosind referintele ascendente, pe langatabloul parinte, este necesar si un alt tablou, pe care ıl vom numi fiu, care sa indice faptul ca undescendent este fiu stang sau drept. Astfel, fiu[i] = 0, daca i este radacina arborelui, fiu[i] = -1,daca i este fiu stang si fiu[i] = 1, daca i este fiu drept. Tablourile parinte si fiu corespunzatoarearborelui binar reprezentat ın Figura 25 sunt

parinte[] = { -1, 0, 0, 1, 1, 2, 2, 3, 4, 4, 6 }

fiu[] = { 0, -1, 1, -1, 1, -1, 1, 1, -1, 1, 1 }

5.3.2 Arbori binari de cautare

Fiecarui nod al unui arbore i se ataseaza un camp special de informatie utila, numit cheie. Cheile suntunice si pot fi deci folosite pentru a identifica ın mod unic nodurile arborelui. Un arbore binar de cautareeste un arbore binar ın care, pentru fiecare nod, cheia din succesorul stang este mai mica decat cheiaparintelui, iar cheia din succesorul drept este mai mare decat cheia parintelui. Astfel, pentru fiecarenod, subarborele sau stang contine noduri cu valori mai mici ale cheii, iar subarborele sau drept continenoduri cu valori mai mari ale cheii. Un exemplu de arbore binar de cautare:

Figura 26. Arbore binar de cautare

Principalele operatii de prelucrare a unui arbore binar de cautare sunt: creare, parcurgere, actualizare(inserarea sau eliminarea unui nod) si cautare. Aceste operatii se realizeaza astfel ıncat structura dearbore binar de cautare sa nu fie afectata.

166

Page 167: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Inserarea unui nod ıntr-un arbore binar de cautare. Ne propunem sa inseram ıntr-un arborebinar de cautare un nod care are drept cheie o valoare data. Astfel, trebuie cautat nodul parinte pentruacesta si apoi precizat tipul de succesor. Arborele este gestionat prin intermediul adresei radacinii sale,iar cautarea nodului parinte pentru nodul nou si determinarea tipului de succesor al nodului nou (stangsau drept) se face cu ajutorul unui pointer care va indica nodul curent. Nodul de la care pornim esteradacina arborelui. Prin intermediul acestui pointer se avanseaza pe nivelurile arborelui prin succesorulstang sau succesorul drept al nodului curent, ın functie de rezultatul comparatiei dintre valoarea data sicheia din nodul curent. Avansarea ın arbore continua pana cand succesorul nodului curent este arborelevid.Algoritmul este urmatorul: daca arborele este vid, atunci se creeaza nodul terminal ce are valoarea datadrept cheie. Daca arborele este nevid atunci: daca valoarea noua de introdus coincide cu cheia noduluicurent, nu se mai realizeaza inserarea valorii, deoarece cheile trebuie sa fie unice; daca valoarea estemai mica decat cheia nodului curent, se reia algoritmul pentru subarborele stang, iar daca valoarea estemai mare decat cheia cheia nodului curent, se reia algoritmul pentru subarborele drept. Transmitereaadresei nodului nou se face prin referinta.

Daca ne propunem sa inseram ın arborele din Figura 26 nodul de cheie 15, deoarece 15 > 7 se vacontinua cu subarborele drept al nodului radacina 7. Succesiv, 15 > 11, 15 > 13, 15 > 14, deci nodulde cheie 15 va fi inserat ca fiu drept al nodului de cheie 14.

Crearea unui arbore binar de cautare se poate realiza prin apelul repetat al functiei de inserare. Deexemplu, pentru a obtine arborele binar de cautare din Figura 26, ordinea de citire a cheilor ın functiade creare a arborelui este 7, 3, 11, 1, 5, 9, 13, 0, 2, 4, 6, 8, 10, 12, 14 (arborele este traversat ın latime). Dacaam citi cheile de la 0 la 14 sau de la 14 la 0 am obtine arbori binari de cautare degenerati (arbori cu nnoduri dispuse pe n niveluri).

Stergerea unui nod dintr-un arbore binar de cautare. Ne propunem sa stergem un nod careare cheia egala cu o valoare data.Daca arborele este vid, evident stergerea nu are loc. Altfel, daca valoarea data coincide cu cheia noduluicurent si nodul curent este terminal (subarborele stang si subarborele drept sunt vizi), acesta va fi sters,adresa retinuta de parintele sau (corespunzatoare nodului curent) va deveni 0. Daca nodul nu esteterminal, dar subarborele sau stang este vid, nodul este sters, iar parintele lui va retine, ın locul adreseinodului curent, adresa subarborelui sau drept. Daca nodul nu este terminal, dar subarborele sau drepteste vid, nodul este sters, iar parintele lui va retine, ın locul adresei nodului curent, adresa subarboreluisau stang. Daca ambii subarbori ai nodului curent sunt nevizi, atunci se identifica cel mai din dreaptanod al subarborelui stang, iar cheia acestui nod va fi retinuta de nodul curent. Nodul astfel identificateste sters ca ın cazul ın care nodul are subarborele drept vid.Daca valoarea data este mai mica decat cheia nodului curent, se reia algoritmul pentru subarborelestang, iar daca valoarea este mai mare decat cheia nodului curent, se reia problema pentru subarboreledrept.

Daca am vrea sa stergem nodul de cheie 6 din arborele din Figura 26, ne aflam ın primul caz de stergere(subarborii stang si drept ai acestuia sunt vizi). Astfel, se va elibera spatiul de memorie ocupat denodul 6, iar ın nodul parinte (nodul 5) se va completa campul de adresa corespunzator nodului sters cupointerul null. Arborele devine

167

Page 168: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Figura 27. Arbore binar de cautare

Daca ne-am propune acum sa stregem nodul de cheie 5 din arborele reprezentat ın Figura 27, ne situamın cazul al treilea de stergere (nodul nu este terminal, dar subarborle sau drept este vid). Se elibereazaspatiul de memorie ocupat de nodul de sters, iar parintele sau, nodul de cheie 3, va retine ın campul deadresa corespunzator fiului drept, adresa subarborelui stang al nodului sters (adresa nodului de cheie4). Arborele devine

Figura 28. Arbore binar de cautare

Sa presupunem ca dorim sa stregem nodul de cheie 11 din arborele reprezentat ın Figura 28. Ne aflamın cazul al patrulea de stergere, cand ambii subarbori ai nodului de sters sunt nevizi. Astfel, mai ıntaise determina cel mai din dreapta nod al subarorelui stang al acestuia, adica nodul 10. Cheia acestuiaeste copiata ın nodul de sters. Apoi se elibereaza spatiul de memorie ocupat de nodul 10, iar parintelesau, nodul de cheie 9, va retine ın campul de adresa corespunzator fiului drept, pointerul null. Arboreledevine

Figura 29. Arbore binar de cautare

168

Page 169: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Cautarea ıntr-un arbore binar de cautare. Ne propunem sa verificam daca o valoare data seafla ın lista cheilor de identificare ale nodurilor unui arbore binar de cautare. Regula de cautare este:daca arborele este vid, evident ca nu exista ın arbore o cheie egala cu valoarea considerata; altfel, dacavaloarea coincide cu cheia nodului curent, atunci valoarea se gaseste ın arbore. Daca valoarea este maimica decat cheia nodului curent, se reia algoritmul pentru subarborele stang, iar daca valoarea este maimare decat cheia nodului curent se reia algoritmul pentru subarborele drept.

Parcurgerea unui arbore binar de cautare. Se poate utiliza oricare dintre modalitatile de par-curgere studiate pentru arbori binari. De remarcat ınsa, este faptul ca parcurgerea ın inordine a unuiarbore binar de cautare are ca efect afisarea cheilor de identificare a nodurilor acestuia ın ordine strictcrescatoare.

1 // arbore binar de cautare cu chei numere naturale

2

3 #include <iostream >

4 using namespace std;

5

6 struct NOD

7 {

8 int cheie; //pot fi adaugate si alte informatii utile

9 NOD *s, *d; // adresele fiului stang si fiului drept

10 };

11

12 typedef NOD* ABC;

13

14 void inserare(ABC &p, int c)

15 {

16 if (p != nullptr)

17 if (p->cheie == c)

18 cout << "Cheie deja existenta." << endl;

19 else

20 if (c < p->cheie)

21 inserare(p->s, c);

22 else

23 inserare(p->d, c);

24 else

25 {

26 p = new (nothrow) NOD;

27 if(p == nullptr)

28 {

29 cout <<"Eroare la alocare."<<endl;

30 exit (1);

31 }

32 p->cheie = c;

33 p->s = nullptr;

34 p->d = nullptr;

35 }

36 }

37

38

169

Page 170: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

39 ABC creare ()

40 {

41 ABC rad = nullptr;

42 int c;

43 cout << "Cheia noua: ";

44 cin >> c;

45 while (c != -1) // -1 este valoare de stop

46 {

47 inserare(rad , c);

48 cout << "Cheia noua: ";

49 cin >> c;

50 }

51 return rad;

52 }

53

54 void afisare(ABC p)

55 {

56 if (p != nullptr)

57 {

58 afisare(p->s);

59 cout << p->cheie << " ";

60 afisare(p->d);

61 }

62 }

63

64 void subarbori(ABC &p, ABC &q) // cazul ambilor subarbori nevizi

65 {

66 if (q->d != nullptr) subarbori(p, q->d);

67 else

68 {

69 p->cheie = q->cheie;

70 ABC t = q;

71 q = q->s;

72 delete t;

73 }

74 }

75

76 void stergere(ABC &p, int c)

77 {

78 ABC q;

79 if (p != nullptr)

80 if (p->cheie == c)

81 if (p->s == nullptr && p->d == nullptr)

82 {

83 delete p;

84 p = nullptr;

85 }

86 else

87 if (p->s == nullptr)

170

Page 171: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

88 {

89 q = p->d;

90 delete p;

91 p = q;

92 }

93 else

94 if (p->d == nullptr)

95 {

96 q = p->s;

97 delete p;

98 p = q;

99 }

100 else

101 subarbori(p, p->s);

102 else

103 if (c < p->cheie)

104 stergere(p->s, c);

105 else stergere(p->d, c);

106 else

107 cout << "Cheie inexistenta " << endl;

108 }

109

110 ABC cautare(ABC p, int c)

111 {

112 if (p != nullptr)

113 if (c < p->cheie)

114 return cautare(p->s, c);

115 else

116 if (c > p->cheie)

117 return cautare(p->d, c);

118 else return p;

119 else return nullptr;

120 }

121

122 int minim(ABC p)

123 {

124 while (p->s != nullptr)

125 p = p->s;

126 return p->cheie;

127 }

128

129 int maxim(ABC p)

130 {

131 while (p->d != nullptr)

132 p = p->d;

133 return p->cheie;

134 }

135

136

171

Page 172: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

137 int main()

138 {

139 ABC rad = nullptr;

140 int optiune , c;

141 do

142 {

143 cout << endl;

144 cout << "Arbore binar de cautare." << endl;

145 cout << "1. Creare ABC prin inserari repetate." << endl;

146 cout << "2. Inserarea unui nod nou." << endl;

147 cout << "3. Stergerea unui nod." << endl;

148 cout << "4. Cautarea unei chei in arbore." << endl;

149 cout << "5. Afisare (inordine) arbore." << endl;

150 cout << "6. Cheia minima din arbore." << endl;

151 cout << "7. Cheia maxima din arbore." << endl;

152 cout << "8. Terminare." << endl << endl;

153

154 cout << "Optiunea conform numarului de ordine: ";

155 cin >> optiune;

156 switch (optiune)

157 {

158 case 1:

159 rad = nullptr;

160 rad = creare ();

161 break;

162 case 2:

163 cout << "Cheia noua: ";

164 cin >> c;

165 if (c != -1) inserare(rad , c);

166 break;

167 case 3:

168 cout << "Cheia de sters: ";

169 cin >> c;

170 stergere(rad , c);

171 break;

172 case 4:

173 cout << "Cheia de cautat in arbore: ";

174 cin >> c;

175 if (cautare(rad , c) != nullptr)

176 cout << c << " este in arbore" << endl;

177 else

178 cout << c << " nu este in arbore" << endl;

179 break;

180 case 5:

181 afisare(rad);

182 break;

183 case 6:

184 if(rad != nullptr)

185 cout << "Cheia minima din arbore: " << minim(rad) << endl;

172

Page 173: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

186 else

187 cout << "Arbore vid." << endl;

188 break;

189 case 7:

190 if(rad != nullptr)

191 cout << "Cheia maxima din arbore: " << maxim(rad) << endl;

192 else

193 cout << "Arbore vid." << endl;

194 break;

195 case 8:

196 break;

197 default:

198 cout <<"Introduceti un numar valid de optiune (1 -> 8)!"<< endl;

199 }

200 } while (optiune != 8);

201 return 0;

202 }

Cheia cu valoarea minima se afla ın nodul cel mai din stanga, iar identificarea sa se face prin parcurgereaarborelui pornind din radacina, avansand doar prin legatura cu succesorul stang. Cheia cu valoareamaxima este ın nodul cel mai din dreapta, iar identificarea sa se realizeaza avansand doar prin legaturacu succesorul drept, pornind din radacina.

5.3.2.1 Arbori binari de cautare echilibrati. Arbori AVL

Ordinul de complexitate al operatiilor specifice pe un arbore binar de cautare este O(h), unde h esteınaltimea arborelui. In cel mai defavorabil caz, un arbore cu n noduri are ınaltimea egala cu h = n− 1(arbore degenerat). In acest caz, operatiile se executa cu un timp proportional cu numarul de noduri.Cazul cel mai favorabil ar fi ca arborele sa fie echilibrat, adica sa aiba ınaltimea O(log2 n).Sunt mai multe moduri de a defini echilibrarea si astfel se definesc mai multe tipuri de arbori de cautareechilibrati: arbori AVL, arbori B sau B+, arbori rosu–negru etc.Un arbore binar este total echilibrat daca diferenta dintre numarul nodurilor celor doi subarbori (stangsi drept) ai oricarui nod este cel mult 1. Vom relaxa aceasta definitie, deoarece crearea unui arbore binarde cautare total echilibrat este destul de costisitoare si vom spune ca un arbore binar este echilibrat dacadiferenta dintre ınaltimile celor doi subarbori ai oricarui nod este cel mult 1. Aceasta definitie a fostpropusa de Adelson–Velskii si Landis, de unde si denumirea arborilor binari de cautare corespunzatori,arbori AVL. Diferenta dintre ınaltimea subarborelui drept si ınaltimea subarborelui stang se numestefactor de echilibru (FE). Factorul de echilibru al oricarui nod al unui arbore AVL poate fi doar 0, 1sau −1. Acesta devine camp al structurii ce defineste fiecare nod. Notam cu hs si cu hd ınaltimeasubarborelui stang, respectiv drept al unui nod. Asadar, factorul de echilibru al nodului este definitprin FE = hd − hs.Ideea crearii unui arbore AVL este ca la inserarea fiecarui nod sa se pastreze echilibrarea, iar atuncicand un nod ısi pierde echilibrul, sa se aplice un procedeu de reechilibrare, prin asa numitele rotatiispecfice: rotire la stanga daca FE > 1 sau la dreapta, daca FE < 1. Nodul ın jurul caruia se realizeazarotirea este situat pe cel mai de jos nivel astfel ıncat factorul sau de echilibru este diferit de −1, 0 sau 1.

Adaugarea unui nod ıntr-un arbore AVL. In urma inserarii unui nod ıntr-un subarbore, ınaltimeaacestuia poate sa creasca sau nu. Astfel, adaugarea unui nod nou ıntr-un arbore binar de cautare poateconduce la dezechilibrarea anumitor noduri. O cheie noua se insereaza initial, ın mod obisnuit pentruun arbore binar de cautare, pornind de la radacina si continuand cu fiul stang sau drept, ın functie

173

Page 174: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

de rezultatul comparatiei dintre cheia noua si cheia nodurilor curente. Parcurgerea continua pana seajunge la un fiu nul, unde se realizeaza adaugarea. Se continua cu parcurgerea drumului invers cautandprimul nod care nu este echilibrat. Acest nod trebuie echilibrat, existand patru tipuri de rotatii pentrureechilibrare: o rotatie simpla la dreapta, o rotatie dubla la dreapta, o rotatie simpla la stanga, o rotatiedubla la stanga. Analizand factorul de echilibru al nodurilor aflate pe drumul de la nodul inserat laradacina arborelui se determina rotatia adecvata de reechilibrare. Astfel, daca factorul de echilibru alunui nod este egal cu 2, atunci acesta este considerat nod critic si se va analiza fiul sau drept. Dacaacesta are FE = 1, atunci se va realiza o rotatie simpla la stanga. Daca acesta are FE = −1, atunci seva realiza o rotatie dubla la stanga ce consta dintr-o rotatie simpla la dreapta ın fiul drept al noduluicritic si o rotatie simpla la stanga ın nodul critic. Daca factorul de echilibru al nodului critic este egalcu −2, atunci se va analiza fiul stang al acestuia. Daca acesta are FE = −1, atunci se va realiza orotatie simpla la dreapta. Daca acesta are FE = 1, atunci se va realiza o rotatie dubla la dreapta ceconsta dintr-o rotatie simpla la stanga ın fiul stang al nodului critic si o rotatie simpla la dreapta ınnodul critic.

In figura urmatoare se poate urmari un exemplu de executare a unei rotatii simple la dreapta pentrureechilibrare, unde prin cercuri sunt desemnate noduri, iar prin romburi subarbori de aceeasi ınaltime:

Figura 30. AVL: rotatie simpla la dreapta.

Observam ca ınainte de inserarea nodului nou C, arborele era echilibrat. Dupa inserare, nodul A sedezechilibreaza, avand FE = −2. In acest caz, se examineaza fiul sau stang (nodul B) si se observa caacesta are FE = −1. Conform tehnicilor de reechilibrare discutate, se va executa o rotatie simpla ladreapta, iar arborele devine iar echilibrat. Presupunand ca p este adresa nodului dezechilibrat, adica anodului A, rotatia simpla la dreapta se realizeaza astfel:

1 ABC q = p->s;

2 p->s = q->d;

3 q->d = p;

4 p = q;

Cazul ın care este necesara executia unei rotatii simple spre stanga este simetric ın oglinda fata deprecedentul. Aceasta se realizeaza astfel:

1 ABC q = p->d;

2 p->d = q->s;

3 q->s = p;

174

Page 175: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

4 p = q;

Un exemplu de executare a unei rotatii duble la dreapta pentru reechilibrare, unde prin cercuri suntdesemnate noduri, iar prin romburi subarbori de ınaltime specificata:

Figura 31. AVL: rotatie dubla la dreapta.

Sa observam ca ınainte de inserarea nodului nou D, arborele era echilibrat. Dupa inserare, nodul Ase dezechilibreaza, avand FE = −2. Se analizeaza fiul sau stang, nodul C: acesta are FE = 1 si decieste necesara o rotatie dubla spre dreapta ce consta dintr-o rotatie simpla la stanga ın fiul stang C si orotatie simpla la dreapta ın nodul A. Astfel arborele se reechilibreaza.Cazul ın care este necesara executia unei rotatii duble la stanga este simetric ın oglinda fata de prece-dentul.

Stergerea unui nod ıntr-un arbore AVL. Un nod este sters dintr-un arbore AVL ın mod obisnuitpentru un arbore binar de cautare. Doar ca, daca inserarea unui nod necesita cel mult o rotatie simplasau dubla pentru reechilibrare, stergerea unei chei poate determina o rotatie pentru fiecare nod pecalea catre radacina. Analizand factorul de echilibru al nodurilor aflate pe drumul de la nodul sters laradacina arborelui, se determina rotatia adecvata pentru reechilibrare.

1 #include <iostream >

2 using namespace std;

3

4 struct NOD

5 {

6 int cheie;

7 int fe; // factor echilibru = hd - hs

8 NOD *s, *d; // adresele fiului stang si fiului drept

9 };

10

11 typedef NOD* ABC;

12

13 int max(int a, int b)

14 {

175

Page 176: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

15 return a > b ? a : b;

16 }

17

18 int inaltime(ABC p) // inaltimea unui subarbore

19 {

20 if (p == nullptr) return -1;

21 int hs = inaltime(p->s);

22 int hd = inaltime(p->d);

23 return max(hs , hd) + 1;

24 }

25

26 void calcul_fe(ABC p) // factorul de echilibru

27 {

28 int hs , hd;

29 hs = inaltime(p->s);

30 hd = inaltime(p->d);

31 p->fe = hd - hs;

32 }

33

34 void srot_dr(ABC& p) // rotatie simpla la dreapta

35 {

36 ABC q = p->s;

37 p->s = q->d;

38 q->d = p;

39 calcul_fe(p);

40 calcul_fe(q);

41 p = q;

42 }

43

44 void srot_st(ABC& p) // rotatie simpla la stanga

45 {

46 ABC q = p->d;

47 p->d = q->s;

48 q->s = p;

49 calcul_fe(p);

50 calcul_fe(q);

51 p = q;

52 }

53

54 void drot_dr(ABC& p) // rotatie dubla la dreapta

55 {

56 srot_st(p->s);

57 srot_dr(p);

58 }

59

60 void drot_st(ABC& p) // rotatie dubla la stanga

61 {

62 srot_dr(p->d);

63 srot_st(p);

176

Page 177: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

64 }

65

66 void echilibrare(ABC& p)

67 {

68 ABC q;

69 calcul_fe(p);//se calculeaza factorul de echilibru a nodului curent p

70 if (p->fe == -2) //p este nod critic

71 {

72 q = p->s; //se analizeaza fiul stang al lui p

73 if (q->fe == 1) //daca FE(q) = 1

74 drot_dr(p); //se executa o rotatie dubla la dreapta

75 else //daca FE(q) = -1, se executa o rotatie simpla la dreapta

76 srot_dr(p);

77 }

78 else

79 if (p->fe == 2) //p este nod critic

80 {

81 q = p->d; //se analizeaza fiul drept al lui p

82 if (q->fe == -1) //daca FE(q) = -1

83 drot_st(p); //se executa o rotatie dubla la stanga

84 else //daca FE(q) = 1, se executa o rotatie simpla la stanga

85 srot_st(p);

86 }

87 }

88

89 void inserare(ABC& p, int c)

90 {

91 if (p != nullptr)

92 if (p->cheie == c)

93 cout << "Cheie deja existenta " << endl;

94 else

95 if (c < p->cheie)

96 inserare(p->s, c);

97 else

98 inserare(p->d, c);

99 else

100 {

101 p = new (nothrow) NOD;

102 if(p == nullptr)

103 {

104 cout <<"Alocare esuata."<<endl;

105 exit (1);

106 }

107 p->cheie = c;

108 p->fe = 0;

109 p->s = nullptr;

110 p->d = nullptr;

111 }

112 echilibrare(p);

177

Page 178: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

113 }

114

115 ABC creare ()

116 {

117 ABC rad = nullptr;

118 int c;

119 cout << "Cheia noua: ";

120 cin >> c;

121 while (c != -1)//sau alt criteriu de oprire

122 {

123 inserare(rad , c);

124 cout << "Cheia noua: ";

125 cin >> c;

126 }

127 return rad;

128 }

129

130 void afisare(ABC p, int n) //n = nivelul

131 {

132 if(p != nullptr)

133 {

134 afisare(p->d, n + 1);

135 for (int j = 0; j < n; j++)

136 cout << " ";

137 cout << p->cheie << "(fe = " << p->fe << ")" <<endl;

138 afisare(p->s, n + 1);

139 }

140 }

141

142 void inordine(ABC p)

143 {

144 if (p != nullptr)

145 {

146 inordine(p->s);

147 cout << p->cheie << " ";

148 inordine(p->d);

149 }

150 }

151

152 void fe(ABC p) // factorii de echilibru

153 {

154 if (p != nullptr)

155 {

156 fe(p->s);

157 cout << p->fe << " ";

158 fe(p->d);

159 }

160 }

161

178

Page 179: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

162 ABC maxim(ABC p) // nodul de cheie maxima dintr -un subarbore

163 {

164 while (p->d != nullptr)

165 p = p->d;

166 return p;

167 }

168

169 void stergere(ABC &p, int c)

170 {

171 ABC q;

172 if (p != nullptr)

173 if (p->cheie == c)

174 if (p->s == nullptr && p->d == nullptr)

175 {

176 delete p;

177 p = nullptr;

178 }

179 else

180 if (p->s == nullptr)

181 {

182 q = p->d;

183 delete p;

184 p = q;

185 }

186 else

187 if (p->d == nullptr)

188 {

189 q = p->s;

190 delete p;

191 p = q;

192 }

193 else

194 {

195 ABC temp = maxim(p->s);

196 p->cheie = temp ->cheie;

197 stergere(p->s, temp ->cheie);

198 }

199 else

200 if (c < p->cheie)

201 stergere(p->s, c);

202 else stergere(p->d, c);

203 else

204 cout << "Cheie inexistenta " << endl;

205 if (p != nullptr) echilibrare(p);

206 }

207

208 ABC cautare(ABC p, int c)

209 {

210 if (p != nullptr)

179

Page 180: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

211 if (c < p->cheie)

212 return cautare(p->s, c);

213 else

214 if (c > p->cheie)

215 return cautare(p->d, c);

216 else return p;

217 else return nullptr;

218 }

219

220 int main()

221 {

222 ABC rad = nullptr;

223 int optiune , c;

224 do

225 {

226 cout << endl;

227 cout << "Arbore AVL." << endl;

228 cout << "1. Creare AVL prin inserari repetate." << endl;

229 cout << "2. Inserarea unui nod nou." << endl;

230 cout << "3. Stergerea unui nod." << endl;

231 cout << "4. Cautarea unei chei in arbore." << endl;

232 cout << "5. Afisare (inordine) arbore." << endl;

233 cout << "6. Afisare arbore pe nivele identate." << endl;

234 cout << "7. Afisare factori de echilibru." << endl;

235 cout << "8. Terminare." << endl << endl;

236

237 cout << "Optiunea conform numarului de ordine: ";

238 cin >> optiune;

239 switch (optiune)

240 {

241 case 1:

242 rad = nullptr;

243 rad = creare ();

244 break;

245 case 2:

246 cout << "Cheia noua: ";

247 cin >> c;

248 if(c != -1) inserare(rad , c);

249 break;

250 case 3:

251 cout << "Cheia de sters: ";

252 cin >> c;

253 stergere(rad , c);

254 break;

255 case 4:

256 cout << "Cheia de cautat in arbore: ";

257 cin >> c;

258 if (cautare(rad , c) != nullptr)

259 cout << c << " este in arbore" << endl;

180

Page 181: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

260 else

261 cout << c << " nu este in arbore" << endl;

262 break;

263 case 5:

264 inordine(rad);

265 break;

266 case 6:

267 afisare(rad , 0);

268 break;

269 case 7:

270 fe(rad);

271 break;

272 case 8:

273 break;

274 default:

275 cout <<"Introduceti un numar valid de optiune (1 -> 8)!" << endl;

276 }

277 } while (optiune != 8);

278 return 0;

279 }

Se observa ca exista un oarecare efort de implementare si de executare a codului asociat rotatiilor deechilibrare, dar arborii AVL ofera o eficienta sporita operatiei de cautare. Astfel, arborii AVL se vorfolosi atunci cand numarul cautarilor este mult mai mare decat numarul inserarilor.

5.3.3 Arbori binari completi. Ansamblul Heap binar

Un arbore binar este complet daca pe fiecare nivel s ∈ {0, 1, 2, ..., h}, unde h este ınaltimea arborelui,se afla exact 2s noduri. Un arbore binar complet de ınaltime h are 2h+1 − 1 noduri. Un arbore binarcomplet cu n noduri are ınatimea h = log2 (n+ 1)− 1.

Un arbore binar este aproape complet daca pe fiecare nivel s ∈ {0, 1, 2, ..., h− 1}, unde h este ınaltimeaarborelui, se afla exact 2s noduri, iar de pe nivelul h, lipsesc p ≥ 1 noduri, fie primele din stanga, fieultimele din dreapta. Asadar, toate nodurile terminale se afla pe ultimele doua niveluri ale arborelui,iar h = [log2(n)].

Daca lipsesc nodurile din dreapta, pentru a stoca un arbore aproape complet se poate folosi un tablouv cu n componente, ın care radacina arborelui este memorata ın v[0], iar pentru nodul memorat ın v[i],parintele este stocat ın v[(i− 1)/2] (i 6= 0), radacina subarborelui stang este memorata ın v[2i+ 1], iarradacina subarborelui drept ın v[2i+ 2].Daca lipsesc nodurile din stanga, arborele poate fi stocat convenind ca pentru nodul memorat ın v[i],radacina subarborelui drept sa fie stocata ın v[2i+ 1], iar radacina subarborelui stang ın v[2i+ 2].In cele ce urmeaza, vom considera numai arbori binari aproape completi pentru care, pe ultimul nivel,lipsesc numai noduri din dreapta.

Un ansamblu Heap binar este un arbore binar aproape complet, ın care cheile oricarei perechi de noduriparinte–fiu sunt ıntr-o anumita relatie de ordine.Un ansamblu Heap maxim este un ansamblu Heap binar ın care cheia oricarui parinte este mai mare sauegala cu cheia fiului, iar un ansamblu Heap minim este un ansamblu Heap binar ın care cheia oricaruiparinte este mai mica sau egala cu cheia fiului.

181

Page 182: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Figura 32. Ansamblu HEAP minim

Spre deosebire de arborii binari de cautare, ıntr-un ansamblu Heap pot exista chei cu aceeasi valoare.Intr-un ansamblu Heap minim, cea mai mica cheie se gaseste ın radacina arborelui, iar cheia din oricenod a ansamblului este mai mica sau egala cu cheile tuturor nodurilor din cei doi subarbori ai sai.Intr-un ansamblu Heap maxim, cea mai mare cheie se gaseste ın radacina arborelui, iar cheia din oricenod a ansamblului este mai mare sau egala cu cheile tuturor nodurilor din cei doi subarbori ai sai.

Deoarece sunt arbori aproape completi, putem reprezenta un ansamblu Heap cu ajutorul unui vectorv, ın care radacina arborelui este memorata ın v[0], fiul stang al nodului stocat ın v[i] este memoratın v[2i + 1], iar fiul drept ın v[2i + 2]. Asadar, parintele nodului stocat ın elementul v[i] (i 6= 0) vafi memorat ın v[(i − 1)/2]. Vectorul corespunzator stocarii ansamblului Heap minim din Figura 32este v[] = {11, 14, 19, 26, 31, 41, 27, 44, 35, 33}. Evident, ın acest caz, v[i] ≥ v[(i − 1)/2],pentru orice i = 1, 2, . . . , n− 1.

Vom implementa operatiile de creare a unui ansamblu Heap, inserare a unui nod ın Heap si extragere aunui nod din Heap. In urma prelucrarii unui ansamblu Heap prin intermediul acestor operatii, arboreletrebuie sa-si conserve proprietatea de ansamblu Heap. Exemplificam implementarea operatiilor pentruun ansamblu Heap maxim.

Adaugarea unui nod ıntr-un ansamblu Heap. Deoarece lucram cu un vector static, la fiecareadaugare a unui nod nou, trebuie verificat ca lungimea logica a vectorului sa nu depaseasca lungimeafizica a acestuia. Cheia fiecarui nod nou se adauga initial imediat dupa ultimul element ın tablou. Cumcheile oricarei perechi de noduri parinte–fiu trebuie sa fie ın relatia de ordine prestabilita, cheia noduluinou este ımpinsa ın arbore catre radacina, daca e cazul, pana cand este ındeplinita relatia de ordine ıntrenodul curent (nodul ın care a ajuns cheia) si parintele sau. Complexitatea algoritmului de adaugare ınHeap este O(log2 n).

Crearea unui ansamblu Heap. Crearea unui ansamblu Heap este utilizata pentru a construi arbo-rele corespunzator cu un numar dat de noduri initiale. Trebuie verificat faptul ca numarul de nodurinu depaseste lungimea fizica a vectorului. Crearea unui Heap are la baza aceleasi principii ca operatiade adaugare.

Extragerea elementului din radacina arborelui. Extragerea unui nod din ansamblul Heap pre-supune prelucrarea informatiei din radacina si eliminarea acestui nod din arbore. Dupa extragere,arborele trebuie sa-si conserve proprietatea de ansamblu Heap. Este copiata ın radacina cheia ultimuluinod, apoi este eliminat ultimul element prin decrementarea lungimii logice a vectorului. Cum cheile dinorice pereche de noduri parinte–fiu trebuie sa fie ıntr-o anume relatie de ordine, cheia din radacina este

182

Page 183: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

ımpinsa catre nivelurile inferioare, pana cand este ındeplinita relatia de ordine ıntre nodul curent si fiiisai. Daca se extrag din arbore, pe rand, toate nodurile si se afiseaza cheile nodurilor extrase, acestea vorfi afisate ın relatia de ordine care defineste ansamblul Heap. Complexitatea algoritmului de extrageredintr-un Heap este O(log2 n).

1 // Ansamblu Heap maxim

2 #include <iostream >

3 #define dim 100

4 using namespace std;

5

6 int n, v[dim];

7

8 void creare ()

9 {

10 int i, j, k, aux;

11 cout << "Numarul de noduri: ";

12 cin >> n;

13 if (n > dim)

14 cout << "Depasire lungime fizica a vectorului" << endl;

15 else

16 {

17 cout << " Cheia noua: ";

18 cin >> v[0];

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

20 {

21 bool ok = false;

22 cout << " Cheia noua: ";

23 cin >> v[j];

24 i = j; // pozitia nodului curent

25 while (i > 0 && ok == false)// nodul curent este diferit de

radacina

26 {

27 k = (i-1)/2;

28 if (v[i] > v[k])

29 /* daca cheia nodului curent este mai mare decat cheia

parintelui sau , se interschimba cheile */

30 {

31 aux = v[i];

32 v[i] = v[k];

33 v[k] = aux;

34 i = k; //se urca in arbore; se trece la nodul parinte

35 }

36 else ok = true; // iesirea din while

37 }

38 }

39 }

40 }

41

42 void adaugare ()

43 {

183

Page 184: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

44 int i, k, aux;

45 bool ok = false;

46 if (n == dim)

47 cout << "Nu se poate adauga nod nou , "

48 << "se depaseste lungimea fizica a vectorului." << endl;

49 else

50 {

51 n++;// cresterea dimensiunii logice a vectorului v

52 cout << " Cheia noua ";

53 cin >> v[n-1];

54 i = n-1;// pozitia nodului curent

55 while (i > 0 && ok == false)

56 {

57 k = (i-1)/2;

58 if (v[i] > v[k])

59 {

60 aux = v[i];

61 v[i] = v[k];

62 v[k] = aux;

63 i = k;

64 }

65 else ok = true;

66 }

67 }

68 }

69

70 int stergere ()

71 {

72 int i, j, elem = v[0], aux;

73 bool ok = false;

74 v[0] = v[n-1]; // cheia ultimului nod este copiata in radacina

75 n--; // decrementarea lungimii logice a vectorului

76 i = 0;// indicele nodului curent

77 while (i < n && ok == false)

78 {

79 if (2 * i + 1 < n)//daca nodul curent are succesor stang

80 {

81 j = 2 * i + 1;

82 if (j + 1 < n && v[j + 1] > v[j]) j++;

83 //daca nodul curent are si succesor drept

84 if (v[i] < v[j])

85 /*cheia nodului curent este mai mica decat cheia fiului */

86 {

87 aux = v[i];

88 v[i] = v[j];

89 v[j] = aux;

90 i = j; //se coboara in arbore; se trece la nodul fiu

91 }

92 else ok = true;

184

Page 185: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

93 }

94 else ok = true;

95 }

96 return elem;

97 }

98

99 void afisare ()

100 {

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

102 cout << v[i] << " ";

103 cout << endl;

104 }

105

106 int main()

107 {

108 int optiune;

109

110 do {

111 cout << endl;

112 cout << "Ansamblu Heap maxim" << endl;

113 cout << "1. Creare ansamblu Heap maxim." << endl;

114 cout << "2. Adaugare in Heap." << endl;

115 cout << "3. Eliminare din Heap." << endl;

116 cout << "4. Afisarea Heap." << endl;

117 cout << "5. Terminare." << endl;

118 cout <<endl <<"Introduceti optiunea conform numarului de ordine: ";

119 cin >> optiune;

120 switch (optiune)

121 {

122 case 1:

123 creare ();

124 break;

125 case 2:

126 adaugare ();

127 break;

128 case 3:

129 cout << "Cheia extrasa este " << stergere () << endl;

130 break;

131 case 4:

132 afisare ();

133 break;

134 case 5:

135 break;

136 }

137 } while (optiune != 5);

138 return 0;

139 }

185

Page 186: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Algoritmul de sortare HeapSort. Daca un vector este organizat ca ansamblu Heap, extragereasuccesiva a cheilor din noduri are ca efect afisarea acestora ın ordinea prestabilita ce defineste ansam-blul Heap. Aceasta caracteristica a ansamblurilor Heap sta la baza algoritmului de sortare HeapSort.Apoi, daca fiecare element extras este copiat dupa ultimul element din vectorul actualizat (ce contineelementele neextrase ınca), ın vectorul nou obtinut, cheile vor fi ordonate invers decat prin relatia deordine ce defineste ansamblul Heap. Astfel, daca vectorul era organizat ca un ansamblu Heap minim,va fi ordonat descrescator, iar daca este organizat ca un ansamblu Heap maxim, va fi ordonat crescator.

1 #include <iostream >

2 #define dim 100

3 using namespace std;

4

5 int n, v[dim];

6

7 void creare ()

8 {

9 /* functia de mai sus */

10 }

11

12 int stergere ()

13 {

14 /* functia de mai sus */

15 }

16

17 void HeapSort ()

18 {

19 int m = n;

20 for (int i = n - 1; i > 0; i--)

21 v[i] = stergere ();

22 n = m;

23 }

24

25 void afisare ()

26 {

27 /* functia de mai sus */

28 }

29

30 int main()

31 {

32 creare ();

33 HeapSort ();

34 afisare ();

35 return 0;

36 }

In algoritmul HeapSort se parcurge secvential vectorul pentru a elimina cele n elemente, complexitateaparcurgerii secventiale fiind O(n). Pentru fiecare nod se apeleaza functia de extragere din Heap, careare complexitatea O(log2 n). Astfel, complexitatea algoritmului de sortare a unui vector organizatca ansamblu Heap este O(n log2 n). Algoritmul de sortare a unui vector folosind metoda HeapSortpresupune mai ıntai organizarea vectorului ca un ansamblu Heap (operatia de creare a unui Heap are

186

Page 187: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

complexitatea O(n log2 n)), urmata apoi de aplicarea algoritmului HeapSort. Deci, sortarea unui vectorfolosind algoritmul HeapSort are complexitatea O(n log2 n).

Cozi de prioritati. O coada de prioritati este o structura de date ın care elementele componentesunt identificate prin chei, numite chei de prioritati. Astfel, pe langa informatia utila continuta de unelement al cozii, trebuie memorata si cheia de prioritate a acestuia. Elementele unei astfel de structuride date sunt procesate ın ordinea prioritatii lor: mai ıntai sunt procesate elementele cu prioritateacea mai mare. Sunt acceptate doua operatii pe o coada cu prioritati : adaugarea unui nou element sistergerea elementului cu cea mai mare cheie de prioritate. Aceasta structura de date face parte dinclasa cozilor generalizate. Cozile de prioritati pot fi ascendente, ın care informatiile din noduri suntpastrate ın ordinea crescatoare a cheilor de prioritate ale acestora, sau descendente, ın care informatiiledin noduri sunt pastrate ın ordinea descrescatoare a cheilor de prioritate.Un ansamblu Heap (maxim sau minim) poate fi folosit pentru a implementa o coada cu prioritati. Cozilecu prioritati sunt utilizate, de exemplu, ın planificarea sarcinilor pe calculatoare partajate. Sarcinilecare trebuie efectuate si prioritatile asociate se memoreaza ıntr-o coada cu prioritati. Cand o sarcinaeste terminata sau ıntrerupta, functia stergere va selecta sarcina avand prioritatea cea mai mare dintrecele aflate ın asteptare. Functia adaugare se foloseste pentru a introduce ın coada o sarcina noua.

187

Page 188: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Capitolul 6

Tabele de dispersie

6.1 Tabele cu acces direct

Presupunem ca dispunem de o colectie de date, ın care fiecare element este caracterizat printr-o perechede tipul (cheie, valoare). Ne propunem sa construim o structura de date pentru a stoca colectia deelemente si ın care operatiile de inserare, cautare si stergere sa se realizeze ın mod eficient.In cazul ın care cheile sunt unice si apartin unei multimi U = {0, 1, . . . ,m−1} de numere naturale (ce senumeste domeniul sau universul cheilor), cu m nu foarte mare, atunci putem reprezenta datele folosindo tabela cu acces direct. Aceasta este memorata ıntr-un tablou T [0..m − 1], unde T [k] va corespundecheii k, ın sensul ın care T [k] poate contine un pointer catre elementul de cheie k sau chiar elementulınsusi, adica cheia si valoarea acestuia. Notam cu K domeniul actual (efectiv) al cheilor, adica al cheilordin U ce sunt folosite pentru reprezentarea elementelor din colectia de date. T [k] = null, pentru k ∈ U ,dar k /∈ K. Figura urmatoare ilustreaza cele prezentate anterior:

Figura 33. Tabela cu acces direct.

Astfel, fiecarei chei din domeniul U = {0, 1, . . . , 9} ıi corepunde un indice ın tabloul T . MultimeaK = {2, 3, 4, 5, 8} determina locatiile din tablou care contin pointeri catre elemente. Celelalte locatiicontin null. Operatiile de inserare, cautare si stergere sunt rapide, necesitand un timp constant.

1 #include <iostream >

2 #define m 10

3 using namespace std;

188

Page 189: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

4 struct Element

5 {

6 int cheie;

7 int valoare;

8 };

9

10 Element* T[m];

11

12 void inserare(Element* e)

13 {

14 T[e->cheie] = e;

15 }

16

17 Element* cautare(int k)

18 {

19 return T[k];

20 }

21

22 void stergere(Element* e)

23 {

24 T[e->cheie] = nullptr;

25 }

26

27 void afisare ()

28 {

29 for (int k = 0; k < m; k++)

30 {

31 if (T[k] != nullptr)

32 cout << T[k]->cheie << " " << T[k]->valoare << endl;

33 }

34 }

35

36 int main()

37 {

38 int k, n;

39 cout << "Numarul de elemente din colectie: ";

40 cin >> n;

41 while (n > m) cin >> n;

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

43 {

44 Element *e = new (nothrow) Element;

45 if(e == nullptr)

46 {

47 cout <<"Alocare esuate"<<endl;

48 exit (1);

49 }

50 cout << "Cheie: ";

51 cin >> e->cheie;

52 while(e->cheie < 0 || e->cheie >= m)

189

Page 190: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

53 cin >> e->cheie;

54 cout << "Valoare: ";

55 cin >> e->valoare;

56 inserare(e);

57 }

58 afisare ();

59 cout << "Cheia elementului de sters: ";

60 cin >> k;

61 if(T[k] != nullptr) stergere(T[k]);

62 afisare ();

63 cout << "Cheia elementului de cautat: ";

64 cin >> k;

65 if (cautare(k) != nullptr)

66 cout << "Elementul se gaseste in colectie" << endl;

67 else

68 cout << "Elementul nu se gaseste in colectie" << endl;

69 return 0;

70 }

O astfel de abordare este avantajoasa doar daca domeniul cheilor U este mic (m este mic), deoarececomplexitatea spatiu este O(m), referindu-ne doar la spatiul necesar memorarii tabloului de pointeriT . Daca m este foarte mare, stocarea tabloului T nu numai ca nu este practica, ci uneori este chiarimposibila, memoria fiind una limitata. Mai mult, daca numarul de chei efective este mult mai micdecat m, spatiul alocat tabloului T este inutil irosit.

6.2 Tabele de dispersie

Tabela de dispersie (hash table) este o structura de date ce generalizeaza notiunea de tabela cu accesdirect. Cand numarul cheilor memorate efectiv este relativ mic fata de numarul total de chei posibile,tabelele de dispersie sunt o alternativa eficienta la tabelele cu acces direct, deoarece o tabela de dispersiefoloseste, de regula, un tablou de marime proportionala cu numarul de chei efective. Presupunem ıncontinuare ca |U | > m. In cazul tabelelor de dispersie, pe baza cheii k si a unei functii de dispersie h,se calculeaza locatia h(k) unde va fi memorat pointerul catre elementul de cheie k. Asadar, domeniulcheilor U este transformat prin intermediul functiei de dispersie h : U −→ {0, 1, ...,m− 1}, ın locatiileunei tabele de dispersie T [0..m−1]. Spunem ca un element cu cheia k se disperseaza ın locatia h(k), iarh(k) este valoarea de dispersie a cheii k. Rolul functiilor de dispersie este acela de a reduce domeniulindicilor tabloului T si deci complexitatea spatiu (ın loc sa manipulam |U | valori, va trebui sa lucamdoar cu m valori).Dezavantajul metodei este ca exista posibilitatea ca doua elemente, desi au chei distincte, sa fie dis-persate ın aceeasi locatie, fenomen numit coliziune. Chiar daca sunt alese functii de dispersie foartebune, cum |U | > m, pot exista doua chei care au aceeasi valoare de dispersie si deci evitarea totala a acoliziunilor este imposibila.

190

Page 191: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Figura 34. Aparitia unei coliziuni.

6.2.1 Functii de dispersie

Vom analiza mai ıntai ce presupune alegerea unei functii de dispersie. O functie de dispersie buna artrebui sa fie usor de calculat si sa produca cat mai putine coliziuni. Majoritatea functiilor de dispersiepresupun ca U ⊂ N. Deci, vom presupune ın cele ce urmeaza ca se folosesc doar chei naturale. Daca nuar fi asa, ar trebui identificate metode care sa asocieze cheilor, numere naturale (de exemplu, o cheie ceeste un sir de caractere o putem interpreta prin numarul natural obtinut prin sumarea codurilor ASCIIale caracterelor).Prin functie de dispersie buna ıntelegem o functie care satisface ipoteza dispersiei uniforme simple, adicafiecare cheie se poate dispersa cu aceeasi probabilitate ın oricare dintre cele m locatii ale tabloului,independent de cum au fost dispersate celelalte chei. Daca aceasta ipoteza este verificata, atunci seminimizeaza numarul de coliziuni. Deoarece, ın general, distributia de probabilitate dupa care suntalese cheile nu este cunoscuta, verificarea acestei ipoteze nu este posibila. In practica pot fi folositetehnici euristice pentru a crea functii de dispersie care sa se comporte bine.

Metoda diviziunii. Folosind aceasta metoda, un element de cheie k este dispersat ıntr-una din cele mlocatii ale tabelei de dispersie T , considerand restul ımpartirii lui k la m. Asadar, functia de dispersieeste h(k) = k mod m. De exemplu, pentru m = 23 si cheia k = 105, valoarea sa de dispersie esteh(k) = 105 mod 23 = 13. Experimental, s-a determinat ca valorile bune pentru m sunt numereleprime ce nu sunt prea apropiate de puterile exacte ale lui 2.

Metoda ınmultirii. Aceasta metoda se realizeaza ın doi pasi. La primul pas, se ınmulteste cheiak cu o constanta A ∈ (0, 1), se calculeaza kA − [kA] si se ınmulteste aceasta valoare cu m. La celde-al doilea pas, se calculeaza partea ıntreaga a rezultatului. In acest caz, functia de dispersie esteh(k) = [m(kA− [kA])]. Un avantaj al acestei metode este ca valoarea lui m nu este critica (ın general,se considera o putere a lui 2). Desi aceasta metoda functioneaza cu orice valoare a lui A, se obtinrezultate mai bune pentru anumite valori. Donald Knuth argumenteaza ca alegerea optima pentru A

este

√5− 1

2(raportul de aur). Pentru m = 23 si k = 105, se obtine h(k) = b23(105A− b105Ac)c = 20.

191

Page 192: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

Dispersia universala. Daca functia de dispersie aleasa are un comportament cat mai apropiat de ogenerare de numere aleatoare, elementele ar putea avea o ımprastiere uniforma. Astfel, aceasta metodapresupune selectarea functiei de dispersie ın mod aleator ın momentul executiei dintr-o colectie de functiiconstruita ın acest sens. Aceasta abordare asigura o performanta buna pentru cazul mediu si nu depindede cheile de intrare.Fie H o colectie finita de functii de dispersie. O astfel de colectie se numeste universala daca, pentrufiecare pereche de chei distincte k1 si k2 din U , numarul de functii de dispersie h ∈ H, pentru careh(k1) = h(k2) este |H|/m. Astfel, cu o functie de dispersie aleasa aleator din H, sansa unei coliziuniıntre k1 si k2, cu k1 6= k2, este 1/m, egala cu sansa de coliziune atunci cand h(k1) si h(k2) sunt alesealeator din multimea {0, 1, . . . ,m− 1}.De exemplu, o colectie universala de functii de dispersie se definete astfel: se alege m un numar prim si sedescompune cheia k ın r+1 octeti astfel ıncat k = 〈k0, k1, . . . , kr〉 cu singura cerinta ca valoarea maximaa fiecarui octet sa fie mai mica decat m. Fie x = 〈x0, x1, . . . , xr〉 un sir de r+1 elemente generate aleatordin multimea {0, 1, . . . ,m− 1}. Colectia de functii de dispersie va fi hx(k) = (

∑ri=0 xiki) mod m.

6.2.2 Rezolvarea coliziunilor

Exista mai multe metode de rezolvare a coliziunilor, pe masura ce acestea apar. In continuare, vomprezenta doua dintre acestea.

Rezolvarea coliziunilor prin ınlantuire. Folosind aceasta metoda, toate elementele ce sunt dis-persate ın aceeasi locatie a tabelei de dispersie, vor fi plasate ıntr-o lista ınlantuita. Locatia i a tabeleide dispersie va contine un pointer catre primul element al listei ınlantuite ce pastreaza elementele carese disperseaza ın locatia i, daca aceasta lista este nevida, iar daca este vida, se va memora null.

Figura 35. Rezolvarea coliziunilor prin ınlantuire.

Daca m este dimensiunea tabelei de dispersie, iar n este numarul de chei efective, atunci, ın ipotezadispersiei uniforme simple, definim factorul de ıncarcare al tabelei prin α = n/m (reprezinta numarulmediu de elemente ce sunt memorate ıntr-o lista ınlantuita). Inserarea ın lista se va realiza la ınceputulacesteia, deci ın timp constant. Operatiile de cautare si stergere au timpul de executie ın cazul celmai defavorabil proportional cu lungimea listei (toate elementele sunt dispersate ın aceeasi locatie, fiindnecesara o lista de lungime n). Analizandu-se timpul mediu de executie, s-a constatat ca toate cele treioperatii se pot executa ın timp constant (pentru stergere este necesar sa se foloseasca liste liniare dublu

192

Page 193: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

ınlantuite) deoarece valoarea asteptata a timpului de executie pentru operatii de inserare, cautare sistergere este O(1 + α) si deci complexitatea acestor operatii este O(1), daca α este O(1).

1 #include <iostream >

2 #define m 13

3 using namespace std;

4

5 struct NOD

6 {

7 int cheie;

8 int valoare;

9 NOD* next;

10 };

11

12 typedef NOD* LISTA;

13 LISTA T[m];

14

15 int h(int k)

16 {

17 return k % m;

18 }

19

20 void inserare(int k, int v)

21 {

22 int i = h(k);

23 LISTA p;

24 p = new (nothrow) NOD;

25 if(p == nullptr)

26 {

27 cout <<"Alocare esuata"<<endl;

28 exit (1);

29 }

30 p->cheie = k;

31 p->valoare = v;

32 p->next = T[i]; // adaugare la inceput

33 T[i] = p;

34 }

35

36 LISTA cautare(int k)

37 {

38 int i = h(k);

39 if (T[i] != nullptr)

40 {

41 LISTA p = T[i];

42 while (p != nullptr)

43 {

44 if (p->cheie == k)

45 return p;

46 p = p->next;

47 }

193

Page 194: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

48 }

49 return nullptr;

50 }

51

52 void stergere(int k)

53 {

54 int i = h(k);

55 LISTA p, q;

56 if (T[i] != nullptr)

57 {

58 if (T[i]->cheie == k)

59 {

60 q = T[i];

61 T[i] = T[i]->next;

62 delete q;

63 }

64 else

65 {

66 p = T[i];

67 while (p->next != nullptr && p->next ->cheie != k)

68 p = p->next;

69 if (p->next != nullptr)

70 {

71 q = p->next;

72 p->next = q->next;

73 delete q;

74 }

75 }

76 }

77 }

78

79 void afisare ()

80 {

81 for (int k = 0; k < m; k++)

82 {

83 if (T[k] != nullptr)

84 {

85 LISTA p = T[k];

86 while (p != nullptr)

87 {

88 cout << p->cheie << " " << p->valoare << "; ";

89 p = p->next;

90 }

91 cout << endl;

92 }

93 }

94 }

95

96 int main()

194

Page 195: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

97 {

98 int n, k, v;

99 cout << "Numarul de elemente din colectie: ";

100 cin >> n;

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

102 {

103 cout <<"Cheie: ";

104 cin >> k;

105 cout <<"Valoare: ";

106 cin >> v;

107 inserare(k, v);

108 }

109 afisare ();

110 cout << "Cheia elementului de sters: ";

111 cin >> k;

112 stergere(k);

113 afisare ();

114 cout << "Cheia elementului de cautat: ";

115 cin >> k;

116 if (cautare(k) != nullptr)

117 cout << "Elementul se gaseste in colectie" << endl;

118 else

119 cout << "Elementul nu se gaseste in colectie" << endl;

120 return 0;

121 }

Cu o functie de dispersie buna ar trebui sa se obtina liste cu lungimi aproximativ egale. Altfel, se poaterecurge la o redispersare.Aceasta metoda de rezolvare a coliziunilor se utilizeaza, de obicei, cand nu este cunoscut a priori cate cheivor fi inserate/sterse. Dezavantajul acestei metode consta ın faptul ca este necesar spatiu suplimentarpentru memorarea listelor ın exteriorul tabelei de dispersie.

Rezolvarea coliziunilor prin adresare deschisa. Folosind aceasta metoda toate elementele colectieise stocheaza ın interiorul tabelei de dispersie. Vom presupune ca fiecare element este caracterizat doarprin cheia sa, numar natural. Astfel, fiecare locatie a tabelei va contine fie o cheie, fie −1. Dezavantajuleste ca, prin inserari repetate, tabela se poate umple, caz ın care este necesara cresterea lui m si redis-persarea elementelor. Folosind adresarea deschisa, se evita folosirea pointerilor, obtinandu-se astfel unspatiu de memorie suplimentar. Prin aceasta abordare pot rezulta coliziuni mai putine si acces rapid.Cand se cauta un element, se examineaza ın mod sistematic locatiile tabelei. Secventa de locatii carese examineaza nu se determina folosind pointerii, ci se calculeaza. Pentru a insera o cheie ın tabela,se examineaza locatiile, pana se ıntalneste o locatie libera ın care se poate stoca cheia. Ordinea deverificare a tabelei depinde de cheia care se adauga.Functia de dispersie devine h : U × {0, 1, . . . ,m− 1} −→ {0, 1, . . . ,m− 1}, cel de-al doilea argument alfunctiei numindu-se numar de verificare. Pentru o cheie k ∈ U , sirul h(k, 0), h(k, 1), . . . , h(k,m− 1) senumeste secventa de verificare a cheii k. O secventa de verificare a oricarei chei k ∈ U trebuie sa fie opermutare a multimii {0, 1, . . . ,m− 1}.In aceasta abordare, se pleaca de la ipoteza dispersiei uniforme: fiecare cheie poate avea ca secventade verificare oricare dintre cele m! permutari ale multimii {0, 1, . . . ,m − 1}, cu aceeasi probabilitate.Dispersia uniforma generalizeaza notiunea de dispersie uniforma simpla, functia de dispersie producand

195

Page 196: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

nu doar un singur numar, ci o secventa de verificare. Deoarece dispersia uniforma este dificil de imple-mentat, ın practica sunt folosite anumite aproximari.Sunt utilizate trei tehnici pentru a calcula secventele de verificare: verificarea liniara, verificarea patraticasi dispersia dubla. Acestea nu satisfac ipoteza dispersiei uniforme, deoarece nu genereaza mai mult dem2 secvente de verificare distincte.

Verficarea liniara. Se foloseste functia de dispersie h : U × {0, 1, . . . ,m − 1} −→ {0, 1, . . . ,m − 1},h(k, i) = (h1(k) + i) mod m, unde h1 : U −→ {0, 1, . . . ,m − 1} este o functie de dispersie obisnuita(de exemplu, h1(k) = k mod m). Secventa de verificare a unei chei k este h1(k), h1(k) + 1, h1(k) +2, . . . ,m − 1, 0, 1, . . . , h1(k)− 1. Dezavantajul acestei tehnici este ca se formeaza siruri lungi de locatiiocupate si astfel creste timpul mediu de cautare. Avantajul este ca se implementeaza usor.

Verficarea patratica. Se foloseste functia de dispersie h : U×{0, 1, . . . ,m−1} −→ {0, 1, . . . ,m−1},h(k, i) = (h1(k) + c1i + c2i

2) mod m, unde h1 : U −→ {0, 1, . . . ,m − 1} este o functie de dispersieobisnuita, iar c1, c2 6= 0 sunt constante fixate la initializarea functiei de dispersie. Aceste constante sepot determina euristic: de exemplu, m se alege o putere a lui 2 si c1 = c2 = 0.5 sau m se alege numarprim de forma 4t+ 3, h1(k) = k mod m si c1 = 0, c2 = (−1)i. Prima pozitie examinata pentru o cheiedata k este h1(k), urmatoarele locatii examinate fiind decalate cu valori ce depind patratic de locatiaexaminata anterior. Dezavantajul acestei tehnici este ca, daca doua chei au aceeasi pozitie de start averificarii, atunci secventele lor de verificare coincid. Avantajul este ca functioneaza mai bine decatverificarea liniara.

Dispersia dubla. Se foloseste functia de dispersie h : U × {0, 1, . . . ,m − 1} −→ {0, 1, . . . ,m − 1},h(k, i) = (h1(k) + ih2(k)) mod m, unde h1, h2 : U −→ {0, 1, . . . ,m − 1} sunt functii de dispersieobisnuite. Aceasta tehnica este considerata una dintre cele mai bune metode disponibile pentru adresa-rea deschisa. Prima locatie examinata pentru o cheie data k este h1(k), urmatoarele locatii examinatefiind apoi decalate fata de cea anterioara cu h2(k) mod m. Pentru a fi parcursa ıntreaga tabela, h2(k)si m trebuie sa fie prime ıntre ele. De exemplu, m se alege o putere a lui 2, iar h2 trebuie construitaasa ıncat sa produca un numar impar. O alta posibilitate este sa se aleaga m prim si h2 astfel ıncatsa returneze un ıntreg pozitiv mai mic decat m. Astfel, putem alege m prim si h1(k) = k mod m,h2(k) = 1 + (k mod m1), cu m1 ales sa fie nu mult mai mic decat m (de exemplu m − 1 sau m − 2).Dispersia dubla aduce o ımbunatatire fata de verificarea liniara sau patratica ın sensul ca sunt folositeΘ(m2) secvente de verificare, fata de Θ(m).

1 #include <iostream >

2 #define m 13

3 using namespace std;

4

5 int T[m];

6

7 void init()

8 {

9 for (int k = 0; k < m; k++)

10 T[k] = -1;

11 }

12

13 int h1(int k)

14 {

15 return k % m;

196

Page 197: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

16 }

17

18 int h2(int k)

19 {

20 return 1 + (k % (m-1));

21 }

22

23 int h(int k, int i)

24 {

25 return (h1(k) + i*h2(k)) % m ;

26 }

27

28 void inserare(int k)

29 {

30 int i = 0; // numarul de verificare

31 bool gasit = false; //nu s-a gasit pozitia de inserare

32 do {

33 int j = h(k, i);

34 if (T[j] == -1)

35 {

36 T[j] = k;

37 gasit = true;

38 }

39 else

40 i = i + 1;

41 } while(i < m && !gasit);

42 if (i == m)

43 cout << "Tabela este plina" << endl;

44 }

45

46 void afisare ()

47 {

48 for (int k = 0; k < m; k++)

49 {

50 if (T[k] != -1)

51 cout << T[k] << endl;

52 }

53 }

54

55 bool cautare(int k)

56 {

57 int i = 0, j;

58 bool gasit = false;

59 do {

60 j = h(k, i);

61 if (T[j] == k)

62 gasit = true;

63 else

64 i = i + 1;

197

Page 198: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Structuri de date Note de curs

65 } while (T[j] != -1 && i < m && !gasit);

66 return gasit;

67 }

68

69 int main()

70 {

71 int k, n;

72 init();

73 cout << "Numarul de elemente din colectie: ";

74 cin >> n;

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

76 {

77 cin >> k;

78 inserare(k);

79 }

80 afisare ();

81 cout << "Cheia elementului de cautat: ";

82 cin >> k;

83 if (cautare(k) != false)

84 cout << "Elementul se gaseste in colectie" << endl;

85 else

86 cout << "Elementul nu se gaseste in colectie" << endl;

87 return 0;

88 }

Pentru o tabela de dispersie cu adresare deschisa cu factorul de ıncarcare α = n/m < 1, ın cazul mediusunt necesare cel mult 1/(1−α) verificari atat pentru operatia de inserare, cat si pentru cea de cautare,ın ipoteza dispersiei uniforme simple. Stergerea dintr-o tabela de dispersie cu adresare deschisa nu esteo operatie simpla. Daca se doreste stergerea unei chei din locatia i, nu este suficient sa se marchezeacea locatie ca fiind libera (T [i] = −1), deoarece astfel va fi imposibil sa se acceseze orice cheie k acarei inserare a verificat locatia i si a gasit-o ocupata. O posibilitate este sa se marcheze locatia cu oalta valoare speciala si sa se rescrie functia de inserare pentru a trata astfel de locatii ca si cum ar filibere. Folosind aceasta abordare, timpii de cautare nu mai depind de factorul de ıncarcare al tabeleisi, din acest motiv, daca este necesara operatia de stergere, este preferata rezolvarea coliziunilor prinınlantuire.

198

Page 199: Introducerebeda/Note de curs.pdfStructuri de date Note de curs Pointerii se deosebesc prin tipul variabilelor (obiectelor) la care fac referire (a c aror adres a o cont˘in), chiar

Bibliografie

[1] F. Carrano, J. Prichard, Data abstraction and problem solving with C++: walls and mirrors (4thed.), Boston Addison Wesley, 2005.

[2] T.H. Cormen, C.E. Leiserson, R.L. Rivest. Introduction to Algorithms (3rd ed.), MIT Press, 2009.

[3] T.H. Cormen, C.E. Leiserson, R.L. Rivest, Introducere ın Algoritmi, Computer Libris Agora, Cluj-Napoca, 2000 (traducere).

[4] A. Drozdek, Data structures and Algorithms in C++, 2nd ed., Brooks/Cole Publishing Co., 2001.

[5] I. Ignat, C.L. Ignat, Structuri de date si Algoritmi, Editura Albastra, 2014.

[6] K. Mehlhorn, P. Sanders, Algorithms and Data Structures: The Basic Toolbox, Springer, 2008.

199