Sunteți pe pagina 1din 21

7. Arbori.

Definiii i generaliti. Un arbore este un model abstract al unei relaii ierarhice. Un arbore const din noduri aflate n relaii de tip fiu-printe. Sistemul de fiiere i mediile de programare sunt aplicaii ce pot fi modelate folosind arbori. Structurile arborescente: sunt modele naturale pentru ierarhii de obiecte permit meninerea ordinii n colecii de date dinamice, n care se fac multe inserri i tergeri. sunt structuri de cutare rapid Un arbore liber T=(N,A) este un graf conex aciclic, n care N este o mulime de noduri, iar A este o mulime de arce care conecteaz perechi de noduri. Arborele T=(N,A), n=|N| are urmtoarele proprieti: 1. are n noduri i n-1 arce 2. nu are cicluri 3. ntre oricare pereche de noduri exist o cale unic 4. tergerea unui arc formeaz doi arbori 5. adugarea unui arc creaz un ciclu. Un arbore cu rdcin are urmtoarele proprieti: 1. are un nod special numit nod rdcin 2. fiecare nod (exceptnd rdcina) are un printe (predecesor sau tat) unic 3. fiecare nod are 0 sau mai muli subarbori T1, T2,...,Tn nevizi, ai cror rdcini sunt fii (succesori) ai nodului rdcin.. Numrul de fii ai unui nod definete gradul nodului. 4. un nod fr succesori este un nod frunz (celelalte sunt noduri interne) 5. o cale ntre 2 noduri este o secven de noduri i arce: cale(u,v)={u, (u,i), i, (i,j), j,,k, (k,v), v} 6. lungimea cii este numrul de arce de pe cale. 7. Un nod u este strmo al unui nod v sau v este descendent al unui nod u dac: Pred(Pred(...Pred(v)...))=u 8. nlimea unui nod u este numrul de arce de pe cea mai lung cale de la u la un nod frunz. Inlimea unui nod se exprim recursiv astfel: h(n) = 0 dac n este frunz 1 + max(h(Sk(n))
k

n care Sk(n) este subarborele avnd ca rdcin fiul ck al nodului n. 9. nlimea arborelui este nlimea rdcinii, adic numrul de arce de pe cea mai lung cale de la rdcin la o frunz 10. Adncimea unui nod este numrul de arce de la acel nod la rdcin. Rdcina are adncimea 0, iar un arbore vid are adncimea 1.. Adncime(nod) = Adncime(Pred(nod)) + 1 Adncime(rdcin) = 0

Intr-un arbore, pe nivelul 0 se afl rdcina, pe nivelul 1 succesorii direci ai rdcinii, etc. Succesorul unui nod este rdcin a unui subarbore. Un arbore n care nodurile pot avea orice grad este un arbore general sau arbore multici Un arbore poate fi definit recursiv astfel: 1. un arbore este un singur nod nodul rdcin 2. un arbore este un nod rdcin cu fiii c1, c2,...,ck i aceste noduri sunt rdcini de arbori. Operaiile generale pe arbori multici se refer la: deplasarea n arbore traversarea arborelui cutarea, inserarea i tergerea de noduri ntr-un arbore binar gradul maxim al oricrui nod nu depete 2. Un arbore binar se definete recursiv ca: un arbore vid un arbore cu un nod rdcin cu doi fii: stng i drept, care sunt rdcini de arbori binari.

7.1. Arbori Binari.


Operaii specifice TAD Arbore Binar. struct arb; typedef struct arb *Arb; Arb AB_New(); - creaz un arbore binar vid int AB_Empty(Arb a); - test arbore vid int AB_Size(Arb a); - numrul de noduri al arborelui a Arb AB_Root(Arb a); - d rdcina nodului a void *AB_GetKey(Arb a); - d cheia din nodul a void AB_SetKey(Arb a, void *k); - face adresa cheii nodului a egal cu k int AB_Leaf(Arb a); - 1 dac nodul a este frunz, 0 altfel Arb AB_GetPred(Arb a); - d nodul tat al nodului a sau NULL void AB_SetPred(Arb a, Arb p, int dir);-face nodul p tat al nodului a. Nodul predecesor p va avea nodul a subarbore stng sau drept,dup cum dir este -1 sau 1 Arb AB_GetSS(Arb a); - d rdcina subarborelui stng a lui a void AB_SetSS(Arb a, Arb s); - face s subarbore stng a lui a Arb AB_GetSD (Arb a); - d rdcina subarborelui drept a lui a

void AB_SetSD(Arb a, Arb d); - face d subarbore drept a lui a Arb AB_CrNod(void *ch, Arb s, Arb d, Arb p, int dir);-creaz un nod cu cheie, subarbori i printe specificai; nodul creat este subarbore stng sau drept a lui p dup cum dir este -1 sau 1. Arb AB_CrArb(Arb r, Arb s, Arb d); -creaz un arbore dintr-un nod rdcin i doi subarbori specificai int AB_Height(Arb a); - d nlimea arborelui a int AB_Depth(Arb n); - d advwesaewancimea nodului n Traversarea arborilor binari i aplicaii ale traversrilor. Traversarea , parcurgerea sau vizitarea unui arbore binar reprezint o modalitate sistematic de enumerare a nodurilor arborelui, n scopul efecturii unei prelucrri asupra nodurilor. Se pot defini urmtoarele traversri de arbori binari: traversare n preordine RSD : rdcin subarbore stng subarbore drept traversare n inordine SRD : subarbore stng rdcin subarbore drept traversare n postordine SDR : subarbore stng subarbore stng rdcin traversare n inordine invers DRS : subarbore drept rdcin subarbore stng traversare n lime Traversarea n preordine.

void PreOrd(Arb a, void (*Vizitare)(void*)){ if(!AB_Empty(a)){ Vizitare(AB_GetKey(a)); PreOrd(AB_GetSS(a), Vizitare); PreOrd(AB_GetSD(a), Vizitare); } } O traversare nerecursiv n preordine utilizeaz o stiv: void PreOrd(Arb a){ Stiva S=S_New (); Arb n, q; Push(S, a); while(!S_Empty(S)){ n=Pop(S);

Vizitare(AB_GetKey(n)); if(q=AB_GetSS(n)) Push(S, q); if(q=AB_GetSD(n)) Push(S, q); } } Traversarea poate fi abstractizat sub forma: Arb a,p; for(p=RSDBegin(a); p!=RSDEnd(a); p=RSDNext(a,p)) Vizitare(AB_GetKey(p)); La traversarea n preordine, primul nod vizitat este nodul rdcin, aadar: Arb RSDBegin(Arb a){ return a; } Traversarea se ncheie n momentul n care se revine n nodul rdcin, care nu are predecesor: Arb RSDEnd(Arb a){ return NULL; } Avansarea la urmtorul nod se face astfel: - dac nodul curent are fiu stnga, atunci se trece pe acesta - dac nodul curent nu are fiu stnga, dar are fiu dreapta, se trece pe acesta - dac nodul curent este frunz, se revine pe nodul printe i se caut un fiu dreapta care nu a fost prelucrat, adic se verific dac printele are fiu drept. Ct timp nodul curent este fiu drept sau fiul drept al printelui este vid, se trece pe nodul printe. Arb RSDNext(Arb a, Arb p) { Arb q; q = AB_GetSS(p); if(q) return q; q = AB_GetSD(p); if(q) return q; q = p; p = AB_GetPred(p); while(p && AB_GetSD(p)==q || AB_GetSD(p)==NULL){ q = p; p = AB_GetPred(p); } if(p && AB_GetSD(p)) p = AB_GetSD(p); return p; } Traversarea n postordine. O funcie de traversare recursiv este: void PostOrd(Arb a){ if(!AB_Empty(a)){ PostOrd(AB_GetSS(a)); PostOrd(AB_GetSD(a)); Vizitare(AB_GetKey(a)); } }

O traversare iterativ n postordine pornete dintr-un nod frunz n care se ajunge pornind din rdcin i cobornd pe subarborele stng, i n absena acestuia pe subarborele drept: Arb SDRBegin(Arb a){ Arb s, d, p = a; if(AB_Empty(p) return NULL; while((s==AB_GetSS(p))||(d=AB_GetSD(p)) if(s) p = s; else p = d; return p; } Succesorul n postordine a nodului curent p se obine astfel: -dac p nu are printe (s-a ajuns n rdcin), atunci succesorul este NULL - dac p este fiu stnga, atunci: - dac are frate (exist i fiu dreapta al printelui nodului), se trece pe nodul frate i se ajunge ntr-un nod frunz cobornd pe subarborele stng, i n absena acestuia pe subarborele drept - dac nu are frate, atunci succesorul este nodul printe - dac p este fiu dreapta, atunci succesorul este nodul printe Arb SDRNext(Arb a, Arb p){ Arb q, r; if(p==a) return NULL; q = AB_GetPred(p); if(AB_GetSS(q)==p) if(r=AB_GetSD(q)){ p = r; return SDRBegin(p); } else return q; else return q; }

Traversarea n postordine se ncheie n momentul n care a fost vizitat i nodul rdcin, deci succesorul va fi printele nodului rdcin, adic NULL: Arb SDREnd(Arb p){ return NULL; } Traversarea n inordine. O funcie recursiv de traversare n inordine este: void InOrd(Arb a){ if(!AB_Empty(a)){ InOrd(AB_GetSS(a)); Vizitare(AB_GetKey(a)); InOrd(AB_GetSD(a)); } } Varianta iterativ de traversare n inordine ncepe din nodul situat n extremitatea stng: Arb SRDBegin(Arb a){ if(AB_Empty(a)) return NULL; Arb p = a; while(AB_GetSS(p)) p = AB_GetSS(p); return p; } Succesorul n inordine a unui nod p este cel mai din stnga nod al subarborelui drept a lui p, dac acesta exist. n caz contrar succesorul q lui p este rdcina unui arbore n care nodul p este situat n extremitatea dreapt a subarborelui stng a lui q. Arb SRDNext(Arb a, Arb p){ } Traversarea n lime. Traversarea n lime (sau pe niveluri) a unui arbore binar presupune folosirea unei cozi n care se pun adresele nodurilor. Ct timp coada nu este vid, se scoate un nod, se prelucreaz i i se pun succesorii n coad. void travLat(Arb a, void (*Vizit)(Arb )){ Coada Q = Q_New (); Arb p; Enq(Q, a); while(!Q_Empty(Q)){ p = Deq(Q); Vizitare(AB_GetKey(p)); if(AB_GetSS(p)) Enq(Q, AB_GetSS(p)); if(AB_GetSD(p)) Enq(Q, AB_GetSD(p)); } }

Exemplele se ncadreaz n traversarea n postordine: 1. tergerea unui arbore binar void AB_Delete(Arb *a){ if(!AB_Empty(*a)){ AB_Delete(&AB_GetSS(*a)); AB_Delete(&AB_GetSD(*a)); free(*a); *a = NULL; } } 2. copierea un unui arbore binar Arb copie (Arb a){ if(AB_Empty(a)) return NULL; Arb b = AB_CrNod(AB_GetKey(a),NULL,NULL,NULL,0); Arb s = copie(AB_GetSS(a)); Arb d = copie(AB_GetSD(a)); return AB_CrArb(b,s,d); } 3. Afiarea unui arbore binar.. O traversare mai puin obinuit traversarea n inordine inversat DRS se folosete pentru afiarea arborilor binari. Dac rotim un arbore binar cu 90 0, rdcina va apare n stnga, nodurile din partea dreapt vor apare primele, aa c trebuie efectuat o parcurgere DRS. Fiecare nivel din arbore va fi indentat cu 5 spaii libere. void indent(int niv){ for(int j=0; j<niv; j++) printf( ); } void afRot(Arb a, int niv){ if(a){ afRot(AB_GetSD(a), niv+1);

indent(niv); printf( %4d\n, *(int*)AB_GetKey(a)); afRot(AB_GetSS(a), niv+1); } } 4. Evaluarea unui arbore expresie. O expresie aritmetic poate fi reprezentat printr-un arbore, avnd termenii ca noduri frunze i operatorii ca noduri interne. In reprezentarea expresiei nu sunt necesare paranteze.De exemplu expresia (3 + 2 * 4) / (5 1) se reprezint prin arborele:

/
+

/
*

3 3 3/ 2

4/

Pentru evaluarea arborelui expresie se evalueaz mai nti subarborii stng i drept i apoi se aplic operatorul. Nodurile frunze conin termeni. Traversarea arborelui expresie se face n postordine.Cheile sunt pointeri la iruri de caractere. double eval(Arb a){ if(AB_Leaf(a)) return atod(AB_GetKey(a)); char op = *(AB_GetKey(a)); double x = eval(AB_GetSS(a)); double y = eval(AB_GetSD(a)); switch(op){ case +: return x+y; case -: return x-y; case *: return x*y; case /: return x/y; } } 5. Afiarea unui arbore sub form parantezat. Pentru afiarea unui arbore sub form parantezat se verific nodul curent: dac este rdcin sau frunz se afieaz. In caz contrar, se afieaz (, apoi subarborele stng, ,, subarborele drept i ). void printArbPar(Arb a){ if(AB_Root(a) || AB_Leaf(a)) printf(%4d, *(int*)AB_GetKey(a));

else { printf((); printArbPar(AB_GetSS(a)); printf(,); printArbPar(AB_GetSD(a)); printf()); } } Afiarea unui arbore expresie se face astfel: pentru o frunz (termen) se face afiare pentru un nod intern (operator) se afieaz: ( subarborele stng operator subarborele drept ) Fiind o structur definit recursiv, operaiile uzuale cu arbori binari, pot fi de asemeni reprezentate n mod recursiv. Cutarea unei chei n arborele binar presupune cutarea n nodul rdcin, i dac nu se gsete se continu cutarea n subarborele stng i apoi dac este cazul n subarborele drept. Algoritmul nu poate fi considerat o traversare, deoarece nu sunt vizitate toate cheile, oprindu-ne la gsirea cheii cutate. 6. cutarea unei valori ntr-un arbore binar Arb Search(Arb a, void *x, PFC comp){ if(AB_Empty(a)) return NULL; if(comp(AB_GetKey(a), x)==0) return a; Arb p = Search(AB_GetSS(a), x, comp); if(p) return p; return Search(AB_GetSD(a), x, comp); }

Implementarea arborilor binari. Un arbore binar poate fi reprezentat printr-un vector sau folosind pointeri. 5.1. Arbori binari implementai cu pointeri. n reprezentarea cu pointeri, un nod al arborelui binar este precizat printr-o structur alocat dinamic, care conine o cheie i doi ponteri la nodurile succesori (i eventual un pointer la nodul predecesor). struct arb{ void *key; struct arb *stg; struct arb *dr; struct arb *pred; }; Arborele binar va fi precizat printr-un pointer la nodul rdcin. typedef struct arb *Arb;

Utilizatorul, care nu cunoate detaliile structurii nod, va folosi funciile precizate mai sus Arb int AB_New (){return NULL;} AB_Empty(Arb a){return a==NULL;}

Numrarea nodurilor se face recursiv: int AB_Size(Arb a){ if(!a) return 0; return AB_Size(a->stg)+AB_Size(a->dr)+1; } void *AB_GetKey(Arb a){ assert(a); return a->key; } void AB_SetKey(Arb a, void *k){ assert(a); a->key = k; } int AB_Leaf(Arb a){ assert(a); return (a->st==0 && a->dr==0); } Arb AB_Root(Arb a){ assert(a); while(a->pred) a = a->pred; return a; } Arb AB_GetPred(Arb n){ assert(n); return n->pred; } Arb AB_GetSS(Arb a){ assert(a); return a->st; } Arb AB_GetSD(Arb a){ assert(a); return a->dr; } void AB_SetSS(Arb a, Arb s){ assert(a); a->st = s; if(s) s->pred = a; } void AB_SetSD(Arb a, Arb d){

10

assert(!AB_Empty(a)); a->dr = d; if(d) d->pred = a; } void AB_SetPred(Arb a, Arb p, int dir){ assert(!AB_Empty(a)); a->pred = p; if(dir==-1) p->st = a; else if(dir==1) p->dr = a; } Arb AB_CrNod(void *ch, Arb s, Arb d, Arb p, int dir){ Arb r = (Arb)malloc(sizeof(struct arb)); r->key = ch; r->st = s; r->dr = d; r->pred = p; if(p) switch(dir){ case -1: p->st = r; break; case 1: p->dr = r; break; case 0: ; } return r; } Arb AB_CrArb(Arb r, Arb s, Arb d){ assert(r); AB_SetSS(r, s); AB_SetSD(r, d); return r; } int AB_Height(Arb a){ int hs, hd; if(!a) return 1; hs = AB_Height(a->st); hd = AB_Height(a->dr); return 1 + (hs > hd? hs: hd); } int AB_Depth(Arb n){ if(!n) return -1; return 1 + AB_Depth(n->pred); }

11

5.2. Arbori binari implementai cu pointeri simulai (cursori). Pointerii sunt simulai prin ntregi reprezentnd poziii ale valorilor n tablouri. Am pstrat numai pointerii generici la chei pentru a permite reprezentarea independent de tip Alocarea de memorie pentru tablouri se va face, din considerente de eficien, o singur dat, prin constructor. struct arb{ int n; void **key; int *stg; int *dr; int *pred; }; 5.3. Arbori binari reprezentai prin vectori. Acest mod de reprezentare este specific arborilor binari complei i va fi prezentat ulterior. Probleme propuse. 1. Scriei o funcie nerecursiv care care traverseaz un arbore binar n preordine afind n cursul traversrii cheile caractere, n dou variante a) folosind o stiv b) fr a folosi stiv. Indicaii: a) Se coboar pe subarborele stng, punnd subarborele drept n stiv (dac exist). La ntlnirea unui subarbore stng vid, se scoate subarborele drept din stiv. b) Se coboar, pornind din rdcin pe fiul stng pn n frunz, afind cheile traversate. Se revine trecndu-se pe frate drept sau tat n absena fratelui drept. Traversarea se consider ncheiat cnd se revine n rdcin din subarborele drept, sau nu exist subarbore drept. Se pot folosi funciile interfeei arborelui binar.

