Sunteți pe pagina 1din 59

PREZENTARE GENERAL

Tipul de date - nteles ca o mulime de valori plus un set de operaii definit asupra acestora - poate fi predefinit (standard) sau construit pe baza tipurilor deja definite. Tipul de date predefinit se refer la valori simple, elementare: numere ntregi i reale, caractere, valori logice etc. Tablourile - colecii omogene de date, sau nregistrrile - colecii neomogene de date - reprezint modaliti puternice i n acelasi timp frecvente de reprezentare a unor structuri de date. Totui, diversitatea problemelor propuse a fi rezolvate cu calculatorul promoveaz tot mai clar ideea construirii unor tipuri de date care sa reflecte ct mai fidel functiile i atributele obiectelor din universul problemei respective. Lista, stiva, coada, arborele reprezint posibiliti de structurare a datelor, faciliteaz descrierea obiectelor i usureaz rezolvarea anumitor tipuri de probleme. Limbaje ca (Borland) PASCAL sau (Turbo/Borland) C++ ofer posibiliti de implementare a acestor structuri att n manier static (cu ajutorul tablourilor) ct i dinamic (cu ajutorul pointerilor). Prezenta conversaie i propune s ilustreze operaiile eseniale asupra structurilor de date amintite mai sus ntr-o manier ct se poate de accesibil. Programele sunt elaborate n (Borland) Pascal i sunt construite pe principiul tehnicii meniurilor. Dac meniurile contin operaii de generare a structurii sau initializare, atunci acestea trebuie executate primele. Am avut n vedere ca numele identificatorilor s fie sugestive, iar comentariile i mesajele transmise de program s fie cele strict necesare. Pe ct posibil s-au evitat operaii de intrare/iesire n proceduri, asigurndu-se o anumit independent a lor; pe baza unui cod de eroare mesajele corespunzatoare au fost transferate n programul principal. De cele mai multe ori validarea datelor s-a facut utiliznd filtre iterative de validare. Informaia util din structuri s-a considerat ca e de tip ntreg, fr a afecta ns generalitatea procedurilor.

LISTA
Exemple de liste se ntlnesc destul de des n practica prelucrarii datelor pe calculator. Iat cteva: lista studenilor dintr-o grup i a notelor primite la un examen, lista crilor dintr-o bibliotec, lista clienilor unei banci, lista cadrelor didactice dintr-o catedr etc. Putem defini lista ca o colecie omogen, secvenial de date. n continuare vom prezenta cele dou variante de implementare a listelor n calculator: varianta static i varianta dinamic. Varianta static presupune memorarea elementelor listei ntr-un tablou; ordinea elementelor listei este dat de ordinea elementelor tabloului. Nota: n programul ilustrativ Static_Lista elementele de baz au urmatoarea semnificaie: max_aloc - dimensiunea maxim a tabloului listei; n - lungimea listei la un moment dat; k - poziia de inserie sau tergere; x - valoarea care se insereaz.
//Static_Lista # include "stdio.h" # include "conio.h" const max_aloc=20; int lista[max_aloc]; int i,n,k; char ch; int x; int Creare() { if (n>max_aloc) return 0; else { for (i=1;i<=n;i++) { printf("lista[%d]=",i); scanf("%d",&lista[i]); } return 1;

} } int InsertEl(int k,int x) { if (n<=max_aloc) { for (i=n;i>=k;i--) lista[i+1]=lista[i]; lista[k]=x; n++; return 1; } else return 0; } int StergEl(int k) { if (n>=1) { for (i=k;i<=n-1;i++) lista[i]=lista[i+1]; n--; return 1; } else return 0; } void Listare() { printf("\nElementele listei sunt:"); for (i=1;i<=n;i++) printf("%d ",lista[i]); } void main(void) { clrscr(); do { printf("\nC: Creare lista"); printf("\nI: Insertie element in lista"); printf("\nS: tergere element din lista"); printf("\nL: Listare elemente din lista"); printf("\nE: Iesire program"); printf("\n\nOptiunea dvs. va rog:"); ch=getch(); switch (ch) { case 'C': do { printf("\nNumarul elementelor din lista="); scanf("%d",&n); } while (n<0); if (Creare()) Listare(); else

printf("\nDimensiune alocare lista insuficienta"); break; case 'I': do { printf("\nDati pozitia 1<=k<=%d unde se insereaza=",n+1); scanf("%d",&k); } while ((k<1)&&(k>n+1)); printf("\nDati valoare care se va insera="); scanf("%d",&x); if (InsertEl(k,x)) printf("\nS-a inserat elementul %d pe pozitia %d",x,k); else { printf("\nNu se poate insera elementul %d",x); printf("\nDimensiune alocare lista insuficienta!"); } break; case 'S': if (n>0) { do { printf("\nDati pozitia 1<=k<=%d a elementului de sters=",n); scanf("%d",&k); } while ((k<1)&&(k>n)); if (StergEl(k)) printf("\nA fost sters elemetul de pe pozitia %d",k); } else printf("\nLista este vida!"); break; case 'L': Listare(); getch(); break; case 'E': break; default: printf("\nIntroduceti alta litera!"); getch(); } clrscr(); } while (ch!='E'); }

Se observ c operaiile de inserie/tergere ale unui element n/din list implic deplasri cu o pozitie spre dreapta/stnga a subirului ntre poziiile k i n. Grafic, lucrurile se prezint astfel:

(a)

(b) Operatii de inserie (a), tergere (b) Aceste deplasri (care consum timp calculator) precum i necesitatea de a alege o dimensiune optim de alocare, diminueaz principalul avantaj oferit de implementarea static a listei: evidenierea natural a succesiunii elementelor sale. Varianta dinamic presupune memorarea elementelor listei n spaii de memorie gestionate cu ajutorul pointerilor. Astfel, un element din list se va reprezenta ntr-o structur (celul) cu mai multe cmpuri: o parte din cmp conine informaia propriu-zis (partea util), iar cealalt parte adresa elementului urmtor sau/si a elementului precedent din list. Dac lista este organizat ntr-o singur directie (de exemplu, stnga-dreapta) spunem ca lista este simplu nlanuit. Grafic, o list simplu nlantui cu patru elemente se poate reprezenta sub forma:

Lista simplu nlanuit

l reprezint adresa primului element din list, sageile sugereaz


