Sunteți pe pagina 1din 9

Disciplina: Programarea calculatorului Profesor:Gribineț L.

Structuri dinamice de date. Arbori binari


1.5.1. Generalităţi despre arbori
În figura următoare este prezentat un arbore, având rădăcina A:

Rădăcina arborelui:
A nivelul 0

B C ----------------------------- nivelul 1

Înălţime=3
D E F G H ------------------ nivelul 2

I J K ------------- nivelul 3

Într-o structură de tip arbore elementele sunt sructurate pe nivele; pe primul nivel, numit nivel
0, există un singur element numit rădăcină, de care sunt legate mai multe elemente numite fii (sau
descendenţi direcţi), care formează nivelul 1; de acestea sunt legate elementele de nivelul 2 şi a.m.d.
Există un drum unic de la rădăcina arborelui la fiecare nod x din arbore. Dacă y se găseşte pe
drumul unic de la rădăcină la x, atunci y se numeşte ascendent (strămoş) al lui x.
Un nod pe un anumit nivel este nod părinte pentru nodurile legate de el, situate pe nivelul
următor, acestea reprezentând fii săi. Nodurile care au acelaşi părinte se numesc noduri fraţi. Fiecare
nod are un singur părinte, cu excepţia rădăcinii, care nu are părinte. Nodurile fără fii se numesc
noduri terminale (sau frunze). Termenii nod părinte, nod fiu sau frate sunt preluaţi de la arborii
genealogici, cu care arborii în studiu se aseamănă foarte mult.
Nivelul maxim din arbore se numeşte înălţimea (adâncimea) arborelui.
Arborii, ca structură dinamică de date, au extrem de multe aplicaţii (căutări, sortări, etc.), unde
se poate obţine economie de timp şi memorie, o minimizare a numărului de operaţii, ajungându-se la
algoritmi eficienţi, şi, implicit, la programe performante. Deosebit de utilizată în aplicaţii este structura
de tip arbore binar.

1.5.2. Structura de date de tip arbore binar

Un arbore binar este un arbore în care fiecare nod are cel mult doi fii, fiul stâng şi fiul drept
(fiul stâng este legat în stânga părintelui şi cel drept în dreapta).
Exemplu de arbore binar:
Dacă în această figură se elimină rădăcina
1 şi legăturile ei, se obţin doi arbori binari
care se numesc subarborii stâng şi drept ai
Fiul stâng arborelui iniţial. Deci, arborele binar este o
al nodului 1--- 2 3 -----Fiul drept structură recursivă de date.
al nodului 1 Un arbore binar nevid fie se reduce la
rădăcină, fie cuprinde rădăcina şi cel mult
doi subarbori binari.
4 5 6

7 8
Disciplina: Programarea calculatorului Profesor:Gribineț L.

Pentru reţinerea informaţiei în calculator se va folosi alocarea dinamică. Deoarece pentru fiecare
nod în parte este necesar să se reţină, pe lângă informaţia utilă şi legăturile cu nodurile fii (adresele
acestora), ne putem imagina următoarea structură a unui nod:
st inf dr

Absenţa unui fiu se va marca cu pointerul NULL. Implementarea arborelui de mai sus este
prezentată în următoarea figură:
1 Rădăcina

2 NULL 3

NULL 4 NULL 5 NULL 6 NULL

NULL 7 NULL NULL 8 NULL

Pentru descrierea unui nod putem folosi declaraţiile:


typedef struct nod
{ int inf; //sau orice alt tip pentru informatie
nod * st, * dr;}; // adresele fiilor
ARB; //ARB –identificatorul tipului definit

Din multitudinea operaţiilor care se pot efectua asupra unui arbore binar vom menţiona
următoarele:
1) parcurgerea (traversarea) arborelui;
2) crearea arborelui;
3) căutarea (localizarea) unui nod;
4) inserarea unui nod;
5) ştergerea unui nod.

1.5.3. Parcurgerea arborilor binari