2. Scriei o funcie care numar nodurile frunze de pe un nivel dat al unui arbore binar. 3. Scriei o funcie avnd ca parametru un arbore expresie, care creaz un ir de caractere continnd reprezentarea expresiei astfel: - pentru un nod frunz se pune n irul de caractere reprezentarea extern a termenului - pentru un nod intern se pun n expresie "(", ss, operator, sd, ")". 4. a) Pentru un arbore binar, avnd cheile din noduri caractere, traversrile n inordine i
preordine sunt respectiv: ACZGYXBTDHMF i AXYCZGBDTFHM Reconstituii pe baza lor arborele binar. b) Scriei o funcie recursiv, avnd ca parametri irurile de caractere reprezentnd cele dou traversri, funcie care ntoarce adresa nodului rdcin al arborelui reconstituit 5. a) Pentru un arbore binar, avnd cheile din noduri caractere, traversrile n inordine i postordine sunt respectiv: ATZBYCGEHDFX i ZTYBGHEFDCAX Reconstituii pe baza lor arborele binar. b) Scriei o funcie recursiv, avnd ca parametri irurile de caractere reprezentnd cele dou traversri, funcie care ntoarce adresa nodului rdcin al arborelui reconstituit

12

6. a) Pentru un arbore binar, avnd cheile din noduri caractere, traversrile n inordine i pe niveluri (n lime) sunt respectiv:YEZAMCXTFBDG i MAXYCBZTDEFG Reconstituii pe baza lor arborele binar. b) Scriei o funcie recursiv, avnd ca parametri irurile de caractere reprezentnd cele dou traversri, funcie care ntoarce adresa nodului rdcin al arborelui reconstituit 7. Pentru reconstituirea unui arbore binar avnd chei caractere, folosind numai traversarea n preordine mai sunt necesare anumite informaii. Astfel fiecrui nod din traversare i se asociaz un caracter cu urmtoarele semnificaii: F nodul este frunz L nodul are numai fiu stnga R n odul are numai fiu dreapta 2 nodul are ambii succesori a) Dndu-se irurile de caractere MAYZEXCBTFDG i2LRLF2F2RFRF reconstituii arborele b) Scriei o funcie avnd ca parametri dou iruri de caractere coninnd traversarea n preordine i proprietile nodurilor traversate, funcie care ntoarce un pointer la rdcina arborelui reconstituit 8. Scriei o funcie nerecursiv de traversare a unui arbore binar folosind o stiv: a) n postordine b) n preordine 9. Definii o funcie nerecursiv care numr frunzele unui arbore binar folosind o stiv. 10. Scriei o funcie nerecursiv care numr nodurile frunze (noduri interne avnd ca fii noduri externe) ale unui arbore binar, folosind o stiv. Indicaie: Se numr nodurile frunze pe stnga, cobornd n subarborele stng i punnd subarborele drept n stiv. La ntlnirea unui subarbore stng vid se scoate subarborele drept din stiv. 10. Un arbore binar are chei caractere. Pentru acesta se dau dou iruri de caractere: traversrile n preordine i inordine. De exemplu, pentru arborele a / \ / \ b c / \ \ / \ \ d e f / / g traversarea n preordine este: abdecfg , iar cea n inordine este dbeacgf . Traversarea n postordine a arborelui este: debgfca a) Dndu-se traversrile n preordine: vaetilnrq i n inordine ateivnqrl, dai traversarea n postordine.