pointerul spre urmatoarea celul, punctul din ultima celul specific sfritul listei (valoarea nil-pointer spre orice tip de date). Nota. n programul ilustrativ Dinamic_Lista, elementele de baz au urmtoarea semnificatie: n - reprezint lungimea listei la un moment dat; l - reprezint adresa primei celule din list; k - poziia celulei dup care se face inseria (pentru adugare la nceput se ia k=0, pentru adaugare la sfrsit se ia k=n); x - valoarea elementului care se insereaz. Prin meniul programului se activeaz cele patru proceduri care au acelai nume i aceleai funcii ca i n cazul static.
//Dinamic_Lista # include "stdio.h" # include "conio.h" # include "alloc.h" const max_aloc=50; struct pstruct { int util; struct pstruct *urm; }; typedef struct pstruct PSTRUCT; PSTRUCT *l,*p,*q; int i,n,k; char ch; int x; int Creare() { if (n*sizeof(PSTRUCT)>max_aloc) return 0; else { l=NULL; for (i=1;i<=n;i++) { printf("\nValoare celulei nr. %d este ",n-i+1); scanf("%d",&x); p=(PSTRUCT*)malloc(sizeof(PSTRUCT)); p->util=x; p->urm=l; l=p; } return 1; }

} int InsertEl(int k,int x) { if (sizeof(PSTRUCT)<max_aloc) { p=(PSTRUCT*)malloc(sizeof(PSTRUCT)); if (k) // insertie propriu-zisa sau adaugare la sfarsit { q=l; for (i=2;i<=k;i++) q=q->urm; p->urm=q->urm; q->urm=p; } else //adaugare la inceput { p->urm=l; l=p; } p->util=x; n++; return 1; } else return 0; } int StergEl(int k) { if (k==1)//stergere primul element din lista { p=l; l=l->urm; } else//stergere orice element diferit de primul { q=l; for (i=2;i<=k-1;i++) q=q->urm; p=q->urm; q->urm=p->urm; } free(p); n--; return 1; } void Listare() { printf("\nElementele listei sunt: "); p=l; while (p!=NULL) { printf("%d ",p->util); p=p->urm; } } void main(void) {

clrscr(); do { printf("\nC: Creare lista"); printf("\nI: Insertie element in lista"); printf("\nS: tergere element din lista"); printf("\nL: Listare elemente din lista"); printf("\nE: Iesire program"); printf("\n\nOptiunea dvs. va rog:"); ch=getch(); switch (ch) { case 'C': do { printf("\nNumarul elementelor din lista="); scanf("%d",&n); } while (n<0); if (Creare()) Listare(); else printf("\nDimensiune alocare lista insuficienta"); break; case 'I': do { printf("\nDati pozitia 0<=k<=%d dupa care se insereaza=",n); scanf("%d",&k); } while ((k<0)&&(k>n)); printf("\nDati valoare care se va insera="); scanf("%d",&x); if (InsertEl(k,x)) printf("\nS-a inserat elementul %d pe pozitia %d",x,k); else { printf("\nNu se poate insera elementul %d",x); printf("\nDimensiune alocare lista insuficienta!"); } break; case 'S': if (n>0) { do { printf("\nDati pozitia 1<=k<=%d a elementului de sters=",n); scanf("%d",&k); } while ((k<1)&&(k>n)); if (StergEl(k)) printf("\nA fost sters elemetul de pe pozitia %d",k); } else

printf("\nLista este vida!"); break; case 'L': Listare(); getch(); break; case 'E': break; default: printf("\nIntroduceti alta litera!"); getch(); } clrscr(); } while (ch!='E'); }

Operaiile de insertie i tergere presupun evidenierea distinct a urmtoarelor cazuri: adaugare la nceput, insertie propriu-zis sau adugare la sfrit (insertie); tergere prim element, stegere orice element din lista diferit de primul (stergere).

Operaii de inserie:
adugare la nceput:

Inserie (adaugare la nceput)

inserie propriu-zis sau adugare la sfrit:

Inserie propriu-zis sau adugare la sfrit

Operaii de tergere:
tergere prim element;

tergerea primului element din lista

tergere orice element din list diferit de primul:

tergerea oricrui element din list diferit de primul

n cazul listelor simplu nlantuite cunoscnd adresa unei celule putem cunoate adresa celulei imediat urmatoare, n timp ce adresa celulei precedente nu este accesibil cu aceeasi rapiditate. Se poate obine aceeai rapiditate n parcurgerea unei liste la stnga sau la dreapta unui element adaugnd fiecrei celule nca un cmp de legatur; acest cmp va conine adresa celulei precedente, cnd exist, iar n caz contrar valoarea NIL. Se obine astfel o list dublu nlanuit. Grafic, o astfel de list cu trei elemente se poate reprezenta ca mai jos:

Lista dublu nlanuit

Nota. Ca structura i elemente de baz, programul Dublu_Lista este asemntor programului Dinamic_Lista.

10

//Dublu_Lista # include "stdio.h" # include "conio.h" # include "alloc.h" const max_aloc=50; struct pstruct { int util; struct pstruct *prec,*urm; }; typedef struct pstruct PSTRUCT; PSTRUCT *l,*p,*q; int i,n,k; char ch; int x; int Creare() { if (n*sizeof(PSTRUCT)>max_aloc) return 0; else { l=NULL; for (i=1;i<=n;i++) { printf("\nValoare celulei nr. %d este ",n-i+1); scanf("%d",&x); p=(PSTRUCT*)malloc(sizeof(PSTRUCT)); p->util=x; p->urm=l; if (l!=NULL) l->prec=p; l=p; } if (l!=NULL) l->prec=NULL; return 1; } } int InsertEl(int k,int x) { if (sizeof(PSTRUCT)<max_aloc) { p=(PSTRUCT*)malloc(sizeof(PSTRUCT)); if (n==0) //adaugare in lista vida { p->util=x; p->prec=NULL; p->urm=NULL; l=p; } else if (k==0) //adaugarea la inceput { p->urm=l;

11

l->prec=p; p->prec=NULL; l=p; } else //insertie propriu-zisa sau adaugare la sfarsit { q=l; for (i=2;i<=k;i++) q=q->urm; p->urm=q->urm; p->prec=q; q->urm=p; if (k!=n) p->urm->prec=p; } p->util=x; n++; return 1; } else return 0; } int StergEl(int k) { if (n==1) //stergere unic element din lista { q=l; l=NULL; } else { q=l; if (k==1) //stergere primul element din lista n>1 { l=l->urm; l->prec=NULL; } else { for (i=2;i<=k;i++) q=q->urm; if (k==n) //stergere ultim element din lista n>1 q->prec->urm=NULL; else //stergere orice element aflat pe pozitia 1<k<n { q->prec->urm=q->urm; q->urm->prec=q->prec; } } } free(p); n--; return 1; } void Listare() { printf("\nElementele listei sunt: "); p=l; while (p!=NULL)

12

{ printf("%d ",p->util); p=p->urm; } getch(); } void main(void) { clrscr(); do { printf("\nC: Creare lista"); printf("\nI: Insertie element in lista"); printf("\nS: tergere element din lista"); printf("\nL: Listare elemente din lista"); printf("\nE: Iesire program"); printf("\n\nOptiunea dvs. va rog:"); ch=getch(); switch (ch) { case 'C': do { printf("\nNumarul elementelor din lista="); scanf("%d",&n); } while (n<0); if (Creare()) Listare(); else printf("\nDimensiune alocare lista insuficienta"); break; case 'I': do { printf("\nDati pozitia 0<=k<=%d dupa care se insereaza=",n); scanf("%d",&k); } while ((k<0)&&(k>n)); printf("\nDati valoare care se va insera="); scanf("%d",&x); if (InsertEl(k,x)==1) { printf("\nS-a inserat elementul %d pe pozitia %d",x,k); getch(); } else { printf("\nNu se poate insera elementul %d",x); printf("\nDimensiune alocare lista insuficienta!"); getch(); } break; case 'S': if (n>0)

13

{ do { printf("\nDati pozitia 1<=k<=%d a elementului de sters=",n); scanf("%d",&k); } while ((k<1)&&(k>n)); if (StergEl(k)) { printf("\nA fost sters elemetul de pe pozitia %d",k); getch(); } } else { printf("\nLista este vida!"); getch(); } break; case 'L': Listare(); break; case 'E': break; default: printf("\nIntroduceti alta litera!"); getch(); } clrscr(); } while (ch!='E'); }

Operaiile de inserie i tergere presupun evidenierea distinct a urmtoarelor cazuri: adugare n lista vid, nevid (inserie); tergere unic element, prim element, ultim element, orice element aflat n interiorul listei (tergere).

Operaii de inserie:
adugare n lista vid:

Adaugare n lista vida adugare n lista nevid: adugare la nceput:

14

Adaugare la nceput inserie propriu-zis sau adugare la sfrit:

Inserie propriu-zis

Adugare la sfrit

Operaii de tergere:
tergere unic element din list;

tergere unic element tergere prim element din list;

tergere prim element

15

tergere ultim element din list;

tergere ultim element

tergere orice element aflat n interiorul listei.

tergere orice element Uurina cu care se realizeaz n variantele de implementare dinamic operaiile de adugare i tergere este evident. De altfel, acest lucru reprezint principalul avantaj al implementarii dinamice a listei; preul pltit const n necesarul suplimentar de memorie pentru reprezentarea legturilor dintre elementele sale.

STIVA
Stiva este un caz special de list liniar n care intrarile i ieirile se fac la un singur capt al ei. Exemple de stive de obiecte sunt oarecum evidente: stive de lemne, de lzi, de baloi de paie, de farfurii, de dosare etc. Structura de stiv presupune, conform definiiei, o anumit disciplin: totdeauna se adaug un obiect "deasupra" ultimului depus i se extrage totdeauna ultimul obiect adugat. Se spune ca accesul la o stiv este de tip LIFO (Last In - First Out). Modul de lucru cu stiva impune cunoaterea n permanent a poziiei elementului din vrful ei. n programul care implementeaz static stiva, Static_Stiva, variabila vrf reprezint poziia pe care se poate introduce un 16

element (vrf-1 este poziia ultimului element introdus), iar n varianta dinamic (programul Dinamic_Stiva) vrf este adresa ultimului element introdus. Grafic, cele dou variante de implementare se pot reprezenta astfel:

Variante de implementare a stivei

a) varianta static

b) varianta dinamic

Dou operaii sunt eseniale n lucrul cu stiva: adugarea unui element i extragerea unui element din stiv. Lista programului Static_Stiva (implementarea static) este prezentat n continuare.
//Static_Stiva # include "stdio.h" # include "conio.h" const max_aloc=20; int stiva[max_aloc]; char ch; int x,varf; void InitStiva() { varf=1; } int AdaugaEl(int x) { if (varf<=max_aloc) {

