Documente Academic
Documente Profesional
Documente Cultură
B5 Cap 5
B5 Cap 5
5.1 Generalităţi
x a
y
y p p x
S3
S1
S1 S2 S2 S3
84
b) Rotaţia simplă spre stânga
85
II
x
y I p Sdx
Ssy z
Ssz Sdz
p
z
y x
86
5.3 Arbori AVL
hAVL ≤ 1,4404log2(n + 2)
87
x
Hstx Hdr
Hsty Hdr x
Sstx y
Ssty Sdry
x
H'sty Hdr
Hstx y
Hsty
Sdry
Sstx Ssty
88
Funcţia care implementează rotaţia simplă spre stânga este
prezentată în continuare. Vom folosi varianta în care structura care
defineşte un nod reţine şi o valoare care reprezintă înălţimea vârfului
respectiv (câmp de tip short int denumit h):
90
II
x
x
I y
Sstx
Ssty Sdry
II
y
x I
Sdry
Sstx Sdrx
Codul funcţiei:
GENERIC void rotddrAVL(nod<T> *&y)
{ rotsst(y->st);
rotsdr(y);
}
91
În ceea ce priveşte complexitatea rotaţiilor, aceasta este în toate cele
patru cazuri O(1) deoarece toate instrucţiunile din funcţiile care
implementează aceste operaţii sunt fie atribuiri fie instrucţiuni de
decizie.
92
Valoarea 40 este inserată într-un
10 nod frunză după ce s-a parcurs
drumul format din nodurile 10 şi
20 20. Se verifică dacă a apărut un
dezechilibru la nodurile de pe
acest drum: prima dată la nodul
40 20, dar la nivelul acestui nod nu
apare vreun dezechilibru. Apoi la
nodul 10. Aici, subarborele stâng
e vid, deci are înălţime -1 iar
20 subarborele drept are înălţimea 1,
deci a apărut un dezechilibru. Este
necesară o rotaţie simplă spre
10 40 stânga în raport cu nodul rădăcină.
10 40
25
93
Se inserează valoarea 28, drumul
20 urmat fiind format din nodurile 20,
II 40 şi 25. Se verifică apariţia
10 40 dezechilibrelor în sens invers.
Primul nod la care se constată un
I dezechilibru este 40 (subarborele
25 stâng are înălţimea 1, cel drept -1.
Pentru echilibrare este necesară o
28 rotaţie dublă spre dreapta în raport
cu acest nod.
28
10 28
25 40
94
Se inserează valoarea 30 drumul
20 urmat fiind format din nodurile 20,
28 şi 40. Se verifică apariţia
10 28 dezechilibrelor în sens invers. La
nodurile care reţin valorile 40 şi 28
nu a apărut vreun dezechilibru. La
25 40 nodul rădăcină a apărut un
dezechilibru: subarborele stâng
are înălţimea 0 iar cel drept
30 înălţimea 2. Pentru echilibrare este
necesară o rotaţie simplă spre
stânga raport cu acest nod.
28
20 40
10 25 30
28
20 35
10 25 30 40
95
Inserarea valorii 51 nu produce
28 vreun dezechilibru.
20 35
10 25 30 40
51
arborele rezultat
cheia 28,inaltime 3
cheia 20,inaltime 1
cheia 10,inaltime 0
cheia 25,inaltime 0
cheia 35,inaltime 2
cheia 30,inaltime 0
cheia 40,inaltime 1
cheia 51,inaltime 0
98
5.3.3 Eliminarea unei chei dintr-un arbore AVL
5 13 27 49
16
99
28 Se verifică nodul 28. Şi aici se
constată că a apărut un
dezechilibru: subarborele
20 43
stâng a acestuia are înălţimea
3 iar cel drept 1. Se aplică o
10 25 40 49 rotaţie spre dreapta în raport
cu acest nod. Se obţine
arborele echilibrat!
5 13 27
16
20
10 28
5 13 25 43
16 27 40 49
eliminareAVL(radacina,30);
cout<<"arborele dupa eliminarea valorii 30\n";
preord(radacina);
Dacă fişierul text valori.in conţine valorile folosite pentru a construi
arborele din figura 5.8 şi anume
28 20 40 10 25 30 43 5 13 27 49 16
programul va afişa în fereastra consolă următoarele:
arborele rezultat dupa inserare
cheia 28,inaltime 4
cheia 20,inaltime 3
cheia 10,inaltime 2
cheia 5,inaltime 0
cheia 13,inaltime 1
cheia 16,inaltime 0
cheia 25,inaltime 1
cheia 27,inaltime 0
cheia 40,inaltime 2
cheia 30,inaltime 0
cheia 43,inaltime 1
cheia 49,inaltime 0
arborele rezultat dupa eliminarea valorii 30
cheia 20,inaltime 3
cheia 10,inaltime 2
cheia 5,inaltime 0
cheia 13,inaltime 1
cheia 16,inaltime 0
cheia 28,inaltime 2
cheia 25,inaltime 1
cheia 27,inaltime 0
cheia 43,inaltime 1
cheia 40,inaltime 0
cheia 49,inaltime 0
102
5.4 Arbori Roşu-Negru
5.4.1 Definiţie, proprietăţi
28 28
20 40 20 40
10 25 30 10 25 30
103
3. orice nod colorat roşu are fiii coloraţi cu negru (sau orice
nod roşu are nodul tatăl colorat cu negru);
4. fiecare drum de la rădăcină la o frunză conţine acelaşi
număr de noduri negre (acest număr este denumit înălţime
neagră).
28
20 40
10 25 30
28 28
20 40 20 40
10 25 30 10 25 30
hRN ≤ 2lg(n+1).
A. Algoritmul de inserare
nod bunic
nod inserat
15 32
41
Configuraţia corespunde cazului I.
24 Se recolorează nodul tată şi nodul
unchi în negru iar nodul bunic în
roşu. Arborele rezultat are
15 32 rădăcina roşie deci nu respectă
regula 2. Se recolorează rădăcina
41 cu negru.
106
În final se obţine un arbore RN
24 corect!
15 32
41
Fig. 5.15 Rezolvarea încălcării regulii 3 apărută după inserarea unei
chei în cazul I.
ii) Cazul II: nodurile bunic şi unchi negre, nodul tată roşu, nodul tatăl
este fiul din dreapta al nodului bunic
Soluţia în acest caz este alcătuită din:
- o rotaţie simplă spre stânga în raport cu nodul bunic;
- interschimbarea culorilor nodului bunic şi nodului tată
(care în urma rotaţiei sunt la adresele unde iniţial erau nodul
unchi şi respectiv nodul bunic!).
11 24
19 32
27 41
107
Nodul cu cheia 50 nu respectă
14 regula 3. Se aplică metoda
recolorării celor trei noduri (cele cu
cheile 32, 27 şi 41).
11 24
19 32
27 41
50
În configuraţia rezultată nodul cu
14 cheia 32 nu respectă regula 3. El
este fiu drept al nodului 24 care
este tot roşu. Nodurile bunic şi
11 24 unchi sunt negre. Se efectuează
rotaţia simplă spre stânga în
19 32 raport cu nodul cu cheia 14 (nodul
bunic a lui 32).
27 41
50
Apoi se interschimbă culorile
24 nodurilor cu cheile 24 şi 14 (iniţial
bunic şi tată).
14 32
11 19 27 41
50
Arborele RN rezultat este corect
24 iar înălţimile negre ale tuturor
ramurilor nu au fost modificate!
14 32
11 19 27 41
50
Fig. 5.16 Refacerea arborelui RN după inserare, cazul II.
108
iii) Cazul III: : nodurile bunic şi unchi negre, nodul tată roşu, nodul tatăl
este fiul din stânga al nodului bunic
În acest caz se efectuează:
- o rotaţie dublă spre stânga în raport cu nodul bunic;
- interschimbarea culorilor nodului bunic şi nodului care
încălca regula 3 (care în urma rotaţiei sunt la adresele unde
iniţial erau nodul unchi şi respectiv nodul bunic!).
Menţionăm că prin prima rotaţie simplă a rotaţiei duble (cea
simplă spre dreapta în raport cu nodul tată se ajunge la
configuraţia specifică cazului I, configuraţie care se rezolvă prin
rotaţia simplă spre stânga în raport cu nodul bunic – observaţi
analogia cu necesitatea aplicării rotaţiilor duble la arborii AVL!)
11 24
19 32
41
16 21
19 32
41
16 21
13
109
II În configuraţia rezultată nodul cu
14 cheia 19 nu respectă regula 3. El
este fiu stâng al nodului 24 care
I este tot roşu. Nodurile bunic şi
11 24 unchi sunt negre. Se efectuează
rotaţia dublă spre stânga în raport
19 32 cu nodul cu cheia 14 (nodul bunic
41 a lui 19).
16 21 21
13
14 24
11 16 21 32
13
11 16 21 32
13
110
Cazurile în care nodul a fost inserat în stânga nodului bunic sunt
simetricele cazurilor descrise mai înainte şi nu le mai prezentăm. Ele
pot fi urmărite în funcţia de inserare listată în continuare.
111
Funcţia de inserare pe care o prezentăm are la bază funcţia de
inserare într-un arbore binar de căutare (prezentată în subcapitolul
4.2.1). La fel ca la funcţia de inserare într-un arbore AVL, după apelul
recursiv trebuie verificat dacă nu a fost deteriorată structura arborelui
RN prin încălcarea regulii 3. Pentru aceste verificări trebuie să ştim
dacă a apărut vreo alterare a regulii 3 în subarborele unde a fost
inserată cheia precum şi ce culori au nodurile aflate pe drumul urmat
de funcţia de inserare şi aflate cu două niveluri mai jos faţă de nodul
curent (nodul fiu şi nodul nepot). Din acest motiv funcţia de inserare pe
care o prezentăm returnează o valoare de tip int care oferă informaţii
referitoare la culorile celor două noduri, şi anume:
- valoarea 0: nodul părinte este negru, deci nu este necesară
nicio corecţie;
- valoarea 1: nodul fiu este roşu, nodul nepot este negru;
- valoarea 21: nodul fiu este în stânga, este roşu şi la rândul lui
are un fiu roşu;
- valoarea 22: nodul fiu este în dreapta, este roşu şi la rândul lui
are un fiu roşu;
Funcţiile simple de rotaţie sunt similare cu cele de la arborii AVL din
care lipsesc instrucţiunile de recalculare a înălţimilor din câmpurile h:
GENERIC void rotsst(nod<T> * &x)
{ nod<T> *y = x->dr;
x->dr = y->st;
y->st = x;
x = y;
}
GENERIC void rotsdr(nod<T> * &y)
{ nod<T> *x = y->st;
y->st = x->dr;
x->dr = y;
y = x;
}
112
else
if (cheie < nodcrt->info)
{ int nrrosii = inserareRN(nodcrt->st,cheie);
//nrrosii = cate rosii consecutive sunt sub el
if (nrrosii==0) return 0;
if (retculoare(nodcrt)==negru)
{ if (nrrosii == 1)
return 0;
//nrrosii=21 sau 22, trebuie facute corectii
if (retculoare(nodcrt->dr)==rosu)
{//recolorarea celor trei noduri
nodcrt->cul = rosu;
nodcrt->st->cul = negru;
nodcrt->dr->cul = negru;
return 1;
}
if (nrrosii == 21) //cazul I
rotsdr(nodcrt);
if (nrrosii == 22) //cazul II
rotddr(nodcrt);
swap(nodcrt->cul, nodcrt->dr->cul);
return 0;
}
else // nodcrt->cul==rosu
{ if (nrrosii == 0)
return 1;
else //nodul crt e rosu,apelul spre stanga
return 21;
}
}
else //cheia inserata mai mare=>apel spre dreapta
{ int nrrosii = inserareRN(nodcrt->dr,cheie);
if (nrrosii==0) return 0;
if (retculoare(nodcrt)==negru)
{ if (nrrosii == 1)
return 0;
//nrrosii=21 sau 22, trebuie facute corectii
if (retculoare(nodcrt->st)==rosu)
{// recolorarea celor trei noduri
nodcrt->cul = rosu;
nodcrt->st->cul = negru;
nodcrt->dr->cul = negru;
return 1;
}
if (nrrosii == 22) //cazul I
113
rotsst(nodcrt);
if (nrrosii == 21) //cazul II
rotdst(nodcrt);
swap(nodcrt->cul,nodcrt->st->cul);
return 0;
}
else // nodcrt->cul==rosu
{ if (nrrosii == 0)
return 1;
else //nodul crt e rosu,apelul spre dreapta
return 22;
}
}
}
}
În funcţia main(), după fiecare apel al funcţiei de inserare este
necesară recolorarea rădăcinii cu negru.
Vom ilustra în figura 5.18 modul în care este echilibrat un arbore binar
prin metoda RN presupunând că urmează să fie inserate în arbore chei
numerice cu următoarele valori (acelea şir de valori ca în cazul
ilustrării metodei AVL):
10, 20, 40, 25, 28, 30,35,51.
20
10 40
Este inserată valoarea 40 la
20 stânga nodului cu cheia 40. Apare
alterarea regulii 3 (nod roşu cu fiu
10 40 roşu). Configuraţia corespunde
cazului I, deci se aplică
recolorarea nodurilor bunic, unchi
25
şi tată.
20
10 40
25
Aceasta este urmată de
recolorarea cu negru a nodului
rădăcină.
20
10 40
25
115
20 Aceasta este urmată de o
interschimbare a culorilor
10 28 nodurilor aflate în poziţiile
nodurilor bunic şi respectiv unchi.
25 40
20
10 28
25 40
30
20
10 28
25 40
30
116
20
10 28
25 35
30 40
20
10 28
25 35
30 40
51
20
A apărut o alterare a regulii 3
28 datorită nodurilor cu cheile 35 şi
10
28. Configuraţia corespunde
cazului II: bunic şi unchi negri
25 35 tatăl roşu, fiul roşu e în dreapta
tatălui. Se aplică o rotaţie simplă
30 40 spre stânga în raport cu nodul cu
cheia 20.
51
117
Rotaţia este urmată de
28 interschimbarea culorilor dintre
nodurile ajunse în poziţiile unde
20 35 se aflau nodul bunic şi nodul
unchi, deci între nodurile cu cheile
10 25 30 40 28 şi 20:
51
10 25 30 40
51
118
5.4.3 Eliminarea unei chei dintr-un arbore RN
119
nod tată
120
nod roşu Presupunem că din acest arbore
sau negru eliminăm cheia cu valoarea 20.
28
20 35
10 30 51
Apoi
35 - se interschimbă culorile
nodurilor aflate în urma rotaţiei la
28 51 adresele unde se aflau nodul tată
şi nodul eliminat;
10 30 - se colorează în negru nodul aflat
la adresa unde iniţial se afla nodul
frate.
35
28 51
10 30
II
28 Rezultă configuraţia alăturată.
I
10 Se efectuează o rotaţie dublă
35 spre stânga în raport cu nodul
cu cheia 28.
30 51
1 2
0 0
10 1 2 51
0 0
122
Apoi se colorează în negru
30 nodul aflat la la adresa unde
iniţial se afla nodul eliminat.
28 35
10 1 2 51
0 0
În configuraţia rezultată:
30 - toate înălţimile negre au
acelaşi valori ca în configuraţia
28 35 de dinaintea eliminării (inclusiv
la cei doi subarbori notaţi cu 1
10 2 şi 2);
1 51
0 - nodul rădăcină şi-a păstrat
0
culoarea.
Fig. 5.21 Rezolvarea alterării regulii 4 prin eliminarea unei chei
în configuraţia specifică cazului II.
30
123
28
Presupunem că din acest
28 arbore eliminăm cheia cu
valoarea 20.
20 35
10 30 51
28
Rezultă configuraţia alăturată.
10 35 Se interschimbă culorile
nodurilor cu cheile 28 (nodul
tatăl) cu nodul cu cheia 35
30 51 (nodul frate).
28
10 35 În configuraţia rezultată:
- toate înălţimile negre au
acelaşi valori ca în configuraţia
30 51 de dinaintea eliminării;
20 35
10 30 51
30 51
În configuraţia rezultată:
- toate înălţimile negre ale
28 subarborelui cu rădăcina în
nodul cu cheia 28 au o valoare
10 35 mai mică cu 1 decât în
configuraţia de dinaintea
30 51 eliminării; se transmite la nivelul
superior alterarea regulii 4.
Fig. 5.23 Rezolvarea alterării regulii 4 prin eliminarea unei chei în
configuraţia specifică cazului IV.
10 30 51
28
10 Rezultă configuraţia alăturată.
35
Se efectuează o rotaţie
simplă spre stânga în raport
30 51 cu nodul cu cheia 28.
35
În configuraţia rezultată
28 51 pentru subarborele cu
rădăcina în nodul cu cheia 28
10 30 se observă că:
- înălţimea neagră a
subarborelui stâng a scăzut
cu 1;
- înălţimea subarborelui drept
s-a păstrat;
Se aplică corecţiile specifice
cazurilor III, IV sau V având
în vedere că rădăcina
subarborelui din chenar este
roşie!
126
În concluzie relativ la operaţiunea de ştergere a unei chei dintr-un
arbore RN putem afirma următoarele:
- are la bază operaţiunea de ştergere dintr-un arbore binar de
căutare neechilibrat;
- re-echilibrarea se face prin recolorări şi/sau rotaţii;
- în cel mai defavorabil caz se fac trei rotaţii (cazul numerotat cu
VII);
- complexitatea operaţiunii este dată de înălţimea arborelui
deoarece se parcurge „dus-întors” un drum de la rădăcină până
la un nod care poate fi eliminat (de tip frunză sau căruia îi
lipseşte unul dintre fii). Deci complexitatea este O(log n).
128
Funcţia care aplică corecţiile necesare restaurării proprietăţilor RN
după eliminarea unei chei din subarborele drept a fost denumită
reparadrRN şi este simetrica celei prezentate mai înainte. Invităm
cititorii să scrie codul acestei funcţii cu antet-ul:
}
else //cheia apare in nodul curent
{//cazul 1: frunza
if (nodcrt->st == NULL && nodcrt->dr == NULL)
{ culoare culeliminat = nodcrt->cul;
129
delete nodcrt;
nodcrt = NULL;
if (culeliminat == negru)
return -1; // deficit de noduri negre
else
return 0;
}
//cazul 2: lipseste subarborele stang
if (nodcrt->st == NULL && nodcrt->dr != NULL)
{ culoare culeliminat = nodcrt->cul;
nod<T> *aux = nodcrt->dr;
delete nodcrt;
nodcrt = aux;
if (culeliminat == negru)
return -1;
else
return 0;
}
//cazul 2: lipseste subarborele drept
if (nodcrt->st != NULL && nodcrt->dr == NULL)
{ culoare culeliminat = nodcrt->cul;
nod<T> *aux = nodcrt->st;
delete nodcrt;
nodcrt = aux;
if (culeliminat == negru)
return -1;
else
return 0;
}
//cazul 3: ambii subarbori prezenti
T cheieaux = minimarb(nodcrt->dr);
nodcrt->info = cheieaux;
cout << "se elimina "<<cheieaux << endl;
int dezechilibru;
dezechilibru=eliminareRN(nodcrt->dr,cheieaux);
if (dezechilibru == 0) return 0;
if (retculoare(nodcrt->dr)== rosu)
{ nodcrt->dr->cul = negru;
return 0;
}
int rez = reparadrRN(nodcrt);
return rez;
}
}
130
5.5 Îmbogăţirea structurilor de date
131
să definim un tip structură cu două câmpuri: cheie şi număr
de apariţii. Programele din subcapitolul 5.3 fiind generice
d.p.d.v. al tipului cheii vor folosi modificări minore, însă va
trebui să redefinim operatorii de comparaţie pentru tipul
structură nou declarat.
să folosim pentru cheie tipul pair;
să adăugăm un câmp nou pentru numărul de apariţii în
structura denumită nod utilizată în subcapitolul 5.3:
132
5.6 Containerul set
133
5.6.2. Utilizarea variabilelor de tip set
Variabilele de tip mulţime vor avea numele din declaraţia anterioară iar
elementele acestor mulţimi, care vor constitui cheile nodurilor arborilor
vor avea informaţia propriu-zisă de tipul precizat între parantezele
unghiulare. Deoarece într-o structură de tip arbore se consideră că
există o relaţie de ordine definită pentru cheile reţinute, pentru tipul
precizat între parantezele unghiulare trebuie să fie definit operatorul de
comparaţie <.
În urma unei astfel de declaraţii, mulţimile reţinute de aceste variabile
vor fi vide.
De exemplu:
set <int> v1;
reprezintă declararea unei variabile v1 care conţine o mulţime de
valori de tip int
134
este declararea unei variabile denumită v2 care conţine o mulţime cu
patru elemente: 10, 20, 30, 40.
begin()
Returnează un pointer la primul element din mulţime.
end()
Returnează un pointer la sfârşitul mulţimii (adresa de după ultimul
element din mulţime).
rbegin()
Returnează un pointer la ultimul element din mulţime.
rend()
Returnează un pointer „capătul din stânga” al mulţimii (util pentru
parcurgerea în sens invers)
size()
Returnează numărul de elemente din mulţime (valoare de tip
unsigned)
135
max_size()
Returnează numărul maxim de elemente pe care le-ar putea avea
mulţimea.
empty()
Testează dacă mulţimea este vidă.
insert(val)
Inserează un element care va conţine valoarea val în mulţime.
erase(val)
Elimină elementul cu val din mulţime. În versiunile C++11 returnează
un iterator la elementul aflat după elementul eliminat sau valoarea
egală cu valoarea funcţiei end() dacă elementul eliminat era ultimul.
În versiunile anterioare nu returnează vreo valoare.
find(val)
Caută valoarea val în mulţime. Returnează o valoare egală cu
iteratorul la nodul care reţine elementul respectiv când val apare în
mulţime sau iteratorul de la sfârşitul mulţimii (valoare egală cu valoarea
returnată de funcţia end()) când val nu este în mulţime.
Aceste trei funcţii au complexitatea O(log n) (n=numărul de elemente
din mulţime).
136
Vom exemplifica utilizarea iteratorilor prin declararea a două funcţii
pentru afişarea informaţiilor conţinute de o mulţime. În prima funcţie
vom parcurge mulţimea în ordinea crescătoare a valorilor (practic vom
parcurge arborele binar în inordine) iar în a doua funcţie vom
parcurge mulţimea în sens invers.
137
5.7 Probleme propuse
138
1 dacă subarborele cu rădăcina în nodul dat ca parametru respectă
regula 4 impusă arborilor roşu-negru şi valoarea 0 în caz contrar. Se
reaminteşte că regula 4 impusă arborilor roşu-negru este următoarea:
fiecare drum de la rădăcină la o frunză conţine acelaşi număr de noduri
negre.
Indicaţie: se va presupune că orice legătură spre fiul stâng sau fiul
drept a unui nod care are valoarea NULL pointează spre un nod
santinelă negru, deci are înălţimea neagră 1.