Parcurgerile sunt cele mai frecvent utilizate operaţii pe arbori. A parcurge un arbore înseamnă a
vizita fiecare nod al arborelui o singură dată, în scopul prelucrării informaţiei asociate nodului
respectiv.
Există mai multe posibiltăţi de parcurgere a arborelor –în adâncime (preordine, inordine, postordine )
sau pe niveluri.
a. Parcurgerea în adâncime
Dacă convenim ca parcurgerea subarborelui stâng să se facă înaintea subarborelui drept, atunci se
poate afirma că există trei modalităţi importante de traversare a unui arbore binar:
1) în preordine;
2) în inordine;
3) în postordine.
Aceste denumiri sugerează poziţia rădăcinii la traversare: înaintea celor doi fii, între ei, respectiv
după ei. Modurile de traversare se definesc recursiv astfel:
1) În preordine (RSD): se vizitează rădăcina, apoi tot în preordine se vizitează nodurile
subarborelui stâng şi apoi acele ale subarborelui drept.
Exemplu (parcurgerea în preordine a arborelui din paragraful precedent):1, 2, 4, 7, 3, 5, 6, 8.
Disciplina: Programarea calculatorului Profesor:Gribineț L.

2) În inordine (SRD): se vizitează în inordine nodurile subarborelui stâng, apoi rădăcina, şi apoi
tot în inordine, nodurile subarborelui drept.
Exemplu (parcurgerea în inordine a arborelui din paragraful precedent): 4, 7, 2, 1, 5, 3, 8, 6.
3) În postordine (SDR): se vizitează în postordine nodurile subarborelui stâng, apoi tot în
postordine, nodurile subarborelui drept şi, la sfârşit rădăcina.
Exemplu (parcurgerea în postordine a arborelui din paragraful precedent):7, 4, 2, 5, 8, 6, 3, 1.

b. Parcurgerea pe niveluri –se vizitează întâi rădăcina, apoi fiul stâng al rădăcinii, apoi cel
drept şi se continuă în acest mod, vizitând nodurile de pe fiecare nivel de la stânga la dreapta. Pentru
arborele prezentat în paragraful precedent, şirul parcurgerii pe niveluri este:1 2 3 4 5 6 7 8.

1.5.4. Crearea arborelui binar


Crearea unui arbore binar alocat dinamic se realizează cel mai uşor aplicând următoarea schemă:
- se generează un nod, adică se alocă spaţiu în heap şi se încarcă informaţia;
- pentru fiecare nod se construieşte subarborele său stâng, apoi subarborele său drept şi se
completează adresele descendenţilor nodului cu adresele acestor subarbori;
- un descendent vid trebuie marcat printr-o proprietate stabilită asupra informaţiei (spre exemplu
apariţia valorii 0 drept conţinut informaţional al nodului curent).
Spre exemplu, pentru arborele binar prezentat în p. 1.5.3. şirul datelor furnizat la intrare va arăta
astfel:
12407000350069000
ceea ce corespunde unei liste în preordine a nodurilor.

În continuare scriem 4 funcţii recursive destinate realizării celor trei metode de parcurgere în
adâncime şi creării unui arbore binar. Fie r este o variabilă de tip referinţă care indică rădăcina
arborelui.

void RSD(ARB *r) //Parcurgere preordine void SRD(ARB *r) //Parcurgere inordine
{ {
if(r) if(r)
{ {
cout<<r->inf<<" "; RSD(r->st); RSD(r->dr); SRD(r->st); cout<<r->inf<<" "; SRD(r->dr);
} }
} }

void SDR(ARB *r) //Parcurgere postordine void Creare(ARB* &r) //Creare arbore binar
{ { int x; // x-informatie pentru subarbore
if(r) cin>>x;
{ if(!x) //urmeaza subarbore vid
SDR(r->st); SDR(r->dr); cout<<r->inf<<" "; r=0;
} else //Creare nod curent şi memorare informaţie
} { r=new ARB; r->inf=x;
Creare(r->st); Creare(r->dr); } //Autoapelări
}

Programul următor realizează crearea unui arbore binar şi afişează listele nodurilor obţinute prin cele
trei metode de parcurgere în adâncime.
Disciplina: Programarea calculatorului Profesor:Gribineț L.

#include<iostream>
using namespace std;
typedef struct nod //Definirea tipului ARB
{ int inf;
nod *st,*dr;
} ARB;

void Creare(ARB* &r) //r –pointer asociat radăcinii arborelui ce se creează