17

stiva[varf]=x; varf++; return 1; } else return 0; } int ExtragEl(int x) { if (varf==1) return 0; else { varf--; x=stiva[varf]; return x; } } void Listare() { int i; printf("\nElementele stivei sunt:"); for (i=1;i<=varf-1;i++) printf("%d ",stiva[i]); getch(); } void main(void) { int k; clrscr(); do { printf("\nI: Initializare stiva"); printf("\nA: Adauga element in stiva"); printf("\nE: Extrage element din stiva"); printf("\nL: Listare elemente din stiva"); printf("\nS: Sfarsit program"); printf("\n\nOptiunea dvs. va rog:"); ch=getch(); switch (ch) { case 'I': InitStiva(); break; case 'A': printf("\nValoarea de adaugat="); scanf("%d",&x); if (AdaugaEl(x)) Listare(); else { printf("\nDimensiune alocare stiva insuficienta!"); getch(); } break; case 'E': k=ExtragEl(x);

18

if (k) { printf("\nS-a extras elementul %d",k); getch(); } else { printf("\nStiva vida!"); getch(); } break; case 'L': Listare(); break; case 'S': break; default: printf("\nIntroduceti alta litera!"); getch(); } clrscr(); } while (ch!='S'); }

Lista programului Dinamic_Stiva (implementarea dinamica) este prezentata mai jos.


//Dinamic_Stiva # include "stdio.h" # include "conio.h" # include "alloc.h" const max_aloc=50; struct pstruct { int util; struct pstruct *urm; }; typedef struct pstruct PSTRUCT; PSTRUCT *varf,*p; int x,n; char ch; void InitStiva() { n=0; varf=NULL; } int AdaugaEl(int x) { if (sizeof(PSTRUCT)<=max_aloc) { p=(PSTRUCT*)malloc(sizeof(PSTRUCT)); p->urm=varf; p->util=x; varf=p; n++; return 1;

19

} else return 0; } int ExtragEl(int x) { if (varf==NULL) return 0; else { x=varf->util; p=varf; varf=p->urm; free(p); n--; return x; } } void Listare() { p=varf; printf("\nElementele stivei sunt: "); while (p!=NULL) { printf("%d ",p->util); p=p->urm; } getch(); } void main(void) { int k; clrscr(); do { printf("\nI: Initializare stiva"); printf("\nA: Adauga element in stiva"); printf("\nE: Extrage element din stiva"); printf("\nL: Listare elemente din stiva"); printf("\nS: Sfarsit program"); printf("\n\nOptiunea dvs. va rog:"); ch=getch(); switch (ch) { case 'I': InitStiva(); break; case 'A': printf("\nValoarea de adaugat="); scanf("%d",&x); if (AdaugaEl(x)) Listare(); else { printf("\nMemorie disponibila insuficienta"); printf("\npentru adaugare element in stiva");

20

getch(); } break; case 'E': k=ExtragEl(x); if (k) { printf("\nS-a extras elementul %d",k); getch(); } else { printf("\nStiva vida!"); getch(); } break; case 'L': Listare(); break; case 'S': break; default: printf("\nIntroduceti alta litera!"); getch(); } clrscr(); } while (ch!='S'); }

Nota: Programele ilustrative Static_Stiva i Dinamic_Stiva conin procedurile pentru efectuarea acestor operaii (AdaugaEl i ExtrageEl) i n plus codificarea operatiilor de initializare stiva i listare elemente stiva (InitStiva i Listare).

COADA
Coada este lista liniar n care adugarea de noi elemente se face la un capt, iar extragerea unui element se poate face numai prin cellalt capt al listei. Coada, ca element cotidian, este un element binecunoscut. Se poate sta la "coada" la un ghieu pentru procurarea unor bilete, la un magazin pentru cumprarea unor produse, la urcarea ntr-un mijloc de transport etc.

21

Din definiie rezult c, ntotdeauna, dintr-o coad se extrage elementul cel mai vechi. Se spune c accesul la o coad este de tip FIFO (First In First Out). Ca i n cazul stivei, operaiile de baz asupra unei cozi sunt dou: adaugarea unui element n coad i extragerea unui element din coad. Att n implementarea static (programul Static_Coada) ct i n implementarea dinamic (programul Dinamic_Coada), o coad e determinat de: capul cozii (arat locul de unde se extrage); coada cozii (arat locul unde se adaug); lungimea cozii (numrul elementelor din coad). Grafic, pentru o coad cu lungimea n=4 elementele, cele dou variante de implementare pot arta astfel: Variante de implementare a cozii:

a) varianta static

b) varianta dinamic

Lista

programului

Static_Coada

(implementarea

static)

este

prezentat n continuare.
//Static_Coada # include "stdio.h" # include "conio.h" const max_aloc=20; int coada[max_aloc]; char ch; int n,x,pcap,pcoada;

22

void InitCoada() { pcap=pcoada=1; n=0; } int AdaugaEl(int x) { if (n<max_aloc) { coada[pcoada]=x; pcoada=(pcoada+1)%max_aloc; if (pcoada==0) pcoada=max_aloc; n++; return 1; } else return 0; } int ExtragEl(int x) { if (n==0) return 0; else { x=coada[pcap]; pcap=(pcap+1)%max_aloc; if (pcap==0) pcap=max_aloc; n--; return x; } } void Listare() { int i; printf("\nElementele cozii sunt:"); if (pcap<pcoada) for (i=pcap;i<=pcoada-1;i++) printf("%d ",coada[i]); else { for (i=pcap;i<=max_aloc;i++) printf("%d ",coada[i]); for (i=1;i<=pcoada-1;i++) printf("%d ",coada[i]); } getch(); } void main(void) { int k; clrscr(); do { printf("\nI: Initializare coada");

23

printf("\nA: Adauga element in coada"); printf("\nE: Extrage element din coada"); printf("\nL: Listare elemente din coada"); printf("\nS: Sfarsit program"); printf("\n\nOptiunea dvs. va rog:"); ch=getch(); switch (ch) { case 'I': InitCoada(); break; case 'A': printf("\nValoarea de adaugat="); scanf("%d",&x); if (AdaugaEl(x)) Listare(); else { printf("\nDimensiune alocare coada insuficienta!"); getch(); } break; case 'E': k=ExtragEl(x); if (k) { printf("\nS-a extras elementul %d",k); getch(); } else { printf("\nCoada vida!"); getch(); } break; case 'L': Listare(); break; case 'S': break; default: printf("\nIntroduceti alta litera!"); getch(); } clrscr(); } while (ch!='S'); }

Lista programului Dinamic_Coada (implementarea dinamic) este prezentat mai jos.


//Dinamic_Coada # include "stdio.h" # include "conio.h" # include "alloc.h" const max_aloc=50;

24

