Sunteți pe pagina 1din 10

Laborator 4 Arbori

4.1 Probleme rezolvate

Un arbore reprezint o colecie de noduri relaionate ntre ele n mod ierarhic. Fiecare nod are asociat
o anumit informaie, de exemplu informaii despre angajaii unei companii (vezi Figura 1), arborele
reprezentnd n acest caz ierarhia de organizare a companiei.

n funcie de poziia lor n arbore, nodurile pot fi de mai multe tipuri:


- printele unui nod: nodul care se afl imediat deasupra nodului n cauz;
- copilul unui nod: nodul care urmeaz imediat sub acesta;
- nod rdcin: nod unic n arbore, care nu are nici un printe (primul nod);
- noduri frunz: nod care nu are nici un copil (ultimele noduri).

Andrei Alexandru nod rdcin


Director

Matei Rzvan Tudor Alin Popescu Victor


Director Tehnic Director Economic Director Vnzri

Radu Vlad Ion Drago Nicolae Ioana Marin Andreea


Manager IT Manager Com. Contabil ef Manager Vnzri

noduri frunz

Figura 1 Exemplu de structur arborescent.

n exemplul de mai sus, Andrei Alexandru este rdcina arborelui, deoarece nu are nici un printe.
Copiii si direci sunt: Matei Rzvan, Tudor Alin i Popescu Victor, care au la rndul lor ali copii.
Acetia din urm sunt frunze ale arborelui, deoarece nu mai au ali copii.

Orice arbore are urmtoarele proprieti:


- Exist o singur rdacin;
- Toate nodurile au exact un singur printe, excepie fcnd nodul rdcin;
- Nu exist cicluri, ceea ce nseamn c, pornind de la un nod oarecare nu se poate parcurge un
anumit traseu, astfel nct s se ajung napoi la nodul de plecare.

Bogdan Boteanu, Bogdan Ionescu, Structuri de Date i Algoritmi 1


Un caz particular de arbori, sunt arborii binari. Acetia au proprietatea c fiecare nod are maxim 2
copii: fie copilul din stnga i copilul din dreapta, fie un singur copil, fie niciunul.

Fiecare nod al unui arbore binar reine informaia propriu-zis, precum i adresele de memorie a
copiilor din stnga i din dreapta, dac acetia exist (vezi Figura 2). Structura unui astfel de nod poate
fi de tipul urmtor:

struct NOD
{
int informatie;
struct NOD *adresa_copil_dreapta;
struct NOD *adresa_copil_stanga;
}

adresa: 000 100 inf. 200 nivel 1

adresa: 100 300 inf. NULL adresa: 200 NULL inf. NULL nivel 2

adresa: 300 NULL inf. NULL nivel 3

Figura 2 Exemplu de arbore binar

Arborele binar de cutare este un arbore binar particular care stocheaz suplimentar informaiilor
existente o valoare numeric cu rol de ordonare. Acesta are proprietatea c, pentru oricare nod n,
fiecare dintre descendenii din subarborele din stnga are valoarea numeric mai mic dect a nodului
n, iar fiecare din descendenii din subarborele din dreapta va avea valoarea informaiei mai mare sau
egal. Un exemplu de arbore binar de cutare este ilustrat n Figura 3.

20

10 30

5 15 78

Figura 3 Exemplu de arbore binar de cutare

Bogdan Boteanu, Bogdan Ionescu, Structuri de Date i Algoritmi 2


n cuprinsul acestui laborator se va implementa un arbore binar de cutare, mpreun cu operaiile
uzuale ce se pot realiza cu acesta, preciznd c pentru oricare alt tip de arbore implementrile se
realizeaz n mod similar.

Operaiile uzuale sunt:


- inserarea unui nod n arbore;
- parcurgerea arborelui;
- tergerea unui subarbore.

P4.1 S se realizeze un program ce permite implementarea unui arbore binar de cutare, precum i a
operaiilor uzuale cu acesta. Programul permite afiarea pe ecran a unui meniu cu urmtoarele
operaii posibile:
- [1] Citirea unei valori de la tastatur i inserarea acesteia n arbore;
- [2] Afiarea arborelui n preordine;
- [3] Afiarea arborelui n inordine;
- [4] Afiarea arborelui n postordine;
- [5] tergerea unui subarbore, descendent dintr-un nod specificat;
- [6] Cutarea unui nod n arbore;
- [0] Ieire din program.
Fiecare opiune din meniu va fi implementat folosind funcii.