{ int x; cin>>x; // x-valoare asocită subarborelui ce urmează

if(!x) //urmează subarbore vid


r=0;
else
{ r=new ARB; r->inf=x; //creare nod curent şi memorare informaţie
Creare(r->st); Creare(r->dr); //crearea recursivă a subarborilor
}
}
void RSD(ARB *r) //parcurgere preordine
{
if(r)
{ cout<<r->inf<<" "; RSD(r->st); RSD(r->dr);}
}
void SRD(ARB *r) //parcurgere inordine
{ if(r)
{ SRD(r->st); cout<<r->inf<<" "; SRD(r->dr); }
}

void SDR(ARB *r) //parcurgere postordine


{ if(r)
{
SDR(r->st); SDR(r->dr); cout<<r->inf<<" ";
}
}

void Sterge(ARB *r) // Ştergerea arborelui din HEAP


{ if(r)
{
Sterge(r->st); Sterge(r->dr); delete r;
}
}

int main()
{ ARB *rad; //adresa radacinii arborelui
cout<<"Introduceti nodurile : ";
Creare(rad); //crearea arborelui binar
cout<<"\nNodurile in preordine: "; RSD(rad);
cout<<"\nNodurile in inordine: "; SRD(rad);
cout<<"\nNodurile in postordine: "; SDR(rad);
cout<<"\n";
Sterge(rad); //Stergerea arborelui din heap
return 0;
}
Disciplina: Programarea calculatorului Profesor:Gribineț L.

1.5.5. Arbore binar de căutare (ABC)


1.5.5.1. Noţiune de ABC
Arborii sunt foarte des utilizaţi pentru memorarea şi regăsirea rapidă a unor informaţii. Problema
căutării (localizării) unui nod se poate pune doar într-un arbore binar de căutare.
Un arbore binar de căutare este un arbore binar care trebuie să respecte următoarele
proprietăţi: pentru orice nod n, fiecare din descendenţii din subarborele din stânga va avea
valoarea informaţiei mai mică decât a nodului n, iar fiecare din descendenţii din subarborele din
dreapta va avea valoarea informaţiei mai mare decât a nodului n.
Într-un ABC informaţia din noduri este unică.
Exemple:
7 6
3 9
3 8

1 10
1 5 7 9

2 8
4
4
(a) (b)

În figurile de mai sus avem 2 arbori binari.


Arborele din figura (b) este arbore binar de căutare deoarece respectă regula de mai sus, şi anume
că în orice subarbore stâng valorile sunt mai mici decât ale rădăcinii acestuia şi în orice subarbore din
dreapta, valorile sunt mai mari decât ale rădăcinii respective.
Arborele din figura (a) nu este arbore binar de căutare deoarece nu toate nodurile respectă regula
menţionată. De exemplu, nodul cu informaţia 4 nu are ce “căuta” în subarborele drept, iar cifra 8 nu
este corect plasată.
Observaţie. O proprietate foarte importantă a ABC-ului este aceea că oferă posibilitatea ordonării
crescătoare a valorilor memorate în noduri prin parcurgerea arborelui în inordine.

1.5.5.2. Crearea şi inseratea într-un ABC


Crearea unui ABC se realizează adăugând intr-un arbore iniţial vid, pe rând, câte un nod. Ideea
este următoarea:
- dacă arborele este vid, se creează rădăcina;
- dacă arborele nu este vid, se compară informaţia noului nod cu informaţia din nodul curent.
Dacă este mai mare, se reia procesul pentru subarborele drept; dacă este mai mică se reia
procesul pentru subarborele stâng.
Exemplificăm procesul de inserare (adăugare) a unui nod:

Vom considera arborele din dreapta. Să 62 90


presupunem că dorim să inserăm nodul cu valoarea
62. Acesta se va insera ca nod frunză. Pentru a-l 50 97
insera va trebui să căutăm o poziţie în arbore care
respectă regula de integritate a arborilor binari de 20 75
căutare.
5 25
Disciplina: Programarea calculatorului Profesor:Gribineț L.

Vom începe prin compararea nodului de inserat 62 90


(62), cu rădăcina arborelui (90).
Observăm că este mai mic decât ea, deci va trebui 50 97
inserat undeva în subarborele stâng al rădăcinii.
20 75