13

b) Scriei o funcie avnd ca parametri dou iruri de caractere (traversrile n preordine i inordine), care calculeaz ca rezultat un al treilea ir de caractere (traversarea n postordine. Indicaie: Pe baza traversrilor se reconstituie arborele, n felul urmtor: se parcurge traversarea n preordine de la stnga la dreapta. Caracterul curent (a) este rdcina unui arbore. Cei doi subarbori ai acestuia se gsesc n stnga i dreapta caracterului, n traversarea n inordine dbeacgf. a / \ dbe cgf Un subarbore este precizat prin cheile cuprinse ntre doi indici i i j n traversarea n inordine. Pentru fiecare subarbore, se determin, n acelai mod (recursiv) rdcina i subarborii. Se ias din recursivitate la ntlnirea unui nod vid. Arborele reconstituit este traversat n postordine. n exemplul de mai sus, rdcina arborelui a este preluat din preord[0]; ea se regsete n inord n poziia 3 i delimiteaz doi subarbori avnd chei n intervalele 0:2, respectiv 4:6. Urmtoarea cheie b este gsit n poziia 1 i delimiteaz subarborii cu chei n 0:0 i 2:2 . a / \ b c / \ \ d e gf Cheia urmtoare d este gsit n poziia 0 i nu are subarbori; la fel cheia e, aflat n poziia 2 nu are subarbori, etc. 11. Un arbore binar are chei caractere. Pentru acesta se dau dou iruri de caractere: traversrile n postordine i inordine. De exemplu, pentru arborele a / \ / \ b c / \ .\ / \ \ d e f / / g traversarea n postordine este: : debgfca, iar cea n inordine este dbeacgf . Traversarea n preordine a arborelui este abdecfg a) Dndu-se traversrile n postordine:otelnrauc i n inordine: toaenlrcu, dai traversarea n preordine. b) Scriei o funcie avnd ca parametri dou iruri de caractere (traversrile n postordine i inordine), care calculeaz ca rezultat un al treilea ir de caractere (traversarea n preordine. Indicaie: Pe baza traversrilor se reconstituie arborele, n felul urmtor: se parcurge traversarea n postordine de la dreapta la stnga. Caracterul curent (a) este rdcina unui arbore. Cei doi subarbori ai acestuia se gsesc n stnga i dreapta caracterului, n traversarea n inordine dbeacgf.

14

a / \ dbe cgf Pentru fiecare subarbore, se determin, n acelai mod (recursiv) rdcina i subarborii. Se ias din recursivitate la ntlnirea unui nod frunz. a a a / \ / \ / \ dbe c dbe c b c \ \ / \ \ gf f d e f / / g g Arborele reconstituit este traversat n postordine. n exemplul de mai sus, rdcina arborelui a este preluat din preord[n-1]; ea se regsete n inord n poziia 3 i delimiteaz doi subarbori avnd chei n intervalele 0:2, respectiv 4:6. Urmtoarea cheie c este gsit n poziia 4 i delimiteaz numai un subarbore drept cu chei n 5:6 Cheia urmtoare f este gsit n poziia 6 i are numai subarbore stng cu chei n 5:5; apoi cheia g, aflat n poziia 5 nu are subarbori, etc. Un arbore binar este reprezentat sub forma R(S D), n care R este cheia din rdcin (un caracter), iar S i D sunt subarborii stng i drept, definii recursiv ca arbori binari. Dac subarborele stng lipsete, arborele va fi reprezentat ca R(* D), iar dac subarborele drept lipsete, reprezentarea va fi R(S). Scriei o funcie recursiv, avnd ca parametru un arbore binar, care ntoarce ca rezultat un ir de caractere reprezentnd expresia cu paranteze corespunztoare arborelui. Un arbore binar este reprezentat sub forma R(S D), n care R este cheia din rdcin (un caracter), iar S i D sunt subarborii stng i drept, definii recursiv ca arbori binari. Dac subarborele stng lipsete, arborele va fi reprezentat ca R(* D), iar dac subarborele drept lipsete, reprezentarea va fi R(S). Scriei o funcie recursiv. avnd ca parametru un ir de caractere descriind un arbore binar, funcie care creaz arborele binar corespunztor i ntoarce un pointer la rdcina acestuia. Funcie pentru determinarea nlimii unui arbore binar, ca nlime maxim dintre cei doi subarbori, plus unu. Un arbore binar poate fi reprezentat sub forma R(S,D), n care R desemneaza cheia din rdcin, reprezentat printr-o liter, iar S i D sunt subarborii stng si drept. Daca subarborele stng lipsete, arborele va fi reprezentat sub forma R(*,D), iar daca subarborele drept lipseste, arborele binar va fi reprezentat ca R(S). Definii o funcie recursiva, creere_exp(Arb a, char* expr), avnd ca parametri un arbore binar i un ir de caractere, care creaz structura arborelui sub forma unei expresii cu paranteze si o depune n parametrul expr. De exemplu pentru arborele binar dat mai jos, funcia va crea n al doilea parametru expresia: a / \ b c \ a(b(*,d(e)),c)

15

d / e Operaia inversa, de creere n memorie a unui arbore binar pe baza unei expresii cu paranteze se exprima printr-o strategie divide et impera astfel: void creare_arb(nod*& a, char *expr) { char *es, *ed; if (expr) if (expr[0] != '*') { legare(a, expr[0]); separa(expr, es, ed); creare_arb(a->st, es); creare_arb(a->dr, ed); } } n care: - funcia "legare" ataseaza la un pointer dat ca prim argument un nod avand drept cheie al doilea parametru - funcia "separa" creeaza din arborele descris prin primul argument sir de caractere reprezentarile prin formule (siruri de caractere) ale celor doi subarbori (stang si drept). Scriei funciile "legare()" si "separa()", care implementeaza funcia de creere a arborelui binar. Funcie pentru afiarea arborelui sub forma unei expresii cu paranteze (pornind de la explorarea prefixat). De exemplu, pentru arborele creat cu prima funcie i cheile "msdpetbh", prin aplicarea acestei funcii se obine expresia: m(d(b,e( ,h)),s(p,t)) Funcie pentru desenarea structurii unui arbore binar intr-o folosind o traversare in latime. Exemplu: m d s b e p t h Funcie pentru eliminarea unui nod cu valoare dat dintr-un arbore binar de cutare. Scriei o funcie main() n care creai, pe baza unui ir de caractere citit, un arbore binar de cutare, l afiai, determinai expresia cu paranteze i o afiai, determinai i afiai numrul de noduri i nlimea arborelui i apoi tergei cte un nod, afind de fiecare dat arborele rmas. Pentru reconstituirea unui arbore binar avnd chei caractere, folosind numai traversarea n preordine mai sunt necesare informaii asupra tipului nodurilor. Astfel fiecrui nod din traversare i se asociaz un caracter cu urmtoarele semnificaii: F nodul este frunz L nodul are numai fiu stnga R nodul are numai fiu dreapta 2 nodul are ambii succesori

16

a) Dndu-se irurile de caractere MAYZEXCBTFDG i2LRLF2F2RFRF reconstituii arborele b) Scriei o funcie Arb RecAB(char *ch, char *tip), avnd ca parametri dou iruri de caracteren coninnd cheile caractere, la traversarea n preordine i proprietile nodurilor traversate, funcie care ntoarce un pointer la rdcina arborelui reconstituit. Indicaie: Se aloc memorie pentru nodul rdcin, care devine nod curent i se pune cheia n acesta. Se repet pentru toate celelalte noduri: -se verific numrul de succesori ai nodului curent: dac sunt 2 se aloc memorie pentru dou noduri care se leag la arbore i se pune adresa fiului drept n stiv. Nodul stng devine nod curent. dac exist un singur succesor (stng sau drept), se face cellalt NULLse aloc memorie pentru nod, care devine nod curent

