Sunteți pe pagina 1din 75

3.

Structuri de cutare
Problema cea mai frecvent ntlnit n practic este, probabil, cea a regsirii rapide a unor informaii. De exemplu, cutarea unui cuvnt n dicionar sau a unui numr n cartea de telefon. De obicei, informaiile sunt reprezentate sub forma unor nregistrri, fiecare nregistrare coninnd un cmp numit cheie, ce permite identificarea nregistrrii (de exemplu, ntr-un dicionar nregistrrile sunt formate dintr-un cuvnt ce reprezint cmpul cheie, din pronunia i definiia corespunztoare cuvntului respectiv). O problem de cutare const n identificarea unei nregistrri cu cheia specificat, n scopul prelucrrii informaiilor corespunztoare. Presupunem c nregistrrile au chei distincte; cazul nregistrrilor avnd chei identice poate fi tratat n diverse moduri, n funcie de aplicaie (de exemplu, fiecare nregistrare poate conine o list simplu nlnuit format din nregistrri avnd aceeai cheie comun). Definim o nregistrare astfel : Inregistrare = record Cheie: TipCheie; Inf: TipInformaie; end; 3.1. Cutarea secvenial S presupunem c nregistrrile sunt memorate ntr-un vector global R de dimensiune n. Pentru a identifica nregistrarea cu cheia specificat x, o prim soluie ar fi de a compara secvenial cheia x cu cheile nregistrrilor din R: function Cutare-Secvenial (x: TipCheie): byte; //funcia ntoarce indicele nregistrrii din R cu cheia x, dac o astfel //de nregistrare exist, altfel ntoarce 0 var i: byte; begin i := 1; while (i n) and (r[i].cheie x ) do inc(i); if (i > n) then Cutare-Secvenial := 0
65

end;

else Cutare-Secvenial := i;

n cazul n care cheia nu se gsete n vector se fac exact n comparaii. n cazul n care cheia se gsete n vector, n ipoteza c nregistrrile sunt cutate cu aceeai probabilitate (1/n), se fac n medie 1 n+ 1 (1+ 2+...+ n) = comparaii. n 2 Deci algoritmul de cutare secvenial este de O(n). Cutarea secvenial poate fi adaptat n mod natural pentru cazul n care nregistrrile sunt reinute ntr-o list simplu nlnuit, complexitatea algoritmului rmnnd neschimbat. Observaie La fiecare pas indicele i este comparat cu n, dimensiunea tabloului. Pentru a evita aceste comparaii, adugm artificial o component suplimentar n tablou, pe care o iniializm cu x, valoarea cutat. 3.2. Cutarea binar * Dac numrul de nregistrri este mare, putem diminua timpul de cutare presupunnd c nregistrrile sunt n ordinea cresctoare a cheilor i aplicnd o strategie de tip "divide et impera ". Comparm cheia cutat x cu cheia nregistrrii din mijloc. n caz de egalitate, cutarea se ncheie cu succes, altfel njumtim spaiul de cutare, determinnd jumtatea n care exist anse s se gseasc cheia x i relund procedeul pentru aceast jumtate. function Cutare-Binar (x: TipCheie): byte; //funcia ntoarce indicele nregistrrii din R cu cheia x, dac o astfel //de nregistrare exist, altfel ntoarce 0 var St, Dr, Mijloc: byte; gsit: boolean; begin St := 1; Dr := n; gsit := false; while (St Dr) and not gsit do
*

Programul Cutare-Binar de la sfritul capitolului curent implementeaz algoritmul de cutare binar pe un fiier cu tip. 66

begin Mijloc := (St+Dr) div 2; if R[Mijloc].cheie = x then gsit := true else if R[Mijloc].cheie > x then Dr := Mijloc-1 else St := Mijloc+1; end; if gsit then Cutare-Binar := Mijloc else Cutare-Binar := 0; end; Observaii 1. Algoritmul poate fi descris foarte elegant recursiv. 2. Strategiei de cutare binar i se poate asocia un arbore binar astfel : - rdcina arborelui este indicele elementului din mijloc, (n+1) div 2; - subarborele stng este format din arborele binar corespunztor cutrii n irul elementelor cu indici mai mici dect rdcina; - subarborele drept este format din arborele binar corespunztor cutrii n irul elementelor cu indici mai mari dect rdcina.

Fig. 1. De exemplu, pentru n=15 arborele binar asociat algoritmului de cutare binar este

67

Fig. 2. pe fiecare nivel i{0,1,2,3} fiind situate 2i vrfuri. Pentru n = 10 arborele este :

Fig. 3. primele 3 niveluri fiind complete, al patrulea coninnd 3 vrfuri. Observaii 1. Parcurgnd n inordine arborele, obinem irul indicilor ordonat cresctor. 2. Pe fiecare nivel i n arbore exist 2 i vrfuri, cu excepia ultimului nivel care este incomplet n cazul n care n nu este de forma 2k-1. Folosind aceste observaii, putem calcula nlimea arborelui asociat algoritmului de cutare binar n funcie de n, dimensiunea spaiului de cutare. Dac notm cu h nlimea arborelui, atunci : 1+2+...+2h-1 < n 1+2+...+2h 2h-1 < n 2h+1-1 2h n < 2h+1 h log2n < h+1 h = [log2n] Algoritmul de cutare binar efectueaz cel mult o comparaie pe fiecare nivel n arbore : -dac elementul cutat nu se gsete n spaiul de cutare, cutarea se termin pe un nod terminal, deci pe nivelul h sau h-1. -dac elementul cutat se gsete n spaiul de
68

cutare, n funcie de poziia sa, cutarea se poate termina pe orice nivel, deci sunt necesare cel mult h+1 comparaii. Putem enuna o teorem de caracterizare a complexitii algoritmului de cutare binar. Teorem Algoritmul de cutare binar efectueaz cel mult [log2n]+1 comparaii, n cazul unei cutri cu succes, respectiv [log2n] sau [log2n]+1 comparaii, n cazul unei cutri fr succes, unde n este dimensiunea spaiului de cutare. Deci complexitatea algoritmului de cutare binar este de O(log2n). Observaie Problema enunat presupune c mulimea de valori n care se face cutarea este static (deci nu suport inserri sau tergeri de elemente) i deci arborele binar asociat rmne total echilibrat, cu nlimea h = [log 2n]. De asemeni, n analiza complexitii am presupus c orice valoare este cutat cu aceeai probabilitate. 3.3. Cutarea pe arbori binari de cutare * Utilizarea arborilor constituie o metod simpl de cutare ntr-o mulime dinamic i reprezint unul din algoritmii fundamentali n informatic. Definiie Un arbore binar de cutare este un arbore binar n care pentru orice nod x cheile nodurilor din subarborele stng sunt mai mici dect cheia lui x, iar cheile nodurilor din subarborele drept al lui x sunt mai mari dect cheia lui x. De exemplu:

Programul Operaii-pe-Arbore-Binari-de-Cutare de la sfritul capitolului curent realizeaz operaiile elementare pe arbori binari de cutare. 69

Fig. 4. Pentru a trata n mod unitar nodurile arborelui, am adugat noduri fictive, notate , care se numesc noduri externe. Astfel, orice cutare fr succes se va termina pe un nod extern. O parcurgere n inordine a unui arbore binar de cutare produce cheile ordonate cresctor. Definiie Numim dicionar o structur abstract de date care implementeaz eficient urmtoarele operaii : search(x): caut cheia x n structura de date; insert(x) : insereaz cheia x n structura de date, dac este posibil (cheia x nu se gsete n structura de date); delete(x) : terge cheia x din structura de date, dac este posibil (cheia x se gsete n structura de date). Arborii binari de cutare constituie o implementare eficient a dicionarelor. Vom utiliza reprezentarea nlnuit a arborilor binari (fiecare nod din arbore va fi o nregistrare cu trei cmpuri: cheia, adresa rdcinii subarborelui stng i adresa rdcinii subarborelui drept) : Arbore = ^NodArbore; NodArbore = record c: TipCheie ; st, dr: Arbore; end ; 3.3.1. Operaia search(x) (cutarea unei valori ntr-un arbore binar de cutare) Comparm valoarea x cu cheia rdcinii rad a arborelui: -dac rad^.c = x, atunci cutarea se ncheie cu succes; -dac x < rad^.c, vom continua cutarea n subarborele stng, altfel vom cuta pe x n subarborele
70

drept.

S observm c fiecare cheie din arborele binar de cutare partiioneaz spaiul de cutare, format din cheile din subarborele corespunztor, n dou mulimi: mulimea valorilor mai mici dect cheia dat (reprezentat prin subarborele stng) i mulimea valorilor mai mari (reprezentat prin subarborele drept). function RSearch (rad: Arbore, x: TipCheie): Arbore; // funcia caut recursiv cheia x n arborele cu rdcina rad i //ntoarce nil dac x nu se gsete n arbore, respectiv adresa nodului //cu cheia x, n caz contrar begin if (rad =nil) or (rad^.c = x) then RSearch := rad else if x < rad^.c then RSearch := Search(rad^.st, x) else RSearch := RSearch(rad^.dr, x); end; O variant iterativ a aceleiai funcii este : function Search (rad: Arbore, x: TipCheie): Arbore; begin while (rad nil) and (rad^.c x) do if x < rad^.c then rad := rad^.st else rad := rad^.dr; Search := rad; end; Analiznd complexitatea algoritmului, observm c pe fiecare nivel n arbore se face cel mult o comparaie. n cazul cel mai defavorabil, cnd nodul cutat se gsete pe ultimul nivel, numrul de comparaii este egal cu h+1, unde h este nlimea arborelui. Deci complexitatea algoritmului n cazul cel mai defavorabil este de O(h). n cazul unui arbore degenerat obinem n cazul cel mai defavorabil un algoritm de O(n). S analizm complexitatea algoritmului n medie,
71

n ipoteza c orice valoare este cutat n arbore cu aceeai probabilitate (1/n). Notm cu Cn numrul de comparaii efectuate pentru cutarea cu succes a unei valori ntr-un arbore binar de cutare cu n noduri. Observm c Cn reprezint lungimea drumurilor interne In raportat la n.

