Documente Academic
Documente Profesional
Documente Cultură
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;
*
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
2h n < 2h+1
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
R
|1, n
C S
|Tn11nk1(C
n
dac n1
k1Cn k),
dac n>1
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
Fig. 5.
n acest caz tergerea este simpl: nlocuim legtura spre p cu nil.
73
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
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
pi (nivel (ai ) 1)
i 1
i 1
i0
pi qi 1
i0
i 1
i0
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
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.
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
il j
qbg
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
*
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
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
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
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
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
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
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;
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
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
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
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 :
*
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
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
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;
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);
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
Fiul drept');
writeln(fout,'Costul
',c[0,n]:5:2);
close(fout);
end.
arborelui
de
cautare
optimal
este
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;
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}
{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.