- dac este frunz, se scoate din stiv o adres de nod, care devine nod curen - se pune cheia n nodul curent Un arbore binar are drept chei numere ntregi. Definii o funcie cu semntura: Arb suma(Arb a); care adun la cheia din fiecare nod suma cheilor din nodurile descendente. Funcia ntoarce un arbore cu aceeai structur, dar cu cheile modificate. .Scriei o funcie void Print(Arb a, int h); care afieaz cheile caractere ale arborelui binar a, astfel: h -cheia din rdcin este afiat pe o linie, fiind precedat de spaii 2 -cheia dintr-un alt nod este precedat de: h L d 1 spaii, dac nodul este fiu stnga al printelui, sau 2 h L d 1 spaii, dac nodul este fiu dreapta al printelui. 2 L este indentarea (poziionarea) nodului printe, iar d adncimea nodului curent. Indicaie: Nodurile se vor vizita pe niveluri (deoarece nodurile de pe un nivel se vor afia pe aceeai linie), ceea ce impune folosirea unei cozi. La afiarea cheii dimtr-un nod se pune poziia n nod, pentru a fi folosit de nodurile fii. Sugerm ca informaia din nod s fie de tipul: typedef struct{ char cheie; int poz; } info;

Arbori generali (multici).


Interfaa TAD Arbore General. Introducem tipul Arb pentru referirea la un nod oarecare din arbore, inclusiv nodul rdcin. Precizarea tipului depinde de modul de reprezentare al arborelui. struct arb; typedef struct arb *Arb; Arb A_New (Arb r); void A_Delete(Arb *a); int A_Size(Arb a); // creaz un arbore cu rdcina r //distruge arborele a // determin numrul de noduri al arborelui