Termenul n-1 provine din observaia c rdcina arborelui contribuie cu 1 la lungimea drumului de la rdcin la nod pentru toate celelalte n-1 noduri interne.Termenul n (Ck 1 + Cn k) k=1 se obine din faptul c rdcina are n stnga un subarbore cu k-1 noduri, iar n dreapta un subarbore cu n-k noduri (cheia rdcinii fiind cea de a k valoare n irul ordonat al cheilor nodurilor arborelui). Printr-un calcul analog celui de la analiza complexitii medii a algoritmului de sortare rapid obinem : 2 n Cn = n 1+ Ck 1 n k=1 nmulind ambii membri cu n obinem : n nCn = n (n 1) + 2 Ck 1 k=1 Scdem din aceast formul, formula obinut n cazul n-1, adic n1 (n 1)Cn 1 = (n 1) (n 2) + 2 Ck 1 k=1 Obinem : nCn (n 1)Cn 1 = n (n 1) (n 1) (n 2) + 2Cn 1 nCn = (n + 1)Cn 1 + 2n mprim ambii membri cu n(n+1) :
72

R |1, n C =S n1+ 1 (C | =1 T n k
n

dac n=1 dac n> 1

k1+Cn k),

Cn Cn 1 2 2 Cn 2 2 = + = + + = n+ 1 n n+ 1 n+ 1 n 1 n 2 2 Cn 3 2 C2 n 2 = + + + =... = + n+ 1 n n 2 n 1 3 k= 3 k + 1
n1 1 2 dx = 2ln n 1 x k=1 k Deci complexitatea medie a cutrilor cu succes este de O(log n). n mod analog se poate analiza complexitatea medie a cutrilor fr succes.

3.3.2. Operaia insert(x) (inserarea unui nod ntr-un arbore binar de cutare) Fiind dat o cheie x, pentru a insera cheia x ntr-un arbore binar de cutare cu rdcina rad, efectum mai nti o cutare a valorii x n arbore. Dac gsim un nod cu cheia x inserarea va fi abandonat, deoarece ntr-un arbore binar de cutare nu este permis existena a dou noduri cu chei egale. n cazul n care cutarea se ncheie fr succes, nodul cu cheia x va fi inserat ca fiu stng sau drept al nodului la care s-a terminat cutarea (n funcie de relaia dintre x i cheia nodului respectiv). Deci procedura de inserare va fi o variant uor modificat a funciei de cutare. procedure Insert (var rad: Arbore, x: TipCheie); //insereaz, dac este posibil, valoarea x n arborele cu rdcina rad var p, q: Arbore; gata: boolean ; // indic sfritul operaiei de inserare begin //creeaz un nod terminal cu cheia x new(p); p^.c := x; p^.st := nil; p^.dr := nil; if rad = nil then //arborele este vid rad := p else begin q := rad; // caut n arbore un nod cu cheia x
73

gata := false; //devine true dac gsim un nod cu cheia x sau am inserat p while not gata do if q^.c = x then //abandonm inserarea, cheia x este n arbore begin dispose(p); gata := true; end else if q^.c > x then if q^.st = nil then //cheia x nu este n arbore begin q^.st := p; // inserm p ca fiu stng al lui q gata := true end else //cutm n subarborele stng q := q^.st else if q^.dr = nil then //cheia x nu este n arbore begin q^.dr := p; //inserm p ca fiu drept al lui q gata := true; end else //cautm n subarborele drept q := q^.dr; end; end;
74

Propunem ca exerciiu varianta recursiv a procedurii de inserare. Inserarea unei valori ntr-un arbore binar de cutare fiind o variant uor modificat a algoritmului de cutare, toate observaiile referitoare la complexitate rmn valabile. 3.3.3. Operaia delete(x) (tergerea unui nod de cheie dat dintr-un arbore binar de cutare) Ca i n cazul operaiei de inserare, tergerea este precedat de o cutare a unui nod cu cheia dat x n arborele binar. n cazul n care un astfel de nod este gsit, s l notm cu p, iar cu t s notm printele su. Exist trei situaii posibile : a) Nodul p este un nod terminal (fig. 5. ) :

Fig. 5. n acest caz tergerea este simpl: nlocuim legtura spre p cu nil. b) Nodul p are un singur fiu (fig. 6.) :

Fig. 6. nlocuim legtura spre p cu o legtur spre unicul fiu al lui p. c) Nodul p are exact doi fii Vom reduce acest caz la una din situaiile a) sau b), conservnd proprietatea de arbore binar de cutare.
75

Pentru aceasta, vom determina nodul cu cea mai mare cheie din subarborele stng 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 stng al lui p, are cheia mai mic dect nodurile din subarborele drept i mai mare dect a nodurilor din subarborele stng, deci arborele astfel obinut este arbore binar de cutare. Se poate proceda n mod analog, pstrnd consistena arborelui binar de cutare i nlocuind cheia lui p cu cheia nodului cu cea mai mic cheie din subarborele drept al lui p, notat min, care nu are fiu stng i tergnd apoi nodul min. procedure delete (var rad: Arbore, x: TipCheie); //procedura terge nodul cu cheia x din arborele binar de cutare cu //rdcina rad, dac acesta exist var p, t, fiu, max, tmax: Arbore; begin // caut n arbore nodul cu cheia x, reinnd i t, printele acestuia p := rad; t := nil; while (p nil) and (p^.c x) do begin t := p; if x < p^.c then p := p^.st else p := p^.dr; end; if p = nil then writeln(' Nodul cu cheia ', x ,' nu se gsete n arbore ') else begin if (p^.st nil) and (p^.dr nil) then //cazul c). begin //determin max i tmax, printele su tmax := p; max := p^.st; while max^.dr nil do
76

stng

begin tmax := max; max := max^.dr end; p^.c := max^.c; // cheia lui p este nlocuit cu cheia lui max p := max; // max devine nod de ters t := tmax; end; // acum nodul de ters p are cel mult un fiu if p^.st nil then fiu := p^.st else fiu := p^.dr; if t nil then if t^.st = p then // p este fiu else t^.st := fiu // p este fiu drept t^.dr := fiu // p este chiar rdcina

arborelui

else

end;

rad := fiu; dispose(p); end;

Deoarece tergerea efectiv a nodului se face n timp constant, complexitatea algoritmului de tergere se reduce la complexitatea algoritmului de cutare a cheii x n arbore. n concluzie, timpul de execuie a operaiilor search, insert, delete depinde de forma arborelui de cutare, fiind n cel mai defavorabil caz de O(h), unde h este nlimea arborelui. n cazul n care arborele este degenerat, complexitatea algoritmilor de cutare, inserare i tergere devine liniar. n cazul n care arborele este echilibrat (nlimea lui este aproximativ log2n), operaiile devin eficiente. Complexitatea n medie a algoritmilor de cutare, inserare, tergere este de O(log n). 3.3.4. Alte operaii pe arbori binari de cutare
77

3.3.4.1. Aflarea nodului cu cheie minim . Din definiia arborilor binari de cutare deducem c nodul cu cheia cea mai mic este nodul cel mai din stnga. function minim (rad: Arbore): Arbore; //funcia ntoarce adresa nodului cu cheia cea mai mic din arborele //nevid cu rdcina rad begin while rad^.st nil do rad := rad^.st; minim := rad; end; 3.3.4.2. Aflarea nodului cu cheie maxim. Conform proprietilor arborilor binari de cutare, nodul cu cheia cea mai mare din arbore este nodul cel mai din dreapta. function maxim (rad: Arbore): Arbore; //funcia maxim ntoarce adresa nodului cu cheia cea mai mare din //arborele nevid cu rdcina rad begin while rad^.dr nil do rad := rad^.dr; maxim := rad; end; 3.3.4.3 Determinarea succesorului inordine al unui nod specificat Numim succesorul inordine al unui nod dat x, nodul cu cea mai mic cheie mai mare dect cheia lui x. Deci succesorul inordine al unui nod este nodul care urmeaz nodului respectiv n parcurgerea inordine a arborelui binar de cutare. Dac nodul x are fiu drept, atunci succesorul inordine al lui x este nodul cu cea mai mic cheie din subarborele drept al lui x (minim(x^.dr)). Dac nodul x nu are fiu drept, atunci succesorul inordine al lui x este cel mai apropiat ascendent al lui x al crui fiu stng este x sau un ascendent al lui x (dac exist), sau nil (dac un astfel de nod nu exist, adic x este nodul cu cheia
78

cea mai mare din arbore). Deci cel mai apropiat ascendent al lui x care l conine pe x n subarborele stng. De exemplu, pentru arborele din figura 7

Fig. 7. - succesorul inordine al nodului cu cheia 50 este nodul cu cheia 60 - succesorul inordine al nodului cu cheia 80 este nodul cu cheia 100 - succesorul inordine al nodului cu cheia 200 nu exist. Deci, pentru a determina succesorul inordine al unui nod specificat este necesar ca n structura nodului s reinem i adresa nodului printe: Arbore = ^NodArbore; NodArbore = record c: TipCheie ; st, dr, p: Arbore; end ; function succesor (x: Arbore): Arbore; //funcia ntoarce succesorul inordine al lui x dac acesta exist, sau //nil n caz contrar var y: Arbore; begin if x^.dr nil then succesor:=minim(x^.dr) else begin y := x^.p;
79

end;

while (y nil) and (x = y^.dr) do begin //x este fiul drept al lui y x := y; y := x^.p; end; succesor := y end;

3.3.4.4 Aflarea predecesorului inordine al unui nod specificat Numim predecesorul inordine al unui nod dat x, nodul cu cea mai mare cheie mai mic dect cheia lui x. Deci predecesorul inordine al unui nod este nodul care precede nodul specificat n parcurgerea inordine a arborelui. Dac x are fiu stng, atunci predecesorul inordine al lui x este nodul cu cheia cea mai mare din subarborele stng (maxim(x^.st)). Dac x nu are fiu stng, predecesorul inordine al lui x este cel mai apropiat ascendent al lui x al crui fiu drept este x sau un ascendent al lui x (dac un astfel de nod nu exist). Pentru exemplul din figura 7: - predecesorul inordine al nodului cu cheia 100 este nodul cu cheia 80 - predecesorul inordine al nodului cu cheia 60 este nodul cu cheia 50 - predecesorul inordine al nodului cu cheia 10 nu exist. function predecesor (x: Arbore): Arbore; //functia ntoarce predecesorul inordine al lui x, dac exist sau nil // n caz contrar var y: Arbore; begin if x^.st nil then predecesor := maxim(x^.st) else begin y := x^.p;
80

end;

while (y nil) and (x = y^.st) do begin // x este fiul stng al lui y x := y; y := x^.p; end; predecesor := y end;

