You are on page 1of 15

Laborator de Structuri de Date si Algoritmi – Lucrarea nr.

Arbori de grad oarecare. Arbori binari


I. Arbori de grad oarecare
I.1. Reprezentarea standard
I.2. Reprezentarea FIU-FRATE
I.3. Reprezentarea prin noduri de dimensiune variabila

II. Arbori binari


II.1. Reprezentarea standard
II.2. Parcurgeri
II.3. Exemplu: calcularea valorii maxime dintr-un arbore binar

I. Arbori de grad oarecare


I.1. Reprezentarea "standard"
In reprezentarea standard in fiecare nod al arborelui, pe linga informatia utila se
memoreaza informatii de inlantuire care indica descendentii.
Intr-o prima varianta de reprezentare, fiecare nod este compus din informatia utila si
un vector de dimensine fixa, in care se memoreaza legaturilor de tip pointer la
descendenti. Dimensiunea acestui vector este data gradul maxim al nodurilor arborelui.
Declaratiile de tip folosite de aceasta reprezentare sint:

struct Nod{
Atom data;
Nod* vDesc[GRMAX];
};

Un nod va ocupa o zona de memorie de dimensiune fixa:

In figura de mai jos am reprezentat inlantuirile pentru arborele:


Laborator de Structuri de Date si Algoritmi – Lucrarea nr. 5

In aceasta reprezentare, daca p este un pointer la un nod, descendentul i al nodului va fi


identificat direct prin pointerul

p->vDesc[i]

Intr-un arbore cu un numar N de noduri vor exista N-1 muchii, deci in total N-1 elemente
din vectorii de pointeri la descendenti sint ocupate cu informatie utila. Se obtine raportul:

N −1 1
=
N * GRMAX GRMAX

care indica gradul de utilizare eficienta a memoriei. In consecinta aceasta reprezentare


este acceptabila numai pentru arbori de grad mic.

I.2. Reprezentarea "FIU-FRATE"

Pentru a obtine o utilizare mai eficienta a memoriei, pot fi utilizate listele de


descendenti. Fiecare nod va contine pe linga informatia utila, doi pointeri: unul va indica
lista cu descendentii sai iar cel de-al doilea urmatorul nod din lista de descendenti din
care face parte.

struct Nod {
Atom data;
Nod* desc;
Nod* next;
};
Laborator de Structuri de Date si Algoritmi – Lucrarea nr. 5

In aceasta varianta arborele de mai sus va avea urmatoarea reprezentare:

Avind in vedere semnificatia pointerilor continuti intr-un nod aceasta reprezentare se mai
numeste "reprezentarea FIU-FRATE".
In aceasta reprezentare, daca p este un pointer la un nod, identificarea descendentul i
al nodului va necesita parcurgerea listei inlantuite a descendentilor, lista care incepe cu:
p->desc

I.3. Reprezentarea prin noduri de dimensiune variabila

O a treia solutie de reprezentare combina economia de memorie cu avantajele


accesarii descendentilor pe baza de index. Aceasta solutie se bazeaza pe posibilitatea de a
aloca blocuri de memorie de lungime precizata.
Vom considera aceeasi declaratie pentru tipul Nod ca si in prima varinata de
reprezentare, adugind in plus un cimp in care sa se memoreze gradul nodului.

struct Nod{
Atom data;
int grad;
Nod* vDesc[GRMAX];
};

Economia de memorie se va realiza prin alocarea unor zone de memorie de lungime


variabila, adaptata gradului fiecarui nod.

Iata cum vor arata:

- Un nod de grad 3:
Laborator de Structuri de Date si Algoritmi – Lucrarea nr. 5

- Un nod de grad 1:

- Un nod terminal:

Pentru a realiza economia de memorie este necesar ca la alocarea spatiului pentru un nod
sa se cunoasca numarul de descendenti si in functie de acest numar sa se aloce spatiul
necesar pentru vectorul de descendenti. Iata cum trebuie scrisa functia make_nod care
aloca spatiu pentru un nod de grad dat:

Nod* make_nod(int grad)


{
Nod *p=(Nod*)malloc(sizeof(Nod)-(GRMAX-grad)*sizeof(Nod*));
p->grad = grad;
return p;
}

Utilizind operatorul new, specific limbajului C++, alocarea va avea forma:

Nod* p = (Nod*) new char[sizeof(Nod)-(GRMAX-grad)*sizeof(Nod*)];

Iata cum va arata, in aceasta varianta, reprezentarea arborelui dat:

TEMA 1

Functia

Nod* creareArbore();
Laborator de Structuri de Date si Algoritmi – Lucrarea nr. 5

din AnexaA citeste de la intrare o expresie aritmetica, cu paranteze, care contine operanzi
de o cifra si operatorii + si *, si creaza arborele de grad oarecare asociat expresiei. De
exemplu:

1+2*3+4*(5+6) i se asociaza arborele:

Arborele este reprezentat dupa metoda 3, tipul Atom fiind echivalat cu tipul int (vezi
Anexa A).

Se cere:
■ Sa se determine si sa se afiseze gradul arborelui.
■ Sa se afiseze valoarea tuturor operanzilor utilizati in expresie (fara operatori).
■ Sa se evalueze expresia si sa se afiseze rezultatul.

II. Arbori binari


1. Reprezentarea standard
In reprezentarea standard, un nod al arborelui este o structura cu un cimp continind
eticheta nodului (data) si doua cimpuri pointeri la cei doi descendenti (lchild si rchild):

struct Nod
{
type data;
Nod* stg, *drt;
};

Astfel, arborele:

va avea urmatoarea reprezentare:


Laborator de Structuri de Date si Algoritmi – Lucrarea nr. 5

Pentru a putea prelucra un arbore este suficient sa cunostem un pointer la nodul radacina.
Valoarea nil pentru acest pointer va semnifica un arbore vid.

II.2. Pacurgeri
Un arbore binar poate fi privit conform urmatoarei scheme recursive:

rad = radacina
SAS = SubArbore Sting
SAD = SubArbore Drept

Pe aceasta schema se definesc cele trei moduri de parcurgere a arborelui:

PREORDINE : rad SAS SAD


Se prelucreaza mai intii radacina apoi se parcurg in preordine subarborii sting si drept.

INORDINE : SAS rad SAD


Se parcurge in inordone subarborele sting, se prelucreaza radacina si apoi se parcurge in
inordine subarborele drept.

POSTORDINE : SAS SAD rad


Se parcurg mai intii in postordine subarborii sting si drept apoi se prelucreaza radacina.

Pentru arborele:
Laborator de Structuri de Date si Algoritmi – Lucrarea nr. 5

cele trei parcurgeri prelucreaza nodurile in ordinea:

PREORDINE: ABDCEF
INORDINE: BDAECF
POSTORDINE: DBEFCA

Putem realiza aceste parcurgeri utilizind subrutine recursive. De exemplu:

void PREORDINE(pNod p);


{
if (p!=NULL){
┌───────────────────┐
│ prelucreaza(*p); │
└───────────────────┘
PREORDINE(p->lchild);
PREORDINE(p->rchild);
}
}

sau

void PREORDINE(Nod* p);


{
┌──────────────────┐
│ prelucreaza(*p); │
└──────────────────┘
if(p->stg!=NULL) PREORDINE(p->stg);
if(p->drt!=NULL) PREORDINE(p->drt);
}

A doua varianta nu poate fi aplicata unui arbore vid, in timp ce prima trateaza corect
arborele vid, in schimb executa un apel recursiv in plus pentru fiecare legatura care este
NULL.

II.3. Exemplu - Calcularea valoarii maxime dintr-un arbore binar


Varianta 1

char max ; // max este variabila globala

void CautaMax(Nod* p)
{/* ----------------Parcurgere preordine Varianta 2*/
if (p!=NULL){
if (p->data>max) max=p->data;
Laborator de Structuri de Date si Algoritmi – Lucrarea nr. 5

CautaMax(p->stg);
CautaMax(p->drt);
}
}

