Arbori binari

Arbori binari

Arborii binari sunt arbori în care nodurile conţin cel mult doi descendenţi. Pentru memorarea acestor arbori se poate folosi o structură mai simplă de forma:

// tipul de date al informatiilor utile

typedef double TipArbore;

// structura care reprezinta un nod

// dintr-un arbore binar

struct NodArbore

{

// informatiile utile stocate in nod

TipArbore Informatii;

// vector de legaturi catre fii

NodArbore *Stanga, *Dreapta;

};

Operaţiile sunt aceleaşi ca şi în cazul arborilor oarecare.

Exemplu: parcurgerea arborelui în ordinea stânga – rădăcină - dreapta şi stocarea elementelor într-o listă:

// nodul listei simplu inlantuite

struct NodLista

{

// informatia utila

TipArbore Informatii;

// legastura catre elementul urmator

NodLista *Legatura;

// constructorul pentru initializarea unui nod

NodLista(TipArbore info, NodLista* leg = NULL)

: Informatii(info), Legatura(leg) {}

};

// functie de concatenare a doua liste simplu inlantuite

NodLista* Concatenare(NodLista *cap1, NodLista *cap2)

{

// cazul 1: prima lista e vida

if (cap1 == NULL)

return cap2;

// cazul 2: prima lista e nevida

// parcurgem prima lista

while (cap1->Legatura != NULL)

cap1 = cap1->Legatura;

// si facem legatura

cap1->Legatura = cap2;

// intoarcem capul listei

return cap1;

}

// procedura recursiva de parcurgere a unui arbore binar

NodLista* Parcurgere(NodArbore *nod)

{

// conditia de oprire din recursie

if (nod == NULL)

return NULL;

// initializam lista

NodLista *cap = NULL;

// adaugam subarborele stang

cap = Concatenare(cap, Parcurgere(nod->Stanga));

// adaugam nodul curent

cap = Concatenare(cap, new NodLista(nod->Informatii));

// adaugam subarborele drept

cap = Concatenare(cap, Parcurgere(nod->Dreapta));

return cap;

}

Arborii oarecare pot fi memoraţi ca arbori binari schimbând semantica legăturilor nodului astfel:

· legătura stânga va adresa primul fiu

· legătura dreapta va adresa următorul frate

Exemplu de transformare:

a) arbore oarecare:

b) arbore oarecare memorat ca arbore binar:

Arbori binari de căutare

Arborii binari de căutare sunt arbori binari în care nodurile sunt memorate ordonat în funcţie de o cheie. Toate nodurile din arbore au în subarborele stâng noduri care au chei mai mici şi în subarborele drept chei mai mari.

Arborii de căutare permit regăsirea rapidă a informaţiilor (O(log2 n)) atât timp cât arborele este echilibrat. În cazul cel mai defavorabil, timpul de căutare este identic cu cel al unei liste simplu înlănţuite.

Operaţiile de bază pe un arbore de căutare sunt următoarele:

Implementarea operaţiilor de bază este prezentată în următoarea bibliotecă:

#ifndef ARBORE_H

#define ARBORE_H

// un nod din arbore

struct NodArbore

{

// informatia utila

TipArbore Date;

// legaturile catre subarbori

NodArbore *Stanga, *Dreapta;

// constructor pentru initializarea unui nod nou

NodArbore(TipArbore date,

NodArbore *stanga = NULL, NodArbore *dreapta = NULL) :

Date(date), Stanga(stanga), Dreapta(dreapta){}

};

// Arborele este manipulat sub

// forma unui pointer catre radacina

typedef NodArbore* Arbore;

// Creaza un arbore vid

Arbore ArbCreare()

{

return NULL;

}

// Testeaza daca un arbore este vid

bool ArbEGol(Arbore& arbore)

{

return arbore == NULL;

}

// Adauga un element intr-un arbore de cautare

void ArbAdauga(Arbore& arbore, TipArbore date)

{

// Cazul 1: arbore vid

if (ArbEGol(arbore))

{

arbore = new NodArbore(date);

return;

}

// Cazul 2: arbore nevid

if (date < arbore->Date)

// daca exista subarborele stang

if (arbore->Stanga != NULL)

// inseram in subarbore

ArbAdauga(arbore->Stanga, date);

else

// cream subarborele stang

arbore->Stanga = new NodArbore(date);

if (date > arbore->Date)

// daca exista subarborele drept

if (arbore->Dreapta != NULL)

// inseram in subarbore

ArbAdauga(arbore->Dreapta, date);

else

// cream subarborele drept

arbore->Dreapta = new NodArbore(date);

}