3.4. Arbori binari de cutare optimali Ne punem problema s construim un arbore binar de cutare optimal pentru o mulime static de chei a1<a2< ...<an. Deci, odat arborele construit, nu se vor efectua operaii de inserare sau tergere, ci numai operaii de cutare. Pentru evaluarea optimalitii unui arbore binar de cutare se impune definirea unei funcii cost pe mulimea tuturor arborilor binari de cutare cu cheile a1<a2<...an. Notm cu pi probabilitatea de a cuta valoarea a i. n cazul n care s-ar efectua numai cutri cu succes, costul arborelui ar fi:
i =1

pi (nivel (ai ) + 1)

deoarece pentru cutarea valorii a i n arbore, numrul de comparaii necesare este egal cu numrul de noduri de pe drumul de la rdcin la nodul cu cheia a i, deci, n ipoteza c nivelurile sunt numerotate de la zero, este egal cu nivel (ai)+1. Dar nu se execut numai cutri cu succes, de aceea trebuie s includem n costul arborelui i costul cutrilor fr succes. S partiionm mulimea valorilor care nu sunt n arbore n n clase, astfel: C0 ={ x x <a1} Cn ={ x x > an} Ci ={ x ai < x <ai+1}, i{1,2,...,n-1}. Notm cu qi probabilitatea ca valoarea cutat s fie n clasa Ci, i{0,1,...,n}.
81

i =1 n

pi + qi = 1
i=0

Costul cutrilor fr succes va fi:


i=0

qi nivel (nod_ externi )

deoarece orice cutare fr succes se termin pe un nod extern. Nodul extern i, 0 i n, (considernd numerotarea de la stnga la dreapta) este nodul corespunztor clasei Ci. Definiie Numim arbore binar de cutare optimal pentru mulimea a1<a2<...<an un arbore binar de cutare care minimizeaz expresia p (nivel (a ) + 1) + q nivel (nod_ extern ) i =1 i=0
i i i i

De exemplu, fie a1=5, a2=10, a3=20. Arborii binari de cutare pentru aceast mulime de valori sunt:

Fig. 8. Dac vom considera p1=p2=p3=1/7 i q0=q1=q2=q3=1/7, deci orice valoare este cutat n arbore cu aceeai probabilitate, obinem: cost(arbore a.) = 15/7 cost(arbore b.) = 13/7 cost(arbore c.) = 15/7 cost(arbore d.) = 15/7 cost(arbore e.) = 15/7 Aa cum era de ateptat, arborele binar de cutare optimal este b. Dar, pentru p1=0.5; p2=0.1; p3=0.05 i q0=0.15; q1=0.1; q2=0.05; q3=0.05 obinem : cost(arbore a.) = 2.65
82