Rezolvare:

#include<stdio.h>
#include<stdlib.h>

/* definire structura arbore */


struct NOD
{
int x;
struct NOD *NOD_stanga;
struct NOD *NOD_dreapta;
};

/* functie creare nod nou */


struct NOD *creare_nod(int x)
{
struct NOD *nod_nou;

/* alocare memorie nod*/


nod_nou=(struct NOD *)malloc(sizeof(struct NOD));

Bogdan Boteanu, Bogdan Ionescu, Structuri de Date i Algoritmi 3


if (nod_nou==NULL)
{
printf("Eroare: Memoria nu a putut fi alocata! \n");
return NULL;
}

/* initializare informatii */
nod_nou->x=x;
nod_nou->NOD_stanga=NULL;
nod_nou->NOD_dreapta=NULL;

return nod_nou;
}

/* inserare nod in arbore */


struct NOD *inserare_nod(struct NOD *prim, int x)
{
struct NOD *nod_nou, *nod_curent, *nod_parinte;

nod_nou=creare_nod(x);

if (prim==NULL)
{
/* arborele este vid */
prim=nod_nou;
printf("A fost adaugat primul nod: %d. \n", prim->x);
return prim;
}
else
{
/* pozitionare in arbore pe parintele nodului nou */
nod_curent=prim;

while (nod_curent!=NULL)
{
nod_parinte=nod_curent;

if (x<nod_curent->x) /* parcurgere spre stanga */


nod_curent=nod_curent->NOD_stanga;
else /* parcurgere spre dreapta */
nod_curent=nod_curent->NOD_dreapta;
}

/* creare legatura nod parinte cu nodul nou */


if (x<nod_parinte->x)
{
/* se insereaza la stanga nodului parinte */
nod_parinte->NOD_stanga=nod_nou;
printf("Nodul %d a fost inserat la stanga nodului %d. \n",
x, nod_parinte->x);
}

Bogdan Boteanu, Bogdan Ionescu, Structuri de Date i Algoritmi 4


else
{
/* se insereaza la dreapta nodului parinte */
nod_parinte->NOD_dreapta=nod_nou;
printf("Nodul %d a fost inserat la dreapta nodului %d.\n",
x, nod_parinte->x);
}

return prim;
}
}

/* parcurgere arbore in preordine */


void afisare_preordine(struct NOD *prim)
{
if (prim!=NULL)
{
/* parcurgere radacina, stanga, dreapta */
printf("%d \n", prim->x);
afisare_preordine(prim->NOD_stanga);
afisare_preordine(prim->NOD_dreapta);
}
}

/* parcurgere arbore in inordine */


void afisare_inordine(struct NOD *prim)
{
if (prim!=NULL)
{
/* parcurgere stanga, radacina, dreapta */
afisare_inordine(prim->NOD_stanga);
printf("%d \n", prim->x);
afisare_inordine(prim->NOD_dreapta);
}
}

/* parcurgere arbore in postordine */


void afisare_postordine(struct NOD *prim)
{
if (prim!=NULL)
{
/* parcurgere stanga, dreapta, radacina */
afisare_postordine(prim->NOD_stanga);
afisare_postordine(prim->NOD_dreapta);
printf("%d \n", prim->x);
}
}

Bogdan Boteanu, Bogdan Ionescu, Structuri de Date i Algoritmi 5


/* stergerea unui arbore sau subarbore */
struct NOD *stergere_arbore(struct NOD *tmp)
{
if (tmp!=NULL)
{
stergere_arbore(tmp->NOD_stanga);
stergere_arbore(tmp->NOD_dreapta);
free(tmp);
}

return NULL;
}

/* cautarea unui nod dorit */


struct NOD *cauta_nod(struct NOD *tmp, int x)
{
if (tmp!=NULL)
{
if (x==tmp->x)
{
printf("Nodul a fost gasit. \n");
return tmp;
}
else if (x<tmp->x)
return cauta_nod(tmp->NOD_stanga, x);
else
return cauta_nod(tmp->NOD_dreapta, x);
}
else
{
printf("Nodul dorit nu exista in arbore.\n");
return NULL;
}
}