17

Arb A_Root(Arb a); // d rdcina arborelui la care aparine nodul a int A_Depth(Arb a, Arb n); // adncimea vrfului n din arborele a int A_Height(Arb a); //nlime arbore a Operaii specifice nodurilor Arb A_Pred(Arb n); // d nodul printe a nodului n(NULL pentru rdcin) Arb A_PrimFiu(Arb n); //primul fiu al nodului n Arb A_FrateUrm(Arb n); //fratele urmtor al nodului n Arb A_FratePrec(Arb n); //fratele precedent al nodului n int A_Leaf(Arb n); // 1 dac n este frunz void A_InsPrimFiu(Arb a, Arb n); //insereaz arborele n ca prim fiu a lui a void A_InsFrateUrm(Arb a, Arb n); //insereaz arborele n ca frate urmtor lui a void A_RemovePrimFiu(Arb a); //terge primul fiu a lui a void A_RemoveFrateUrm(Arb a); //terge frate urmtor a lui a void A_Modif(Arb a, void *ch); //schimb cheia din nodul a 3. Implementarea arborilor generali. Sunt dou alternative de reprezentare: cu tablouri i cu pointeri
C

prin tabel de cursori (pointeri simulai) la predecesori Reprezentare se face prin 2 tablouri: un tablou de chei asociate celor n noduri: void **ch; un tablou cu poziiile predecesorilor nodurilor: int *pred; struct arb{ int n; void **ch; int *pred; }; n acest mod unele dintre operaii se fac mai puin eficient (deplasarea prin arbore i adugarea de noi noduri se fac simplu, pe cnd celelalte operaii sunt mai complicate). Reprezentarea este folosit pentru colecii de mulimi disjuncte i va fi prezentat n acel context. cu tablouri, prin liste de adiacene Elementul lsuc[k] din tabloul de adiacene lsuc este un pointer la lista de succesori ai nodului k..Pentru a permite i accesul n sus trebuie adugat un tabel de predecesori. struct nodl{ int crs; struct nodl *next; };