cost(arbore b.) = 1.9 cost(arbore c.) = 2.05 cost(arbore d.) = 1.5 cost(arbore e.) = 1.6 Deci, arborele binar de cutare optimal este arborele d. Pentru determinarea arborelui binar de cutare optimal pentru o mulime dat de valori a 1<a2<...<an, o prim soluie ar fi s generm toi arborii binari de cutare pentru mulimea de valori dat, s calculm costul fiecrui arbore i s selectm arborele de cost minim. ns n capitolul introductiv am demonstrat c numrul arborilor binari cu n vrfuri date este B(n) = O(
4 n
n 3/2

ceea ce face ca aplicarea acestei metode s nu fie posibil pentru valori mari ale lui n. Fcnd unele observaii referitoare la proprietile arborilor binari de cutare optimali, vom descrie un algoritm mult mai eficient. Notaii:i,j{1,2,...,n} arborele binar de cutare optimal pentru ai+1,...,a; j dac i<j arborele vid; dac i= j Ti, j = nedefinit; dac i>j

ci, j

ri, j

wi, j

R | S | T R |cost(T ); dac i<j =S 0; dac i= j | nedefinit; dac i>j. T R |rdcina arborelui T ; dac i<j =S 0; dac i= j | nedefinit; dac i>j T R |q + (q + p )=ponderea arborelui T =S q; | nedefinit; T
i, j i,j i j i k= i+1 k k

i,j;

dac i<j dac i= j dac i>j.

T0n

Conform acestor notaii: este arborele binar de cutare optimal pentru


83

a1, ... ,an; c0n este costul arborelui binar de cutare optimal T 0n; r0n este rdcina arborelui binar de cutare optimal pentru a1, ... ,an; w0n =1. Fie Tij este un arbore binar de cutare optimal pentru ai+1, ... ,aj i rij = k (i < k j).Tij are doi subarbori : subarborele stng, care conine cheile a i+1,...,ak-1, i subarborele drept, care conine cheile a k+1, ... ,aj.

Atunci: cij = pk + cost(S) + cost( D) + pondere(S) + pondere(D) pondere(S) = pondere(Ti, k 1) = wi, k 1 pondere( D) = pondere(Tk, j ) = wk, j pk + wi, k 1 + wk, j = wi, j Deci, cij = cost(S) + cost( D) + wij . Din aceast relaie deducem c T ij este optimal dac i numai dac S i D sunt optimali, deci S=T i,k-1 i D= Tkj (altfel nlocuind S sau D cu un arbore de cost mai mic, am obine un arbore pentru ai+1, ... ,aj de cost mai mic dect cij, n contradicie cu ipoteza c Tij optimal). Problema are substructur optimal pe care o putem defini recursiv astfel : ci, j = ci, k 1 + ck, j + wi, j Cum Tij este optimal, rij=k, rdcina sa, este selectat astfel nct wi, j + ci, k 1 + ck, j = min ci, l 1 + cl , j + wi, j ci, k 1 + ck, j = min ci, l 1 + cl , j *
i<l j

Fig. 9.

i<l j

b g q

Vom folosi aceast relaie pentru a obine T 0n n mod bottom-up, conform principiilor programrii
84

dinamice. De exemplu, pentru n=4 i 0 1 2 3 4 a 5 10 15 20 = p 3/16 3/16 1/16 1/16 = q 2/16 3/16 1/16 1/16 1/16 = Utiliznd relaia (*) calculm wi,j, ci,j, ri,j, pentru i,j{0,1,2,3,4}, i j : w0,0=2/1 w1,1=3/1 w2,2=1/1 w3,3=1/1 w4,4=1/1 6 6 6 6 6 c0,0=0 c1,1=0 c2,2=0 c3,3=0 c4,4=0 r0,0=0 r1,1=0 r2,2=0 r3,3=0 r4,4=0 w0,1=8/1 w1,2=7/1 w2,3=3/1 w3,4=3/1 -----------6 6 6 6 -----------c0,1=8/1 c1,2=7/1 c2,3=3/1 c3,4=3/1 -----------6 6 6 6 r0,1=1 r1,2=2 r2,3=3 r3,4=4 w0,2=12/ w1,3=9/1 w2,4=5 ----------- -----------16 6 c2,4=8 ----------- -----------c0,2=19/ c1,3=12/ r2,4=3 ----------- -----------16 16 --r0,2=1 r1,3=2 w0,3=14/ w1,4=11/ ----------- ----------- -----------16 16 ----------- ----------- -----------c0,3=25/ c1,4=19/ ----------- ----------- -----------16 16 ----r0,3=2 r1,4=2 w0,4=1 ----------- ----------- ----------- -----------c0,4=2 ----------- ----------- ----------- -----------r0,4=2 ----------- ----------- ----------- -----------------Deci arborele binar de cutare optimal este:

85

Fig. 10. Algoritm de determinare a unui arbore binar de cutare optimal:* procedure calcul; //calculeaz r, c, w; //tablourile r, c, w, p, q sunt considerate variabile globale var i, j, k, t, d: byte; begin for i := 0 to n-1 do //iniializare begin w[i, i] := q[i]; c[i, i] := 0; r[i, i] := 0; w[i, i+1] := q[i]+q[i+1]+p[i+1]; c[i, i+1] := w[i, i+1]; r[i, i+1] := i+1; end; c[n,n] := 0; r[n,n] := 0; w[n,n] := q[n]; for d := 2 to n do for i := 0 to n-d do begin j := i+d; w[i, j] := w[i, j-1]+p[j]+q[j]; k := i+1; for t := i+2 to j do if c[i, k-1]+c[k, j] > c[i, t-1]+c[t, j] then k := t; r[i, j] := k; c[i, j] := c[i, k-1]+c[k, j]; end; end; Observaie Construirea unui arbore binar de cutare optimal
*

Programul Arbori-Binari-de-Cutare-Optimali de la sfritul capitolului curent genereaz un arbore binar de cutare optimal pentru o mulime static de chei, probabilitile de cutare a valorilor fiind cunoscute. 86

prin metoda programrii dinamice are complexitatea timp de O(n3). Se poate mbunti timpul de execuie a algoritmului, utiliznd un rezultat datorat lui D. E. Knuth, conform cruia putem restrnge domeniul n care se caut valoarea ce minimizeaz expresia lui c ij de la intervalul [i+1, j] la intervalul [r i,j-1; ri+1,j]. n acest caz procedura de calcul va fi O(n2). 3.5. Arbori echilibrai n paragraful precedent am prezentat o metod de construcie a unui arbore binar de cutare optimal pentru o mulime static de chei a 1, a2, ... , an. Din pcate, dac mulimea de chei este dinamic, deci se execut i operaii de inserare, respectiv tergere a unor chei din arbore, structura dinamic a arborelui binar de cutare obinut poate conduce la algoritmi de O(n), n cazul cel mai defavorabil. Ideea este de a lucra cu arbori echilibrai, care s asigure un timp de execuie pentru operaiile de cutare, inserare i tergere de O(log n), att n medie, ct i n cazul cel mai defavorabil. 3.5.1. Arbori Adelson-Velskii-Landis (AVL) Arborii AVL, introdui de Adelson-Velskii i Landis n 1962, reprezint prima structur de date care garanteaz un timp de execuie de O(log n) n cazul cel mai defavorabil pentru operaiile de cutare, tergere i inserare. Definiie Un arbore AVL este un arbore binar de cutare astfel nct pentru orice nod diferena dintre nlimile subarborelui stng, respectiv drept este cel mult 1. Observaii 1. Un arbore binar vid este arbore AVL. 2. Orice subarbore al unui arbore AVL este arbore AVL. Exemplu :
87

Fig. 11. Adelson-Velskii i Landis au demonstrat urmtoarea teorem, care garanteaz c operaia de cutare ntr-un arbore AVL necesit O(log n) comparaii. Teorem nlimea h a unui arbore AVL cu n noduri satisface relaia : h < 1,4404 log2(n+2)-0,328 Operaiile de inserare i tergere din arborele AVL trebuie s fie nsoite de o reechilibrare a arborelui, astfel nct nlimea arborelui s fie permanent de O(log n). Timpul necesar operaiei de reechilibrare trebuie s fie de O(log n), altfel inserrile i tergerile din arbore devin operaii prea costisitoare. Definiie Numim factor de echilibru (notat FE) al unui nod dintr-un arbore binar diferena dintre nlimea subarborelui stng i nlimea subarborelui drept al nodului respectiv. Observaie ntr-un arbore AVL, factorul de echilibru al oricrui nod poate fi -1,0 sau 1. De exemplu, factorul de echilibru al nodului cu cheia 6 este 0 iar, factorul de echilibru al nodului cu cheia 4 este 1. Inserarea ntr-un arbore AVL S considerm, de exemplu, arborele AVL : Fig. 12. Insernd un nod cu cheia 9 obinem urmtorul arbore echilibrat :
88

Fig. 13. Insernd un nod cu cheia 10, obinem un arbore neechilibrat :

Fig. 14. Pentru a reechilibra arborele vom face o rotaie stnga (RS), deoarece arborele este neechilibrat dreapta. Nodul cu cheia 9 va deveni rdcin, nodul cheia 8 va fi fiul stng al rdcinii, iar nodul cu cheia va fi fiul drept.

la n cu 10

Fig. 15. Insernd un nod cu cheia 2, arborele obinut rmne echilibrat :

Fig. 16. Inserm un nod cu cheia 1 :

89

Fig. 17. Arborele a devenit neechilibrat n stnga; efectum o rotaie la dreapta (RD) :

Fig. 18. Insernd un nod cu cheia 5 obinem din nou un arbore neechilibrat :

Fig. 19. n acest caz situaia este mai complicat, deoarece neechilibrul nu apare ntr-o singur direcie. Vom face o rotaie la stnga, urmat de o rotaie la dreapta ( RSD) :

Fig. 20.
90

Insernd un nod cu cheia 3, apoi un nod cu cheia 6 obinem :

Fig. 21. Arborele este echilibrat, dar n urma inserrii unui nod cu cheia 4 obinem :

Fig. 22. Reechilibrarea o vom face cu o rotaie la dreapta, urmat de o rotaie la stnga (RDS) :

Inserarea nodului neechilibrat :

cu

Fig. 23. cheia 7

genereaz

arborele

91

Fig. 24. n urma unei rotaii RSD obinem :

Fig. 25. Propunem n continuare, ca exerciiu, inserarea nodurilor 11 i 12. Din acest exemplu deducem c inserarea nodurilor ntr-un arbore echilibrat poate conduce la dezechilibrarea lui; arborele trebuie s fie atunci reorganizat, modificndu-i-se structura, ori de cte ori o inserare dezechilibreaz un nod. Situaiile n care se produce dezechilibrarea, precum i modul de reechilibrare vor fi prezentate n continuare. 1. Considerm arborele :

92

Fig. 26. Adugnd un nou nod n BS factorul de echilibru al nodului A devine 2, A fiind cel mai apropiat ascendent al noului nod cu factorul de echilibru 2. Aceast situaie se rezolv executnd o rotaie simpl la dreapta:

2. Considerm arborele :

Fig. 27.

Fig. 28. Insernd un nou nod n BD factorul de ecchilibru al nodului A devine -2, A fiind cel mai apropiat ascendent al noului nod cu factorul de echilibru -2. Rezolvm dezechilibrul printr-o rotaie simpl la stnga:
93

3. Considernd arborele:

Fig. 29.

Fig. 30. Adugnd un nou nod n CS factorul de echilibru al nodului A devine 2, A fiind cel mai apropiat ascendent al noului nod cu aceast proprietate. Reechilibrm arborele executnd o rotaie dubl RSD:

Fig. 31. Acelai tip de rotaie dubl se aplic i n cazul n care noul nod se insereaz n CD, obinnd:
94

Fig. 32. 4. Considerm arborele

Fig. 33. Insernd un nou nod n C S factorul de echilibru al nodului A devine -2, A fiind cel mai apropiat ascendent al noului nod cu aceast proprietate. Aceast dezechilibrare se rezolv cu o rotaie dubl RDS:

Fig. 34. Cu acelai tip de rotaie se rezolv situaia simetric, cnd inserm noul nod n CD:

95

Fig. 35. n concluzie, dac notm cu A cel mai apropiat ascendent al nodului ce este inserat cu factorul de echilibru + 2, reechilibrarea arborelui se va face astfel : Cazul 1 : Dac FE(A) = 2, noul nod este inserat n subarborele stng al lui A. Notm cu B fiul stng al lui A. Dac noul nod este inserat n subarborele stng al lui B, caz n care FE(B) = 1, reechilibrarea se face printro rotaie simpl spre dreapta (RD). Dac noul nod este inserat n subarborele drept al lui B, caz n care FE(B) = -1, reechilibrarea se face printro rotaie dubl stnga i dreapta (RSD). Cazul 2 : Dac FE(A) = -2, noul nod este inserat n subarborele drept al lui A. Notm cu B fiul drept al lui A. Dac noul nod este inserat n subarborele drept al lui B, caz n care FE(B) = -1, reechilibrarea se face printro rotaie simpl spre stnga (RS). Dac noul nod este inserat n subarborele stng al lui B, caz n care FE(B) = 1, reechilibrarea se face printro rotaie dubl dreapta i stnga (RDS). Pentru implementarea algoritmului de inserare a unui nod ntr-un arbore AVL, va fi necesar ca la structura nodului s adugm un cmp suplimentar n care s reinem factorul de echilibru : Arbore = ^NodArbore; NodArbore = record c: TipCheie; fe: -1..1; st, dr: Arbore; end;
96

Vom descrie o procedur recursiv de inserare a unui nod ntr-un arbore AVL. procedure insert_avl(var rad: Arbore, x:TipCheie, var cr: boolean); //procedura insereaz, dac este posibil, n arborele AVL cu rdcina // rad, un nod cu cheia x i reechilibreaz eventual arborele; //parametrul logic cr indic dac nlimea subarborelui n care se face //inserarea a crescut relativ ( fa de nlimea subarborelui frate) begin if rad = nil then // arborele este vid begin new(rad); rad^.c := x; rad^.fe := 0; rad^.st := nil; rad^.dr := nil; cr := true; end else if x = rad^.c then begin writeln(' Cheia ', x, ' se gsete deja n arbore'); cr : = false; end else if x < rad^.c then begin // nodul trebuie inserat n subarborele stng insert_avl(rad^.st, x, cr); if cr then // n subarborele stng s-a produs o cretere relativ case rad^.fe of -1 : begin rad^.fe : = 0; cr : = false; end; 0 : rad^.fe : = 1; 1 : rotaie_dreapta(rad, cr); end
97

else begin // nodul trebuie inserat n subarborele drept insert_avl(rad^.dr, x, cr); if cr then // subarborele drept s-a produs o cretere relativ case rad^ .fe of 1: begin rad^ .fe := 0; cr := false: end; 0: rad^ .fe := -1; -1: rotaie_stnga(rad, cr); end end end; Observaie Iniial, la apelul procedurii insert_avl, valoarea parametrului cretere relativ cr trebuie s fie false. Cnd apelm procedura rotaie-dreapta suntem n cazul 1, A fiind nodul indicat de rad. Evident, n momentul apelului cr este true. procedure rotaie_dreapta(var rad: Arbore, var cr: boolean); var fiu, nepot: Arbore; begin // inserarea s-a fcut n subarborele stng al lui rad fiu := nod^.st; if fiu^ .fe = 1 then begin //reechilibrarea se face printr-o rotaie simpl dreapta (Fig. 36.) rad^.st := fiu^.dr;

98

fiu^ .dr := rad; rad^.fe := 1; fiu^.fe := 0; rad := fiu; end else Fig. 36. begin // reechilibrarea se face printr-o rotaie dubl RSD (Fig. 37.) nepot := fiu^.dr; fiu^.dr := nepot^.st; rad^.st := nepot^.dr;

nepot^.st := fiu; nepot^.dr := rad; if nepot^.fe = 1 then begin rad^.fe := -1; fiu^ .fe := 0; end else begin

rad^ .fe := 0; fiu^ .fe := 1; end


99

nepot^.fe := 0; rad := nepot; cr := false; end;

end

Fig. 37. n mod similar, vom descrie procedura rotaiestnga, corespunztoare cazului 2 procedure rotaie_stnga(var rad: Arbore, var cr: boolean); var fiu, nepot: Arbore; begin // inserarea s-a fcut n subarborele drept al rdcinii fiu := rad^.dr; if fiu^.fe = -1 then begin

//reechilibrarea prin RS (Fig. 38.) rad^.dr := fiu^.st; fiu^.st := rad; rad^.fe := 0; fiu^.fe := 0; ad := fiu; end else Fig. 38. begin // reechilibrarea se face printr-o rotaie dubl RDS (Fig. 39.)

100

nepot := fiu^.st;

fiu^.st := nepot^.dr; rad^.dr := nepot^.st; nepot^.st := rad; nepot^.dr := fiu; if nepot^.fe = 1 then begin rad^.fe := 0;

fiu^.fe := -1; end else begin rad^.fe := 1; fiu^.fe := 0; end; nepot^.fe := 0; rad := nepot; Fig. 39. end; cr := false; end; Pentru a intui mai bine algoritmul, propunem ca exerciiu aplicarea procedurii insert_avl pentru inserarea succesiv a cheilor : ianuarie, februarie, martie, aprilie, mai, iunie, iulie, august, septembrie, octombrie, noiembrie, decembrie ntr-un arbore iniial vid.
101

Complexitatea procedurii insert_avl depinde, ca i n cazul arborilor binari de cutare neechilibrai, de nlimea arborelui n care se face inserarea. Dar n cazul arborilor AVL, nlimea h este O(log n), unde n este numrul de noduri din arbore. Deci algoritmul de inserare ntr-un arbore AVL este de O(log n). tergerea unui nod dintr-un arbore AVL poate conduce de asemenea la dezechilibrarea arborelui, restabilirea echilibrului fcndu-se tot prin aplicarea unor rotaii. Propunem ca exerciiu descrierea unui algoritm de tergere a unui nod dintr-un arbore AVL. Concluzii Arborii AVL constituie o structur de cutare eficient. n cazul cel mai defavorabil, sunt necesare cu cel mult 45% mai multe comparaii dect pe un arbore optimal (afirmaie demonstrat de Adelson-Velskii i Landis). Principalele dezavantaje sunt necesitatea memorrii unui cmp suplimentar pentru factorul de echilibru i faptul c algoritmii de inserare, respectiv tergerea sunt relativ complicai. 3.6. Arbori bicolori O alt structur de cutare care garanteaz un timp de execuie de O(log n) n cazul cel mai defavorabil pentru cele trei operaii fundamentale pe arbori binari de cutare sunt arborii bicolori, cunoscui i sub denumirea de arbori Rou-Negru. Definiie Un arbore Rou-i-Negru este un arbore binar de cutare n care fiecare nod are asociat o culoare (rou sau negru) conform urmtoarelor reguli : 1. orice nod extern (frunz) este negru ; 2. dac un nod este rou, ambii fii sunt negri ; 3. toate drumurile de la rdcin la nodurile terminale conin acelai numr de noduri negre. Observaii 1. Pentru ca toate nodurile care conin o cheie s fie noduri interne, am adugat n arbore noduri fictive, marcate , pe care le-am numit externe i care vor constitui frunzele negre ale arborelui.
102

2. Din definiie deducem c orice subarbore a unui arbore Rou-i-Negru este arbore Rou-i-Negru. Regula 3 poate fi reformulat echivalent astfel: toate drumurile de la un nod la nodurile frunz descendente conin acelai numr de noduri negre. De exemplu,

Fig. 40. Definiie Fiind dat un nod x, numim adncime neagr a nodului x, notat AN(x) numrul de noduri negre de la nodul x (exclusiv) la o frunz. Observaie Datorit regulii 3 din definiia arborilor Rou-iNegru, AN(x) este bine definit. Pentru exemplul din figur, adncimea neagr a nodurilor arborelui va fi :
x AN(x) x AN(x) 1 2 3 4 5 6 7 8 9 1 1 2 1 2 1 1 3 1 1 2 2 1 3 1 1 4 3 1 5 1 1 6 2 1 7 1 1 0 1 1 8 1 103 1 1 1 1 9 1 2 0 2 21 1

Teorem Un arbore Rou-i-Negru cu n noduri interne are nlimea h cel mult egal cu 2log 2(n+1). Demonstraie : Vom demonstra n prealabil c, pentru orice nod x din arbore, subarborele cu rdcina x conine cel puin 2AN(x)-1 noduri interne. Vom proceda prin inducie dup nlimea arborelui cu rdcina n x. P(0) Dac nlimea arborelui cu rdcina n x este 0, rezult c x este un nod frunz, deci AN(x) = 0. Numrul de noduri interne ale subarborelui cu rdcina x este 0 = 2AN(x)-1. P(k) Presupunem adevrat afirmaia pentru toate nodurile x astfel nct nlimea arborelui cu rdcina n x este k. P(k+1) Demonstrm c pentru orice nod x, astfel nct nlimea subarborelui cu rdcina n x este k+1, numrul nodurilor interne din arbore este cel puin egal cu 2AN(x)-1. Fie x un nod astfel nct nlimea subarborelui cu rdcina n x este k+1. Numrul de noduri interne din arborele cu rdcina x este egal cu numrul de noduri interne din subarborele stng al lui x (NS), la care adugm numrul de noduri interne din subarborele drept al lui x (N D) i rdcina arborelui. n funcie de culoarea lui x, adncimea neagr pentru fiii lui x este AN(x) sau AN(x)-1.

Fig. 41. Cum subarborii corespunztori fiilor lui x au nlimea k, putem aplica ipoteza inductiv i obinem : NS 2AN(x)-1-1
104

ND 2AN(x)-1-1 Deci, numrul de noduri interne din arborele cu rdcina x este N(x) = NS+ND+1 2AN(x)-1-1+ 2AN(x)-1-1+1 = 2AN(x)-1 Dac aplicm proprietatea demonstrat anterior, rdcinii arborelui Rou-i-Negru obinem c n 2AN(rd)1.(*) Din regula 2 de construcie a unui arbore Rou-iNegru, deducem c cel puin jumtate din nodurile oricrui drum de la rdcin la o frunz trebuie s fie negre. Deci AN(rd) h/2, unde h este nlimea arborelui. nlocuind n relaia (*), obinem : n 2h/2-1 h 2log2(n+1). Q.E.D. Observaie Teorema precedent garanteaz c operaia de cutare ntr-un arbore bicolor se execut ntr-un timp de O(log n), n cazul cel mai defavorabil . Inserarea unui nod ntr-un arbore Rou-i-Negru * Pentru a insera un nod cu cheia x ntr-un arbore Rou-i-Negru, vom cuta mai nti n arbore cheia x. Dac aceast cutare eueaz, vom insera n arbore un nod cu cheia x, ca fiu al ultimului nod ntlnit n timpul cutrii (deci, o inserare uzual ntr-un arbore binar de cutare). Pentru a nu nclca regula 3 din definiia arborilor bicolori, vom colora noul nod inserat n rou. n acest mod, singura regul ce ar putea fi nclcat este regula 2, n cazul n care tatl nodului nou inserat este de asemeni rou.Vom lua n considerare toate cazurile posibile i vom preciza strategia de restabilire a consistenei arborelui Rou-i-Negru. Observaie Strategia de restaurare a arborelui bicolor se
*

Programul Bicolor de la sfritul capitolului curent creeaz un arbore bicolor prin inserri succesive de noduri i realizeaz tergerea unui nod de cheie specificat. 105

bazeaz pe o parcurgere de jos n sus (bottom-up) a drumului de la rdcin la nodul cu cheia x inserat. Din acest motiv, n structura nodului arborelui bicolor, pe lng cmpul necesar pentru memorarea culorii, vom reine i adresa nodului tat. const rou = 0; negru = 1; type Culoare = rou..negru; ArboreBicolor = ^NodArboreBicolor; NodArboreBicolor = record k: TipCheie; c: Culoare; st, dr, p: ArboreBicolor end; Notm q adresa nodului cu cheia x inserat, t adresa tatlui lui q (q^.p) i b adresa tatlui lui t (q^.p^.p). Cazul 1 Ambii fii ai lui b sunt colorai n rou :

Fig. 42. Recolorm fiii lui b n negru, iar b va fi recolorat n rou. n acest mod, regula 3 se conserv, iar restaurarea arborelui va continua de la b spre rdcin, deoarece nodul b i-a schimbat culoarea din negru n rou. Cazul 1' (simetric cu 1, t este fiul drept al lui b).

Fig. 43. Cazul 2 t este fiul stng al lui b, iar fiul drept al lui b este colorat negru; q este fiul stng al lui t :
106

Fig. 44.. 2.1. Colorm t n negru. n acest mod drumurile de la b la nodurile terminale din subarborele stng conin mai multe noduri negre dect drumurile de la b la nodurile terminale din subarborele drept, deci este nclcat regula 3. 2.2. Executm o rotaie la dreapta i colorm b n rou. n acest mod, restabilim egalitatea dintre numrul de noduri negre de pe toate drumurile de la rdcina subarborelui la noduri terminale descendente, iar regula 2 este de asemenea respectat. Cazul 2' (simetric cu 2, t este fiu drept al lui b, iar q fiu drept al lui t)

Fig. 45. n acest caz, la pasul 2.2' rotaia se face la stnga. Cazul 3 t este fiu stng al lui b, fiul drept al lui b fiind colorat n negru; q este fiu drept al lui t.

Fig. 46.
107

Efectund o rotaie la stnga n t, am redus cazul 3 la cazul 2. Cazul 3' (simetric cu 3, t este fiu drept al lui b, q este fiu stng al lui t)

Fig. 47. Am efectuat o rotaie la dreapta n t, pentru a reduce cazul 3' la 2'. De exemplu, s presupunem c dorim s construim un arbore bicolor insernd succesiv cheile 3, 4, 5, 1, 2, 7, 6, 8: Pas 1 Inserm un nod cu cheia 3 ntr-un arbore vid. Obinem:

Fig. 48. Pas 2 Inserm nodul cu cheia 4, l colorm n rou:

Fig. 49. Restaurm arborele, colornd rdcina n negru :

Fig. 50. Pas 3 Insernd un nod cu cheia 5, ne vom situa n cazul 2' :
108

Fig. 51. Pas 4 Inserm un nod cu cheia 1 :

Fig. 52. Am restaurat arborele folosind strategia corespunztoare cazului 1. Pas 5 Insernd un nod cu cheia 2 ne vom situa n cazul 3, pe care l vom reduce printr-o rotaie stnga la cazul 2.

Fig. 53. Pas 6 Inserm nodul cu cheia 7; obinem tot un arbore bicolor:

109

Fig. 54. Pas 7 Inserm nodul cu cheia 6. Aplicnd strategia corespunztoare cazului 3, ne vom situa n situaia 2:

Fig. 55. Pas 8 Inserm un nod cu cheia 8 i aplicm strategia din cazul 1' :

Fig. 56. Relum restaurarea, q fiind nodul cu cheia 6; colorm t n negru (t fiind rdcina arborelui). Obinem:

110

Fig. 57. Propunem ca exerciiu inserarea n continuare a cheilor 9, 10, 11, 12, 13, 14, 15. procedure insert-bicolor(var rad: ArboreBicolor; x: TipCheie); //insereaz n arborele bicolor cu rdcina rad un nod cu cheia x, //dac este posibil, restaurnd eventual arborele. var t, q, b, u: ArboreBicolor; gsit, inserat: boolean; begin new(q); q^.k := x; q^.st := nil; q^.dr := nil; q^.c := rosu; if rad = nil then //arborele este vid begin rad := q; rad^.p := nil; end else begin // caut n arbore un nod cu cheia x t := rad; gsit := false; inserat:=false; while not gsit and not inserat do if t^.k = x then begin writeln(' In arbore exist deja un nod cu cheia ',x); gsit:=true; dispose(q); end else
111

if t^.k < x then if t^.dr = nil then begin t^.dr :=q; q^.p := t; inserat := true; end else t := t^.dr else if t^.st = nil then begin t^.st :=q; q^.p := t; inserat := true; end else t := t^.st; if inserat then begin //am inserat nodul q cu cheia x; t este nodul tat //nodul q este rou; restaurm, dac este cazul, arborele while (q rad) and (t^.c = rosu) do begin b := t^.p; if b = nil then // t este rdcina arborelui t^.c := negru else if t = b^.st then begin u := b^.dr; if u^.c = rosu then //cazul 1 begin t^.c := negru; u^.c := negru; b^.c := rosu; q := b; t := q^.p; end else begin if q = t^.dr then
112

//cazul 3 stnga q^.p := b;

begin //rotaie b^.st := q; t^.dr := q^.st; q^.st^.p := t; q^.st := t; q := t; t := q^.p end; //cazul 2 t^.c := negru; b^.c := rou; // rotaie dreapta t^.p := b^.p; if b^.p = nil then rad := t else if b^.p^.st = b then b^.p^.st := t else begin b^.p^.dr := t; b^.st := t^.dr; t^.dr^.p := b; t^.dr := b; b^.p := t; end end end

t^.p := q;

113

//cazurile simetrice, t este b^.dr p := b^.st; if u^.c = rou then begin //cazul 1' t^.c := negru; u^.c := negru; b^.c := rosu; q := b; t := q^.p end else begin if q = t^.st then begin //cazul 3', rotaie dreapta b^.dr := q; q^.p := b; t^.st := q^.dr; q^.dr^.p := t; q^.dr := t; t^.p := q; q := t; t := q^.p; end; //cazul 2' t^.c := negru; b^.c := rou; //rotaie stnga t^.p := b^.p; if b^.p = nil then rad := t else if b^.p^.st = b then b^.p^.st := t else b^.p^.dr := t; b^.dr := t^.st; t^.st^.p := b; t^.st := b; b^.p := t end; end; end; Observaii 1. Restaurarea arborelui bicolor n urma inserrii unui nod prin aceast metod necesit cel mult dou rotaii. 2. Algoritmul de inserare bottom-up execut un
114

else begin

numr de comparaii de O(h) pentru inserarea nodului i cel mult de O(h) recolorri de noduri (de jos n sus). Cum nlimea unui arbore bicolor este de O(log n), deducem c algoritmul de inserare a unui nod ntr-un arbore bicolor este de O(log n). tergerea unui nod dintr-un arbore bicolor Arborii bicolori fiind arbori binari de cutare, tergerea unui nod dintr-un arbore bicolor se rezolv n acelai mod cu tergerea unui nod dintr-un arbore binar de cutare, numai c anumite situaii presupun restaurarea arborelui. Pentru simplificare, vom nlocui toi pointerii nuli din arbore cu pointeri spre o santinel S, care are aceleai cmpuri ca un nod obinuit i culoarea neagr (S va fi cu rol de nod extern). Astfel, vom putea trata un fiu NIL al unui arbore ca pe un fiu obinuit. Pentru a nu face o risip prea mare de memorie, nu vom folosi o santinel pentru fiecare fiu NIL din arbore, de aceea trebuie s fim ateni cnd ne referim la printele unui nod. procedure delete_bicolor(var rad: ArboreBicolor; x: TipCheie); //procedura terge, dac este posibil, din arborele cu rdcina rad //nodul cu cheia x var q, f, max: ArboreBicolor; begin //caut n arbore un nod q cu cheia x q := rad; while (q S) and (q^.k x) do if q^.k < x then q := q^.dr else q := q^.st; if q = S then writeln(' Nodul cu cheia', x, ' nu se gsete n arbore') else begin if (q^.st S) and (q^.dr S) then begin //q are doi fii;
115

stnga max^.dr;

// determin nodul cu cheia maxim din max:=q^.st; while max^.dr S do max :=

q^.k := max^.k; q := max; end; //acum nodul q are cel mult un fiu indicat de f if q^.st S then f := q^.st else f := q^.dr; f^.p := q^.p; //terg nodul q; chiar dac fiul este S, cmpul p va fi definit if q = rad then //q este chiar rdcina arborelui rad := f else if q = q^.p^.st then q^.p^.st := f else q^.p^.dr := f; if q^.c = negru then // am ters un nod negru, restaurm arborele delete_fixup(rad,f ); dispose(q); end; end; Observm c tergerea unui nod rou nu afecteaz proprietile caracteristice arborilor bicolori. Dac nodul de ters este negru, apar urmtoarele situaii posibile:

Fig. 58.
116

i evident, alte 3 situaii simetrice, pentru cazul n care q este fiul drept al lui t. Dup tergerea nodului q, poate apare una din urmtoarele situaii : I. Nodul f este rou : recolorm f n negru i am restaurat arborele II. Nodul f este negru : 1. Dac u rou (t este negru ) :

Fig. 59. u este rou t este negru, u devine nodul 1 i suntem n situaia de a restaura subarborele cu rdcina t rou, u negru (cazul 2.1. b). 2. Dac u este negru : 2.1. Dac ambii fii ai lui u sunt negri : a) t este negru

