Sunteți pe pagina 1din 68

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
else Cutare-Secvenial := i;
end;
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
65

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
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;
*

Programul Cutare-Binar de la sfritul capitolului curent implementeaz


algoritmul de cutare binar pe un fiier cu tip.
66

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

Fig. 2.
pe fiecare nivel i0,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

67

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 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 = [log2n]. De asemeni, n analiza complexitii am presupus
c orice valoare este cutat cu aceeai probabilitate.
3.3. Cutarea pe arbori binari de cutare*
*

Programul Operaii-pe-Arbore-Binari-de-Cutare de la sfritul


capitolului curent realizeaz operaiile elementare pe arbori binari
de cutare.
68

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:

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)
69

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 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, n ipoteza c
orice valoare este cutat n arbore cu aceeai probabilitate (1/n).
70

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.

R
|1, n
C S
|Tn11nk1(C
n

dac n1

k1Cn k),

dac n>1

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)
k1
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 k1
nmulind ambii membri cu n obinem :
n
nCn n (n 1) 2 Ck 1
k1
Scdem din aceast formul, formula obinut n cazul n-1, adic
n1
(n 1)Cn 1 (n 1) (n 2) 2 Ck 1
k1
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) :
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
n

n1
1
2
dx 2ln n
1 x
k1 k
Deci complexitatea medie a cutrilor cu succes este de O(log n).

71

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
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;
72

// 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;
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.
73

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. 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
74

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
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 stng
t^.st := fiu
else
// p este fiu drept
t^.dr := fiu
else
// p este chiar rdcina arborelui
rad := fiu;
dispose(p);
end;
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).

75

3.3.4. Alte operaii pe arbori binari de cutare


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 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

76

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;
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;
end;
77

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;
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;
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
78

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 ai. n cazul n care
s-ar efectua numai cutri cu succes, costul arborelui ar fi:
n

pi (nivel (ai ) 1)

i 1

deoarece pentru cutarea valorii ai n arbore, numrul de comparaii


necesare este egal cu numrul de noduri de pe drumul de la rdcin la
nodul cu cheia ai, 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}, i1,2,...,n-1.
Notm cu qi probabilitatea ca valoarea cutat s fie n clasa C i,
i0,1,...,n.
n

i 1

i0

pi qi 1

Costul cutrilor fr succes va fi:


n

qi nivel (nod_ externi )

i0

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
n

i 1

i0

pi (nivel (ai) 1) qi nivel (nod_ externi)


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