char ValMax(Nod* p)
{
max = 0;
CautaMax(rad);
return max;
}

Functia Valmax apeleaza o procedura recursiva CautaMax care face o parcurgere


prin arbore testind valoarea fiecarui nod. La sfirsitul parcurgerii, variabila "max", care
este o variabila globala (externa) pentru procedura recursiva, si care a fost initializata cu
cea mai mica valoare de tip char, va contine valoarea maxima a etichetelor din arbore.

Varianta 2

Pornind de la schema:

stabilim urmatoarea definitie recursiva:

ValMax(arbore) = max(rad, ValMax(SAS), ValMax(SAD))

Apelurile ValMax(SAS) si ValMax(SAD) se vor executa numai daca subarborii nu


sint vizi.

Iata implementarea:

char max(char v1,char v2)


{
if(v1>=v2) return v1;
else return v2;
}

char ValMax(pNod rad)


{
char vmax;
Laborator de Structuri de Date si Algoritmi – Lucrarea nr. 5

vmax = rad->data;
if rad->lchild!=NULL
vmax = max(vmax,ValMax(rad->lchild));
if rad->rchild!=NULL
vmax = max(vmax,ValMax(rad->rchild));
return vmax;
}

Aceasta varianta nu se poate aplica unui arbore vid, dar are avantajul ca se poate aplica si
in cazuri in care nu exista o valoare de eticheta pentru nod mai mica decit toate etichetele
posibile (cum am folosit mai sus, valoarea 0).

TEMA 2

1. Modulul ARBORE_BINAR.CPP (vezi ANEXA B) contine declaratiile tipurilor:

struct Nod {
char data;
struct Nod *stg, *drt;
}

si functia:

Nod* creareArboreB();

care citeste un arbore specificat conform urmatoarei diagrame de sintaxa, si intoarce


pointer la radacina arborelui citit.

In diagrama: '-' -semnifica un arbore vid;


nume -este eticheta unui nod formata dintr-o litera.

Exemple: Arborele vid: -


Laborator de Structuri de Date si Algoritmi – Lucrarea nr. 5

Sa se scrie si sa se testeze urmatoarele subrutine. Incercati pe rind, pentru fiecare, cele


doua variante de abordare prezentate in exemplul cu aflarea valorii maxime):

- Sa se afiseze continutul arborelui in INORDINE.


- Sa se afiseze continutul arborelui in POSTORDINE.
- O functie pentru determinarea adincimii arborelui.
- O functie pentru determinarea numarului de noduri din arbore.
- O functie pentru determinarea numarului de frunze ale arborelui.
- Sa se afiseze toate nodurile care au valoarea din radacina mai mare decit toate
valorile din subarborii descendenti.
- Sa se afiseze toate nodurile pentru care toate valorile continute in subarborele sting
sint mai mici decit toate valorile continute in subarborele drept.
- Pentru fiecare nod sa se comute subarborele sting cu cel drept si sa se afiseze
continutul arborelui in forma cu paranteze.

2. Sa se scrie un program care citeste expresii formate din operanzi, numere intregi de o
cifra, si operatorii + si *. Creeaza arborele expresiei si calculeaza valoarea expresiei pe
arbore.

De exemplu expresia: (1+3)*4*(5+6)


este citita in arborele:
Laborator de Structuri de Date si Algoritmi – Lucrarea nr. 5

Anexa A

Fisierul Arbore.h
#ifndef _ARBORE_H_
#define _ARBORE_H_

#define DIM_EXPR 100


#define GRMAX 20

#define Atom int

struct Nod
{
Atom data;
int grad;
Nod* vDesc[GRMAX];
};

Nod* creareArbore();

#endif

Fisierul Arbore.cpp

#include "arbore.h"
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <conio.h>
#include <stdio.h>
#include <iostream.h>

Nod* parse(char buffer[], int start, int end);