5 25

Vom compara apoi 62 cu 50. Din moment ce 62 90


este mai mare decât 50, nodul 62 va trebui plasat
undeva în subarborele drept al lui 50. 62 50 97

20 75

5 25

Se compară apoi 62 cu 75. Deoarece 75 este mai 90


mare decât 62, 62 trebuie să se afle în subarborele
din stânga al nodului 75. 50 97

20 75 62

5 25

Dar 75 nu are nici un fiu în partea stângă. Asta


90
înseamnă că am găsit locaţia pentru nodul 62. Tot
ceea ce mai trebuie făcut este să modificăm în 50 97
nodul 75 adresa către fiul din stânga, încât să indice
spre 62. 20 75

5 25 62

În prezenţa declaraţiilor:
typedef struct nod
{ int inf; nod *st,*dr;
} ABC;

următoarea funcţie realizează adăugarea unui nod într-un ABC după metoda descrisă mai sus:

void Adauga(ABC* &r, int info) // r-pointer la radacina arborelui;


{ if(!r) //arborele este vid, creez radacina
{ r=new ABC; r->inf=info; r->st=r->dr=0; }
else
if(info<r->inf) Adauga (r->st, info);
else if(info>r->inf) Adauga(r->dr, info);
else cout<<"\ninformatie dublicat\n";}
Parametrii funcţiei Adauga() reprezintă nodul curent la un moment dat. Deoarece se alocă
memorie în heap pentru un nod nou, adresa acestuia este transmisă prin referinţă.
Disciplina: Programarea calculatorului Profesor:Gribineț L.

1.5.5.2. Căutarea unei informaţii într-un ABC.


Această operaţie se bazează pe ideea construcţiei arborelui. Se compară informaţia cu cea din
nodul curent. Dacă sunt egale, căutarea se încheie, dacă nu, căutarea continuă în subarborele stâng sau
în subarborele drept după cum valoarea căutată este mai mică, respectiv mai mare decât informaţia din
nodul curent. În cazul în care se ajunge într-un subarbore vid (adresă nulă), informaţia căutată nu se
găseşte în ABC şi se afişează un mesaj. Funcţia următoare implementează acestă metodă de căutare:

void Cauta(ABC *p, int x)


{ if(!p) cout<<"\ninformatia "<<x<<" nu exista in arbore\n";
else if(x<p->inf) Cauta(p->st, x);
else if(x>p->inf) Cauta(p->dr, x);
else cout<<"\ninformatia "<<x<<" exista in arbore\n"; }
1.5.5.3. Ştergerea unui nod dintr-un ABC
Ca şi în cazul operaţiei de inserare, ştergerea este precedată de o căutare a nodului cu cheia
(informaţia) dată x în arborele binar. În cazul în care un astfel de nod este găsit –să îl notăm cu p, iar
cu t să notăm părintele său –există trei situaţii posibile:
a. Nodul p este un nod terminal. În acest caz, ştergerea este simplă: înlocuim legăturile spre p cu
NULL. t t
p

`
A1 A1

b. Nodul p are un singur fiu. Înlocuim legătura spre p cu o legătură spre unicul fiu al lui p.
t t
p

A2
`
A1 A2 A1

c. Nodul p are exact doi fii. Vom reduce acest caz la una dintre situaţiile a. sau b., conservând
proprietatea de arbore binar de căutare. Pentru aceasta vom determina nodul cu cea mai mare cheie din
subarborele stâng al lui p, notat max.
Vom înlocui cheia lui p cu cheia nodului max şi vom şterge nodul max, care nu are fiu drept.
Deoarece nodul max este situat în subarborele stâng al lui p, are cheia mai mică decât nodurile din
subarborele drept şi mai mare decât a nodurilor din subarborele stâng, deci arborele astfel obţinut este
arbore binar de căutare.
Se poate proceda în mod analog, păstrând consistenţa arborelui binar de căutare înlocuind cheia lui
p cu cheia nodului cu cea mai mică cheie din subarborele drept al lui p, notat min, nod care nu are fiu
stâng şi ştergând apoi nodul min.
Exemplu. Să ştergem, din arborele binar de căutare următor, nodul cu valoarea 18.
25
Acest nod are doi fii. Vom determina maximul din
18 50 subarborele stâng al nodului cu valoarea 18 (nodul
cu valoarea 12), înlocuim cheia 18 cu cheia 12, apoi
12 20 46 99
ştergem maximul din subarborele stâng (acest nod
7 19 22 49 nu are fiu drept ). Obţinem arborele:
Disciplina: Programarea calculatorului Profesor:Gribineț L.