int main()
{
struct NOD *prim=NULL, *nod_gasit;
char operatie;
int x;

printf("MENIU: \n");
printf("[1] Inserare nod in arbore \n");
printf("[2] Afisare arbore preordine \n");
printf("[3] Afisare arbore inordine \n");
printf("[4] Afisare arbore postordine \n");
printf("[5] Stergere arbore \n");
printf("[6] Cautare nod in arbore \n");
printf("[0] Iesire din program \n");

Bogdan Boteanu, Bogdan Ionescu, Structuri de Date i Algoritmi 6


do
{
printf("\nIntroduceti operatie: ");
operatie=getche();

printf("\n");
switch (operatie)
{
case '1':
printf("#Inserare nod in arbore# \n");
printf("Introduceti valoarea nodului care va fi inserat: ");
scanf("%d", &x);
prim=inserare_nod(prim, x);
break;

case '2':
printf("#Afisare arbore preordine# \n");
if (prim==NULL)
printf("Atentie: Arborele este gol.");
else
afisare_preordine(prim);
break;

case '3':
printf("#Afisare arbore inordine# \n");
if (prim==NULL)
printf("Atentie: Arborele este gol.");
else
afisare_inordine(prim);
break;

case '4':
printf("Afisare arbore postordine: \n");
if (prim==NULL)
printf("Atentie: Arborele este gol.");
else
afisare_postordine(prim);
break;

case '5':
printf("#Stergere arbore# \n");
if (prim==NULL)
printf("Atentie: Arborele este gol.");
else
{
printf("Introduceti valoarea nodul al carui arbore va fi sters: ");
scanf("%d", &x);
nod_gasit=cauta_nod(prim, x);
if (nod_gasit!=NULL)
{
nod_gasit->NOD_stanga=
stergere_arbore(nod_gasit->NOD_stanga);

Bogdan Boteanu, Bogdan Ionescu, Structuri de Date i Algoritmi 7


nod_gasit->NOD_dreapta=
stergere_arbore(nod_gasit->NOD_dreapta);
printf("Arborele a fost sters. \n");
}
}
break;

case '6':
printf("#Cautare nod in arbore# \n");
if (prim==NULL)
printf("Atentie: Arborele este gol.");
else
{
printf("Introduceti valoarea nodului: ");
scanf("%d", &x);
cauta_nod(prim, x);
}
break;

case '0':
printf("Iesire din program \n");
stergere_arbore(prim);
system("PAUSE");
return 0;
break;

default:
printf("Operatie invalida \n");
}
} while(1);
}

Discuie:
- Acest program implementeaz un arbore binar de cutare, ale crui noduri conin ca informaie
numere ntregi. Fiecare nod al arborelui este de tipul struct NOD. Nodurile relaioneaz
ntre ele prin intermediul pointerilor struct NOD *NOD_stanga i struct NOD
*NOD_dreapta, care stocheaz adresele de memorie ale copiilor acestora;
- Funcia struct NOD *creare_nod(int x) este o funcie general, prin intermediul
creia se creeaz i se aloc memorie pentru un nod nou. Coninutul acestuia este iniializat cu
informaia primit ca parametru de intrare (int x), iar locaiile copiilor sunt iniializate cu
NULL, deoarece n prima faz acest nod nu este inserat n arbore. Adresa nodului nou creat
este returnat printr-un pointer la structura NOD;

- Inserarea unui nod n arbore se face cu ajutorul funciei struct NOD*


inserare_nod(struct NOD *prim, int x). Aceasta primete ca parametru de
intrare adresa de memorie a primului nod (rdcina arborelui), dac aceasta exist, i valoarea

Bogdan Boteanu, Bogdan Ionescu, Structuri de Date i Algoritmi 8