struct pstruct { int util; struct pstruct *urm; }; typedef struct pstruct PSTRUCT; PSTRUCT *prim,*ultim,*p; int x,n; char ch; void InitCoada() { prim=ultim=NULL; n=0; } int AdaugaEl(int x) { if (sizeof(PSTRUCT)<max_aloc) { p=(PSTRUCT*)malloc(sizeof(PSTRUCT)); p->util=x; p->urm=NULL; if (prim==NULL) prim=ultim=p; else { ultim->urm=p; ultim=p; } n++; return 1; } else return 0; } int ExtragEl(int x) { if (prim==NULL) return 0; else { if (prim==ultim) ultim=NULL; x=prim->util; p=prim; prim=prim->urm; free(p); n--; return x; } } void Listare() { p=prim; printf("\nElementele cozii sunt: "); while (p!=NULL) {

25

printf("%d ",p->util); p=p->urm; } getch(); } void main(void) { int k; clrscr(); do { printf("\nI: Initializare coada"); printf("\nA: Adauga element in coada"); printf("\nE: Extrage element din coada"); printf("\nL: Listare elemente din coada"); printf("\nS: Sfarsit program"); printf("\n\nOptiunea dvs. va rog:"); ch=getch(); switch (ch) { case 'I': InitCoada(); break; case 'A': printf("\nValoarea de adaugat="); scanf("%d",&x); if (AdaugaEl(x)) Listare(); else { printf("\nMemorie disponibila insuficienta"); printf("\npentru adaugare element in coada"); getch(); } break; case 'E': k=ExtragEl(x); if (k) { printf("\nS-a extras elementul %d",k); getch(); } else { printf("\nCoada vida!"); getch(); } break; case 'L': Listare(); break; case 'S': break; default: printf("\nIntroduceti alta litera!"); getch(); } clrscr(); } while (ch!='S');

26

Nota: Cele dou programe (Static_Coada i Dinamic_Coada) simuleaz operaiile de iniializare a unei cozi (Initcoada), adugarea i extragerea unui element (AdaugaEl, ExtrageEl) precum i operaia de listare elemente (Listare).

ARBORELE
Listele reprezint mijloace simple i practice de a reprezenta organizarea liniar a obiectelor. Realitatea complex pe care o modelm ne arat nsa legturi ntre obiecte care depesc modelul liniar. Grafurile, digrafurile i ca un caz particular al acestora - arborii - reprezint structuri capabile s surprind complexitatea legturilor dintre obiecte.

Arborele

Cu ajutorul arborilor se pot descrie foarte fidel structurile de tip ierarhic (piramidal). Iat cteva exemple: structura de conducere a unei firme, organizarea administrativ teritorial dintr-o ar, organizarea unei armate, structura unei cari, descrierea unui obiect ca o reuniune de obiecte componente care, la rndul lor, se descompun n obiecte s.a.m.d.

27

Ultimul exemplu reliefeaz cel mai bine caracterul recursiv al structurii arborescente. Definim recursiv un arbore ca un set finit de unul sau mai multe noduri, astfel nct sunt ndeplinite condiiile: exist un nod unic numit radacina arborelui; celelalte noduri sunt repartizate n k>0 seturi disjuncte, fiecare set fiind la rndul sau un arbore. Grafic, putem reprezenta un arbore ca o multime de noduri sau vrfuri (fiecare obiect are asociat un nod) legate ntre ele prin arce care reflecta natura relaiei dintre obiecte. Observatii: n orice nod intr cel mult un arc; n nodul radacin nu intr nici un arc; Nodurile pot fi etichetate sau nu. Terminologia folosit n descrierea arborilor este mprumutat din asemnarea structurii cu un arbore ntors sau din paralelismul cu arborii genealogici. Remarci: nodul 1 este "radacina" i n acelasi timp "tata" pentru nodurile"fii" 2,5,6 care sunt ntre ele "frati"; nodurile 9 i 10 sunt "descendenti" ai nodului "stramos" 5; nodurile fra nici un fiu se numesc noduri terminale sau "frunze" (exemple: 3,4,9,10,6). Observaie: Termenii tata, fiu, frate oglindesc relaii directe ntre noduri, iar termenii stramos, descendent - relaii indirecte. Definim nivelul unui nod, recursiv, astfel: Nivelul nodului rdcina este 1; Nivelul oricarui nod diferit de nodul rdcina este nivelul tatlui sau +1. Observaie. Nivelul unui nod este 1+numrul de arce care alctuiesc un "drum" de la nodul "radacina" la el. Exemplu: nodurile 3,4,7 au nivelul 3, nodurile 9 i 10 au nivelul 5.

28

Un subarbore B pentru arborele A este orice arbore care ndeplinete condiiile: 1. 2. 3. nodurile lui B sunt i noduri n A; arcele lui B sunt i arce n A; orice frunz din A care poate fi atins din rdacina arborelui B trebuie s aparin mulimii nodurilor lui B. Observaie. Condiia 3 este esenial; mai jos prezentm un exemplu de arbore care, dei ndeplinete primele dou condiii, nu este totui subarbore.

nalimea unui arbore este maximum dintre nivelele nodurilor terminale sau echivalent 1+maximul dintre nalimile subarborilor si. Exemplu: Arborele prezentat n figura de mai sus are nalimea 5. Un tip de arbore special, cu aplicaii interesante n tehnicile de programare este arborele binar. Arborele binar este arborele n care un nod are cel mult doi fii. n aceast situaie se poate vorbi (pentru un arbore nevid) de cei doi subarbori (stng i drept) ai unui arbore. Schematic avem:

29

Arbore binar

Reprezentarea n memorie a arborilor poate fi static sau dinamic. n cazul static arborii se pot simula cu ajutorul tablourilor. Exemplu: n tabloul arbore cu n componente, arbore(i) (i=1...n) reprezint tatal nodului i. Astfel, arborele din figura de mai sus se poate reprezenta sub forma:

Avantajul acestei implementari este urmtorul: fiecarui nod avnd cel mult un tata, i atm n tablou o singur informaie (Luam arbore(i)=0 dac nodul i este radacin). Datorit dinamismului structurilor modelate printr-un arbore, varianta de implementare dinamica este preferabil variantei statice. n acest caz, dac arborele este binar, o celula va conine trei cmpuri: un cmp pentru memorarea informaiei specifice nodului (informaia util) i dou cmpuri care conin adresa rdcinii subarborelui stng, respectiv drept. Operaiile fundamentale asupra arborilor includ: parcurgerea arborelui, tergerea, cutarea sau adugarea unui nod. Vom prezenta operaia de parcurgere a unui arbore, care const n vizitarea fiecrui nod o singur dat i prelucrarea informaiei coninut n el. Dou tipuri de parcurgere a unui arbore sunt folosite frecvent: parcurgerea n laime i parcurgerea n nalime. n cazul parcugerii n laime se viziteaz i prelucreaz nodurile n ordinea: radacin, nodurile de la stnga spre dreapta de pe primul nivel, de pe

30

al doilea nivel etc. Astfel, rezultatul parcurgerii n latime a arborelui din figura 12.18 este list de noduri 1, 2, 5, 6, 3, 4, 7, 8, 9, 10. Putem realiza pacurgerea n laime a unui arbore binar printr-un algoritm care utilizeaz o coad drept element ajutator: Algoritm Parc_latime (rdcina):
Initializeaza coada Adauga radacina n coada Ct timp coada nu este vida executa ...Extrage element p din coada ...Prelucreaza informatia utila din p ...Daca p are fiu stng atunci ......Adauga fiu stng n coada ...Daca p are fiu drept atunci ......Adauga fiu drept n coada Sfrsit.

Observaie. Practic la fiecare pas al iteraiei se extrage un element din coad, se prelucreaz, apoi se adaug - dac exist - fiii sai. n cazul parcurgerii n adncime trecerea de la un nod x la fratele sau y din dreapta se face numai dup ce s-au vizitat i prelucrat toate nodurile descendente din x. Liniarizarea arborelui din figura 12.18 prin parcurgerea n adncime este urmtoarea: 1, 2, 3, 4, 5, 7, 8, 9, 10, 6. Revenirea la fratele y dupa vizitarea descendentilor lui x poate fi realizat utiliznd o stiva ajuttoare. Pentru arborii binari, trei tipuri de parcurgere n adncime sunt uzuale: parcurgerea n preordine (RSD), n inordine (SRD) i n postordine (SDR). Prescurtrile au urmtoarea semnificaie: RSD - Rdcina, Stnga, Dreapta - se prelucreaz rdcina, subarborele stng, subarborele drept; SRD - Stnga, Rdcina, Dreapta - se prelucreaz subarborele stng, rdcina, subarborele drept; SDR - Stnga, Dreapta, Rdcina - se prelucreaz subarborele stng, subarborele drept, rdcina.

31

Pentru execuia programelor care urmeaz avei nevoie sa generai mai nti un arbore (optiune G). Introducerea de date se va face mai nti pentru subarborele stng, apoi pentru cel drept. Pentru a ntelege mai bine, analizai figura de mai jos:

Arborele propriu-zis contine doar nodurile colorate. Zerourile "legate" de arbore prin linii punctate doresc doar sa ilustreze locurile n care trebuie intordus terminatorul de ramur (0 pentru programele date). Urmtorul eantion desprins din execuia programului este comun pentru ambele variante (recursiv, nerecursiv) i va ajuta sa nelegei modul n care se genereaz arborele (care va fi ulterior parcurs prin cele 3 metode). Pentru arborele din figura, procedura de generare a arborelui n cele dou programe va decurge astfel:
G: Generare Arbore Binar 1. Parcurgere in preordine (RSD) 2. Parcurgere in inordine (SRD) 3. Parcurgere in postordine (SDR) 4. Numar noduri arbore 5. Inaltime arbore S. Sfarsit program

32

Optiunea dvs. va rog: g x=100 x=20 x=0 x=10 x=0 x=0 x=30 x=40 x=0 x=50 x=0 x=0 x=0 0 G: Generare Arbore Binar 1. Parcurgere in preordine (RSD) 2. Parcurgere in inordine (SRD) 3. Parcurgere in postordine (SDR) 4. Numar noduri arbore 5. Inaltime arbore S. Sfarsit program Optiunea dvs. va rog: ...

Nota. Programul ArbRec ilustreaz cele trei moduri de parcurgere prezentate (procedurile RSD, SRD, SDR) precum i generarea unui arbore (procedura Gener_Arb), aflarea nalimii unui arbore (procedura Inaltime) i aflarea numrului de noduri dintr-un arbore (procedura NumarNod).
//ArbRec # include "stdio.h" # include "conio.h" # include "alloc.h" const max_aloc=50; struct pstruct { int util; struct pstruct *st,*dr; }; typedef struct pstruct PSTRUCT;

33

PSTRUCT *rad,*p; int x,m; char ch; PSTRUCT *Gener_Arb() { PSTRUCT *p; printf("\x=");scanf("%d",&x); if (x) { p=(PSTRUCT*)malloc(sizeof(PSTRUCT)); p->util=x; p->st=Gener_Arb(); p->dr=Gener_Arb(); return p; } else return NULL; } void RSD(PSTRUCT *p) { if (p!=NULL) { printf("%d ",p->util); RSD(p->st); RSD(p->dr); } } void SRD(PSTRUCT *p) { if (p!=NULL) { SRD(p->st); printf("%d ",p->util); SRD(p->dr); } } void SDR(PSTRUCT *p) { if (p!=NULL) { SDR(p->st); SDR(p->dr); printf("%d ",p->util); } } int NumarNod(PSTRUCT *p) { if (p==NULL) return 0; else return 1+NumarNod(p->st)+NumarNod(p->dr); } int Inaltime(PSTRUCT *p) { int ist,idr; if (p==NULL)

34

return 0; else { ist=Inaltime(p->st); idr=Inaltime(p->dr); if (ist>idr) return ist+1; else return idr+1; } } void main(void) { PSTRUCT *t; clrscr(); do { printf("\nG: Generare arbore binar"); printf("\n1: Parcurgere in preordine (RSD)"); printf("\n2: Parcurgere in inordine (SRD)"); printf("\n3: Parcurgere in postordine (SDR)"); printf("\n4: Numar noduri arbore"); printf("\n5: Inaltime arbore"); printf("\nS: Sfarsit program"); printf("\n\nOptiunea dvs. va rog:"); ch=getch(); switch (ch) { case 'G': rad=Gener_Arb(); break; case '1': RSD(rad); getch(); break; case '2': SRD(rad); getch(); break; case '3': SDR(rad); getch(); break; case '4': m=NumarNod(rad); printf("\nArborele are %d noduri",m); getch(); break; case '5': m=Inaltime(rad); printf("\nArborele are inaltimea %d ",m); getch(); break; case 'S': break; default: printf("\nIntroduceti alta litera!"); getch(); } clrscr();

35

} while (ch!='S'); }

Observatii: Toate procedurile sunt recursive, deci stiva este prezent n mod implicit; Procedura de generare creeaz arborele n ordinea RSD; legturile Nil ale nodurilor terminale sunt semnalate tastnd cifra 0 (n locul cifrei zero se poate folosi orice alt marcator); Datorit existenei implicite a stivei, procedurile sunt simple i elegante.

TEMA
Pentru fixarea cunotinelor dobndite pn n prezent, nainte de a trece mai departe, este bine sa rezolvati aceste teme. Dup rezolvarea lor putei selecta o nou conversaie. ntrebri de control Probleme

NTREBRI DE CONTROL
Definii: lista, stiva, coada, arborele. Ce este o list dublu nlnuit? Listai funciile programului Static_Stiva. Listai funciile programului Dinamic_Coada. Listai funciile programului ArbRec.

PROBLEME
S se elaboreze programe / subprograme (Borland) C pentru:

36

1.

Calculul numarului de apariii ale unui numr ntreg a printre componentele unei liste implementate static; Calculul sumei termenilor de rang impar dintr-o list simplu nlanuit; Calculul sumei termenilor mai mari strict dect un numr ntreg a dintr-o list dublu nlanuit; stiv

2.

3.

4.

Calculul

numarului

de

elemente

pozitive

dintr-o

(implementat static sau dinamic); 5. 6. Concatenarea (alipirea) a dou cozi implementate dinamic; Calculul sumei valorilor din nodurile unui arbore binar, cuprinse n intervalul [a,b]. Nota. Informaia util din structuri se consider de tip ntreg.

GRAFUL
Graful este se definete ca fiind perechea G=(V,R),unde V este mulimea nodurilor sau vrfurilor, iar elementele mulimii R se numesc muchii. Muchiile sunt perechi (v,w) din mulimea VxV. Grafurile modeleaz relaii simetrice dintre obiecte simetrice dintre obiecte. Exemple de grafuri : harta cilor ferate dintr-o tara, harta care nfieaz reeaua drumurilor cu dou sensuri de mers dintr-un ora, schema instalaiei de curent electric dintr-o instituie etc. Un caz particular important al grafului este graful orientat sau digraful (noiunea de digraf vine de la directed graph). Digraful modeleaz relaii nesimetrice dintre obiecte. De exemplu, se poate vorbi despre digraful datoriilor dintre membrii unui grup de persoane, despre digraful simpatiilor unui astfel de grup sau despre digraful care reprezint o reea de comunicaii etc.. Matematic, un digraf se definete ca fiind perechea G=(V,E), unde V este mulimea nodurilor sau vrfurilor, iar elementele mulimii E se numesc arce. Arcele sunt perechi (v,w) din VxV : v se numete baza arcului, iar w se numete vrful arcului. Se spune 37

ca nodul w este nod adiacent lui v. Grafic , nodurile unui digraf se pot reprezenta prin cercuri sau ptrate, iar arcurile prin sgei. Dac acestea sunt marcate cu numere sau nume , se spune ca sunt etichetate. Orice graf poate fi considerat digraf daca nlocuim o muchie (v,w) cu arcele (v,w) i (w,v). O noiune important n legtur cu digrafurile este noiunea de drum sau cale. Un drum este o succesiune de vrfuri v_1, v_2, v_3 ,v_k astfel nct exist arcele (v_1, v_2), (v_2, v_3),.., (v_k-1, v_k). Daca un arc nu este etichetat , se consider ca este de lungime 1. Valoarea indicata de eticheta unui arc se numete costul arcului. Un caz particular de drum este ciclul. Ciclul este un drum de la un vrf la el nsui. Un digraf fr cicluri se numete digraf aciclic. Exista doua reprezentari frecvent utilizate pentru digrafuri :cu ajutorul matricii de adiacenta i cu ajutorul listelor de adiacenta. Daca G=(V,E) este un digraf iar V={1,2,3,,n} atunci matricea de adiacenta se definete ca fiind matricea ptrat A(nxn), astfel nct A[i,j]=1 dac exist arc de la nodul i la nodul j i A[i,j]=0 dac nu exist arc de la nodul i la nodul j. Avantajul reprezentrii unui digraf prin matrice de adiacenta permite accesul rapid la arcele grafurilor. n acelai timp, aceasta reprezentare permite demonstrarea elegant a unor importante teoreme din teoria grafurilor. Pe de alta parte, se poate constata cu uurina faptul ca, n cazul unor grupuri de obiecte cu legturi slabe intre ele , matricea de adiacenta este rara i deci mai puin recomandat pentru reprezentare. O alternativa la reprezentarea prin matricea de adiacenta este lista de adiacenta. ntr-o list de adiacenta toate nodurile i sunt indici ntr-un vector x. n fiecare celul x[i] este memorat adresa unei liste care conine nodurile adiacente nodului x[i]. Implementarea listelor de adiacen poate fi static sau dinamic.

38

ALGORITMI DE SORTARE
Algoritmii de sortare permit ordonarea valorilor unui vector cresctor sau descresctor. n cele ce urmeaz vom prezenta civa algoritmi de sortare foarte cunoscui.

Sortarea prin selectie Pseudocod Algoritmul de sortare prin selecie funcioneaz dup urmtorii pai : Pentru fiecare i, i=1,2,,n-1 executa efectueaz atribuirea l=i calculeaz n variabila min, minimul elementelor x[j], j=i+1,i+2,..,n i atribuie valoarea poziiei pe care se afla minimul, variabilei l comuta valorile x[i] i x[l] Implementare n limbajul C
# include "stdio.h" # include "conio.h" int x[20],n,i; void SortSelect(int x[],int n) { int j,l,aux; for (i=1;i<=n-1;i++) { min=x[i]; l=i; for (j=i+1;j<=n;j++) { if (x[j]<=min) { min=x[j]; l=j; } x[l]=x[i] ; x[i]=min ; } } void main(void) { clrscr(); printf("n=");

39

scanf("%d",&n); for (i=1;i<=n;i++) { printf("x[%d]=",i); scanf("%d",&x[i]); } printf("\nvector initial: "); for (i=1;i<=n;i++) printf("%d ",x[i]); SortSelect(x,n); printf("\nvector sortat: "); for (i=1;i<=n;i++) printf("%d ",x[i]); getch(); }