Fig. 60. i restaurarea se propag n sus, cu excepia rdcinii (f := t) b) t este rou

Fig. 61. 2.2. Fii lui u nu sunt ambii negri : a) Fiul drept al lui u este negru, fiul stng este rou

117

Fig. 62. u devine nodul 1; deci u negru cu fiul stng negru i cel drept rou; (cazul b). b) Fiul stng al lui u este negru i fiul drept este rou : - dac t este rou

- dac t este negru

Fig. 63.

Fig. 64. dup care am recolorat nodul 2. Observaie n cazul 2.1.a restaurarea se propag n sus. Celelalte cazuri sunt sau se reduc la un caz n care restaurarea se rezolv prin rotaii i recolorri. procedure delete_fixup(var rad: ArboreBicolor; f: ArboreBicolor); //restaureaz arborele cu rdcina rad n urma tergerii printelui //nodului f var t, u: ArboreBicolor; begin while (f rad) and (f^.c = negru) do begin
118

t := f^.p; if f = t^.st then begin u := t^.dr; if u^.c = rou then //cazul 1 begin u^.c := negru; t^.c := rou; rotaie_stanga(rad, t); u:=t^.dr; end; if (u^.st^.c = negru) and (u^.dr^.c = negru) then //cazul 2.1 begin u^.c := rou; f := t; end else // cazul 2.2 begin if u^.dr^.c = negru then // cazul 2.2.a begin u^.st^.c := negru; u^.c := rou; rotaie_dreapta(rad, u); u := t^.dr; end; // cazul 2.2.b u^.c := t^.c; t^.c := negru; u^.dr^.c := negru; rotaie_stanga(rad, t); f := rad; end; end else //simetric pentru f = t^.dr (se schimb st cu dr) begin ... end; end;
119