18

struct arb{ int n; void **ch; struct nodl **lsuc; int *pred; };
ch pred lsuc 3

-1

prin tablourile PrimFiu i UrmFrate Pentru a permite deplasare rapid n sus n arbore trebuie adugat i un tabel de predecesori. struct arb{ void **Chei; int *PrimFiu; int *UrmFrate; int *Pred; };
ch pred prim_fiu urm_frate
0

-1

-1

-1

-1

-1

-1

-1

-1

-1

4.cu pointeri, cu list de succesori i pointer la predecesor Vom folosi dou structuri, una definind nodurile, cealalt lista de adiacene asociat unui nod. struct ls; struct nod{ void *ch; struct nod *pred;

19

struct ls };

*primfiu:

struct ls{ struct nod *fiu; struct ls *urmfrate; };


C

text

text

text

text

text

text

text

text

text

text text

text

text

text

E X

text

text

4. Traversarea arborilor generali. Traversarea unui arbore presupune vizitarea fiecrui nod, o singur dat, ntr-o anumit ordine. Exist dou tipuri de traversri: traversare n adncime traversare n lime Traversarea n adncime ncearc, poirnind din rdcin, vizitarea nodului cel mai ndeprtat, care trebuie s fie fiu al unui nod deja vizitat. Traversarea n adncime se exprim printr-un algoritm recursiv sau folosete o stiv. Un algoritm generic de traversare n adncime are forma: void DFS(nod T) PreVizita(T) for(fiecare fiu c a lui T) DFS(c) PostVizita(T); Dac se folosete Previzita() avem o traversare n preordine, n timp ce folosirea PostVizita() definete o traversare n postordine.

20

Traversarea n lime viziteaz mai nti nodurile cele mai apropiate, care nu au fost nc vizitate. Traversarea n lime (sau pe niveluri) se exprim printr-un algoritm nerecursiv care folosete o coad. void BFS(nod T) Coada Q=Q_New () Enq(Q, T) while(!Q_Empty(Q)) nod u=Deq(Q) Vizitare(u) for(fiecare fiu c a lui u) Enq(Q, c)

21

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