79

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
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 a1<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(

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.

80

Notaii:i,j1,2,...,n
arborele binar de cutare optimal pentru ai1,...,aj; dac i< j
arborele vid;
dac i j
Ti, j nedefinit;
dac i> j

ci, j

ri, j

R
|S
|T
R
|cost(T ); dac i< j
S
i j
|T0;nedefinit; dac
dac i>j.
R
|rdcina arborelui T ; dac i< j
S
dac i j
|T0;nedefinit;
dac i>j
R
|q (q p )ponderea arborelui T
S
;
|Tqnedefinit;
i, j

i, j

wi, j

k i1

i, j;

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

Conform acestor notaii:


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

Fig. 9.
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 .
81

Din aceast relaie deducem c Tij este optimal dac i numai


dac S i D sunt optimali, deci S=Ti,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

il j

qbg

ci, k 1 ck, j min ci, l 1 cl , j *


il j

Vom folosi aceast relaie pentru a obine T0n n mod bottom-up,


conform principiilor programrii 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,j0,1,2,3,4, i j :
w0,0=2/16 w1,1=3/16 w2,2=1/16 w3,3=1/16 w4,4=1/16
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/16 w1,2=7/16 w2,3=3/16 w3,4=3/16 ------------------------c0,1=8/16
c1,2=7/16
c2,3=3/16
c3,4=3/16
---------r0,1=1
r1,2=2
r2,3=3
r3,4=4
------------ ------------w0,2=12/1 w1,3=9/16 w2,4=5
------------ ------------6
c1,3=12/16 c2,4=8
------------ ---------c0,2=19/16 r1,3=2
r2,4=3
r0,2=1
w0,3=14/1 w1,4=11/1 ------------ ------------ ------------------------ ------------ ------------6
6
c0,3=25/16 c1,4=19/16 ------------ ------------ ---------r0,3=2
r1,4=2
------------ ------------ ------------ ------------w0,4=1
------------ ------------ ------------ ------------c0,4=2
------------ ------------ ------------ ---------r0,4=2
Deci arborele binar de cutare optimal este:

82

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 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 cij de la
*

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.
83

intervalul [i+1, j] la intervalul [ri,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 :

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
84

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 :

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

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

85

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

Fig. 16.
Inserm un nod cu cheia 1 :

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 :

86

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.
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.
87

Reechilibrarea o vom face cu o rotaie la dreapta, urmat de o rotaie la


stnga (RDS) :

Fig. 23.
Inserarea nodului cu cheia 7 genereaz arborele neechilibrat :

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

88

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 :

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:

Fig. 27.
2. Considerm arborele :

Fig. 28.
89

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:

Fig. 29.
3. Considernd arborele:

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.
90

Acelai tip de rotaie dubl se aplic i n cazul n care noul nod


se insereaz n CD, obinnd:

Fig. 32.
4. Considerm arborele

Fig. 33.
Insernd 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. 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:
91

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 printr-o 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 printr-o 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 printr-o 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 printr-o 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;
Vom descrie o procedur recursiv de inserare a unui nod ntr-un
arbore AVL.
92

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 relativcase
rad^.fe of
-1 : begin
rad^.fe : = 0;
cr : = false;
end;
0 : rad^.fe : = 1;
1 : rotaie_dreapta(rad, cr);
end
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:
93

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;

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;

94

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
nepot^.fe := 0;
rad := nepot;
cr := false;

end

end;

Fig. 37.
n mod similar, vom descrie procedura rotaie-stnga,
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

95

//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.)
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;
96

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.
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

97

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.
2. Din definiie deducem c orice subarbore a unui arbore Roui-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-i-Negru, AN(x)
este bine definit.
Pentru exemplul din figur, adncimea neagr a nodurilor
arborelui va fi :
x
AN(x)

1 2 3 4 5 6 7 8 9
1 1 2 1 2 1 1 3 1

x
AN(x)

12
2

13
1

14
3

15
1

16
2

17
1

10
1
18
1

Teorem
98

11
1
19
1

20
2

21
1

Un arbore Rou-i-Negru cu n noduri interne are nlimea h cel


mult egal cu 2log2(n+1).
Demonstraie:
Vom demonstra n prealabil c, pentru orice nod x din arbore,
subarborele cu rdcina x conine cel puin 2 AN(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 (N S), la care
adugm numrul de noduri interne din subarborele drept al lui x (ND) 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
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-i-Negru,
deducem c cel puin jumtate din nodurile oricrui drum de la rdcin

99

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 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 :
*

Programul Bicolor de la sfritul capitolului curent creeaz un arbore bicolor prin


inserri succesive de noduri i realizeaz tergerea unui nod de cheie specificat.
100

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 :

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)

101

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.
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:

102

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

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

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.

103

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

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:

104

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
if t^.k < x then
if t^.dr = nil then
begin
t^.dr :=q; q^.p := t;
inserat := true;
105

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 //cazul 3
begin //rotaie 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 := rou;
// rotaie dreapta
t^.p := b^.p;
if b^.p = nil then
106

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

107

else
begin //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 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

108

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;
// determin nodul cu cheia maxim din stnga
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 indicat de f
if q^.st S then f := q^.st
else f := q^.dr;
109

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.
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 :
110

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

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

Fig. 63.
- dac t este negru

111

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
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;
112

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;
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-iNegru, 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
113

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 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
114

a=(5,10, 15, 20, 25), cu


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 T0n astfel nct w0,k-1-wk,n s fie
ct mai mic posibil, aplicnd aceast 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 nh=Fh2-1 ,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.
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 ntr-un arbore bicolor
n mod top-down.
17. Descriei procedurile rotaie-stnga i rotaie-dreapta 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.
115

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 deletebicolor. Se obine acelai arbore?

116

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

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);

Fiul drept');

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

arborelui

de

cautare

optimal

este

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


begin
u^.c := negru;
{rotatie stanga
b := t^.p;
u^.p := b;
if b = nil then
rad :=u
else
if t = b^.st

{cazul 1}
t^.c := rosu;
}

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.

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