f^.c := negru; end; Rotaiile stnga i dreapta sunt cele definite la arbori AVL. Complexitatea algoritmului de tergere depinde de numrul de comparaii efectuate pentru cutarea n arbore a nodului ce urmeaz s fie ters (O(h)) i de complexitatea algoritmului de restaurare a arborilor. Am observat c operaia de restaurare necesit un timp constant, cu excepia cazului 2.1 cnd se poate propaga n sus, pn la rdcin (O(h)). Cum am demonstrat c nlimea unui arbore bicolor este de O(log n), deducem c tergerea unui nod dintr-un arbore bicolor este de O(log n). Concluzie Analiznd complexitatea operaiilor de cutare, tergere, inserare pe arbori binari de cutare, am vzut c n cazul cel mai defavorabil, timpul de execuie este liniar. Ideea echilibrrii arborilor binari de cutare se datoreaz lui Adelson-Velskii i Landis, care au introdus o clas de arbori echilibrai dup nlime numii arbori AVL. O alt clas de arbori, echilibrai cu ajutorul culorilor asociate nodurilor, sunt arborii Rou-i-Negru, introdui de ctre Bayer. Att arborii AVL, ct i arborii bicolori asigur un timp de execuie de O(logn), n cazul cel mai defavorabil. Aceste clase de arbori sunt utile pentru aplicaii n care n, dimensiunea spaiului de cutare, este suficient de mic pentru ca arborele s poat fi reinut n memoria intern. Pentru cazul n care informaiile se gsesc pe un suport de memorare extern, algoritmii pe arbori AVL sau pe arbori bicolori devin ineficieni datorit numrului mare de cereri de acces la memoria extern (operaii costisitoare ca timp). O soluie n acest sens sunt B-arborii, noiune introdus de Bayer i McCreight. O alt clas de structuri de cutare sunt arbori splay, introdui de Sleator i Tarjan. Un arbore splay este un arbore binar de cutare n care cutarea, inserarea i tergerea sunt executate ca n arborii binari de cutare obinuii, dar fiecare astfel de operaie este urmat de o
120

operaie splay, care const ntr-o secven de rotaii, similar celor executate n arbori AVL sau arbori bicolori. Exist i alte structuri de cutare interesante, cum ar fi de exemplu arborii de cutare numeric, dar acestea depesc cadrul acestei cri. 3.7. Exerciii 1. O ameliorare posibil a algoritmului de cutare binar const n a ncerca s determinm mai exact dac o valoare se gsete sau nu n spaiul de cutare. Ideea este inspirat din strategia pe care o folosim atunci cnd consultm un dicionar: atunci cnd cuvntul cutat ncepe cu B deschidem dicionarul mai la nceput, iar atunci cnd cuvntul ncepe cu Z, deschidem dicionarul mai la sfrit. Aceast metod se numete cutare prin interpolare i nu necesit dect o mic modificare a algoritmului de cutare binar. Dac x este valoarea cutat n vectorul R[st..dr] atunci partiionm spaiul de cutare pe poziia ( x R st . cheie) (dr st) poz = st + ( R dr . cheie R st . cheie) Aceasta este o estimare mai bun n cazul n care cheile sunt valori numerice uniform distribuite. Descriei algoritmul de cutare prin interpolare i analizai complexitatea sa. 2. Scriei o variant recursiv pentru procedura de inserare a unei valori ntr-un arbore binar de cutare. 3. Scriei o variant recursiv pentru funcia de determinare a nodului cu cea mai mic cheie dintr-un arbore binar de cutare. 4. Construii arborele binar de cutare care se obine insernd succesiv ntr-un arbore iniial vid cheile P, R, O, B, L, E, M, A, F, O, A, R, T, E, U, , O, A, R, A. 5. tergei din arborele construit la problema precedent succesiv cheile P,E,T,R,O,L. 6. Construii un arbore de cutare optimal pentru mulimea de valori a=(5,10, 15, 20, 25), cu
121