25
Programul următor gestionează operaţiile de creare şi
12 50 prelucrare a unui ABC. Se crează un arbore iniţial din
n valori citite de la tastatură. Prin intermediul unor
7 20 46 99 opţiuni de meniu se poate realiza în mod repetat
adăugarea unui nod în ABC, căutarea unei valori x,
ştergerea unei valori x şi afişarea nodurilor din arbore în
19 22 49
inordine (ordonate crescător după chei):

include <iostream>
using namespace std;

typedef struct nod // Definirea pointerului ABC


{ int inf;
nod *st,*dr;
}ABC;
ABC *tns;
void Adauga(ABC* &r,int info) // Adăugare unui nod nou
{ if(!r) //arborele este vid, creez radacina
{ r=new ABC; r->inf=info; r->st=r->dr=0; }
else
if(info<r->inf) Adauga(r->st,info);
else if(info>r->inf) Adauga(r->dr,info);
else cout<<"\ninformatie duplicat\n";
}
void Creare(ABC* &r) // Creare Arbore Binar de Căutare
{ int info,n,i;
cout<<"numar de noduri: "; cin>>n;
for(i=1;i<=n;i++)
{ cout<<"informatia nodului "<<i<<": ";
cin>>info;
Adauga(r,info);
}
}
void Cauta(ABC *p, int x) // Căutarea unei informaţii în ABC
{ if(!p) cout<<"\ninformatia "<<x<<" nu exista in arbore\n";
else
if(x<p->inf) Cauta(p->st,x);
else if(x>p->inf) Cauta(p->dr,x);
else cout<<"\ninformatia "<<x<<" exista in arbore\n";
}
void Stergere(ABC* &r, int x) // Ştergerea unui nod din ABC
{ ABC *p=r, *t=NULL, *max, *tmax, *fiu;
// caut noduri cu cheia x; t indica parintele acestui nod
while (p && p->inf!=x)
{ t=p;
if(x<p->inf) p=p->st;
else p=p->dr;
Disciplina: Programarea calculatorului Profesor:Gribineț L.

}
if(!p) return; // cheia x nu se afla I arbore
if(p->st && p->dr) //nodul de sters are 2 fii
{ // determin maximumul din subarborele stang
tmax=p; max=p->st;
while (max->dr)
{ tmax=max; max=max->dr;}
//copiez informatia
p->inf=max->inf;
//nodul maxim devine nod de sters
t=tmax; p=max;
}
//noduri de sters are cel mult un fiu
if(p->st) fiu=p->st;
else fiu=p->dr;
if (t)
if(t->st==p) //p este un fiu sters al parintelui sau
t->st=fiu;
else //p este fiul drept al tatalui sau
t->dr=fiu;
else //sterg radacina arborelui
r=fiu;
delete p;
}
void SRD(ABC *r) // Parcurgerea ABC în inordine
{ if(r)
{ SRD(r->st); cout<<r->inf<<" "; SRD(r->dr); }
}
int main()
{ ABC *rad=0; int x,opt;
Creare(rad); //se creaza ABC-ul initial
do{
cout<<"\n\n\tOptiuni:\n 1-Cauta o valoare in ABC\n 2-Adauga un nod in
ABC\n";
cout<<" 3-Sterge un nod din ABC\n 4-Lista nodurilor in inordine\n 5-Iesire\n";
cout<<"optiunea: "; cin>>opt;

switch(opt)
{ case 1: {cout<<"valoarea: "; cin>>x;
Cauta(rad,x); break;}
case 2: {cout<<"valoarea: "; cin>>x;
Adauga(rad,x); break;}
case 3: {cout<<"valoarea: "; cin>>x;
Stergere(rad,x); break;}
case 4: SRD(rad);
}
}while(opt!=5);
}

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