Sortri prin inserie Algoritmii de sortare prin inserie se bazeaz pe urmtoarea idee : considernd ca primele i-1 elemente ale unui vector x sunt sortate, se va insera elementul x[i] ntre aceste elemente, astfel nct primele i elemente ale irului x s fie ordonate cresctor. Operaia se repet pentru i=1,2,,n. n situaia n care, pentru stabilirea poziiei de inserie se utilizeaz un algoritm de cutare secveniala ntr-un tabel ordonat, se obine algoritmul de inserie direct ; dac se utilizeaz algoritmul se cutare binar se obine algoritmul de inserie binar. Sortarea prin inserie direct Pseudocod Pentru fiecare i, i=2,3,,n execut efectueaz atribuirile aux=x[i] i j=i ct vreme x[j-1]>aux deplaseaz-te spre stnga cu o poziie i efectueaz atribuirea x[j]=x[j-1] cnd s-a gsit un element mai mic dect aux (x[j-1]< aux) sau s-a depit nceputul irului (j<1) procesul de cutare i inserie s-a terminat i se poate face inseria x[j]=aux Implementare n limbajul C
# include "stdio.h" # include "conio.h"

40

int x[20],n,i; void SortIns(int x[],int n) { int i,j,aux; for (i=2;i<=n;i++) { aux=x[i]; j=i; while (x[j-1]>aux) { x[j]=x[j-1]; j--; } x[j]=aux; } } void main(void) { clrscr(); printf("n="); scanf("%d",&n); for (i=1;i<=n;i++) { printf("x[%d]=",i); scanf("%d",&x[i]); } printf("\nvector initial: "); for (i=1;i<=n;i++) printf("%d ",x[i]); SortIns(x,n); printf("\nvector sortat: "); for (i=1;i<=n;i++) printf("%d ",x[i]); getch(); }