p=(1/20, 2/20, 2/20, 3/20, 4/20) i q=(4/20, 2/20, 2/20, 1/20, 1/20). 7. Valorile probabilitilor p i q utilizate pentru construcia unui arbore binar de cutare optimal sunt cunoscute n practic doar aproximativ. Astfel, ar fi suficient s construim un arbore binar de cutare aproape optimal, utiliznd urmtoarea euristic : - alegei rdcina k a arborelui T 0n astfel nct w0,kaceast 1-wk,n s fie ct mai mic posibil, aplicnd funcie i pentru subarborii stng i drept. Scriei un algoritm de complexitate O(n log n) care s construiasc un arbore binar de cutare aproape optimal folosind aceast euristic. 8. Construii un arbore AVL insernd succesiv cheile luni, mari, miercuri, joi, vineri, smbt, duminic ntr-un arbore iniial vid, aplicnd procedura insert-avl Ilustrai grafic i specificai tipul rotaiei aplicate. 9. Demonstrai prin inducie c numrul minim de noduri ntr-un arbore AVL cu nlimea h este n h=Fh+21 ,h0, unde Fh+2 este termenul h+2 din irul lui Fibonacci. 10. Determinai structura general a unui arbore AVL obinut prin inserarea n ordine a valorilor 1,2,...,n. Ce nlime are acest arbore? 11. Scriei un algoritm care s unifice doi arbori AVL, formnd un nou arbore AVL. Timpul de execuie al algoritmului trebuie s fie de O(h), unde h este maximul nlimilor celor doi arbori. 12. Folosind procedura insert-bicolor inserai n arborele din figura 40. un nod cu valoarea 22. 13. Justificai faptul c rotaiile pstreaz proprietatea de arbore binar de cutare. 14. Artai c un arbore oarecare cu n noduri poate fi transformat n oricare alt arbore cu n noduri dup O(n) rotaii. Indicaie : Demonstrai n prealabil c cel mult n-1 rotaii dreapta sunt suficiente pentru a transforma orice arbore ntr-un lan dreapta.
122

15. Cum s-ar mbunti procedura insert-bicolor, dac am stabili pentru rdcina arborelui culoarea neagr? 16. Descriei un algoritm de inserare a unui nod ntrun arbore bicolor n mod top-down. 17. Descriei procedurile rotaie-stnga i rotaiedreapta utilizate n procedura delete-fixup, de restaurare a unui arbore bicolor n urma unei operaii de tergere. 18. Descriei algoritmul de restaurare dup tergere, pentru cazul n care nodul ce a fost ters era fiu drept. 19. Desenai arborii care se obin tergnd succesiv cheile 16, 18, 19, 15, 21, 14, 2 din arborele bicolor din figura 40. 20. Presupunem c ai inserat un nod ntr-un arbore bicolor cu procedura insert-bicolor, apoi l-ai ters imediat cu procedura delete-bicolor. Se obine acelai arbore?

123

Anex
program Cautare_Binara;
type TipCheie = integer; var n, p: Longint; x: TipCheie; fin: file of TipCheie; function Caut(x: TipCheie): Longint; {cauta in fisier valoarea x si intoarce pozitia pe care se gaseste x in caz de succes, respectiv -1 in caz de esec} var st, dr, mijloc: Longint; aux: TipCheie; gasit: boolean; begin st := 0;dr := n-1; {componentele fisierului sunt numerotate de la 0} gasit := false; while (st <= dr) and not gasit do begin mijloc := (st+dr) div 2; seek(fin, mijloc); {pozitionez pointerul de fisier pe componenta din mijloc} read(fin, aux); if aux = x then gasit := true else if aux < x then st := mijloc+1 else dr := mijloc-1; end; if gasit then Caut := mijloc else Caut := -1; end; begin assign(fin,'cautbinf.in'); reset(fin);{fisierul este ordonat crescator dupa cheie} n := filesize(fin); write('Ce valoare cautam? '); readln(x); p := Caut(x); if p = -1 then writeln('Valoarea ',x,' nu se gaseste in fisier!') else writeln('Valoarea ',x,' se afla pe pozitia ',p+1); readln; close(fin); end.

program Operatii_pe_Arbori_Binari_de_Cautare;
uses crt;

const NrMaxVf = 20; type Vf = 1..NrMaxVf; TipCheie = char; Arbore = ^NodArbore; NodArbore = record c: TipCheie; st, dr, p: Arbore; end; var rad, p, aux: Arbore; v: TipCheie; function rsearch (rad: Arbore; x:TipCheie): Arbore; {cauta recursiv un nod de cheie x in arborele cu radacina rad; intoarce adresa nodului gasit sau nil, in caz de esec} begin if(rad = nil) or (rad^.c = x) then rsearch:=rad else if x < rad^.c then rsearch := rsearch(rad^.st, x) else rsearch := rsearch(rad^.dr, x) end; function search (rad: Arbore; x: TipCheie): Arbore; {cauta iterativ un nod de cheie x in arborele cu radacina rad; intoarce adresa nodului gasit sau nil, in caz de esec} begin while (rad <> nil) and (rad^.c <> x) do if rad^.c < x then rad := rad^.dr else rad := rad^.st; search := rad end; function minim (rad: Arbore): Arbore; {functia intoarce adresa nodului cu cea mai mica cheie din arborele nevid cu radacina rad} begin while rad^.st <>nil do rad := rad^.st; minim := rad end; function maxim (rad: Arbore): Arbore; {functia intoarce adresa nodului cu cea mai mare cheie din arborele cu ardacina rad} begin while rad^.dr <> nil do rad := rad^.dr; maxim := rad end; function succesor (x: Arbore): Arbore; {functia intoarce adresa nodului ce urmeaza parcurgerea inordine a arborelui} var tx: Arbore; begin if x^.dr <> nil then succesor := minim(x^.dr) nodului x in

else begin tx := x^.p; while (tx <> nil) and (x = tx^.dr) do begin x := tx; tx := x^.p end; succesor := tx end; end; function predecesor (x: Arbore): Arbore; {functia intoarce adresa nodului ce precede nodul x in parcurgerea inordine a arborelui,daca exista sau nil,altfel} var tx: Arbore; begin if x^.st <> nil then predecesor := maxim(x^.st) else begin tx := x^.p; while (tx <> nil) and (x = tx^.st) do begin x := tx; tx := x^.p end; predecesor := tx; end end; procedure insert(var rad: Arbore; x: Arbore); {insereaza recursiv nodul cu adresa x in arborele cu radacina rad } var q: Arbore; gata: boolean; begin if rad = nil then rad := x else begin q := rad; gata := false; while not gata do if q^.c = x^.c then begin dispose(x); gata := true end else if q^.c < x^.c then if q^.dr = nil then begin q^.dr := x; x^.p := q; gata := true end else

q := q^.dr else if q^.st = nil then begin q^.st := x; x^.p := q; gata := true end else q := q^.st end; end; procedure sterge (var rad: Arbore; x: Arbore); {sterge nodul x din arborele cu radacina rad} var nod_de_sters, fiu, tata: Arbore; begin if (x^.st = nil) or (x^.dr = nil) then nod_de_sters := x else begin nod_de_sters := minim (x^.dr); x^.c := nod_de_sters^.c end; if nod_de_sters^.dr <> nil then fiu := nod_de_sters^.dr else fiu := nod_de_sters^.st; tata := nod_de_sters^.p; if tata <> nil then if nod_de_sters = tata^.st then tata^.st := fiu else tata^.dr := fiu else rad := fiu; if fiu <> nil then fiu^.p := tata; dispose (nod_de_sters) end; procedure CreareArbore; {creeaza un arbore cu radacina rad citind valori din fisier si inserandu-le succesiv in arbore;} var x: Arbore; fin: text; begin rad := nil; assign(fin,'bst.in'); reset(fin); while not seekeof(fin) do begin new(x); x^.st := nil; x^.dr := nil; x^.p := nil; read(fin, x^.c); insert(rad, x); end; close(fin); end; procedure preordine(rad: Arbore);