// Functie privata de stergere a unui nod

void __ArbStergeNod(Arbore& legParinte)

{

// salvam un pointer la nodul de sters

Arbore nod = legParinte;

// daca avem un subarbore drept

if (nod->Dreapta != NULL)

{

// facem legatura

legParinte = nod->Dreapta;

// daca avem si un subarbore stang

if (nod->Stanga)

{

// cautam cel mai mic element din subarborele drept

Arbore temp = nod->Dreapta;

while (temp->Stanga != NULL)

temp = temp->Stanga;

// si adaugam subarborele stang

temp->Stanga = nod->Stanga;

}

}

else

// daca avem doar un subarbore stang

if (nod->Stanga != NULL)

// facem legatura la acesta

legParinte = nod->Stanga;

else

// daca nu avem nici un subnod

legParinte = NULL;

// stergem nodul

delete nod;

}

// Sterge un nod dintr-un arbore de cautare

void ArbSterge(Arbore& arbore, TipArbore date)

{

// Cazul 1: arbore vid

if (ArbEGol(arbore))

return;

// Cazul 2: stergere radacina

if (arbore->Date == date)

{

// salvam un pointer la radacina

Arbore nod = arbore;

// daca avem un subarbore drept

if (nod->Dreapta)

{

// facem legatura

arbore = nod->Dreapta;

// daca avem si un subarbore stang

if (nod->Stanga)

{

// cautam cel mai mic element din subarborele drept

Arbore temp = nod->Dreapta;

while (temp->Stanga != NULL)

temp = temp->Stanga;

// si adaugam subarborele stang

temp->Stanga = nod->Stanga;

}

}

else

// daca avem doar un subarbore stang

if (nod->Stanga != NULL)

// facem legatura la acesta

arbore = nod->Stanga;

else

// daca nu avem nici un subnod

arbore = NULL;

// stergem vechea radacina

delete nod;

return;

}

// Cazul 3: stergere nod in arbore nevid

// cautam legatura la nod in arbore

// si stergem nodul (daca exista)

Arbore nodCurent = arbore;

while (true)

{

if (date < nodCurent->Date)

if (nodCurent->Stanga == NULL)

break; // nodul nu exista

else

if (nodCurent->Stanga->Date == date)

// nodul de sters este descendentul stang

__ArbStergeNod(nodCurent->Stanga);

else

// continuam cautarea in subarborele stang

nodCurent = nodCurent->Stanga;

else

if (nodCurent->Dreapta == NULL)

break; // nodul nu exista

else

if (nodCurent->Dreapta->Date == date)

// nodul de sters este descendentul drept

__ArbStergeNod(nodCurent->Dreapta);

else

// continuam cautarea in subarborele stang

nodCurent = nodCurent->Dreapta;

}

}

// Cauta recursiv un nod in arborele de cautare

bool Cautare(Arbore& arbore, TipArbore info)

{

// conditia de oprire din recursie

if (arbore == NULL)

return false;

// verificam daca am gasit nodul

if (arbore->Date == info)

return true;

// daca cheia este mai mica

if (arbore->Date < info)

// cautam in subarborele stang

return Cautare(arbore->Stanga, info);

else

// altfel cautam in subarborele drept

return Cautare(arbore->Dreapta, info);

}

#endif //ARBORE_H

Parcurgerea unui arbore de căutare în ordinea stânga – rădăcină – dreapta conduce la obţinerea listei nodurilor în ordinea crescătoare a cheilor. Funcţia următoare afişează în ordine elementele unui arbore binar de căutare:

void AfisareSRD(Arbore& arbore)

{

if (ArbEGol(arbore))

return;

AfisareSRD(arbore->Stanga);

cout << arbore->Date << " ";

AfisareSRD(arbore->Dreapta);

}

Parcurgerea nerecursivă a unui arbore binar de căutare se poate face folosind o stivă:

void TraversareNerecursiv(Arbore& arbore)

{

Stiva stiva = StCreare();

// a) ne deplasam pana la primul nod

Arbore nod = arbore;

while (nod != NULL)

{

StAdauga(stiva, nod);

nod = nod->Stanga;

}

if (!StEGoala(stiva))

cout << StVarf(stiva)->Date << " ";

// b) traversam arborele in inordine

Arbore parinte, copil;

while (!StEGoala(stiva))

{

parinte = StExtrage(stiva);

copil = parinte->Dreapta;

while (copil != NULL)

{

StAdauga(stiva, copil);

copil = copil->Stanga;

}

if (!StEGoala(stiva))

cout << StVarf(stiva)->Date << " ";

}

}