Sortare prin inserie binar

Pseudocod Pentru fiecare i, i=2,3,,n execut efectueaz atribuirile aux=x[i], inc=1, sf=i-1 ct timp inc<=sf execut gsete poziia med=(inc+sf)/2care marcheaz mijlocul subvectorului cu elementele x[inc], x[inc+1],, x[sf] dac x[med]>aux atunci cutarea se continu n zona cuprins ntre poziia inc i med-1, n caz contrar cutarea se continu n zona cuprins ntre med+1 i sf

41

fa loc pentru insertia elementului x[i]=aux, deplasnd cu o poziie spre dreapta elementele cuprinse ntre poziia de inserie inc i poziia i-1 efectueaz inseria x[inc]=aux

Implementare n limbajul C
# include "stdio.h" # include "conio.h" int x[20],n,i; void SortInsBin(int n) { int j,inc,sf,med,aux; for (i=2;i<=n;i++) { aux=x[i]; inc=1; sf=i-1; while (inc<=sf) { med=(inc+sf)/2; if (x[med]>aux) sf=med-1; else inc=med+1; } for (j=i;j>=inc+1;j--) x[j]=x[j-1]; x[inc]=aux; } } void main(void) { clrscr(); printf("n="); scanf("%d",&n); for (i=1;i<=n;i++) { printf("x[%d]=",i); scanf("%d",&x[i]); } printf("\nvector initial: "); for (i=1;i<=n;i++) printf("%d ",x[i]); SortInsBin(n); printf("\nvector sortat: "); for (i=1;i<=n;i++) printf("%d ",x[i]); getch();

42

Sortare prin interschimbare de chei (BubbleSort) Ideea metodei se bazeaz pe urmtorul rezultat: dac n subvectorul
x[1], x[2],x[ld] comparam succesiv pentru i=1,2,,ld-1 elementele

vecine x[i] i x[i+1] i le comutam daca x[i]>x[i+1], n final, elementul maxim din subvector se va afla pe poziia ld. Aplicnd succesiv aceasta idee pentru ld=n-1, n-2,, 1 se obine irul ordonat cresctor.

Pseudocod Executa atribuirea ld=n-1 ct timp ld>=2 execu pentru fiecare i=1,2,,ld compar elementele x[i] i x[i+1] ; daca x[i]> x[i+1] atunci comuta x[i] cu x[i+1] efectueaz atribuirea ld=ld-1 Implementare n limbajul C
# include "stdio.h" # include "conio.h" int x[20],n,i; void BubbleSort(int x[],int n) { int ld,aux; ld=n-1; do { for (i=1;i<=ld;i++) if (x[i]>x[i+1]) { aux=x[i]; x[i]=x[i+1]; x[i+1]=aux; } ld--; } while (ld!=1); } void main(void) { clrscr(); printf("n="); scanf("%d",&n); for (i=1;i<=n;i++) { printf("x[%d]=",i);

43

scanf("%d",&x[i]); } printf("\nvector initial: "); for (i=1;i<=n;i++) printf("%d ",x[i]); BubbleSort(x,n); printf("\nvector sortat: "); for (i=1;i<=n;i++) printf("%d ",x[i]); getch(); }

Sortarea rapid (QuickSort)

Algoritmul de sortare rapid are la baz metoda divide et impera : problema iniial - sortarea vectorului de dimensiune n - este descompus recursiv n dou subprobleme, acestea sunt descompuse la rndul lor, de asemenea n dou subprobleme i aa mai departe pn cnd subproblemele obinute se pot rezolv elementar. Problema de sortare a unui vector se poate rezolva elementar dac dimensiunea vectorului este cel mult doi. Ingeniozitatea acestei metode const n modul n care se gsete punctul k n care subvectorul curent x[s], x[s+1],, x[d] este porionat n dou. Dup un numr de pai valoarea elementului x[s] este memorat n poziia k astfel nct x[i]<=x[k] pentru s<=i<=k-1 i x[j]>=x[k] pentru k+1<=j<=d. Determinarea poziiei k se face dup un procedeu numit arderea la ambele capete ale unei lumnri : elementul x[s] este comparat succesiv cu elementele x[d], x[d-1] etc. pana cnd se ntlnete un element x[i] astfel nct x[s]>x[i]( lumnarea a fost ars la captul din dreapta). Se comuta elementele x[i] i x[s] i elementul x[i] se compara in continuare cu elementele x[s], x[s+1] etc.( se arde lumnarea din partea stnga) pn cnd se ntlnete un element x[j] astfel nct x[j]>x[i]. Procesul continua pana cnd elementul x[s] este adus pe poziia k astfel nct x[i]<=x[k] pentru s<=i<=k-1 i x[j]>=x[k] pentru k+1<=j<=d. Se observa ca poziia k reprezint poziia pe care o va ocupa elementul x[s] n vectorul sortat.

44

Pseudocod Se construiesc doua funcii : funcia Pozitie() i funcia Quicksort(). Funcia Pozitie() returneaz poziia k de divizare a vectorului x[s], x[s+1],, x[d] dup procedeul expus mai sus. Funcia Quicksort() se autoapeleaz recursiv ct timp subvectorii rezultai sunt de dimensiune mai mare ca doi (s<d), producnd subvectorii x[s],x[s+1],, x[k-1] i respectiv x[k+1], x[k+2], , x[d]

Implementare n limbajul C
# include "stdio.h" # include "conio.h" int x[20],n,i; int Pozitie(int s,int d) { int j,i1,j1,aux; i=s; j=d; i1=0; j1=-1; while (i<j) { if (x[i]>x[j]) { aux=x[i]; x[i]=x[j]; x[j]=aux; aux=i1; i1=-j1; j1=-aux; } i=i1+i; j=j1+j; } return i; } void QuickSort(int s,int d) { int k; if (s<d) { k=Pozitie(s,d); QuickSort(s,k-1); QuickSort(k+1,d); } }

45

void main(void) { clrscr(); printf("n="); scanf("%d",&n); for (i=1;i<=n;i++) { printf("x[%d]=",i); scanf("%d",&x[i]); } printf("\nvector initial: "); for (i=1;i<=n;i++) printf("%d ",x[i]); QuickSort(1,n); printf("\nvector sortat: "); for (i=1;i<=n;i++) printf("%d ",x[i]); getch(); }