Nod* creareArbore()
{
char buffer[DIM_EXPR];

cin >> buffer;


int length = strlen(buffer);

Nod* n = parse(buffer, 0, length-1);


return n;
}
Laborator de Structuri de Date si Algoritmi – Lucrarea nr. 5

Nod* parse(char buffer[], int start, int end)


{
int openP = 0;
int indici[GRMAX];
int k = 0;

openP = 0;
for (int i = start; i <= end; i++)
{
if ( buffer[i]=='(' )
openP ++;
if ( buffer[i]==')' )
openP --;
if ( buffer[i]=='+' )
{
if ( openP > 0)
continue;
indici[k++] = i;
}
}
if ( k > 0 )
{
Nod *p=(Nod*) new char[sizeof(Nod)-(GRMAX-k+1)*sizeof(Nod*)];
p->grad = k+1;
p->data='+';
p->vDesc[0] = parse(buffer, start, indici[0]-1);
for (int j = 1; j < p->grad - 1; j++)
{
p->vDesc[j] = parse(buffer, indici[j-1]+1, indici[j]-1);
}
p->vDesc[p->grad-1] = parse(buffer, indici[p->grad-2]+1, end);
return p;
}

openP = 0;
for (i = start; i <= end; i++)
{
if ( buffer[i]=='(' )
openP ++;
if ( buffer[i]==')' )
openP --;
if ( buffer[i]=='*' )
{
if ( openP > 0)
continue;
indici[k++] = i;
}
}
if ( k > 0 )
{
Nod *p=(Nod*) new char[sizeof(Nod)-(GRMAX-k+1)*sizeof(Nod*)];
p->grad = k+1;
Laborator de Structuri de Date si Algoritmi – Lucrarea nr. 5

p->data='*';
p->vDesc[0] = parse(buffer, start, indici[0]-1);
for (int j = 1; j < p->grad - 1; j++)
{
p->vDesc[j] = parse(buffer, indici[j-1]+1, indici[j]-1);
}
p->vDesc[p->grad-1] = parse(buffer, indici[p->grad-2]+1, end);
return p;
}

if ( buffer[start] == '(' && buffer[end] == ')' )


return parse(buffer, start+1, end-1);

if ( start==end )
if ( isdigit(buffer[start]) )
{
Nod* p = (Nod*) new char[sizeof(Nod)-(GRMAX)*sizeof(Nod*)];
p->data = buffer[start];
p->grad = 0;
return p;
}

printf("\nExpresia de intrare este eronata. Apasati o tasta");


getch();
exit(1);
}
Laborator de Structuri de Date si Algoritmi – Lucrarea nr. 5

ANEXA B
Arbore_binar.h

struct Nod{
char data;
Nod* stg, *drt;
};

void eroare();
char readchar();
char citesteNume();
Nod* citesteArboreB();
Nod* creareArboreB();

Arbore_binar.cpp

#include <alloc.h>
#include <conio.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "arbore_binar.h"

char car;

void eroare()
{
printf("Sirul de intrare este eronat!\n");
printf("Apasati tasta o tasta...");
getch();
exit(1);
}

char readchar()
{
char c;
do c=getchar(); while(c==' ');
return c;
}

char citesteNume()
{
char c;
if(!isalpha(car)) eroare();
c = car;
Laborator de Structuri de Date si Algoritmi – Lucrarea nr. 5

car = readchar();
return c;
}

Nod* citesteArboreB()
{
Nod* rad;
if( car=='-' ) {
rad=0;
car = readchar();
}
else {
rad = (Nod*) malloc(sizeof(Nod));
rad->data = citesteNume();
if( car!='(' ) {
rad->stg = 0;
rad->drt = 0;
}
else {
car = readchar();
rad->stg = citesteArboreB();
if( car!=',' ) rad->drt = 0;
else {
car = readchar();
rad->drt = citesteArboreB();
}
if( car!=')' ) eroare();
car = readchar();
}
}
return rad;
}

Nod* creareArboreB()
{
printf("\nIntroduceti arborele:");
car = readchar();
return citesteArboreB();
}

You might also like