{parcurge recursiv in preordine arborele binar cu radacina rad} begin write(rad^.c,'('); if rad^.st <> nil then preordine(rad^.st); write(','); if rad^.dr <> nil then preordine(rad^.dr); write(')'); end; procedure AfisareArbore; {afiseaza reprezentarea cu paranteze a arborelui} begin if rad = nil then writeln('Arborele este vid') else begin writeln('Arborele este:'); preordine(rad); writeln; end; end; begin {program principal} clrscr; CreareArbore; AfisareArbore; writeln('Cheia maxima: ', maxim(rad)^.c); writeln('Cheia minima: ',minim(rad)^.c); write('Ce valoare stergem? '); readln(v); p := rsearch(rad, v); if p = nil then writeln('Valoarea ',v,' nu exista in arbore') else begin sterge(rad, p); AfisareArbore; end; write('Pentru ce valoare calculam succesorul si predecesorul inordine?'); readln(v); p := search(rad, v); if p = nil then writeln('Valoarea ',v,' nu exista in arbore') else begin aux := succesor(p); if aux = nil then writeln(v,' este valoarea maxima! Nu exista succesor inordine.') else writeln('succesorul inordine al lui ',v,' este ',aux^.c); aux := predecesor(p); if aux = nil then writeln(v,' este valoarea minima! Nu exista predecesor inordine.') else writeln('predecesorul inordine al lui ',v,'

este ',aux^.c); end; readln; end.

program Arbori_Binari_de_Cautare_Optimali;
const NMax = 50; MaxChar = 20; type Val = string[MaxChar]; Ind = 1..NMax; IndE = 0..NMax; Arbore = ^NodArbore; NodArbore = record inf: Ind; st, dr: Arbore end; var n: Ind; a: array[Ind] of Val; p: array[Ind] of real; q: array[IndE] of real; c, w: array[IndE,IndE] of real; r: array[IndE,IndE] of IndE; rad: Arbore; fout: text; procedure initializare; var i: IndE; fin: text; begin assign(fin, 'obt.in'); reset(fin); readln(fin, n); for i := 1 to n do readln(fin, a[i]); for i := 1 to n do read(fin, p[i]); readln(fin); for i := 0 to n do read(fin, q[i]); readln(fin); close(fin); for i := 0 to n-1 do begin r[i,i] := 0; c[i,i] := 0; w[i,i] := q[i]; w[i,i+1] := q[i]+q[i+1]+p[i+1]; c[i,i+1] := w[i,i+1]; r[i,i+1] := i+1; end; w[n,n] := q[n]; c[n,n] := 0; r[n,n] := 0; end; function knuthmin(i, j: IndE): IndE; var l, k: IndE; begin k := r[i,j-1]; for l := r[i,j-1]+1 to r[i+1,j] do

if c[i,k-1]+c[k,j] > c[i,l-1]+c[l,j] then k := l; knuthmin := k; end; procedure calcul; var d, i, k, j: IndE; begin for d := 2 to n do for i := 0 to n-d do begin j := i+d; w[i,j] := w[i,j-1]+q[j]+p[j]; k := knuthmin(i, j); c[i,j] := w[i,j]+c[i,k-1]+c[k,j]; r[i,j] := k; end end; function constrarb(i, j: IndE): Arbore; var aux: Arbore; begin if r[i,j] = 0 then constrarb := nil else begin new(aux); aux^.inf := r[i,j]; aux^.st := constrarb(i, r[i,j]-1); aux^.dr := constrarb(r[i,j], j); constrarb := aux; end end; procedure afisare(rad: Arbore); begin if rad <> nil then begin write(fout,a[rad^.inf],' ':MaxChar-length(a[rad^.inf])+1); if rad^.st <> nil then write(fout,a[rad^.st^.inf]) else write(fout,' ':MaxChar+1); if rad^.dr <> nil then write(fout,a[rad^.dr^.inf]); writeln(fout); afisare(rad^.st); afisare(rad^.dr); end end; begin {program principal} initializare; calcul; rad := constrarb(0, n); assign(fout, 'obt.out'); rewrite(fout); writeln(fout,'Nod: Fiul stang afisare(rad); writeln(fout,'Costul arborelui de cautare

Fiul drept'); optimal este

',c[0,n]:5:2); close(fout); end.

De exemplu, pentru fiierul de intrare: 3 if while void .5 .1 .05 .15 .1 .05 .05 fiierul de ieire va conine: Nod: Fiul stang Fiul drept if while while void void Costul arborelui de cautare optimal este 1.50

program Bicolor;
uses crt; const rosu = 0; negru = 1; type Culoare = rosu..negru; TipCheie = byte; ArboreBicolor = ^NodArboreBicolor; NodArboreBicolor = record k: TipCheie; c: Culoare; st, dr, p: ArboreBicolor end; var rad, S: ArboreBicolor; x: TipCheie; procedure preordine(rad: ArboreBicolor; nivel, st, dr: byte); begin if rad <> S then begin gotoxy((st+dr) div 2-4, nivel); write(rad^.k:2, '.', rad^.c);{cheie.culoare} preordine(rad^.st, nivel+1,st, (st+dr) div 2 -1); preordine(rad^.dr, nivel+1, (st+dr)div 2 +2, dr); end end; procedure AfisareArboreBicolor; {afisarea arborelui se face pe ecran, indentat, pe niveluri; din acest motiv dimensiunea arborilor ce se pot afisa este limitata, dar putem afisa,de exemplu, reprezentarea cu paranteze a arborelui}

begin clrscr; writeln('Arborele este:'); preordine(rad, 4, 1, 80); end; procedure InsertBicolor(var rad: ArboreBicolor; x: TipCheie); {procedura insereaza in arborele bicolor cu radacina rad un nod cu cheia x, daca este posibil, restaurand eventual arborele.} var t, q, b, u: ArboreBicolor; gasit, inserat: boolean; begin {insereaza un nod cu cheia x in arbore, daca este posibil} new(q); q^.k := x; q^.st := S; q^.dr := S; q^.c := rosu; if rad = nil then begin {arborele este vid} rad := q; rad^.p := nil end else begin{cauta un nod cu cheia x in arbore} t := rad; gasit := false; inserat := false; while not gasit and not inserat do if t^.k = x then {in arbore exista un nod de cheie x, abandonam inserarea} begin dispose(q); gasit := true; end else if t^.k < x then if t^.dr = S then begin t^.dr := q; q^.p := t; inserat := true; end else t := t^.dr else if t^.st = S then begin t^.st := q; q^.p := t; inserat := true; end else t := t^.st; if inserat then begin {restauram daca este cazul, arborele}; while (q <> rad) and (t^.c = rosu) do begin b := t^.p; if b = nil then

{t este radacina arborelui} t^.c := negru else if t = b^.st then begin u := b^.dr; if u^.c = rosu then {cazul 1} begin t^.c := negru; u^.c := negru; b^.c := rosu; q := b; t := q^.p end else begin if q = t^.dr then{cazul 3} begin {rotatie stnga} b^.st := q; q^.p := b; t^.dr := q^.st; q^.st^.p := t; q^.st := t; t^.p := q; q := t; t := q^.p end; {cazul 2} t^.c := negru; b^.c :=rosu; {rotatie dreapta} t^.p := b^.p; if b^.p <> nil then if b = b^.p^.st then b^.p^.st := t else b^.p^.dr := t else rad := t; b^.st := t^.dr; t^.dr^.p := b; t^.dr := b; b^.p := t end end else begin {cazurile simetrice ,t este b^.dr} u := b^.st; if u^.c = rosu then begin {cazul 1'} t^.c := negru; u^.c := negru; b^.c := rosu; q := b; t := q^.p end else begin if q = t^.st then begin {cazul 3'}

{rotatie dreapta } b^.dr := q; q^.p := b; t^.st := q^.dr; q^.dr^.p := t; q^.dr := t; t^.p := q; q := t; t := q^.p end; {cazul 2'} t^.c := negru; b^.c := rosu; {rotatie stanga} t^.p := b^.p; if b^.p <> nil then if b^.p^.st = b then b^.p^.st := t else b^.p^.dr := t else rad := t; b^.dr :=t^.st; t^.st^.p := b; t^.st := b; b^.p := t end end end end {sfarsit if inserat then} end {sfarsit if rad=nil else} end; {sfarsitul procedurii InsertBicolor} procedure CreareArboreBicolor; var fin: text; x: TipCheie; begin rad := nil; assign(fin,'bic.in'); reset(fin); while not seekeof(fin) do begin readln(fin, x); InsertBicolor(rad, x); end; close(fin); end; procedure Delete_Fixup(var rad:ArboreBicolor; f:ArboreBicolor); {restaureaza arborele cu radacina rad in urma stergerii parintelui nodului f;} var t, u, us, ud, b: ArboreBicolor; begin while (f <> rad) and (f^.c = negru) do begin t := f^.p;{parintele lui f} if f = t^.st then begin u := t^.dr; {"unchiul" lui f} if u^.c = rosu then {cazul 1} begin u^.c := negru; t^.c := rosu;

{rotatie stanga } b := t^.p; u^.p := b; if b = nil then rad :=u else if t = b^.st then b^.st := u else b^.dr := u; t^.dr := u^.st; u^.st^.p := t; u^.st := t; t^.p := u; u := t^.dr end; {acum u^.c=negru} if (u^.st^.c = negru) and (u^.dr^.c = negru) then {cazul 2.1} begin u^.c := rosu; f := t end else {cazul 2.2} begin if u^.dr^.c = negru then {cazul 2.2.a} begin u^.st^.c := negru; u^.c := rosu; {rotatie dreapta} us := u^.st; us^.p := t; t^.dr := us; u^.st := us^.dr; us^.dr^.p := u; us^.dr := u; u^.p := us; u := us; end; {acum u^.dr^.c=rosu} {cazul 2.2.b} u^.c := t^.c; t^.c := negru; u^.dr^.c := negru; {rotatie stanga)} b := t^.p; u^.p := b; if b = nil then rad := u else if t = b^.st then b^.st := u else b^.dr := u; t^.dr := u^.st; u^.st^.p := t; u^.st := t; t^.p := u; u := t^.dr; f := rad end end else {simetric pentru f=t^.dr(se schimba st cu dr)} begin u := t^.st; if u^.c = rosu then {cazul 1'}

begin u^.c := negru; t^.c := rosu; {rotatie dreapta } b := t^.p; u^.p := b; if b = nil then rad := u else if t = b^.st then b^.st := u else b^.dr := u; t^.st := u^.dr; u^.dr^.p := t; u^.dr := t; t^.p := u; u := t^.st end; {acum u^.c=negru} if (u^.st^.c = negru) and (u^.dr^.c = negru) then {cazul 2.1'} begin u^.c := rosu; f := t end else {cazul 2.2'} begin if u^.st^.c = negru then {cazul 2.2.a'} begin u^.dr^.c := negru; u^.c := rosu; {rotatie stanga} ud := u^.dr; ud^.p := t; t^.dr := ud; u^.dr := ud^.st; ud^.st^.p := u; ud^.st := u; u^.p := ud; u := ud; end; {cazul 2.2.b'} u^.c := t^.c; t^.c := negru; u^.st^.c := negru; {rotatie dreapta} b := t^.p; u^.p := b; if b = nil then rad:=u else if t = b^.st then b^.st := u else b^.dr := u; t^.st := u^.dr; u^.dr^.p := t; u^.dr := t;t^.p := u; u := t^.st; f := rad end end end; f^.c := negru end;

procedure DeleteBicolor (var rad: ArboreBicolor; x: TipCheie); {procedura sterge ,daca este posibil,din arborele cu radacina rad nodul cu cheia x } var q, f, max: ArboreBicolor; begin {caut in arbore un nod q cu cheia x} q := rad; while (q <> S) and (q^.k <> x) do if q^.k < x then q := q^.dr else q := q^.st; if q = S then writeln('In arbore nu exista un nod cu cheia ',x) else begin {sterg q din arbore} if (q^.st <> S) and (q^.dr <> S) then begin {q are exact doi fii; determin nodul cu cheia maxima din subarborele stang} max := q^.st; while max^.dr <> S do max := max^.dr; q^.k := max^.k; q := max; end; {acum nodul q are cel mult un fiu} if q^.st <> S then f := q^.st {f indica unicul fiu al lui q} else f := q^.dr; f^.p := q^.p; {chiar daca fiul este S, campul p va fi definit} if q = rad then rad := f {q este chiar radacina arborelui} else if q = q^.p^.st then q^.p^.st := f else q^.p^.dr := f; if q^.c = negru then {am sters un nod negru, arborele trebuie restaurat} Delete_Fixup(rad,f); end end; begin {program principal} new(S); S^.c := negru; CreareArboreBicolor; AfisareArboreBicolor; write('Ce valoare stergem? '); readln(x); DeleteBicolor(rad, x); AfisareArboreBicolor; end.