HeapSort Algoritmul de sortare prin selecie se bazeaz pe dou noiuni importante: noiunea de arbore de selecie i de movila(heap). Legtura dintre cele doua noiuni este urmtoarea : prin parcurgerea n lime a unui arbore de selecie se obine o movil. Prin definiie un arbore de selecie este un arbore binar care ndeplinete urmtoarele condiii : este binar de nlime minim toate nivelele (excepie fcnd eventual ultimul) sunt complete ; completarea ultimului nivel se face de la stnga la dreapta cheile pentru orice relaie tata-fiu respecta relaia prestabilit De exemplu, se poate constata uor ca arborele prezentat mai jos este un arbore de selecie, relaia prestabilit fiind: valoarea nodului tata <= valoarea nodului fiu. Parcurgerea n lime a acestui arbore va produce vectorul :

3, 4,5,8,9,6,10,20 Un vector x[n] este movila(heap) dac pentru orice 1<=k<=n/2 sunt satisfcute condiiile : x[k]<=x[2*k] i x[k]<x[2*k+1].

46

Se observ c vectorul rezultat prin parcurgerea n lime a arborelui de selecie prezentat mai sus este o movil. De asemenea , se poate constata uor ca, ntr-un heap, elementul situat pe prima poziie are valoarea minim. Cu alte cuvinte, daca reuim s transformam un vector ntr-un heap, am obinut implicit, pe prima poziie i valoarea minim a vectorului. Procedeul poate fi aplicat n continuare subvectorului care ncepe cu poziia a doua a vectorului obinndu-se pe aceasta poziie al doilea element al vectorului sortat, i aa mai departe, pn cnd tot vectorul a fost sortat.

Pseudocod Se construiesc doua funcii : funcia Propagare() i funcia HeapSort(). Funcia Propagare() aduce n rdcina a unui subarbore valoarea cea mai mica dintre elementele subarborelui, transformndu-l ntr-un arbore de selecie. Imaginea liniarizata, prin parcurgerea n lime a acestui arbore, este un heap. Funcia HeapSort() are doua seciuni. n prima seciune se transform vectorul iniial ntr-un heap, aplicnd succesiv de la frontiera arborelui de selecie corespunztor(i=n/2, n/2-1, n/2-2 ,,1) spre nodul rdcina, funcia Propagare(). In cea de-a doua seciune se realizeaz sortarea propriu-zis. Astfel, pentru fiecare i=n,n-1,n-2,,2 se execut : comutarea elementelor x[1] i x[i] apelul funciei Propagare() pentru subvectorul x[1], x[2],, x[i-1]

Implementare n limbajul C
# include "stdio.h" # include "conio.h" int x[20],n,i; void Propagare(int x[],int k,int n) { int aux,prop,urm; aux=x[k]; prop=1; while (prop) {

47

urm=2*k; if (urm<=n) { if (urm+1<=n) if (x[urm]>=x[urm+1]) urm++; if (aux<=x[urm]) prop=0; else { x[k]=x[urm]; k=urm; } } else prop=0; } x[k]=aux; } void HeapSort(int x[],int n) { int l,aux; for (i=n/2;i>=1;i--) Propagare(x,i,n); l=n; while (l>1) { aux=x[1]; x[1]=x[l]; x[l]=aux; l--; Propagare(x,1,l); } } void main(void) { clrscr(); printf("n="); scanf("%d",&n); for (i=1;i<=n;i++) { printf("x[%d]=",i); scanf("%d",&x[i]); } printf("\nvector initial: "); for (i=1;i<=n;i++) printf("%d ",x[i]); HeapSort(x,n); printf("\nvector sortat: "); for (i=1;i<=n;i++) printf("%d ",x[i]); getch(); }

48

Sortare prin interclasare Sortarea prin interclasare folosete ideea construirii rapide a unui ir ordonat pornind de la dou iruri deja ordonate. Practic, dndu-se dou iruri ordonate x[n] i y[m], dac i arat valoarea curent din x[n], iar j valoarea curent din y[m], atunci irul z[m+n] se construiete transfernd la fiecare pas k, valoarea min(x[i],y[j]) in z[k] . n cazul n care ntr-unul din irurile de intrare elementele se epuizeaz, elementele netransferate n vectorul z din al doilea ir se copiaz n vectorul z, in continuarea celor existente. n algoritmul de sortare prin interclasare prezentat mai jos, vectorul iniial se divide pn cnd vectorii rezultai au mrimi mai mici sau egale cu doi (metoda divide et impera). Vectorii cu astfel de mrimi pot fi sortai cu uurina. Prin interclasarea pas cu pas a acestora va rezulta in final vectorul sortat.

Pseudocod Se folosesc trei funcii : funcia funcia Divizare() Funcia Divizare() realizeaz divizarea unui subvector x[p], x[p+1],.x[q] n dou pri egale, pn cnd q-p<=1; Funcia Sort() realizeaz sortarea vectorilor de dimensiune mai mica sau egala cu doi Funcia Interclas() realizeaz sortarea vectorului iniial, prin interclasarea pas cu pas a subvectorilor sortai Sort(), funcia Interclas() i

Implementare in limbajul C
# include "stdio.h" # include "conio.h" int a[20],n,i; void Sort(int a[],int p,int q) { int aux; if (a[p]>a[q]) { aux=a[p]; a[p]=a[q]; a[q]=aux;

49

} } void Interclas(int a[],int p,int d,int q) { int b[20],i,j,k,l; i=p; j=d+1; k=1; while ((i<=d)&&(j<=q)) { if (a[i]<a[j]) { b[k]=a[i]; i++; } else { b[k]=a[j]; j++; } k++; } if (i<=d) for (l=i;l<=d;l++) { b[k]=a[l]; k++; } else for (l=j;l<=q;l++) { b[k]=a[l]; k++; } k=1; for (i=p;i<=q;i++) { a[i]=b[k]; k++; } } void Divizare(int a[],int p,int q) { int d; if (q-p<=1) Sort(a,p,q); else { d=(p+q)/2; Divizare(a,p,d); Divizare(a,d+1,q); Interclas(a,p,d,q); } } void main(void) { clrscr(); printf("n=");

50

scanf("%d",&n); for (i=1;i<=n;i++) { printf("a[%d]=",i); scanf("%d",&a[i]); } printf("\nvector initial: "); for (i=1;i<=n;i++) printf("%d ",a[i]); Divizare(a,1,n); printf("\nvector sortat: "); for (i=1;i<=n;i++) printf("%d ",a[i]); getch(); }

Sortare prin numrare Ideea pe care o exploateaz algoritmul prin numrare este foarte simpl: poziia unui element intr-un sir sortat este data de numrul elementelor mai mici dect el, la care adunm valoarea 1. Dac n nr[i] vom contoriza numrul elementelor mai mici dect x[i], atunci, dup ce contorizarea s-a terminat, valoarea nr[i]+1 va arata poziia elementului x[i] n irul ordonat. Vectorul ordonat se obine ntr-un nou vector. n programul de mai jos acesta a fost notat cu y.

Implementare n limbajul C
# include "stdio.h" # include "conio.h" int x[20],y[20],n,i; void SortNum(int x[],int n) { int nr[20],j; for (i=1;i<=n;i++) nr[i]=1; for (j=2;j<=n;j++) for (i=1;i<=j-1;i++) if (x[i]<x[j]) nr[j]++; else nr[i]++; for (i=1;i<=n;i++) y[nr[i]]=x[i]; } void main(void) {

51

clrscr(); printf("n="); scanf("%d",&n); for (i=1;i<=n;i++) { printf("x[%d]=",i); scanf("%d",&x[i]); } printf("\nvector initial: "); for (i=1;i<=n;i++) printf("%d ",x[i]); SortNum(x,n); printf("\nvector sortat: "); for (i=1;i<=n;i++) printf("%d ",y[i]); getch(); }

ALGORITMI DE CAUTARE
Cutare secveniala Algoritmul de cutare secveniala rezolva urmtoarea problem: dac x[n] este un vector cu valori oarecare s se decid dac o valoare oarecare a se afla printre elementele vectorului x. Cutarea se numete secvenial deoarece, pentru aflarea rspunsului, vectorul x este parcurs secvenial ncepnd cu prima componenta pn cnd este ntlnita prima apariie a valorii a (cutarea a avut succes) sau pn cnd vectorul x a fost epuizat (cutarea nu a avut succes).

Implementare n limbajul C
# include "stdio.h" # include "conio.h" int x[20],n,i,a; int CautSecv(int n,int x[],int a) { int gasit; gasit=0; i=1; while ((i<=n)&&(gasit==0)) if (x[i]==a) gasit=1; else i++; return gasit; } void main(void)

