Documente Academic
Documente Profesional
Documente Cultură
a
/ \
b c
/ / \\
d e f g
/ \ \
h i j
Exemple de arbori:
poligoane
/ \
triunghi patrulatere
/ \ \
dreptunghi romb oarecare
Definitie
Se numeste arbore cuplul format din V si E : T= (V,E) cu V o multime de noduri
si E VxV o multime de arce, cu proprietatile:
Proprietate a arborelui
Definitii
predecesor(E) = B
succesor(C) = G
E(D) = {H, I, J}
degree(D) = 3
degree(B) = 2
degree(F) = 0
degree(T) = 3
ancestors(L) = {A, B, E}
level(L) = 4 , level(B) = 2 , level(A) = 1
depth(T) = 4
Reprezentarea arborilor
De exemplu:
structura x este diferita de structura x
/ | \ /| \
vid y vid y vid vid
(Reprezentarea TATA):
Arbori binari
rad
/ \
/ \
/ \
/ \
\/ / \
/ SAS \ / SAD \
/______ \ /_______\
SAS subarborele stѓng (binar)
SAD subarborele drept (binar)
Definitii
Exemplu:
a
/ \
c b
/ \ / \
d e f g
/ \
h i
2) Se numeste arbore binar complet un arbore binar strict pentru care y cu:
degree(y) = 0 (frunza) level(y) = depth(T)
Cu alte cuvinte, nodurile terminale apartin ultimului nivel din arbore.
Exemplu:
a
/ \
b c
/ \ / \
d e f g
/ \ / \ / \ / \
h i j kl m n o
Lema 1
Numarul maxim de noduri de pe nivelul i al unui arbore binar este egal cu 2i-1.
Demonstratia se face prin inductie:
La nivelul 1 avem 20 = 1 nod = rad (radacina A). Presupunem conform metodei
P(n): pe nivelul n avem 2n-1 noduri. Demonstram pentru P(n+1): se observa ca
toate nodurile de pe nivelul n+1 sunt noduri descendente de pe nivelul n.
Notѓnd niv(i) numarul de noduri de pe nivelul i,
niv(n+1) 2ъniv(n) 22n-1 = 2n .
Lema 2
Numarul maxim de noduri ale arborelui binar de adѓncime h este egal cu 2h -1.
Demonstratie:
Numarul total de noduri este egal cu:
(progresie geometrica)
Lema 3
Notam cu:
n2 numarul de noduri de grad 2 din arborele binar;
n1 numarul de noduri de grad 1 din arborele binar;
n0 numarul de noduri terminale (frunze) din arborele binar;
In orice arbore binar, n0 = n2 +1 (nu depinde de n1).
Operatii curente:
selectia cѓmpului de date dintr-un nod si selectia descendentilor;
inserarea unui nod;
stergera unui nod.
rad
/ \
SAS SAD
Exemplu de traversare: a
/ \
b c
/ \ \
d e f
/ \
g h
preordine : A B D F G H C F
inordine : D B G E H A C F
postordine : D G H E B F C A
In C++ avem:
struct Nod{
Atom data;
Nod* stg;
Nod* dr;
};
Nod* p;
preorder(t)
{
if(t==0) return
else |Ѕ print (data(t)) // vizitarea uni nod
| preorder (lchild(t)) // parcurgerea |
// subarborilor
|_ preorder (rchild(t))
}
inorder(t)
{
if(t!=0) |Ѕ inorder (lchild(t))
| print (data(t))
|_ inorder (rchild(t))
}
postorder(t)
{
if(t!=0) |Ѕ postorder (lchild(t))
| postorder (rchild(t))
|_ print(data(t))
}
Lema 1
Daca T este un arbore de grad k cu noduri de dimensiuni egale (k pointeri în
fiecare nod), arborele avѓnd n noduri reprezentarea va contine n (k - 1) + 1
pointeri cu valoare zero (nuli).
Tabelele Hashing
Acestea sunt o alta solutie pentru a retine elementele din S. Complexitatea
pentru arborele binar de cautare în cazurile:
cel mai defavorabil: O(n);
mediu: O(log n).
Un arbore binar de cautare este un arbore T ale carui noduri sunt etichetate
cu atomii continuti la un moment dat în dictionar.
T = (V, E) , €V€ = n. (n atomi în dictionar)
Observatii:
1) Consideram ca pe multimea k este definita o relatie de ordine (de exemplu
lexico-grafica);
2) Pentru oricare nod din BST toate nodurile din subarborele stѓng sunt mai
mici decѓt radacina si toate nodurile din subarborele drept sunt mai mari decѓt
radacina.
Exemple: 15 15
/ \ / \
7 25 10 25
/ \ / \ /\ /
2 13 17 40 2 17 13
/ / \
9 27 99
este BST nu este BST.
15
/ \
7 25
/ \ / \
2 13 17 40
/ \ / \
9 19 27 99
make_nod(a)
{
p= get_sp() // alocare de memorie pentru un nod nou
data(p)= a
lchild(p)= 0
rchild(p)= 0
return(p)
}
Observatie:
1)La inserarea unui atom deja existent în arbore, functia insert nu modifica
structura arborelui. Exista probleme în care este utila contorizarea numarului
de inserari a unui atom în arbore.
2)Functia insert poate returna pointer la radacina facѓnd apeluri de forma
p= insert(p,a).
3)Delete:
Detasarea din structura arborelui BST a celui mai mare nod (remove_greatest):
Pentru a gasi cel mai mare nod dintr-un arbore binar de cautare, se înainteaza
în adѓncime pe ramura dreapta pѓna se gaseste primul nod care nu are descendent
dreapta. Acesta va fi cel mai mare.
Vom trata aceasta procedura recursiv:
Caz1: rad=>se returneaza pointer la radacina si arborele rezultat va fi vid.
Caz2: rad=> se returneaza pointer la radacina si arborele rezultat va fi
format doar din SAS subarborele stѓng (SAS).
Caz3: rad=>functia returneaza pointer la cel mai mare nod din SAD, iar
rezultatul va fi SAS arborele care este fomat din radacina,SAS si SAD
cel mai mare nod.
remove_greatest(rad) //rad -referinta la pointer la //radacina: un pointer la
radacina de poate //fi modificat de catre functie
{
if rchild (rad)= 0 then | p= rad
| rad= lchild (rad)
|_ return(p)
else return (remove_greatest (rchild(rad)))
}
Observatie:
Functia remove_greatest modifica arborele indicat de parametru, în sensul
eliminarii nodului cel mai mare, si întoarce pointer la nodul eliminat.
a1
/ \
a1 ai
(ai<a1) (ai>a1)
Notam:
Ti(n) = numarul mediu de comparatii necesar pentru construirea unui BST cu n
noduri atunci cѓnd prima valoare inserata (a1) este mai mare decѓt i-1 dintre
cele n valori de inserat. Putem scrie:
Deci:
Deci:
Exemple:
a a
/ \ / \
b c b c
/ \ / \ / \ \
d e f g d e f
/ \
g h
arbore binar arbore binar
complet echilibrat echilibrat
Consideram arborii T1, T2, T3 echilibrati. Inserѓnd un nod prin rotatie simpla,
rezulta structurile rotit simplu la dreapta si rotit simplu la stѓnga imaginea
oglinda a rotatiei dreapta:
A A
/ \ / \
B T3 T3 B
/ \ / \
T1 T2 T2 T1
care este un BST , deci este arbore echilibrat, si are aceeasi adѓncime (!!!)
cu arborele initial (de dinainte de inserare). Nodurile superioare nu sunt
afectate. Rationament analog pentru imaginea oglinda.
In urma inserarii, unul dintre arborii T2S si T2D îsi mareste adѓncimea.
Aplicam aceiasi tehnica:
T1 < B < T2S < C < T2D < A < T3
Incepem cu C:
C
/ \
B A
// \ / \\
// T2S T2D \\
______T1___________________ T3_____________________
la acelasi nivel
Rezulta un BST echilibrat, de aceeaai adѓncime cu arborele initial. Rotatiile
sunt duble, în sensul ca
s-a facut o rotatie simpla B la stѓnga cu o rotatie simpla A la dreapta.
Operatiile care trebuiesc facute în cazul 1 (rotatie simpla la dreapta):
r- pointer la nodul radacina (A)
a- pointer la radacina
p = lchild(r) b = lchild(a)
lchild(r) = rchild(p) lchild(a) = rchild(b)
rchild(p) = r rchild(b) = a
r=p a=b
Operatiile care trebuiesc facute în cazul 2 (rotatie dubla)
b = lchild(a)
c = rchild(b)
lchild(a) = rchild(c)
rchild(b) = lchild(c)
rchild(c) = a
lchild(c) = b
a = c // se schimba radacina arborelui.
x bf = 1
/
y bf = 0
\
z bf = - 1 (bf = -2 dupa inserare)
\
w bf = 0 (bf = 1 dupa inserare)
/
v bf = 0 (bf = -1 dupa inserare)
\
nodul
inserat
Definitie:
Se numeste nod critic primul nod cu bf 0 întѓlnit la o parcurgere de jos
în sus a ramurii care leaga nodul inserat de radacina.
Observatie: Toate nodurile din ramura care sunt pe nivele inferioare nodului
critic vor capata bf = 1 sau
bf = -1.
55
/ \
20 80
/ \ \
10 35 90
/ \ /
5 15 30
\
7
Nodul critic pentru 3 este 5, iar pentru 9 este este 10. Dupa rotatia aplicata
(T2D, T2S vizi), rezulta arborele:
20
/ \
7 55
/ \ / \
5 10 35 80
/ / \ / \
3 9 15 30 90
Lema
Daca în vectorul V un nod x este reprezentat prin elementul de vector V(i), atunci:
1. left_child(x) este reprezentat în elementul de vector V [2ъi];
2. right_child(x) este reprezentat în elementul de vector V [2ъi + 1];
3. parent(x) este reprezentat în elementul de vector V [[i/2]]
cu observatia ca paranteza patrata interioara este partea întrega.
Demonstratie:
Se face inductie dupa i:
Pentru i = 1 => V[1] radacina
=> V[2] left_child(rad)
=> V[3] right_child(rad)
Presupunem adevarata lema pentru elementul V[i]=>V[2i] left_child
V[2i + 1] right_child
Elementul V[i + 1] este nodul urmator (de pe acelsi nivel sau de pe nivelul urmator) într-o parcurgere binara.
V[i + 1] => left_child în V[2i + 2] = V[2(i + 1)]
right_child în V[2i + 3] = V[2(i + 1) + 1]
Daca avem un arbore binar care nu este complet, reprezentarea lui implicita se
obtine completѓndu-se structura arborelui cu noduri fictive pѓna la obtinerea
unui arbore binar complet.
Definitie:
Se numeste arbore heap un arbore binar T = (V, E) cu urmatoarele proprietati:
1) functia key : V R care asociaza fiecarui nod o cheie.
2) un nod v V cu degree(v) > 0 (nu este nod terminal), atunci:
key(v) > key(left_child(v)), daca left_child(v)
key(v) > key(right_child(v)), daca right_child(v)
(Pentru fiecare nod din arbore, cheia nodului este mai mare decѓt cheile
descendentilor).
Exemplu: 99
/ \
50 30
/ \ / \
45 20 25 23
Observatie: De obicei functia cheie reprezinta selectia unui subcѓmp din cѓmpul
de date memorate în nod.
Aplicatii ale arborilor heap
Coada cu prioritate;
Algoritmul Heap_Sort
Operatia de inserare / \
heap: / \
/ 50 \
/ / \ \
/ 40 30 \
/ / \ /\ \
/ 33 37 12 2 \
/ / \ / _________\
/ 10 15 7 | 42
--------------|
Operatiile insert si remove pentru arbori heap au o forma foarte simpla cѓnd
utilizeaza reprezentarea implicita. Consideram, în continuare, arbori heap în
reprezentare implicita.
insert:
1) In reprezentarea implicita: V [N + 1] = a
N=N+1
In continuare reorganizam structura arborelui astfel încѓt sa-si pastreze
structura de heap.
2) Se utilizeaza interschimbarile. Comparatii:
Iau 2 indici: child = N si
parent = [N/2]
(în cazul nostru N = 11 si [N/2] = 5)
Comparam V[child] cu V[parent]
Interschimbare daca V[child] nu este mai mic decѓt V[parent](se schimb
42 cu 37)
3)Inaintare în sus: child = parent
parent = [child/2]
4) Se reia pasul 2) pѓna cѓnd nu se mai face interschimbarea.
Structura S este un arbore heap. El se afla în reprezentare implicita în 2
informatii:
V vector
N dimensiune
Operatia insert:
se scoate elementul cel mai mare care este radacina heap-ului; se initializeaza
cei 2 indici;
se reorganizeaza structura arborilor: se ia ultimul nod de pe nivelul incomplet
si-l aduc în nodul-radacina, si aceasta valoare va fi retrogradata pîna cѓnd
structura heap-ului este realizata.
parent
conditia de heap: / \
lchild rchild
parent = max(parent, lchild, rchild).
Exista trei cazuri:
1. conditia este îndeplinita deodata;
2. max este în stѓnga retrogradarea se face în stѓnga;
3. max este în dreapta retrogradarea se face în dreapta.
remove(V, N)
{
a= V[1]
N[1]= V[N]
N= N-1
parent= 1
child= 2
while child <= N do
| if child+1 N and key(V[child+1]) >
| > key(V[child]) then
| child= child+1
| if key(V[parent]) < key(V[child]) then
| | interchange(V[parent], V[child])
| | parent= child
| |_ child= 2*parent
| else break
|_ return(a)
}
| |
| |
| |
nr. de noduri nr. de noduri
ale arborelui ale arborelui
complet de complet de
adѓncime k adѓncime k+1
Algoritmul Heap_Sort
Heap_Sort(V, n)
{
heap_gen(V, n) N = n
for i = n downto 2 step -1 do
|N=i
|_ V[i] = remove(V, N)
}
heap_sort
{
N = 1 //se considera pentru început un
// heap cu un singur
//element, dupa care toate celelalte
// elemente vor fi
//inserate în acest heap
for i = 2 to n do
insert(V, N, V[i])
}
Studiul complexitatii
Observatii:
Se fac mai multe operatii insert în heap-uri a caror dimensiune creste de la
1 la N;
Se fac n-1 operatii insert în heap-uri cu dimensiunea N<=n
Rezulta complexitatea acestor operatii nu depaseste O(nlog n). Facem un studiu
pentru a vedea daca nu cumva ea este mai mica decѓt O(nlog n).
Cazul cel mai defavorabil este situatia în care la fiecare inserare se parcurge
o ramura completa. De fiecare data inserarea unui element se face adaugѓnd un
nod la ultimul nivel. Pentru nivelul 2 sunt doua noduri. La inserarea lor se
va face cel mult o retrogradare (comparatie).
Pe ultimul exemplu de arbore, avem:
nivelul 2 : 2 noduri 1 comparatie
nivelul : 4 noduri 2 comparatii
nivelul 4 : 8 noduri 3 comparatii
--------------------------------------------------
nivelul i : 2i-1 noduri i-1 comparatii
Sa aratam:
cu tehnica . Asadar:
Rezulta:
iar: ,
din
Rezulta:
------------------------
termen dominant
retrogradare(V, N, i)
{
parent = i
child = 2*i // fiu stѓnga al lui i
while child N do
| if child+1<=N and
| key(V[child+1]) > key(V[child]) then
| child = child+1
| if key(V[parent]) < key(V[child]) then
| | interchange(V[parent], V[child])
| | parent = child
| |_ child = 2*parent
|_ else break
}
In aceasta situatie, vom avea:
heap_gen
{
for i = [N/2] downto 1 step -1 do
retrogradare(V, n, i)
}
Rezulta:
-------
termen dominant
Curs 10
Coada cu prioritati
Operatia de inserare / \
heap: / \
/ 50 \
/ / \ \
/ 40 30 \
/ / \ /\ \
/ 33 37 12 2 \
/ / \ / _________\
/ 10 15 7 | 42
--------------|
Operatiile insert si remove pentru arbori heap au o forma foarte simpla cѓnd
utilizeaza reprezentarea implicita. Consideram, în continuare, arbori heap în
reprezentare implicita.
insert:
1) In reprezentarea implicita: V [N + 1] = a
N=N+1
In continuare reorganizam structura arborelui astfel încѓt sa-si pastreze
structura de heap.
2) Se utilizeaza interschimbarile. Comparatii:
Iau 2 indici: child = N si
parent = [N/2]
(în cazul nostru N = 11 si [N/2] = 5)
Comparam V[child] cu V[parent]
Interschimbare daca V[child] nu este mai mic decѓt V[parent](se schimb
42 cu 37)
3)Inaintare în sus: child = parent
parent = [child/2]
4) Se reia pasul 2) pѓna cѓnd nu se mai face interschimbarea.
Structura S este un arbore heap. El se afla în reprezentare implicita în 2
informatii:
V vector
N dimensiune
Operatia insert:
se scoate elementul cel mai mare care este radacina heap-ului; se initializeaza
cei 2 indici;
se reorganizeaza structura arborilor: se ia ultimul nod de pe nivelul incomplet
si-l aduc în nodul-radacina, si aceasta valoare va fi retrogradata pîna cѓnd
structura heap-ului este realizata.
parent
conditia de heap: / \
lchild rchild
parent = max(parent, lchild, rchild).
Exista trei cazuri:
1. conditia este îndeplinita deodata;
2. max este în stѓnga retrogradarea se face în stѓnga;
3. max este în dreapta retrogradarea se face în dreapta.
remove(V, N)
{
a= V[1]
N[1]= V[N]
N= N-1
parent= 1
child= 2
while child <= N do
| if child+1 N and key(V[child+1]) >
| > key(V[child]) then
| child= child+1
| if key(V[parent]) < key(V[child]) then
| | interchange(V[parent], V[child])
| | parent= child
| |_ child= 2*parent
| else break
|_ return(a)
}
| |
| |
| |
nr. de noduri nr. de noduri
ale arborelui ale arborelui
complet de complet de
adѓncime k adѓncime k+1
Algoritmul Heap_Sort
Heap_Sort(V, n)
{
heap_gen(V, n) N = n
for i = n downto 2 step -1 do
|N=i
|_ V[i] = remove(V, N)
}
heap_sort
{
N = 1 //se considera pentru început un
// heap cu un singur
//element, dupa care toate celelalte
// elemente vor fi
//inserate în acest heap
for i = 2 to n do
insert(V, N, V[i])
}
Studiul complexitatii
Observatii:
Se fac mai multe operatii insert în heap-uri a caror dimensiune creste de la
1 la N;
Se fac n-1 operatii insert în heap-uri cu dimensiunea N<=n
Rezulta complexitatea acestor operatii nu depaseste O(nlog n). Facem un studiu
pentru a vedea daca nu cumva ea este mai mica decѓt O(nlog n).
Cazul cel mai defavorabil este situatia în care la fiecare inserare se parcurge
o ramura completa. De fiecare data inserarea unui element se face adaugѓnd un
nod la ultimul nivel. Pentru nivelul 2 sunt doua noduri. La inserarea lor se
va face cel mult o retrogradare (comparatie).
Pe ultimul exemplu de arbore, avem:
nivelul 2 : 2 noduri 1 comparatie
nivelul : 4 noduri 2 comparatii
nivelul 4 : 8 noduri 3 comparatii
--------------------------------------------------
nivelul i : 2i-1 noduri i-1 comparatii
Sa aratam:
cu tehnica . Asadar:
Rezulta:
iar: ,
din
Rezulta:
------------------------
termen dominant
retrogradare(V, N, i)
{
parent = i
child = 2*i // fiu stѓnga al lui i
while child N do
| if child+1<=N and
| key(V[child+1]) > key(V[child]) then
| child = child+1
| if key(V[parent]) < key(V[child]) then
| | interchange(V[parent], V[child])
| | parent = child
| |_ child = 2*parent
|_ else break
}
heap_gen
{
for i = [N/2] downto 1 step -1 do
retrogradare(V, n, i)
}