nodului ce se dorete a fi inserat. Dac nodul rdcin nu exist, acesta se creeaz prin apelarea
funciei creare_nod() i se returneaz adresa lui. Dac nodul rdcin exist, nodul nou
creat se insereaz n arbore pe poziia corespunztoare, n funcie de valoarea acestuia. Pentru
gsirea poziiei, este necesar o parcurgere a arborelui pornind de la nodul rdcin i vizitnd
nodurile, pn cnd se ajunge la un nod frunz. Decizia pentru vizitarea copilului unui nod
curent se face n funcie de valoarea nodului ce se dorete a fi inserat, astfel: dac valoarea
nodului de inserat este mai mic dect valoarea nodului curent, atunci se va vizita copilul
nodului curent din stnga, iar dac nu, se va vizita copilul din dreapta. Dup luarea acestei
decizii, nodul curent devine nodul vizitat, i procesul se reia. Parcurgerea acestor noduri s-a
implementat folosind structura repetitiv while. Atunci cnd se ajunge la un nod frunz (nu
are nici un copil), nodul creat se insereaz n arbore prin iniializarea uneia dintre cele 2 adrese
de legtur a acestuia cu locaia nodului creat: nod_parinte->NOD_stanga=nod_nou
sau nod_parinte->NOD_dreapta=nod_nou. Decizia de alegere a acestei adrese
(stnga sau dreapta) se face dup aceeai regul de comparaie definit mai sus. Astfel, nodul
frunz devine nod printe pentru noul nod, iar noul nod devine nod frunza n arbore. n final se
returneaz adresa nodului rdcin, care este necesar doar pentru cazul n care arborele este
gol, n celelalte cazuri ea rmnnd neschimbat;

- Afiarea arborelui se realizeaz folosind cele trei tipuri de parcurgere a unui arbore: preordine,
inordine i postordine. Aceste denumiri corespund modului n care se viziteaz rdcina:

preordine: se viziteaz mai nti rdcina, copilul (copiii) din stnga, iar apoi copilul
(copiii) din dreapta;

inordine: se viziteaz mai nti copilul (copiii) din stnga, apoi rdcina i copilul
(copiii) din dreapta;

postordine: se viziteaz copilul (copiii) din stnga, apoi copilul (copiii) din dreapta i
apoi rdcina.

Fiecare tip de afiare este implementat ntr-o funcie separat, ce primete ca parametru de
intrare adresa de memorie a nodului rdcin: void afisare_preordine(struct
NOD *prim), void afisare_inordine(struct NOD *prim), void
afisare_postordine(struct NOD *prim). Funciile sunt recursive, astfel nct este
important s nelegem ce se ntmpl pe unul din niveluri (de exemplu pe primul), pe restul
procedndu-se identic;

- Cutarea unui nod n arbore se realizeaz cu ajutorul funciei recursive struct NOD*
cauta_nod(struct NOD *tmp, int x) ce primete ca parametru de intrare adresa de
memorie a nodului rdcin i valoarea nodului ce se dorete a fi cutat. Funcia se
autoapeleaz, vizitnd nodurile n funcie de valoarea nodului cutat, dup aceeai regul de
comparaie definit n cazul inserrii unui nod. Condiia de oprire este ndeplinit fie atunci

Bogdan Boteanu, Bogdan Ionescu, Structuri de Date i Algoritmi 9


cnd nodul este gsit, caz n care se returneaz adresa acestuia, fie cnd s-a ajuns la un nod
frunza i nodul nu a fost gsit, caz n care se returneaz NULL;

- Funcia struct NOD* stergere_arbore(struct NOD *tmp) permite tergerea


arborelui descendent al unui nod specificat. Pornind de la adresa acestui nod, se viziteaz i se
terge recursiv fiecare copil, tergndu-se i legtura acestuia cu printele. Acest lucru se
realizeaz prin atribuirea valorii NULL returnate de funcie n iteraia precedent ctre printe.
Pentru a gsi adresa de memorie a nodului specificat este necesar apelarea funciei
cauta_nod(), lucru ce se realizeaz n funcia main(), imediat nainte de tergerea
propriu-zis;

- Meniul programului este implementat n funcia main(), prin intermediul cruia utilizatorul
poate apela funciile definite mai sus.

4.2 Probleme propuse

1 S se modifice corespunztor programul anterior astfel nct s includ n meniu i posibilitatea de


calcul a numrului de frunze din arbore.

2 S se modifice corespunztor programul anterior astfel nct s includ n meniu i posibilitatea de


calcul a numrului maxim de niveluri din arbore.

3 Implementai operaiile de afiare, tergere i cutare a nodurilor unui arbore folosind funcii
nerecursive.

4 S se modifice corespunztor programul anterior astfel nct s includ n meniu i posibilitatea


tergerii complete a arborelui (pornind de la radacin).

Bogdan Boteanu, Bogdan Ionescu, Structuri de Date i Algoritmi 10