52

{ clrscr(); printf("n="); scanf("%d",&n); for (i=1;i<=n;i++) { printf("x[%d]=",i); scanf("%d",&x[i]); } printf("\nafisare vector: "); for (i=1;i<=n;i++) printf("%d ",x[i]); printf("\na="); scanf("%d",&a); if (CautSecv(n,x,a)) printf("Valoarea se afla printre elementele sirului"); else printf("Valoarea nu se afla printre elementele sirului"); getch(); }

Cutare secvenial rapid Cutarea secvenial poate deveni rapid dac modificm puin algoritmul deja prezentat. Modificarea const n completarea vectorului x pe componenta x[n+1] cu valoarea a pe care dorim s o cutam in x[n]. Aa cum se poate constata n implementare de mai jos, numrul de comparaii se reduce substanial : adugarea valorii a pe poziia x[n+1] comparaiei i<=n care depisteaz sfritul irului x. a permis eliminarea

Implementare n limbajul C
# include "stdio.h" # include "conio.h" int x[20],n,i,a; int CautSecv(int n,int x[],int a) { i=1; x[n+1]=a; while (x[i]!=a) i++; if (i<=n) return 1; else return 0; } void main(void) { clrscr(); printf("n=");

53

scanf("%d",&n); for (i=1;i<=n;i++) { printf("x[%d]=",i); scanf("%d",&x[i]); } printf("\nafisare vector: "); for (i=1;i<=n;i++) printf("%d ",x[i]); printf("\na="); scanf("%d",&a); if (CautSecv(n,x,a)) printf("Valoarea se afla printre elementele sirului"); else printf("Valoarea nu se afla printre elementele sirului"); getch(); }

Cutare secvenial n tabel ordonat n situaia n care elementele vectorului sunt ordonate cutarea se poate face mult mai eficient. Astfel, dac irul este ordonat cresctor este suficient s cutam valoarea a n vectorul x doar cat timp a>x[i], i=1,2,. Dac exist o valoare i astfel nct a<x[i] nu are rost s cutam mai departe deoarece irul fiind ordonat cresctor, toate valorile x[j] cu j>i vor fi strict mai mari dect a. Implementare n limbajul C
# include "stdio.h" # include "conio.h" int x[20],n,i,a; int CautSecvTabOrd(int n,int x[],int a) { int gasit; i=1; x[n+1]=a; while (a>x[i]) i++; if (i<=n) if (x[i]==a) return 1; else return 0; else return 0; } void main(void) { clrscr(); printf("n="); scanf("%d",&n);

54

for (i=1;i<=n;i++) { printf("x[%d]=",i); scanf("%d",&x[i]); } printf("\nafisare vector: "); for (i=1;i<=n;i++) printf("%d ",x[i]); printf("\na="); scanf("%d",&a); if (CautSecvTabOrd(n,x,a)) printf("Valoarea se afla printre elementele sirului"); else printf("Valoarea nu se afla printre elementele sirului"); getch(); }

Cutare binar efectueaz atribuirile inc=1, sf=n, gasit=0 ct timp inc<=sf i gasit = 0 execut gsete poziia m=(inc+sf)/2care marcheaz mijlocul subvectorului cu elementele x[inc], x[inc+1],, x[sf] daca x[m] este egal cu a atunci gasit=1, altfel daca x[m]>aux atunci cutarea se continu n zona cuprins ntre poziia inc i m-1, n caz contrar cutarea se continua n zona cuprins ntre m+1 i sf Implementare n limbajul C
# include "stdio.h" # include "conio.h" int x[20],n,i,a; int CautBin(int x[],int a) { int gasit,inc,sf,m; inc=1; sf=n; gasit=0; while ((inc<=sf)&&(gasit==0)) { m=(inc+sf)/2; if (x[m]==a) gasit=1; else if (x[m]>a) sf=m-1; else

55

inc=m+1; } return gasit; } void main(void) { clrscr(); printf("n="); scanf("%d",&n); for (i=1;i<=n;i++) { printf("x[%d]=",i); scanf("%d",&x[i]); } printf("\nafisare vector: "); for (i=1;i<=n;i++) printf("%d ",x[i]); printf("\na="); scanf("%d",&a); if (CautBin(x,a)) printf("Valoarea se afla printre elementele sirului"); else printf("Valoarea nu se afla printre elementele sirului"); getch(); }

ALGORITMI DIN TEORIA GRAFURILOR


Multe rezultate din teoria grafurilor sunt cunoscute sub forma unor algoritmi celebri. Prezentm mai jos doi algoritmi binecunoscui : algoritmul lui Floyd i algoritmul lui Warshall.

Algoritmul lui Floyd

Algoritmul lui Floyd permite calculul matricii drumurilor de cost minim dintr-un graf pornind de la matricea costurilor muchiilor A. Dac notam A1=A, atunci, pentru k>1 se construiete matricea : Ak[i,j]=min (Ak-1[i,j], Ak-1[i,k]+Ak-1[k,j]) La fiecare pas k, matricea Ak[i,j] va conine cea mai mic distan dintre i i j prin noduri care nu depesc valoarea k.

56

Se observa ca Ak[i,k]= Ak-1[i,k] i Ak[k,j]= Ak-1[k,j] i deci se poate lucra succesiv asupra aceleai matrici fr a mai folosi indicii k superiori de la Ak.. Implementarea n limbajul C
/determinarea matricii drumurilor de cost minim intr-un graf neorientat #include<stdio.h> #include<conio.h> int c[20][20],n,i,j; void citire_matrice_costuri(int c[20][20],int n) {printf("\nintroduceti valoarea 9999 unde nu exista muchie\n"); for(i=0;i<n;i++) {c[i][i]=0; for(j=i+1;j<n;j++) {printf("Costul intre %i i %i=",i+1,j+1); scanf("%i",&c[i][j]); c[j][i]=c[i][j]; } } } void afisare(int c[20][20],int n) { for(i=0;i<n;i++) { for(j=0;j<n;j++) printf("%5i",c[i][j]); printf("\n"); } } void floyd(int c[20][20]) { int k; for(k=0;k<n;k++) for(i=0;i<n;i++) for(j=0;j<n;j++) if(c[i][j]>c[i][k]+c[k][j]) c[i][j]=c[i][k]+c[k][j]; } void main() { clrscr(); printf("Nr. de noduri:");scanf("%i",&n); printf("Matricea costurilor:\n"); citire_matrice_costuri(c,n); afisare(c,n); floyd(c); printf("\n Matricea costurilor minime:\n"); afisare(c,n); getch(); }

57

Algoritmul lui Warshall

Implementarea n limbajul C

Algoritmul lui Warshall permite calculul matricii existentei drumurilor dintr-un graf pornind de la matricea de adiacenta A. Daca notam A1=A, atunci, pentru k>1 se construiete matricea : Ak[i,j]= Ak-1[i,j] or (Ak-1[i,k] and Ak-1[k,j]) La fiecare pas k, matricea Ak[i,j] va conine valoarea 1 daca exista un drum de la i la j prin noduri care nu depesc valoarea k i valoarea o daca un astfel de drum nu exista.. Se observa ca Ak[i,k]= Ak-1[i,k] i Ak[k,j]= Ak-1[k,j] i deci se poate lucra succesiv asupra aceleai matrici fr a mai folosi indicii k superiori de la Ak..
/determinarea matricii drumurilor intr-un graf neorientat #include<stdio.h> #include<conio.h> int a[20][20],n,i,j; void citire_matrice_adiacenta(int a[20][20],int n) { for(i=0;i<n;i++) {a[i][i]=0; for(j=i+1;j<n;j++) {printf("a[%i][%i]=",i+1,j+1); scanf("%i",&a[i][j]); a[j][i]=a[i][j]; } } } void afisare(int a[20][20],int n) { for(i=0;i<n;i++) { for(j=0;j<n;j++) printf("%2i",a[i][j]); printf("\n"); } } void warshall(int a[20][20]) { int k; for(k=0;k<n;k++) for(i=0;i<n;i++) for(j=0;j<n;j++) if((i!=j)&&(a[i][j]==0)) a[i][j]=a[i][k]&&a[k][j]; } void main() { clrscr();

58

printf("Nr. de noduri:");scanf("%i",&n); printf("Matricea de adiacenta:\n"); citire_matrice_adiacenta(a,n); afisare(a,n); warshall(a); printf("\n Matricea drumurilor:\n"); afisare(a,n); getch(); }

59

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