Explorați Cărți electronice
Categorii
Explorați Cărți audio
Categorii
Explorați Reviste
Categorii
Explorați Documente
Categorii
In domeniul calculatoarelor, un arbore binar este structura de date abstracte in care fiecarenod are cel mult doi fii.De obicei nodurile sunt denumite stang daca se afla in parteastanga a arborelui (sau a unui sub-arore), si drept daca se afla in partea dreapta a arborelui(sau a vreunui sub-arbore).O denumire mai utilizata pentru arborii binari este arbori binari de cautare. 1.1. Definitii. >>> * O margine directa se refera la legatura dintre nodulparinte pana la nodul-fiu (sagetiledin imaginea arborelui prezentata mai sus). * Nodul radacina al unui arbore este nodul care nu are nici un parinte, deoarece este celmai din varf nod din arbore. * Nodul curent este nodul care nu are fii (noduri predecesoare).el se afla in cel mai de josnivel al arborelui. *Adancimea unui nod n reprezinta lungimea drumului de la nodul radacina pana la noduln.Toate nodurile cuprinse intr-o lungime a unui drum dat, reprezinta uneori nivelularborelui. * Inaltimea arborelui este lungimea drumului incepand de la nodul radacina si pana la celmai de jos nod al arborelui. * Noduri infratite sunt nodurile care pot oferi altor noduri propriul nod parinte. * Daca exista un drum intre nodul p so q, atunci nodul p este un nod succesor pentrunodul q iar nodul q este un predecesor pentru nodul p. * Dimensiunea unui nod este numarul descendentilor inclusi in acel nod.
1.2. Tipuri de arbori binari >>> * Un arbore binar cu radacina este un arbore care porneste de la un nod radacina, iar fiecare nod are cel mult doi descententi. * Unarbore binar plin, sau arbore binar propriu, este un arbore in care fiecare nod are saunici un fiu sau doi fii. * Un arbore binar perfect (denumit uneori arbore binar complet este un arbore binar plinin care toate nodurile curente ("frunzele") sunt la aceasi adancime. * Un arbore binar complet ar putea fi definit ca un arbore binar plin in care toate nodurilecurente au adancimea de n sau n-1 pentru oricare ar fi n.Ordonand un arbore binar, acestava deveni un arbore binar complet, adica toate nodurile-fii de la ultimul nivel trebuie saocupe consecutiv locurile cele mai din stanga, pana cand nu mai ramane nici un locneocupat.De exemplu, daca doua noduri aflate in cel mai de jos nivel al arborelui ocupafiecare cate un loc rezultand astfel un alt loc liber intre cele doua noduri, atunci restulnodurilor descendente vor fi strans marginite intre ele fara sa existe nici un locneocupat.Cu alte cuvinte, un arbore binar complet are nodurile curente ("frunza") fiecarecu cate doi descendenti, neexistand ca printre acestea sa fie un nod curent cu numai unsingur descendent rezultant astfel un loc liber intre acestia. * Un arbore binar complet cu radacina poate fi identificat printr-un vector liber. * Unarbore binar aproape complet este un arbore in care fiecare nod al acestuia are un fiudrept si un fiu stang.Detinerea unui nod-fiu stang nu implica necesitatea ca parintele saaiba neaparat si un nod-fiu drept.Adica, un arbore binar aproape complet este arboreleunde pentru un nod-fiu drept exista un descendent stang,insa nodul-fiu
stang nu esteobligatoriu sa detina un descendent drept propriu. 1.3. Metode pentru stocarea arborilor binari >>> Arborii binari in cele mai multe cazuri pot fi construiti din limbaje de programare primitive.Intr-un limbaj cu inregistrari si referinte,arborii binari sunt construiti tipic printr-o structura bazata pe un nod radacina care contine unele date si referinte desprenodul-fiu stang sau drept.Uneori aceste noduri-fii contin deasemeni o referinta catre nodul-parinte.Daca un nod are mai putin de doi fii, atunci unele referinte ale nodurilo- fiiar putea avea o valoare nula.Arborii binari poti fi deasemeni stocati intr-un tablou ca un tip de structura de dateimplicita, si daca arborele este un arbore binar complet aceasta metoda nu lasa locuriramase neocupate in tablou.In acest aranjament compact, daca un nod are indicele idescendentii lui pot fi gasiti la indicii 2i+1 si 2i+2, atat timp cat nodul-parinte al acestoraeste gasit la indicele-nivel((i-1)/2)(declarand ca nodul radacina are indicele zero).Aceastametoda bebeficiaza de o stocare mai compacta a informatiilor in comparatie cu metodalocalizarii referintelor, efectuant in particular o traversare in preordine.Oricum, ea esteexpansiva si nu utilizeaza un spatiu proportional cu 2h-n pentru un arbore cu inaltime hcu un numar de n noduri.Imaginea arata modul de stocare a unui arbore intr-un SDA tablou.In limabejele ce folosesc uniuni etichetate este limbajul ML, nodul unui arbore esteadesea o uniune etichetata a doua tipuri de noduri iar una dintre acestia este a reprezinta atreia parte a datelor.Fiul stang si
fiul drept si oricare dintre pot fi un nod curent (frunza),care nu contin date si functii, asa cum sunt valorile nule intr-un limbaj cu referinte(pointeri). 1.4. Metode de parcurgere a arborilor binari. >>> Adesea se doreste sa se verifice fiecare nod dintr-un arbore pentru a examina valoareaacestora.Aici sunt prezentate cateva metode prin care nodurile unui arbore pot fiverificati. 1.5. Metode de traversare a arborilor binari. >>> Traversarea in pre-ordine, in ordine si post ordine verifica fiecare nod dintr-un arbore printr-o parcurgfere recursiva a fiecarui nod din sub-arborele stang si drept pana alenodului radacina.Daca nodul radacina este examinat inaintea sub-arborilor descendenti,aceasta metoda de parcurgere se numeste parcurgere in pre-ordine, iar daca nodulradacina este verificat dupa verificarea sub-arborilor atunci aceasta metoda de parcurgerese numeste parcurgere in post-ordine.Daca parcurgerea se efectueaza intre nodul radacinasi nodurile celor doi sub-arbori, atunci aceasta metoda de a parcurge un arbore se numeste parcurgere in ordine.Traversarea in ordine este folositoare in cadrul arborilor binari decautare, unde aceasta traversare verifica nodurile intr-o ordine crescatoare. 1.6. Parcurgerea in adancime (depth-first order). >>> Parcurgerea in adancime verifica prima data nodul radacina si inainteaza prin examinareanodurilor pana la cel mai indepartat nod. 1.7. Parcurgerea in latime (breadth-first order). >>>
Este metoda de parcurgere contrara parcurgerii in adancime (depth-first order), careintotdeuna va verifica nodul radacina dat si apoi nodurile invecinate lui, si apoi nodurileinvecinate ale acestora pana cand se ajunge la alte noduri nevizitate. Implementari pentru arborele binar. 1.8. Implementari concise (succinte). >>> O structura de date concisa (succinta) este o structura de date menita sa ocupe un spatiude memorie cat mai mic posibil, adica arborele sa aiba limite cat mai mici.Numarul altor arbori binari cu un numar de n noduri este Cn, adica al -n-lea numar Catalan (asumand case vor primvi arbori cu structuta identica ca fiind identici).Pentru un numar n mare, cumar fi 4n, va trebui sa se dispuna de un numar log24n biti pentru a fi implementat.Unarbore binar concis va terbui sa cupe doi biti pentru fiecare nod.Acum se doreste a aborda o reprezentare simpla in care se intalneste limitarea parcurgeriiin pre-ordine a nodurilor unui arbore,prin generarea unui "1" logic pentru un nod intern si"0" logic pentru un nod curent.Daca arborele contine date,atunci se poate simplificainstantaneu inmagazinarea lor in pre-ordine intr-un tablou consecutiv.Iata o implementarein Pascal: function TransformaSuccint(node n, bit structura, tablou date) { if n = nil thenappend 0 to structura; else append 1 to structura; append n.date to date;
TransformaSuccint(n.stanga, structura, date); TransformaSuccint(n.dreapta, structura, date); } In exemplul de mai sus sirul structure are numai 2n+1 biti la sfarsit, unde n este numarulnodurilor (interne).Nu se doreste sa se inmagazineze mai multe informatii decat poatecuprinde sirul structura.Pentru a arata cum este posibil sa nu se piarda nici o informatie, se va transforma datele de iesire inapoi intr-un arbore: function TransformaSuccint(bit structura, tablou date) { sterge primul bit din structura si il muta in bif b = 1 then creaza un nou nod nsuprima primul element si il muta in n.daten.stanga = TransformaSuccint(structura, date) n.dreapta = TransformaSuccint(structura, date) return n else return nil } 1.9. Implemenatarea unui numar de n arbori in arbori binari. >>> Aceasta este o mapare unu-la-unu dintre arborii ordonati general si arborii binari, care in paricular sunt folosit in limbajul Lisp pentrui a reprezenta arbori total ordonati in arbori binari.Fiecare N nod din arborele ordonat corespunde unui nod N' dintr-un arbore binar.Fiul stang al nodului N' este nodul corespunzator primului nod-fiu al nodului N, sifiul drept al nodului N' este nodul corespunzator nodului inlocuitor al lui N', acesta fiindurmatorul nod in ordinea parcurgerii descendentilor
cater nodul parinte N.O metoda de a lamuri cele scrise mai sus este aceea ca fiecare nod-fiu se afla intr-o listainlantuita,si este inlantuit inpreuna cu celelalte noduri din partea dreapta, si nodul arenumai o referire catre inceputul listei sau in varful acestei liste, prin intermediul nodurilor din partea stanga. De exemplu, se da in partea stanga un arbore care are la nodul A sase descendenti{B,C,D,E,F,G}.Ei pot fi transformati in arborele binar aflat in partea dreapta. Arborele din stanga este transformat in arborele binar din dreapta.Arborele binar de cautare.Acest arbore are dimensiunea 9,si de adancime 3.Nodul radacina are valoarea 8,iar 1,4,7,23 sunt valorile nodurilor curente.Arborele binar poate fi vazut prin structura arborelui initial prin faptul ca marginile negrereprezinta primul descendent si marginile albastre reprezentand urmatorulinlocuitor.Nodurile curente ale arborelui din stanga poate fi scrise in Lisp astfel: (((M N)H I) C D ((O) (P) F (L)) acestea putind fi implementate in memorie in structura arboreluidin partea dreapta. 2. Arbori binari de cautare. >>> In domeniul calculatoarelor un arbore binar de cautare (ABC) este o structura de datenumita arbore, si care detine urmatoarele proprietati: * Fiecare nod are o valoare. * Ordinea totala este definita pe aceste valori. * Sub-arborele stang al unui nod oarecare contine numai valorile mai mici decat valoarenodului respectiv. * Sub-arborele drept al unui nod oarecare detine numai valorile mai mari sau egale cunodul respectiv. Avantajul major al arborilor binari de cautare consta in relatarea algorimilor de sortare sialgoritmilor de cautare care folosesc metoda traversarii arborilor in ordine, poate
fi foarteeficienta.Arborii binari de cautare sunt un fundament pentru structura de date folosinta inconstruirea mai multor structuri de date abstracte, cum sunt de exemplu colectiile de datesau tablourile asociate. 2.1. Operatii. >>> Toate operatile intr-un arbore binar realizeaza mai multe operatii ce se folosesc de uncomparator, care este o subrutina care calculeaza ordinea totala a oricaror doua valori.Inimplementatiilre simple a arborilor binari de cautare, un program de cele ami multe oritrimite un raspuns catre comparator cand acesta creeaza un arbore, sau altfel explicand, inlimbajele de programare care suporta tipul polimorfism parin folosirea valorilor ca fiindde tip comparabil. 2.2. Cautarea >>> Cautarea unei valori date intr-un arbore binar de cautare este un proces care poate firealizat recursiv in ordinea valorilor care sunt stocate.Se incepe prin axaminarea noduluiradacina.Daca valoarea care este cautata este egala cu valoare nodului radacina, atunciinseamna ca valoarea exista in arbore.Daca valoarea ce trebuie cautata este mai micadecat valoarea nodului radacina, atunci aceasta trebuie sa fie in sub-arborele stang, astfel printr-o cautare recursiva se va cauta valoarea in structura acestui subarborestang.Asemenator,daca valoarea care trebuie cautata este mai mare decat valoareadetinuta de nodul radacina, atunci aceasta trebuie sa fie in structura subarboreluidrept.Daca procedura de cautare a ajuns la un nod curent si valoarea cautata nu s-a gasit,atunci valoarea nu se afla in arbore.O compararatie ar putea fi executata cu o cautare binara, care efectueaza o cautarea a valorii printr-o metoda foarte asemanatoare, insaabordeaza un acces aleator in tabloul valorilor prin urmarirea legaturilor.Aici este prezentat un algoritm de cautare implemntata in limbajul Python:
defcautare_arbore_binar(nod, termen): if nod is None: return None # termenul nu este gasitif termen < nod.termen: return cautare_arbore_binar(nod.stanga, termen) else if termen > nod.termen: return cautare_arbore_binar(nod.dreapta, termen)else: # termenul are valoarea egala cu valoarea noduluireturn nod.value # termenul este gasit. Aceasta operatie necesita un timp de executie O(log n) in cazul in care cerinta impune ocautare de o complexitate medie, insa daca se comporta ineficient atunci algoritmul se vaexecuta intr-un timp de O(n) - adica arborele este neechilibrat si re-aranzeaza o listasortata. 2.3. Insertia. >>> Metoda de insertie se efectueaza asemanator ca si metoda de cautare.Se incepe prinverificarea valorii ce trebuie cautata cu valoarea nodului radacina.Daca valoarea cetrebuie cautata nu este egala cu valoarea nodului radacina, atunci procedura de cautare seva continua ori in structura sub-arborelui stang, ori in structura subarborelui drepta ca sila procedeul de cautare.Eventual, se poate extinde un nod extern si se va adauga valoareace trebuie cautata ca fiind un nod-fiu drept sau stang.Cu alte cuvinte, se poate examinanodul radacina si se ve insera recursiv un nou nod in cadrul structurii sub-arborelui stangdaca valoarea ce trebuie cautata este mai mica sau egala cu valoarea nodului radacina,sau in structura subarborelui daca valoarea cautata este mai mare ca si valoaera noduluiradacina. Aici este prezentat un exeplu de algoritm de insertie implementat in cod C:
void InsertieNod(struct nod **nod_ptr, struct nod *nodNou) { struct nod *nod = *nod_ptr; if (nod == NULL)*nod_ptr = nodNou; else if (nodNou->valoare <= nod->valoare) InsertieNod(&nod->stanga, nodNou); else InsertieNod(&nod->dreapta, nodNou); } Procedura "destructiva" de deasupra modifica variabil structura arborelui.Ea utilizeazanumai un spatiu de memorie constant, insa se pier versiunile anterioare realizaet cuajutorul algorimului.Alternativ, ca in exemplu de mai jos, se poate reconstrui toatenodurile bunici ale nodului inserat.Orice referinta la nodul radacina al arborelui initialramane valida numai realizand o structura de date persistenta: def insertie_arbore_binar(nod, termen, valoare): if nod is None: return NodArbore(None, termen, valoare, None) if termen == nod.termen: return NodArbore(nod.stanga, termen, valoare, nod.dreapta) if termen < nod.termen: return NodArbore((insertie_arbore_binar(nod.stanga, termen, valoare), nod.termen,nod.valoare,nod.dreapta)else: return NodArbore((nod.stanga, nod.termen, nod.valoare, insertie_arbore_binar(nod.dreapta,termen, valoare)) Partea este reconstruita folosind un spatiu de (log n) locatii de memorie in cazul in carecerinta este de
dificultate medie, iar in cel mai rau caz flolseste uin spatiu de (n).Infiecare versiune, aceasta operatie necesita un timp direct proportional cu inaltimea arborelui in cazul in care acesta functioneaza ineficient, adica se efectueaza intr-un timpde O(log n) in cazul (valabil pentru toti arbori creati) in care se rezolva o problema dedificultate medie, insa aceasta va avea (n2) in cazul in care se comporta ineficient.O alta cale de a realiza insertia este prin inserarea in ordinea inserarii noii valori inarbore, aceasta valoare este prima data comparata cu valoarea nodului radacina.Dacaaceasta valoare este mai mica decat valoarea nodului radacina atunci ea va fi comparatacu valoarea nodului-fiu stang.Daca valoarea este mai mare, atunci ea va fi comparata cuvaloarea nodului-fiu drept.Acest pocedeu continua pana cand npoua valoarea inserataeste comparata cu un nod curent fiindu-i adaugata ca un nodfiu (descendent) drept saustang.Pot fi mai multe metode de a insera o valoare in cadrul structurii arborilor binari decautare, insa aceasta este singura metoda care ataseaza nodurilor curente ale arborelui incare se face insertia alte noi noduri fara sa fie afectata structura acestuia. 2.4. Suprimarea unui nod. >>> Aici sunt mai multe cazuri care trebuiesc luate in vedere. * Stergerea unui nod curent: Suprimarea (stergerea) unui nod curent care n-aredescendenti se face usor. * Stergerea unui nod care are un nod-fiu: Se va sterge nodul si se va inlocui cu nodul-fiu. * Stergerea unui nod cu doua noduri-fii: Se presupunem ca nodul este numit N.Se vainlocui valoarea nodului N cu valoarea unui succesor parcurs in ordine (cel mai dinstanga nod-fiu al sub-arborelui drept) sau cu a predecesorului (cel mai din dreapta nod-fiual subarborelui stang).Imaginea arata modalitatea de suprimare a nodurilor curente ale arborelui din mijloc.Astfel se
gaseste oricare predecesor sau succesor al arborelui parcurs in ordine, mutarealui in locul nodului N si stergerea lui.Astfel fiecare dintre aceste noduri trebuie sa aibamai putin de doua noduri-fii (caci altfel el nu poate fi un succesor sau predecesor in cazulin care arborele in care se face suprimarea este parcurs in ordine), el putand fi stersfolosind cele doua cazuri prezentate mai sus.Intr-o buna implementare, in general esterecomandat sa se evite folosirea unuia dintre aceste noduri , deoarecea suprimarea lui ar putea dezechilibra arborele.Aici este prezentat un algoritm C++ care foloseste metoda destructiva a suprimarii (se va presupune ca nodul care trebuie suprimat a fost deja gasit printr-o metoda de cautare): void SuprimareNod(struct nod*& nod) { if (nod->stanga == NULL) {struct nod* temp = nod;nod = nod->dreapta; delete temp; } else if (nod->dreapta == NULL) { struct nod* temp = nod; nod = nod->stanga;delete temp; } Else { // Predecesor parcurs in ordine (cel mai din dreapta nodfiu al sub-arborelui stang) // Nodul are doua noduri-fii - aceasta fiind limita maxima a sub-arborelui stangstruct nod*& temp = nod->stanga; while (temp->dreapta!= NULL) { temp = temp->dreapta;
} nod->valoare = temp->valoare; SuprimareNod(temp); } } Deoarece aceasta operatie nu efectueaza intodeauna o traversare in adancime (catrenodurile curente), poate fi oricand o posibilitate, deoarece ea cere in cazul in carealgoritmul de suprimare se comporta ineficient un timp care-i proportional cu inaltimeaarborelui. 2.5. Traversarea arborilor binari de cautare. >>> Dupa ce arborele binar de cautare a fost creat, elementele lui pot fi pastrate in ordine printravesrsarea recursiva a sub-arborelui stang, verifcarea nodului radacina, apoi treversarearecursiva a sub-arbrelui drept.Arborele ar putea fi traversat in pre-ordine sau in post-ordine.def traversarea_arborelui_binar(nodarbore): if nodarbore is None: returnstanga, valoarenod, dreapta = nodarboretraversarea_arborelui_binar(stanga)visit(valoar enod)traversarea_arborelui_binar(dreapta) Perioada de traversare a algoritmului necesita un timp de (n) atunci cand acesta dorestesa verifice fiecare nod.Algoritmul de traversare se executa intr- un timp determinat defunctia O(n) 2.6. Sortarea arborelui binar de cautare. >>> Un arbore binar de cautare poate fi implementat ca un simplu insa ineficient algoritm desortare.Asemenator cu metoda sortarii prin inserie, cand se doreste a se inseara
toatevalorile termenilor aceasta inseamna ca aceste valori intr-o noua structura de data ordonata - in acest caz un arbore binar de cautare, atunci aceasta noua structura de date seva traversa in ordine oferind rezultatele:def construieste_arbore_binar(valori): arbore = Nonefor v in valori: arbore = inasereaza_arborele_binar(arbore, v)return arbore def traverseaza_arbore_binar(nodarbore): if nodarbore is None: return []else: stanga, valoare, dreapta = nodarborereturn (traverseaza_arbore_binar(stanga) + [valoare] +traverseaza_arbore_binar(dreapta)) Perioadade timp in care construieste_arbore_binar se comporta ineficient este (n2) --daca se lucreaza cu o lista cu valori sortate, el se poate adauga intr-o lista inlantuita farasa aiba fii stangi. De exemplu, structura_arbore_binar([1,2,3,4,5]) arborele devine (Niciunul, 1,(Nici unul, 2,(Nici unul, 3,(Nici unul, 4,(Nici unul, 5, Nici unul))))). Aici este o varietate de scheme pentru a acoperii aceste "Nici unu"-uri cu arbori binarisimpli.Cel mai adesea utilizat este arborele binar echilibrat de cautare.Daca aceeasi procedura este executata folosind de exemplu un arbore, atunci comportarea ineficienta pentru toti este O(nlog n) care este o optiune asimptotica pentru o sortare princomparare.In practica, performanta scazuta in lucrul cu memoria "cache" si spatiul dememorie folosit in cadrul unei sortari dedicate pe lucrul cu arbori (in particular pentrualocarea nodurilor) realizeaza o sortare mult mai inferioara in comparatie cu alte sortarioptime asimptotice cum sunt sortarea rapida ("quick sort") si "heapsort" cand
acestealucreaza cu liste statice sortate.Oricum, aceaste este una din cele mai eficiente metode desortare incrementala, prin fpatul ca se adauga termeni intr-o lista intr-o maniera rapidaatat timp cat lista este sortata. 2.7. Optimizarea arborilor binari de cautare. >>> Daca se doreste sa nu se modifice structura unui arbore binar, si daca se cunoaste metodade accesare a fiecarui termen din arbore, atunci se poate constru un arbore binar decautare optimizat, care va fi de fapt un arbore de cautare unde costul de vizualizare a unuitermen (exceptand costul de timp al metoda de cautare) este minimizat.Sa presupunem ca se cunoaste numarul de elemente din arbore, inclusiv se stie ca pentrufiecare element proportia de timp pentru a fi vizualizat.Astfel se poate utiliza o solutie dea programare dinamica, detaliata in sectiunea 15.5 din cartea Introductiona of algorithmsde Thomas H.Cormec, pentru a construi un arbore de cautare care functioneaza dupa ometoda de cautare foarte eficienta.