Sunteți pe pagina 1din 139

Structuri de date si algortitmi

_________________________________________________________________________
Facultatea de automatica si
calculatoare
_________________________________________________________________________ 1
Structuri de date si algortitmi
_________________________________________________________________________
STRUCTURI DE DATE SI ALGORITMI
Curs de Brsan M.
BIBLIOGRAFIE
1. E. Horowitz, S. Sahni "Fundamentals of
Computer Algorithms" - 1985
2. E. Horowitz, S. Sahni "Fundamentals of Data
Structurs"
3. Livovschi Georgescu "Sinteza si analiza
algoritmilor"
4. U. Mandber "Introduction to Algorithms"
5. S. Baase "Computer Algorithms"
6. M. Shapiro "Algorithms from P to NP"
_________________________________________________________________________ 2
Structuri de date si algortitmi
_________________________________________________________________________
7. N. Wirth "Data Structurs + Algorithms =
Programs"
_________________________________________________________________________ 3
Structuri de date si algortitmi
_________________________________________________________________________
Curs 1
Structuri de date
Structurile de date erau definite n limbajul C
drept organizarea datelor primare. n limbajul C++,
acestea reprezinta o colectie de date mpreuna cu
operatiile lor (data obiect).
De exemplu, prin multimea N a numerelor
naturale se va ntelege si elementele multimii N, dar si
operatiile ce se pot efectua cu acestea: 1, 2, 3, ..., +, -,
*, /. Sau prin multimea numerelor complexe:
C: {z = a + bi/a si bR, i = sqrt(-1)}, -, +,
*, /, etc.
Algoritmul se defineste ca o metoda de rezolvare
a unei probleme ntr-un numar de pasi, metoda
efectiva (pas cu pas), finita (are un numar finit de pasi)
si cu o intrare si o iesire (I/O).
Un algoritm poate avea un limbaj natural (o
specificatie), un limbaj matematic (alta specificatie),
un limbaj de programare (alta specificatie), s.a.m.d.
ntre limbajul natural si cel n C++, de exemplu, vom
_________________________________________________________________________ 4
Structuri de date si algortitmi
_________________________________________________________________________
folosi
un pseudolimbaj (de trecere).
Modele de calcul
Masina este un model de calcul care se constituie
din Unitate Centrala (U.C.), Memorie (M), I/O.
Exemple de modele de calcul:
Masina Von Newman - presupune executia pe baza
modelului de calcul cu:
U C
M
I O
. .
.
/
Programarea este n acest caz programare imperativa
procedurala.
Masina RAM (Random Acces Memory) cu:
P U C
M
S
( . .)
.
.(sir de instructiuni)
I / O
model bazat pe algebra booleana;
programarea este imperativa procedurala;
evolutia se face prin set redus de instruciuni;
viteza foarte mare de executie.
Masina TURING
1. MODELUL functional - bazat pe teoria - calcul.
_________________________________________________________________________ 5
Structuri de date si algortitmi
_________________________________________________________________________
Limbajele n acest model sunt LISP, ML, MIRANDA,
etc. iar programarea este n acest caz programare
functionala.
2. MODELUL logic - bazat pe predicate de ordin I.
Un exemplu de limbaj n acest model este
PROLOG.Iar programarea se numeste programare
logica.
n cele ce urmeaza ne vom limita la modelul Von
Newman.
Asadar limbajul C++ se constituie din:
variabile;
identificatori;
constante;
operatori numerici obisnuiti;
operatori relationali;
structuri de control a executiei: if/else, while,
do/while, for, etc.
Analiza performantelor algoritmului
Analiza performantelor (estimarea algoritmului) se
impune nca nainte de scrierea programelor.
Etapele de realizare a unui produs software
(software engineering)
Aceasta stiinta pune n evidenta metodologii clare
pentru modele.
Modelul initial:waterfall (cascada):
_________________________________________________________________________ 6
Structuri de date si algortitmi
_________________________________________________________________________
Requirmens
Design
Testing
Implement
Etapele de realizare ale unui produs software:
O prima faza:
se pleaca de la cerinte;
se obtin specificatii;
se face analiza specificatiilor;
A doua faza (DESIGN):
proiectare de ansamblu (se sparge modulul n
submodule, etc);
proiectarea structurilor de date;
proiectarea algoritmilor;
analiza performantelor;
codarea (scrierea programului);
A treia faza:
testarea;
Ultima faza:
implementarea.
Programul rezultat se compara cu cerintele, si
daca nu corespunde, se reia ciclul ori de cte ori este
nevoie.
_________________________________________________________________________ 7
Structuri de date si algortitmi
_________________________________________________________________________
Analiza performantelor presupune renuntnd la
acuratete estimarea timpului de lucru si a spatiului
de stocare, nestiind nca limbajul care va fi folosit si
calitatea programului ce se va obtine.
Presupunnd ca modelul RAM de masina pe care
lucram executa instructiuni pseudocod, si ca fiecare
instructiune pseudocod consuma acelasi timp de
executie,rezulta ca timpul estimat pentru executia unui
algoritm este proportional cu numarul instructiunilor
executate de acel algoritm.
Timpul de executie al algoritmului depinde de:
dimensiunea datelor de intrare
spatiul de memorie suplimentar ocupat
Dimensiunea datelor de intrare este o functie f(n)
care calculeaza, pentru un n dat, numarul de
instructiuni al algoritmului respectiv.
n c n f
2
log ) (
Estimarea se face pna la o constanta c.
Spatiul de memorare suplimentar
Definitie: Date doua functii f, g : N N cu f =
O(g) sau f(n) = O(g(n)),
f este ordinul de complexitate a lui g daca
N N si const. c > 0
astfel incat
> < n N f n c g n
0
( ) ( )
.
_________________________________________________________________________ 8
Structuri de date si algortitmi
_________________________________________________________________________
Curs 2
Structuri de date elementare
O structura de date presupune un mod de
organizare a datelor (n tablouri, structuri, etc), si
definirea operatiilor acceptate. Deci, o structura de
date reprezinta o colectie organizata de date si un set
de operatii definite.
Si notiunea de tip de date presupune: |---
reprezentare
|--- operatii acceptate
De exemplu, pentru tipul int: |--- reprezentare:
pe doi octeti: cod
| complementar
|---operatii acceptate: +, -, *, /,
&, |, etc.
Daca pentru un tip de date nu intereseaza
reprezentarea, ci doar operatiile acceptate,nseamna ca
tipul de date este abstract.
_________________________________________________________________________ 9
Structuri de date si algortitmi
_________________________________________________________________________
Structuri de date
Tablouri
Tabloul este o colectie de date n care fiecare
element poate fi identificat pe baza unui index,
colectia asigurnd timp de acces constant pentru
fiecare element. Prin reprezentarea tabloului se
intelege plasarea elementelor n locatii succesive de
memorie:
Locatiile de memorie pot fi numerotate, putnd
accesa direct orice element. Timpul de accesare al
elementului numar, de exemplu, fiind acelasi cu
timpul de accesare al elementului n.
Liste
O lista este o multime de obiecte, numite atomi,
pentru care este definita o ordine:
a a a a a ... a
1 2 3 4 5 n
Operatiile principale care se pot se face n cadrul
listei:
inserare: introducerea unui nou element ntr-o
anumita pozitie;
stergere: scoaterea unui element dintr-o anumita
pozitie;
_________________________________________________________________________ 10
Structuri de date si algortitmi
_________________________________________________________________________
consultare: accesul asupra fiecarui element din lista;
parcurgere.
Tipuri speciale de liste
Stive
O stiva este o lista n care operatiile de inserare,
stergere si consultare se efectueaza asupra unui capat
al listei. Stiva se poate asemana cu un recipient n care
se pun si se pot scoate diferite obiecte. Operatia care
pune obiectele n stiva se numeste push, iar cea care
scoate obiecte din stiva se numeste pop. Capatul
accesibil pentru stiva se numeste vrful stivei:
Asadar:
push insereaza un element n vrful stivei;
pop sterge un element din vrful stivei;
top consulta (citeste) elementul din vrful
stivei;
top(S) citeste vrful stivei.
push pop
Cozi
O coada este o lista n care inserarea se face la un
capat (la sfrsit), iar stergerea se face de la celalalt
capat al cozii (de la nceput). Partea din fata a cozii (a
_________________________________________________________________________ 11
Structuri de date si algortitmi
_________________________________________________________________________
primului element) se numeste front, iar partea din
spate (a ultimului element) se numeste end.
Operatia de inserare n coada add (put)
Operatia de stergere din coada del (get)
add del
Implementari de liste
O lista poate fi realizata ca: lista ordonata
sau
lista nlantuita
Lista ordonata tine cont de o ordine a pozitiilor
elementelor listei, nu de continutul elementelor.
Inserarea ntr-o lista de forma:
//////
a a a ... a
1 2 3
n
se face cu deplasare de o pozitie la stnga din punctul
n care dorim sa inseram (pentru a face acest loc
noului element).
Deplasarea se face nspre zona de memorie libera
(cea hasurata) presupunem ca dorim sa inseram pe
a n pozitia i):
//////
a a a ... a
1 2
i+1
a
i-1
...
Presupunnd acum hasurat corpul de elemente din
lista si nehasurata zona de memorie libera, inserarea s-
ar putea figura astfel:
_________________________________________________________________________ 12
Structuri de date si algortitmi
_________________________________________________________________________
/////////////////////////////
///////////////////////////////////
///////////////// //////////
Stergerea: deplasarea cu o pozitie la stnga din
acel punct.
/////////////////////////////
////////// //////////////
//////////
Liste nlantuite
ntr-o lista nlantuita, ordinea din memorie nu mai
corespunde cu ordinea din lista. Fiecare element al
listei nlantuite va avea urmatoarea structura:
(a(i) , succesor(a(i)))
unde a(i) este atomul listei nlantuite, iar informatia
succesor(a(i)) ne permite sa identificam un nou
element de lista nlantuita. Ordinea din memorie a
elementelor listei nu conteaza. Informatia care indica
primul element al listei se numeste "capul" listei.
Informatiei succesor(a(i)) i se potriveste notiunea de
pointer (identificator), pointer-ul fiind o variabila care
pastreaza o adresa din memorie. El indica pozitia
elementului urmator. Cnd informatiile de nlantuire
sunt pointeri, putem utiliza urmatoarea reprezentare:
_________________________________________________________________________ 13
Structuri de date si algortitmi
_________________________________________________________________________
cap de lista
a a a
1 2 3
Capul de lista este un pointer separat care duce la
primul element din lista, iar 0 este pointer-ul nul
(NULL) cu valoare zero. La implementarea listei
nlantuite concentrarea se face la fluxul instructiunilor,
nu la declaratiile de variabile.
n programe vom utiliza urmatoarele notatii:
x adresa unui element din lista, deci un pointer;
data(x) atomul memorat n elementul de lista
indicat de x;
link(x) informatia de legatura memorata n
elementul de lista indicat de x, adica adresa
elementului urmator;
y = get_sp() y (de acelasi tip cu x) primeste
adresa unei zone de memorie n care se poate memora
un element din lista (get space sau alocare de
memorie cnd este vorba de pointer);
ret_sp(x) elibereza memoria ocupata de
elementul de lista indicat de x (din momentul respectiv
acolo se poate memora altceva).
Un element de lista va fi o astfel de structura:
struct Element {
_________________________________________________________________________ 14
Structuri de date si algortitmi
_________________________________________________________________________
Atom data;
Element* link;
};
Se va scrie:
tipul lui x ---------------- Element* x
data(x) ---------------- x data
link(x) ---------------- x link
y = get_sp() ---------------- y = new Element
ret_sp() ---------------- delete x
Deoarece lucram cu conceptul de lista vom face
declaratia :
typedef Element* Lista;
Un pointer la un element de lista considerat
aproximeaza lista ce porneste cu elementul indicat.
Operatii primitive pentru liste nlantuite
1. Inserarea
Inserarea se face: n fata, sau
n interior (la mijloc ori la sfrsit)
a) Inserarea n fata
_________________________________________________________________________ 15
Structuri de date si algortitmi
_________________________________________________________________________
a
a a
1 2
0
x
1
2
1
1 - Prima atribuire: link(x) = l
2 - A doua atribuire: l = x
Observatie: daca lista este vida, l are valoarea 0
(capatul listei) iar atribuirile de mai sus ramn
valabile:
a
0
0
x
1
0
1
x
0
a
0
b) Inserarea la mijloc
_________________________________________________________________________ 16
Structuri de date si algortitmi
_________________________________________________________________________
a a a
i-1 i i+1
y
a
x
1
2
Analog pentru inserarea la sfrsit.
1 - Prima atribuire: link(x) = link(y)
2 - A doua atribuire: link(y) = x
2.a) Stergerea (stergerea din fata):
a a
1 2
2
1
1 - Prima atribuire: p = l
2 - A doua atribuire: l = link(l)
3 - ret_sp(p)
Sau, stergerea din fata s-ar mai putea reprezenta
astfel:
Situatia initiala:
cap
0
Situatia finala:
_________________________________________________________________________ 17
Structuri de date si algortitmi
_________________________________________________________________________
cap
0
2
1
P
(1) p = cap;
(2) cap = cap link;
delete p ; // Elibereaza zona de
memorie
Elementul care a fost izolat de lista trebuie sa fie
procesat n continuare, cel putin pentru a fi eliberata
zona de memorie pe care o ocupa, de aceea adresa lui
trebuie salvata (sa zicem n variabila pointer p).
2.b) Stergerea de la mijloc sau de la sfrsit
Varibila q va indica elementul din fata celui care
va fi sters.
Situatia initiala:
q
Situatia finala:
_________________________________________________________________________ 18
Structuri de date si algortitmi
_________________________________________________________________________
q
(1) p = q link;
(2) q link = p link; // sau q link = q
link link;
delete p;
Observatii:
Atunci cnd q indica penultimul element dintr-o
lista, atribuirile de mai sus functioneaza corect si sterg
ultimul element din lista.
Nu se poate face stergerea elementului indicat de
q fara parcurgerea listei de la capat.
_________________________________________________________________________ 19
Structuri de date si algortitmi
_________________________________________________________________________
Curs 3
Operatia de inserare ntr-o lista nlantuita
Presupune adaugarea unui element ntr-o pozitie
specificata n lista. Exista posibilitati diferite de a
specifica pozitia n care vrem sa inseram elementul:
Situatia n care pozitia de inserat este data printr-un
numar care sa indice al ctelea element trebuie sa fie
n lista elementul inserat;
Situatia n care pozitia de inserat este data prin
valoarea atomului dupa care sau nainte de care se
face inserarea;
Situatia n care pozitia de inserat poate fi data
implicit prin valoarea atomului de inserat.
Inserarea n fata unui element specificat
Functia nscrie un element n fata altui element
dintr-o lista:
insert (l, a, b)
// l lista (pointer la primul element)
_________________________________________________________________________ 20
Structuri de date si algortitmi
_________________________________________________________________________
// a valoarea atomului de inserat
// b valoarea atomului n fata caruia
se insereaza
{
p=get_sp();
data(p)=a;
if (l==0) or (data(l)==b) then
{
link(p)=l;
l=p;
}
else
{
q=l;
while ((link(q)!=0)and (data(link(q)!
=b))
do q=link(q);
link(p)=link(q);
link(q)=p;
}
}
_________________________________________________________________________ 21
Structuri de date si algortitmi
_________________________________________________________________________
Operatia de stergere dintr-o lista nlantuita
Operatia delete sterge un atom dintr-o lista. Deci
vom avea n pseudocod, o functie de forma:
delete(l, a)
// l lista
// a valoarea atomului care
trebuie sters
{
if l=0 then eroare ("Atomul nu se afla
n lista")
else if data(l)=a then | p=1
| l=link(l)
|_ ret_sp(p)
else | q=l
| while(link(q)!
=0)and(data(link(q))!=a) do
| q=link(q)
| if link(q=0) then eroare("S-a ajuns
la sfrsitul
| listei si atomul nu a
fost gasit")
| else | p=link(q)
| | link(q)=link(p)
|_ |_ ret_sp(p)
_________________________________________________________________________ 22
Structuri de date si algortitmi
_________________________________________________________________________
}
Operatia de parcurgere a listei nlantuite
Operatia de parcurgere a listei nlantuite consta
dintr-o secventa de instructiuni care se foloseste de
fiecare data cnd dorim sa prelucram elementele listei
ntr-un anumit scop.
O parcurgere a listei presupune o prelucrare
efectuata asupra fiecarui element din lista (asadar nu o
functie, ci o secventa de instructiuni):
Fie p pointer-ul care indica pe rnd fiecare
element al listei, si consideram ca p ncepe cu l:
while (p!=0) do | prelucrare (data(p)) //
ex:afisarea |
//atomului
|_ p=link(p) // trece la
urmatorul
Stive ordonate
O stiva este o structura de date de tip "container"
(depoziteaza obiecte de un anumit tip) organizata dupa
principiul LIFO (Last In First Out).
_________________________________________________________________________ 23
Structuri de date si algortitmi
_________________________________________________________________________
Operatiile de acces la stiva (push - adauga un
element in stiva si pop - scoate un element din stiva)
sunt create astfel nct pop scoate din stiva elementul
introdus cel mai recent.
O stiva este un caz particular de lista, si anume
este o lista pentru care operatiile de acces (inserare,
stergere, accesare element) se efectueaza la un singur
capat al listei.
Daca STACK este de tip stiva si ATOM tipul
obiectelor continute n stiva atunci operatiile care
definesc tipul structura de stiva pentru tipul STACK
sunt:
CREATE() STACK
Operatia CREATE nu primeste parametri, creeaza
o stiva care pentru nceput este vida (nu contine nici
un obiect).
PUSH(STACK, ATOM) STACK
Operatia PUSH primeste ca parametri o stiva si un
obiect si produce stiva modificata prin adaugarea
obiectului n stiva.
POP(STACK) STACK, ATOM
Operatia POP primeste ca parametri o stiva pe
care o modifica scotnd un obiect. De asemenea
produce ca rezultat obiectul scos din stiva.
TOP(STACK) ATOM
_________________________________________________________________________ 24
Structuri de date si algortitmi
_________________________________________________________________________
Operatia TOP ntoarce ca rezultat obiectul din
vrful stivei pe care o primeste ca parametru.
ISEMPTY(STACK) boolean
Operatia ISEMPTY este folosita pentru a testa
daca stiva este vida.
Facem notatiile:
S stiva
S.vect vectorul n care se reprezinta
elementele stivei S
S.spindicele vrfului stivei
Elementele sunt memorate asadar unul dupa altul
n vectori, nefiind neaparat n ordine crescatoare. Zona
de memorat trebuie sa contina aceste doua informatii:
S.vect si S.sp grupate ntr-o structura:
struct Stack {
int sp;
Atom vect [DIMMAX]
};
Conditia de stiva vida este: S.sp=0
Se scrie:
push(S,a)
_________________________________________________________________________ 25
Structuri de date si algortitmi
_________________________________________________________________________
{
if S.sp >=DIMMAX then eroare("Stiva
plina")
else | S.sp=S.sp+1
|_ S.vect[S.sp]=a //atomul este pus
pe prima
//pozitie
}
Functia pop scoate un element din stiva:
pop(S)
{
if S.sp=0 then eroare ("Stiva vida")
else S.sp=S.sp-1
}
Observatie: Se obisnuieste ca pe lnga stergerea
elementului, functia pop sa returneze elementul scos
din lista.
top(S)
{
if S.sp=0 then eroare("Stiva vida")
else return(S.vect[S.sp])
_________________________________________________________________________ 26
Structuri de date si algortitmi
_________________________________________________________________________
}
Functia isEmpty(S) testeaza conditia stiva vida:
isEmpty(S)
{
return(S.sp==0)
}
Stive nlantuite
O stiva poate fi implementata ca o lista nlantuita
pentru care operatiile de acces se fac numai asupra
primului element din lista. Deci, operatia PUSH va
nsemna inserare n prima pozitie din lista (n fata) iar
POP va nsemna stergerea primului element din lista.
Pentru a manevra o stiva vom avea nevoie de un
pointer la primul element din nlantuire, deci, vom
echivala tipul Stack cu tipul "pointer la element de
lista", iar functiile care implementeaza operatiile de
acces vor avea aceleasi prototipuri cu cele date mai
sus.
struct Element {
Atom data;
Element* link; //legatura
};
typedef Element* Stack;
_________________________________________________________________________ 27
Structuri de date si algortitmi
_________________________________________________________________________
Fie S pointer-ul la primul element din nlantuire,
se echivaleaza tipul Stack cu typedef Element* Stack,
iar conditia de stiva vida este S=0 :
push(S,a)
{
p=get_sp()
data(p)=a
link(p)=S
S=p
}
pop(S)
{
if S=0 then eroare("Stiva vida")
else | p=S;
| S=link(S)
|_ ret_sp(p)
}
top(S)
{
if S=0 then eroare("Stiva vida")
else return(data(S))
}
_________________________________________________________________________ 28
Structuri de date si algortitmi
_________________________________________________________________________
isEmpty(S)
{
return(S==0)
}
Stivele sunt cele mai simple structuri de date, ele
avnd si operatiile imediate.
Cozi ordonate
O coada este o lista n care operatiile de acces sunt
restrictionate la inserarea la un capat si stergerea de la
celalat capat.
coada
ultimul
primul
GET PUT
Pricipalele operatii de acces sunt:
PUT(Coada, Atom) Coada
Adauga un element la coada.
GET(Coada) Coada, Atom
Scoate un Atom din coada.
Returneaza atomul scos.
_________________________________________________________________________ 29
Structuri de date si algortitmi
_________________________________________________________________________
head tail
O coada poate fi organizata pe un spatiu de
memorare de tip tablou (vector).
Sunt necesari doi indicatori:
head indica: primul element care urmeaza
sa fie scos.
tail indica: locul unde va fi pus urmatorul
element adaugat la coada.
Conditia "coada vida" este echivalenta cu: head =
tail. Initial indicatorii vor fi initializati astfel nct sa
indice ambii primul element din vector.
Operatia PUT nseamna:
- V[tail] primeste Atomul adaugat;
- incrementeaza tail.
Operatia GET nseamna:
- ntoarce V[head];
- incrementeaza head
Se observa ca adaugari si stergeri repetate n
coada deplaseaza continutul cozii la dreapta, fata de
nceputul vectorului. Pentru a evita acest lucru ar
trebui ca operatia GET sa deplaseze la stnga
continutul cozii cu o pozitie. Primul element care
urmeaza sa fie scos va fi ntotdeauna n prima pozitie,
_________________________________________________________________________ 30
Structuri de date si algortitmi
_________________________________________________________________________
indicatorul head pierzndu-si utilitatea. Dezavantajul
acestei solutii consta n faptul ca operatia GET
necesita o parcurgere a continutului cozii.
Facem notatiile:
C coada
C.vect vectorul n care sunt memorate elementele
cozii
C.head indicele elementului ce va fi scos din coada
la urmatoarea operatie get
C.tail indicele (pozitia) n care va fi memorat
urmatorul element adaugat la coada.
Conditia coada vida este C.head=C.tail.
Functia put pune n coada C un atom a:
put(C,a)
{
if C.tail>DIMMAX then eroare("Coada
plina")
else | C.vect [C.tail]=a
|_ C.tail=C.tail+1
}
Functia get scoate un element din coada si-l
returneaza:
get(C)
_________________________________________________________________________ 31
Structuri de date si algortitmi
_________________________________________________________________________
{
if C.head=C.tail then eroare("Coada
vida")
else | C.head=C.head+1
|_ return C.vect [C.head-1];
}
isEmpty(C)
{
return(C.head==C.tail)
}
Cozi ordonate circulare
Pentru a obtine o coada circulara vom porni de la
o coada liniara simpla (cu doi indicatori) si vom face
n asa fel nct la incrementarea indicatorilor head si
tail, cnd acestia ating ultima pozitie din vector sa se
continue cu prima pozitie din vector.
Functia urmatoare poate realiza aceasta cerinta:
int nextPoz(int index)
{
if (index<DIMVECTOR-1) return
index+1;
else return 0;
}
_________________________________________________________________________ 32
Structuri de date si algortitmi
_________________________________________________________________________
unde DIMVECTOR este dimensiunea vectorului n
care se memoreaza elementele cozii.
Continutul cozii va arata asa:
head tail
1 2 3 4 5 6 7 8
sau asa:
head tail
1 2 3 4 5 6 7 8
Conditia "coada vida" ramne echivalenta cu: head =
tail
Coada va fi plina daca: head=1 si tail=DIMVECTOR
_________________________________________________________________________ 33
Structuri de date si algortitmi
_________________________________________________________________________
head
tail
sau daca: tail+1 = head
head tail
Ambele situatii sunt continute n conditia:
nextPoz(tail) = head // conditia "coada
plina"
Coada circulara ordonata asigura reutilizarea spatiului
eliberat de get la urmatoarele inserari n coada.
Observatie:
n coada circulara de dimensiune DIMMAX pot fi
memorate DIMMAX elemente.
"Coada plina" se realizeaza n 2 situatii:
a) C.head=1 si C.tail=DIMMAX
b) C.tail+1=C.head
_________________________________________________________________________ 34
Structuri de date si algortitmi
_________________________________________________________________________
Iar, conditia C.head=inc(C.tail) le contine pe
amndoua.
n cazul cozilor circulare se modifica doar
operatiile put si get:
put(C,a)
{
if C.head=inc(C.tail) then
eroare("Coada plina")
else | C.vect[C.tail]=a
|_ C.tail=inc(C.tail)
}
get(C)
{
if C.head=C.tail then eroare("Coada
vida")
else | a=C.vect [C.head]
| C.head= inc (C.head)
|_ return(a)
}
_________________________________________________________________________ 35
Structuri de date si algortitmi
_________________________________________________________________________
Curs 4
Cozi nlantuite
O coada poate fi implementata printr-o lista
nlantuita la care operatiile de acces sunt restrictionate
corespunzator.
head
tail
nil
Este nevoie de doi indicatori (pointeri):
head indica primul element din coada (primul
care va fi scos);
tail indica ultimul element din coada (ultimul
introdus).
O coada vida va avea: head=tail=nil
n mod obisnuit, adaugarea unui element n coada
modifica numai tail iar stergerea unui element numai
head. ntr-un mod special trebuie sa fie tratate
cazurile:
adaugare ntr-o coada vida:
_________________________________________________________________________ 36
Structuri de date si algortitmi
_________________________________________________________________________
Initial: head=tail=nil
Final: Coada cu un element:
head tail
nil
stergere dintr-o coada cu un element:
Initial: Coada cu un element
Final: head=tail=nil
n aceste cazuri se modifica att head ct si tail.
Facem notatiile :
C.head pointer la primul element din coada;
C.tail pointer la ultimul element din coada;
C coada.
Conditia de coada vida este head=0.
Operatiile cozii nlantuite
Functia put insereaza un element n coada, n
pozitia fata:
put(C,a)
{
p= get_sp();
data(p)=a;
link(p)= 0;
_________________________________________________________________________ 37
Structuri de date si algortitmi
_________________________________________________________________________
if C.head=0 then| C.head= p
|_ C.tail= p
else | link(C.tail)= p
|_ C.tail= p
}
Functia get scoate un element din pozitia fata:
get(C,a)
{
if C.head= 0 then eroare("Coada
goala")
else | a= data(C.head)
| p= C.head
| C.head= link(C.head)
| ret_sp(p)
|_ return(a)
}
Functia front returneaza elementul din fata cozii, fara
a-l scoate din coada.
front(C)
{
_________________________________________________________________________ 38
Structuri de date si algortitmi
_________________________________________________________________________
if C.head=0 then eroare("Coada vida")
else return data(C.head)
}
isEmpty(C)
{
return(C.head=0)
}
Exista aici un element de redundanta: ar fi
convenabil sa nu mai avem spatiu suplimentar de
memorare, ci, sa avem un singur pointer ca sa putem
manevra coada. De aceea apar utile cozile nlantuite
circulare.
Cozi nlantuite circulare
Daca reprezentam coada printr-o structura
nlantuita circulara va fi nevoie de un singur pointer
prin intermediul caruia se pot face ambele operatii de
adaugare si stergere din coada:
_________________________________________________________________________ 39
Structuri de date si algortitmi
_________________________________________________________________________
tail
.....
primul ultimul
Fie:
C pointer la primul () element din coada
link(C) pointer la ultimul() element din coada
Operatiile de plasare si de scoatere din coada,
sunt:
put(C,a)
{
p= get_sp()
data(p)=a
if C=0 then | C= p
|_ link(C)= p
else | link(p)= link(C)
| link(C)= p
|_ C= p
}
get(C)
{
if C= 0 then eroare("Coada vida")
else if C=link(C) then | a= data(C)
| ret_sp(C)
_________________________________________________________________________ 40
Structuri de date si algortitmi
_________________________________________________________________________
| C= 0
|_ return(a)
else | {p= link(C)
| link(C)= link(p)
| a= data(p)
| ret_sp(p)
|_ return(a)
}
front(C) returneaza data(link(C))
isEmpty(C) retuneaza conditia C=0.
Complexitatea algoritmilor
La evaluarea (estimarea) algoritmilor se pune n
evidenta necesarul de timp si de spatiu de memorare al
lui.
Studierea complexitatii presupune analiza completa n
cadrul algoritmului a urmatoarelor 3 puncte de vedere:
1. configuratia de date cea mai defavorabila (cazurile
degenerate);
2. configuratia de date cea mai favorabila;
3. comportarea medie.
Punctul 3 presupune probabilitatea de aparitie a
diferitelor configuratii de date la intrare.
_________________________________________________________________________ 41
Structuri de date si algortitmi
_________________________________________________________________________
Punctul 1 este cel mai studiat si este folosit, de
obicei, pentru compararea algoritmului. Si n ceea ce
priveste timpul, se studiaza configuratia cea mai
defavorabila a algoritmului.
Complexitatea unui algoritm se noteaza cu:
O(f(n)).
Definitie
Fie f : N N si g : N N doua functii.
Spunem ca f O(g) si notam f = O(g) daca si
numai daca o constanta c R si un numar n
0

N astfel nct pentru n > n
0
f(n) < c g(n)
Observatie:
f : N N este o functie f(n) cu n dimensiunea
datelor de intrare.
f(n) reprezinta timpul de lucru al algoritmului
exprimat n "pasi".
Lema 1
Daca f este o functie polinomiala de grad k, de
forma:
f(n) = a
k
n
k
+ a
k-1
n
k-1
+ ... + a
1
n + a
0
,
atunci f = O(n
k
).
_________________________________________________________________________ 42
Structuri de date si algortitmi
_________________________________________________________________________
Facndu-se majorari n membrul drept, obtinem
rezultatul de mai sus:
f(n) = |a
k
| n
k
+ |a
k-1
| n
k-1
+ ... +|a
1
| n +|a
0
|<
n
k
(|a
k
|+ |a
k-1
|+ |a
0
|) < n
k
c pentru n > 1
f(n) < c n
k
, cu n
0
= 1.
Concluzie: f = O(n
k
), si ordinul O exprima viteza de
variatie a functiei, functie de argument.
Exemplu: Calcularea maximului unui sir
maxsir(A,n)
{
max = A[1]
for i= 2 to n do
if A[i] > max then
max = A[i]
return (max)
}
Exprimam:
T(n) timpul de executie n pasi al acestui
algoritm;
T(n)= 1 + 2(n-1) = numarul de atribuiri si
comparatii
_________________________________________________________________________ 43
Structuri de date si algortitmi
_________________________________________________________________________
Cazul cel mai defavorabil: situatia n care vectorul
este ordonat crescator (pentru ca de fiecare data se
face si comparatie si atribuire).
Putem spune ca T(n) = O(n), este o functie
polinomiala de gradul I. Conteaza doar Ordinul
polinomului, nu coeficientul termenului de grad
maxim. Iar la numararea pasilor ne concentram asupra
numarului buclelor, nu asupra pasilor din interiorul
buclei.
Exemplu: Insertion Sort (algoritmul de sortare
prin inserare)
Algoritmul INSERTION SORT considera ca n
pasul k, elementele A[1k-1] sunt sortate, iar
elementul k va fi inserat, astfel nct, dupa aceasta
inserare, primele elemente A[ k] sa fie sortate.
Pentru a realiza inserarea elementului k n
secventa A[1k-1], aceasta presupune:
memorarea elementului intr-o varibila temporara;
deplasarea tuturor elementelor din vectorul
A[1k-1] care sunt mai mari dect A[k], cu o
pozitie la stnga (aceasta presupune o parcurgere de
la dreapta la stnga);
plasarea lui A[k] n locul ultimului element deplasat.
Complexitate: O(n)
_________________________________________________________________________ 44
Structuri de date si algortitmi
_________________________________________________________________________
insertion_sort(A,n)
{
for k= 2 to n do
|temp = A[k]
|i=k-1;
|while (i>=1) and (A[i] > temp) do |
A[i+1] = A[i]
| |_ i=i-1
|_ A[i+1] = temp
}
Cazul cel mai defavorabil: situatia n care
deplasarea (la dreapta cu o pozitie n vederea nserarii)
se face pna la nceputul vectorului, adica sirul este
ordonat descrescator.
Exprimarea timpului de lucru:
T(n) = 3(n - 1) + (1 + 2 + 3+ ... + n - 1) =
3(n-1) + 3n (n - 1)/2
Rezulta complexitatea: T(n) = O(n
2
) functie
polinomiala de gradul II.
Observatie: Cnd avem mai multe bucle imbricate,
termenii buclei celei mai interioare dau gradul
polinomului egal cu gradul algoritmului.
Bucla cea mai interioara ne da complexitatea
algoritmului.
_________________________________________________________________________ 45
Structuri de date si algortitmi
_________________________________________________________________________
i O n
i
n

( )
2
1
Exemplu: nmultirea a doua matrici
prod_mat(A,B,C,n)
{
for i = 1 to n do
for j = 1 to n do
| C[i,j] = 0
|for k = 1 to n do
|_ C[i,j] = C[i,j] + A[i,k] * B[k,j]
}
Rezulta complexitatea O(n
3
).
Exemplu: Cautarea binara(Binary Search)
Fie A, de ordin n, un vector ordonat crescator. Se
cere sa se determine daca o valoare b se afla printre
elementele vectorului. Limita inferioara se numeste
low, limita superioara se numeste high, iar mijlocul
virtual al vectorului, mid (de la middle).
low
middle high
_________________________________________________________________________ 46
Structuri de date si algortitmi
_________________________________________________________________________
Binary_search(A,n,b)
{
low = 1;
high = n;
while low <= high do
| mid = [(low + high)/2] //partea
ntreaga
| if A[mid]=b then return (mid)
| else if A[mid]>b then high=mid-1
//restrng
| //cautarea la
partea stnga
|_ else low = mid+1
//restrng cautarea la dreapta
return(0)
}
Calculul complexitatii algoritmului consta n
determinarea numarului de ori pentru care se
executa bucla while.
Se observa ca, la fiecare trecere, dimensiunea
zonei cautate se njumatateste. Cazul cel mai
defavorabil este ca elementul cautat sa nu se gaseasca
n vector. Pentru simplitate se considera n = 2
k
unde k
este numarul de njumatatiri.
_________________________________________________________________________ 47
Structuri de date si algortitmi
_________________________________________________________________________
Rezulta k = log
2
n si facnd o majorare, T(n)
log
2
n + 1 n, a.. 2
k
n < 2
k+1.
Rezulta complexitatea acestui algoritm: este
O(log
2
n). Dar, baza logaritmului se poate ignora,
deoarece: log
a
x = log
b
x * log
a
b si log
a
b este o
constanta, deci ramne O(log n), adica o functie
logaritmica.
Proprietati:
1) Fie f, g : N N.
Daca f = O(g) | k f = O(g)
| f = O(k g) , k R constant.
2) Fie f, g, h : N N.
si: f = O(g) |
g = O(h) | f = O(h)
3) Fie f
1
, f
2
, g
1
, g
2
: N N.
si: f
1
= O(g
1
) | | f
1
+ f
2
= O(g
1
+ g
2
)
f
2
= O(g
2
) | | f
1
f
2
= O(g
1
g
2
)
Aceasta proprietate permite ca, atunci cnd avem
doua bucle imbricate (de complexitati diferite),
complexitatea totala sa se obtina nmultindu-se cele
_________________________________________________________________________ 48
Structuri de date si algortitmi
_________________________________________________________________________
doua complexitati. Cele doua complexitati se aduna,
daca buclele sunt succesive.
Teorema:
Oricare ar fi doua constante c > 0, a > 1, si f : N
N, o functie monoton strict crescatoare, atunci:
(f(n))
c
= O(a
f(n)
)
Demonstratia se bazeaza pe limita:
lim ( , )
x
p
x
x
a
a p


ntre clasa functiilor logaritmice, si cea a functiilor
polinomiale exista relatia: O(n
c
) O(a
n
).
Au loc urmatoarele incluziuni:
O(1) O(log n) O(n) O(n log n)
O(n
2
) O(n
k
log n) O(n
k+1
) O(2
n
)
Pentru calculul complexitatii se va ncerca ncadrarea
n clasa cea mai mica de complexitate din acest sir:
O(1) clasa algoritmilor constanti;
O(log n) clasa algoritmilor logaritmici;
O(n) clasa algoritmilor liniari;
O(n log n) clasa algoritmilor polilogaritmici;
O(n
2
) clasa algoritmilor patratici;
O(n
k
log n) clasa algoritmilor polilogaritmici;
O(n
k+1
) clasa algoritmilor polinomiali;
O(2
n
) clasa algoritmilor exponentiali.
_________________________________________________________________________ 49
Structuri de date si algortitmi
_________________________________________________________________________
Tehnici de calcul a complexitatii
Se folosesc urmatoarele sume:
) O(n
2
) 1 (
2
1

+

n n
i
n
i
) O(n
6
) 1 2 ( ) 1 (
3
1
2

+ +

n n n
i
n
i
) O(n
4
) 1 (
4
2 2
1
3

n n
i
n
i
1 - 2 2
1
0
1 +

n
n
i
Sa calculam, de exemplu, suma:

n
i
i
1
1
2
Se noteaza:


n
i
i n G
1
1
2 ) (
2 2 ) 1 ( 2 2 2 2 ) 1 ( 2 2
2 2 2 2 2 ) ( 2 ) (
1
2
1 1
2
1 1
1
1
1
1 1
1
1
1
1
+ +

+

+

+



n
n
i
n
n
i
n
n
i
n
i
n
i
n
i
n n i i n
i i i i n G n G
Prin aceeasi tehnica se calculeaza suma:


n
i
n
1
1
2 ) 1 (
_________________________________________________________________________ 50
Structuri de date si algortitmi
_________________________________________________________________________
Curs 5
Am vazut ca:

+

n
i
n n
n i
1
1 1
2 2 2
Algoritmul recursiv si relatii de recurenta
Exemplu: Problema turnurilor din Hanoi
Se dau n discuri: a
1
, a
2
, ... , a
n
de dimensiuni
diferite, cu d
1
< d
2
< ... < d
n
, d
i
- fiind diametrul
discului. Discurile respective sunt stivuite pe o tija:
cazul n=3
Se cere sa se deplaseze aceasta stiva pe o alta tija,
folosind ca manevra o tija auxiliara, respectndu-se
conditia << Un disc nu poate fi plasat dect peste un
disc mai mare >>.
Problema P(n) a deplasarii a n discuri, se rezolva
prin deplasari succesive ale discurilor de pe o tija pe
alta. Deplasarea de pe o tija pe alta este echivalenta cu
deplasarea a n-1 discuri de pe tija intiala (t
i
) pe tija de
manevra, apoi plasarea celui mai lung disc pe tija
_________________________________________________________________________ 51
Structuri de date si algortitmi
_________________________________________________________________________
finala, pentru ca la sfrsit sa se aduca de pe tija de
manevra (t
m
), pe tija finala (t
f
), cele n-1 discuri
deplasate.
Primele miscari s-ar figura astfel:
t
t
t
t
t
t
i
m f
f
i m
Procedura Hanoi:
Hanoi(n, t
i
, t
f
, t
m
)
{
if(n=1) then muta (t
i
, t
f
)//deplaseaza
discul superior t
i
-> tf
else | Hanoi(n-1, t
i
, t
m
, t
f
)
| muta(t
i
, t
f
)
|_ Hanoi(n-1, t
m
, t
f
, t
i
)
}
Pentru o problema P(1) , timpul T(1) = 1 , pentru
o mutare.
_________________________________________________________________________ 52
Structuri de date si algortitmi
_________________________________________________________________________
Pentru P(n) , timpul:
1 ) 1 ( 2 ) ( + n T n T
(1)
Dorim sa aflam ordinul de complexitate a lui
T(n).
) 1 ) 2 ( 2 ( 2 1 1 ) 1 ( 2 ) ( + + + n T n T n T
Asociem relatiei (1) ecuatia caracteristica:
1 2 ; 1 ; 1 2
0 0
+ x x x x x
0 0 0
) ( ) ( ) 1 ( 2 ) ( ) 1 ( ( 2 ) ( x n T n f n f n f x n T x n T
const. cu , ) 1 ( ) 1 (
0 0
x x n T n f
) 1 ( 2 ... ) 4 ( 2 ) 3 ( 2 2 2 ) 2 ( 2 2 ) 1 ( 2 ) (
1 4
f n f n f n f n f n f
n


Facnd identificarea:
x f(1)
1 2 = T(n) 1 ) ( 2 2
0
n 1
+

n T
n

Ordinul este O(2
n
), adica o complexitate exponentiala.
Relatii de recurenta. Clasele relatiilor de
recurenta
1.
f n a f n b ( ) ( ) + 1
f(n) = a f(n - 1) + b
x
0
= a x
0
+ b
Prin scaderea celor doua relatii, rezulta un algoritm
exponential cu baza a: f(n) - x
0
= a (f(n-1) - x
0
)
2.
f n a f n b f n ( ) ( ) ( ) + 1 2
f(n) = t
n
t
n
= a t
n-1
+ b t
n-2
Facnd n = 2, t
2
= a t + b , cu urmatoarele cazuri:
_________________________________________________________________________ 53
Structuri de date si algortitmi
_________________________________________________________________________
a) t
1
, t
2
R solutia ecuatiei este de forma:
f n t t
n n
( ) +
1 2 iar si se calculeaza din conditiile
initiale:

'

+
+

'

2
2
2
2
1
1 2 1
2
1
) 2 (
) 1 (
x t t
x t t
x f
x f


cu x
1 si
x
2 constante.
Astfel, este rezolvata ecuatia recursiva.
b) t
1
= t
2
= t Solutia este de forma:
n
t n n f + ) ( ) (
c) t
1
, t
2
C Solutia este de forma:
n n
t t n f
2 1
) ( +
n care si C, =

(conjugat) solutia
trigonometrica:
) sin cos ( ) (
1 1
nt nt r n f
n
+
3. Clasa de relatii de recurenta pentru algoritmi de
tip "divide et impera"
Exemplu: Algoritmul Merge Sort (sortare prin
interclasare)
Pentru a sorta o secventa de n elemente ale unui
vector A, se mparte vectorul n 2 segmente de
lungime n/2 pe care le sorteaza separat recursiv, dupa
care urmeaza interclasarea.
Pseudocod: Procedura MERGE_SORT primeste
ca argumente A - vectorul de sortat, si doi indici care
_________________________________________________________________________ 54
Structuri de date si algortitmi
_________________________________________________________________________
delimiteaza o portiune din acest vector. Apelul initial
va fi MERGE_SORT(A, 1, n).
MERGE_SORT(A, low, high)
{
if(low high) return
else | | low + high |
| mid=| ---------------- | //partea
ntreaga
| |_ 2 _|
| MERGE_SORT(A, low, mid)
//sortare separata
| MERGE_SORT(A, mid+1, high)
//sortare separata
|_MERGE(A, low, mid, high)
//interclasare
}
Procedura MERGE interclaseaza secventele
sortate A[lowmid] si A[mid+1high]. Pentru aceasta
este nevoie de un vector auxiliar B, de aceeasi
dimensiune cu A.
MERGE(A, low, mid, high)
{
i=low; j=mid+1; k=low;
_________________________________________________________________________ 55
Structuri de date si algortitmi
_________________________________________________________________________
while i mid and j high do
| if A[i] <A [j] then B[k]=A[i];
i=i+1
| else B[k] = A[j]; j=j+1
|_ k = k+1
while i mid do
| B[k] = A[i]; i=i+1
|_ k = k+1
whilej high do
| B[k] = A[j]; j=j+1
|_ k = k+1
for k=low to high do
A[k] = B[k];
}
Aratam complexitatea procedurii MERGE_SORT:
O(n log n)
Aceasta functie cere pentru o secventa c n operatii.
Timpul de executie al algoritmului este:
1 n n, + 2T(n/2)
1 = n , 0
) (

n T
Consideram: n = 2
k
;
T(n) = 2 T(n/2) + n = 2 (2 T(n/4) + n/2) + n = ... =
2
2
T(n/2
2
) + 2n= 2
2
(2T(n/2
3
) + n/2
2
) + 2n =
_________________________________________________________________________ 56
Structuri de date si algortitmi
_________________________________________________________________________
= 2
3
T(n/2
3
) + 3n = ... = 2
k
T(n/2
k
) + k n T(n) =
k n = n log
2
n , pentru ca n = 2
k
, si, deci, k = log
2
n.
Asadar complexitatea algoritmului este O(n log n).
Pentru a rezolva problema de dimensiune n, se
rezolva pentru a probleme de dimensiune n/b, iar
combinarea rezultatelor celor a prbleme, duce la g(n)
operatii.
Se demonstreaza, analog, si relatia de recurenta:
k
n C b n T a n T + ) / ( ) (
Solutia acestei ecuatii de recurenta este:
k k
k k
k a
b a n O
b a n n O
b a n O
n T
b
) , (
) , l o g (
) , (
) (
l o g

Utiliznd aceste retete putem calcula complexitatile


pentru:
algoritmul Merge_Sort: a = 2 , b = 2 , k = 1 , b
k
= a
complexitatea O(n
k
log n)
algoritmul Binary_Search: a = 1 , b = 2 , k = 0
complexitatea O(n
0
log n) = O(log n), (situatia
a
k
= b).
_________________________________________________________________________ 57
Structuri de date si algortitmi
_________________________________________________________________________
4. Relatii de recurenta cu istorie completa
Exemplu:
T n C T i
i
n
( ) ( ) +

1
1
T n C T n T n T ( ) ( ) ( ) ... ( ) + + + + 1 2 1
T n C T i T n T n
i
n
( ) ( ) ( ) ( ) + + +

1 1
1
- se face
Exemplu: Algoritmul Quick_Sort
elemente pivot A[k]=pivot elemente
pivot
l h
Quik_Sort(A, low, high)
{
if(high >low) then
| k= Partition(A, low, high) //
procedura de |
// partitionare
| Quick_Sort (A, low, k-1)
|_ Quick_Sort(A, k+1, high)
}
Pseudocodul pentru functia partition:
Partition(A, low, high)
{
l= low; h= high;
x= A[l];
_________________________________________________________________________ 58
Structuri de date si algortitmi
_________________________________________________________________________
while (l <h) do
I | while (A[l] <= x) and (l <=high)
| do l= l+1
II | while (A[l] >x) and (h low)
| do h= h-1
|_ if (l <h) then interchange (A[l],
A[h])
interchange (A[h], A[low])
return(h);
}
Algoritmul considera pivotul ca fiind: A[low].
Indicele l parcurge vectorul de la stnga la dreapta, iar
indicele h parcurge vectorul de la dreapta la stnga. Ei
se apropie pna se ntlnesc (l = h). Deci, l lasa n
urma numai elemente A[i] pivot, iar h lasa n urma
numai elemente A[i] > pivot.
Ciclul I while nseamna ca nainteaza l ct timp
A[l] pivot. Acest ciclu se opreste pe conditia
A[h] > pivot, fixndu-se aici.
Ciclul II while nsemna ca nainteaza h ct timp
A[h] > pivot. Acest ciclu se opreste pe conditia
A[h] pivot, fixndu-se aici.
_________________________________________________________________________ 59
Structuri de date si algortitmi
_________________________________________________________________________
Cele doua pozitii se schimba, astfel nct sa se
permita naintarea indicilor mai departe.
low h
Pentru aflarea complexitatii, cercetam cazul cel
mai defavorabil. Fie cazul n care vectorul este ordonat
descrescator. Pivotul gasit, la primul pas, este
elementul maxim din vector, rezulta ca trebuie plasat
n ultima pozitie. Pivotul va fi maximul dintre
elementele secventei, deci, va fi plasat n ultima
pozitie din secventa.
Problema se mparte n 2 subprobleme: P(n)
P(n-1) , P(0).
Numarul de comparatii pentru functia Partition
este (n-1). Vectorul se parcurge n doua directii, dar o
singura data.
Rezulta ca timpul de functionare al algoritmului
Quick_Sort este:
T n n T n ( ) ( ) ( ) + 1 1
Rezolvnd aceasta ecuatie, avem:
T n n T n n n T n n n n T ( ) ( ) ( ) ... ... ( ) + + + + + + + + 1 1 1 2 2 1 2 3 1 1
unde: T(1) este 0 (nu se partitioneaza). Rezulta:
T n i n n
i
n
( ) ( ) /

1 2
1
1
_________________________________________________________________________ 60
Structuri de date si algortitmi
_________________________________________________________________________
Aceasta suma este de complexitate O(n
2
). Rezulta
ca este un algoritm ineficient.
Studiul complexitatii algoritmului Quick_Sort n
caz mediu
Pentru complexitatea medie trebuie considerata
probabilitatea tuturor aparitiilor datelor de intrare.
Consideram ca orice configuratie de date la intrare este
egal probabila. Probabilitatea ca pivotul sa fie plasat n
pozitia k este egala pentru
k low high ,
. Asadar, pivotul va
fi plasat n pozitia k prin partitionare, cu o
probabilitate egala cu 1/n, pentru
k low high k n , ( , ) 1
.
Suma tuturor probabilitatilor este 1. Evenimentul
este plasarea pivotului n pozitia k. Consideram T
i
(n)
timpul de executie al algoritmului Quick_Sort atunci
cnd pivotul este plasat n pozitia i:
i
Rezulta:
T n n T i T n i
i
( ) ( ) ( ) + 1 1
Timpul mediu va fi o medie aritmetica:



,
_


,
_

n
i
i
n
i
i
n T
n
n T
n
n T
1 1
) (
1
) (
1
) (
Dezvoltnd,
_________________________________________________________________________ 61
Structuri de date si algortitmi
_________________________________________________________________________




,
_

+
,
_

+
,
_

+

,
_

+
,
_

+
,
_

+ +
,
_

n
i
n
j
n
i
n
i
n
i
n
i
i T
n
n j T
n
i T
n
n
i n T
n
i T
n
n n
n
i n T i T n
n
n T
1 1
1
0
1 1 1
) (
2
1 ) 1 (
1
) 1 (
1
1
) (
1
) 1 (
1
) 1 (
1
)) ( ) 1 ( ) 1 ((
1
) (
(Facnd schimbarea de variabila j = n - i + 1)
Rezulta relatia de recurenta cu istorie completa:


,
_

+
1
0
) (
2
1 ) (
n
i
i T
n
n n T
Aceasta se rezolva astfel: nmultind relatia cu n
rezulta:

+
1
0
) ( 2 ) 1 ( ) (
n
i
i T n n n T n
Scriem acum relatia nlocuind pe n cu n+1:

+ + + +
n
i
i T n n n T n
0
) ( 2 ) 1 ( ) 1 ( ) 1 (
Si scazndu-le acum membru cu membru rezulta:
) ( ) 2 ( 2 ) 1 ( ) 1 (
) ( 2 ) 1 ( ) 1 ( ) ( ) 1 ( ) 1 (
n T n n n T n
n T n n n n n nT n T n
+ + + +
+ + + +
care se nmulteste cu:
) 2 )( 1 (
1
+ + n n
,
1
) (
) 2 )( 1 (
2
2
) 1 (
+
+
+ +

+
+
n
n T
n n
n
n
n T
Notam:
) 1 - ( +
) 1 (
) 1 ( 2
) ( ), (
) 2 )( 1 (
2
) 1 ( ,
1
) (
) ( n F
n n
n
n F n F
n n
n
n F
n
n T
n F
+
+
+
+ +

+
+

Facnd o majorare:
) 1 (
n
2
) ( + n F n F

_________________________________________________________________________ 62
Structuri de date si algortitmi
_________________________________________________________________________
) (
2
2
. . .
2
2
1
2 2
. . . ) 3 (
2
2
1
2 2
) 2 (
1
2 2
) 1 (
2
) ( i F
n n n
n F
n n n
n F
n n
n F
n
n F + + +

+ +

+ +

+ +
0 ) ( i F
(un element nu se ordoneaza).
Rezulta:

,
_


n
i
i
n F
2
1
2 ) ( si
d x
1
2
1
2 ) (
2


,
_

,
_

n
i
n
i
x i
n F
este aria zonei de sub
graficul functiei
x
x f
1
) (
.
) l n ( ) ( l n 2 l n ) 1 ( ) ( ) (
1
) (
l n 2 ) ( 1 l n 2 l n 2 ) (
n n O n T n n n n n T n F
n
n T
n n F n n F
+
+

unde O(n ln n) este complexitatea acestei functii.
_________________________________________________________________________ 63
Structuri de date si algortitmi
_________________________________________________________________________
Curs 6
Analiza spatiului de memorie consumat
ntr-un algoritm
Algoritmi recursivi
Algoritmii recursivi consuma memorie
suplimentara pentru simularea recursivitatii.
Fie urmatoarea procedura recursiva:
parcurgere(l) // l - lista nlantuita (pointer la
primul //element)
{
if(l 0)
| parcurgere(link(l))
|_ prelucrare(data(l)) // exemplu:
afisare
}
Functia afiseaza o lista invers, de la coada la cap.
Apelul functiei se face astfel:
_________________________________________________________________________ 64
Structuri de date si algortitmi
_________________________________________________________________________
se creeaza n stiva programului o "nregistrare de
activare" n care sunt memorate:
- parametrii de apel;
- adresa instructiunii de retur (cu care va
continua programul dupa terminarea executiei
functiei);
se rezerva spatiu pentru variabile locale.
se executa instructiunile functiei care folosesc
pentru parametri si variabile locale din "nregistrarea
de activare";
se scoate din stiva "nregistrarea de activare"
(decrementarea vrfului stivei), stiva fiind ordonata;
se continua cu instructiunea data de adresa de retur
memorata n "nregistrarea de activare".
Asadar, variabilele globale (statice) sunt
memorate ntr-o zona de memorie fixa, mai exact n
segmentele de date. Variabilele automate (locale) se
memoreaza n stiva, iar variabilele dinamice n "heap"-
uri (cu malloc n C, si cu new n C++).
Consumul de memorie al algoritmului recursiv
este proportional cu numarul de apeluri recursive ce se
fac. Variabilele recursive consuma mai multa memorie
dect cele iterative. La prelucrarea unei liste, daca
primul element nu este vid, se prelucreaza acesta,
_________________________________________________________________________ 65
Structuri de date si algortitmi
_________________________________________________________________________
urmnd apoi ca restul listei sa fie considerata ca o
noua lista mai mica, etc.
De exemplu, algoritmul Quick_Sort:
Quick_Sort(A, low, high)
{
if(low < high)
| k = Partition(A, low, high)
| Quick_Sort(A, low, k-1)
|_Quick_Sort(A, k+1, high)
}
low
k
high
Avem n acest algoritm doua apeluri recursive.
Cazul cel mai defavorabil:
low
k
high
Consideram consumul de memorie n stiva : M(n)
= c + M (n - 1) M(n) = O(n) un ordin de
complexitate mare.
Pentru reducerea consumului de memorie, se
concepe un alt algoritm la Quick_Sort, astfel nct un
apel sa fie rezolvat recursiv, iar celalalt apel iterativ.
_________________________________________________________________________ 66
Structuri de date si algortitmi
_________________________________________________________________________
secventa mica rezolvata recursiv
k
secventa mare rezolvata iterativ
Quick_Sort(A, low, high)
{
while (low < high)
| k = Partition(A, low, high)
| if( k-low > high-k)
| | Quick_Sort(A, k+1, high)
| |_high = k-1
| else
| | Quick_Sort(A, low, k-1)
|_ |_low = k-1
}
Necesarul de memorie pentru aceasta este M(n)
c + M(n/2), nsemnnd ca oricare ar fi secventa mai
mica, ea este dect jumatatea M(n) = O(log n)
am redus ordinul de complexitate.
Liste generalizate
Definitie:
_________________________________________________________________________ 67
Structuri de date si algortitmi
_________________________________________________________________________
Data o multime de elemente (atomi), se numeste lista
generalizata o secventa finita (
1
,
2
, ... ,
n
), n care

i
sunt atomi.
Exemplu: A = (a, b, c)
B = (x, A, (a, c), ( ))
| | | \
atom lista lista lista vida
Observatie: Listele generalizate pot sa aiba elemente
comune. Se permite definirea de liste recursive.
Reprezentarea listelor generalizate
Presupunem o lista de forma: (tag, data, link) n
care tag este o eticheta {0,1}.
Daca tag = 0 nodul va corespunde unui element
atomic cmpul data va contine atomul respectiv.
Daca tag = 1 nodul va corespunde unei subliste
cmpul data va semnifica legatura la primul element
al sublistei; link este legatura pentru urmatorul nod din
lista.
Fie urmatoarele primitive de selectie pentru un
nod de adresa p:
p adresa unui nod;
link(p) cmpul "link" din nodul indicat de p;
Notam: tag(p) cmpul "tag" din nodul indicat de p
data(p) cmpul "data" din nodul indicat de p
_________________________________________________________________________ 68
Structuri de date si algortitmi
_________________________________________________________________________
Fie urmatoarele liste:
D = ( )
A = (a, (b, c))
B = (A, A, ( ))
C = (a, C)
cu urmatoarele reprezentari:
D : o lista vida nseamna un pointer nul
A:
B:
C:
0 a 1
b c
0
0 0 0
0 0
0 0
1 1 1
1 a
Ne propunem sa dam nume unei subliste, deci
daca tag = 1, adica tag(p) = 1 data(p) va fi adresa
unei structuri ce contine:
nume lista
pointer la primul element
Asadar, obtinem urmatoarea reprezentare:
_________________________________________________________________________ 69
Structuri de date si algortitmi
_________________________________________________________________________
D:
A:
B:
C:
D 0
A
a 0 0 1
_
b 0
0 0 c
B
1 1
0 1
_
C
a 0
0 1
Operatii la liste generalizate:
functia insert, este asemanatoare cu cea de la liste
nlantuite. Elementul ce se insereaza poate fi un
atom sau o sublista;
functia del ( ) trebuie sa tina seama de existenta unor
liste comune. Deci, este necesara pastrarea n
_________________________________________________________________________ 70
Structuri de date si algortitmi
_________________________________________________________________________
elementul ce contine numele listei A si a unui
indicator care sa contorizeze numarul de referinte
ale lui.
Exemplu:
A 2
Numai daca acest indicator este 0, se face
stergerea efectiva a listei.
Traversarea listelor generalizate
Traversarea listelor generalizate presupune
prelucrarea elementelor listei, si a elementelor
sublistelor componente.
Exemplu: O functie de copiere si o functie de test de
egalitate a doua liste generalizate, realizate recursiv si
iterativ. Functia returneaza o copie a listei. Copie mai
nti primul element, si apoi recursiv restul listei:
Varianta recursiva:
Copy (l) // l - lista nlantuita
{
if (l = 0) then return (0)
else | p = get_sp()
_________________________________________________________________________ 71
Structuri de date si algortitmi
_________________________________________________________________________
| data(p) = data(l)
| link(p) = Copy(link(l))
|_ return(p)
}
Copy (l) // l - lista generalizata
{
if (l = 0) then return (0)
else | p = get_sp()
| if (tag(l) = 0) then data(p) =
data(l)
| else data(p) = Copy(data(l))
| link(p) = Copy(link(l))
|_ return(p)
}
Functia pentru testarea egalitatii este:
isEqual (l
1
,l
2
) // procedura tratata
iterativ
{
p
1
= l
1
; p
2
= l
2
while(p
1
0 and p
2
0)
| if (data(p
1
) data(p
2
)) then
return (FALSE)
| p
1
= link(p
1
)
_________________________________________________________________________ 72
Structuri de date si algortitmi
_________________________________________________________________________
|_ p
2
= link(p
2
)
return(p
1
= p
2
)
}
isEqual (l
1
,l
2
) // procedura tratata
recursiv
{
p
1
= l
1
; p
2
= l
2
while(p
1
0 and p
2
0)
| if (tag(p
1
) tag(p
2
)) then return
(FALSE)
| if (tag(p
1
) = 0 and data(p
1
)
data(p
2
)) then |
return (FALSE)
| if (tag(p
1
) = 1 and not isEqual
|
(data(p
1
),data(p
2
))then return FALSE)
| p
1
= link(p
1
)
|_ p
2
= link(p
2
)
return (p
1
== p
2
)
}
_________________________________________________________________________ 73
Structuri de date si algortitmi
_________________________________________________________________________
Curs 7
Arbori
Exemplu de structura de arbore:
a
/ \
b c
/ \ / \
d e f g
/ \ \
h i j
Exemple de arbori:
poligoane
/ \
triunghi
patrulatere
/ \
\
dreptunghi
romb oarecare
Definitie
_________________________________________________________________________ 74
Structuri de date si algortitmi
_________________________________________________________________________
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:
1) nodul r V (nodul radacina) astfel nct j
V, (j, r) E (nici un arc nu intra in radacina);
2) x V\{r} , y V unic , astfel nct (y, x)
E (Cu alte cuvinte, pentru toate nodurile minus
radacina, un singur arc ce intra n nodul respectiv)
3) y V, un drum { r = x
0
, x
1
, x
2
, ... ,x
n
= y} ,
cu x
i
V si (x
i
, x
i+1
) E (Sau arborele trebuie sa fie
un graf conex: nu exista noduri izolate sau grupuri de
noduri izolate).
Proprietate a arborelui
Daca T, T= (V, E) este un arbore si r V este
radacina arborelui, atunci multimea T\{r} = (V', E'),
cu
V' = V -{r} si E' = E -{ (r, x)/(r, x) E } poate fi
partitionata astfel nct sa avem mai multi arbori, a
caror reuniune sa fie T\{r}, si oricare ar fi doi arbori
intersectati, sa dea multimea vida:
T\{r}= T
1
T
2
... T
k
, T
i
T
j
=
.
_________________________________________________________________________ 75
Structuri de date si algortitmi
_________________________________________________________________________
Definitii
1) Daca avem (x, y) E , x predecesorul lui y
(tata), y succesorul lui x (fiu)
x
/
y
2) Fie E(x) = { y, (x, y) E } multimea succesorilor
lui x.
Definim gradul lui x: degree(x) = E(x) = numarul
de succesori
Definim gradul arborelui : degree(T) =
max{ degree(x)}, x V unde y se mai numeste nod
terminal, sau frunza, daca degree(y) = 0, adica daca
nu are descendenti.
Stramosii unui nod sunt asa-numitii ancestors(x) :
ancestors(x) = {r = x
0
, x
1
, x
2
, ..., x
k
} cu proprietatea
ca
(x
i
, x
i+1
) E , i= {0,k - 1} si (x
k
, x) E
Nivelul unui nod : level(x) = ancestors(x) + 1
Adncimea arborelui : depth(T) = max { level (x)
pentru x V
_________________________________________________________________________ 76
Structuri de date si algortitmi
_________________________________________________________________________
Exemplu:
A
/ | \
B C D
/ \ | / | \
E F G H I J
/ \ |
K L M
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
_________________________________________________________________________ 77
Structuri de date si algortitmi
_________________________________________________________________________
Reprezentarea arborilor
Reprezentarea prin liste generalizate
Se considera ca nodurile terminale sunt elemente
atomice, iar nodurile de grad 1 sunt subliste. Deci,
fie arborele de mai sus scris sub forma :
A( B (E (K, L), F), C (G), D (H (M), I, J)) cu
reprezentarea:
_________________________________________________________________________ 78
Structuri de date si algortitmi
_________________________________________________________________________
0 K L
E
F 1 G
H
M
I J
0
0
0 0 0
B
C D
1
0 0
0
0
A
1 1 1 0
0
Reprezentarea prin structuri nlantuite
_________________________________________________________________________ 79
Structuri de date si algortitmi
_________________________________________________________________________
K L M
0 0
0 0 0 0 0 0 0
0 0 0 0
0 0
0 0 0 0
0 0 0 0 0
E
F G H I J
A
B
C
D
0
0 0
data
link1 link2 link k
......
k=degree(T)
Aceasta reprezentare are calitatea ca, atunci cnd
conteaza ordinea descendentilor, ea poate surprinde
structura diferita.
De exemplu:
structura x este diferita de
structura x
/ | \
/ | \
_________________________________________________________________________ 80
Structuri de date si algortitmi
_________________________________________________________________________
vid y vid
y vid vid
Metodele de reprezentare expuse permit sa putem
identifica legatura nod-descendent (succesor). Dar,
exista aplicatii n care este nevoie de legatura nod-
predecesor. Asadar, pare utila reprezentarea arborelui
sub forma nodului (data, parent):
A
B C D
E F
0
G H I J
K L M
Avnd adresa unui nod, se gasesc toti
predecesorii, obtinndu-se o lista nlantuita:
(Reprezentarea TATA):
M H D A 0
Arbori binari
Un arbore binar este un arbore de grad maxim 2.
n cazul acestor arbori, se pot defini aplicatii,
_________________________________________________________________________ 81
Structuri de date si algortitmi
_________________________________________________________________________
instrumente n plus de operare. Arborii binari pot avea
deci gradele 2, 1, 0:
A A
A
/ \ / /
\
B C B B
C
/ \ \ / / \
/ \
D E F C D
E F G
Observatie: Arborele A este
diferit de A
/ \
/ \
B vid
vid B
Structura de baza a unui arbore binar:
rad
/ \
/ \
/ \
/ \
/ \ / \
_________________________________________________________________________ 82
Structuri de date si algortitmi
_________________________________________________________________________
/ SAS \ /
SAD \
/______ \
/_______\
SAS subarborele stng (binar)
SAD subarborele drept (binar)
Definitii
1) Se numeste arbore binar strict arborele pentru care
oricare ar fi un nod x V degree(x) = 2 , sau
degree(x) = 0.
Exemplu:
a
/ \
b c
/ \ / \
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)
_________________________________________________________________________ 83
Structuri de date si algortitmi
_________________________________________________________________________
Cu alte cuvinte, nodurile terminale apartin ultimului
nivel din arbore.
Exemplu:
a
/ \
b c
/ \ / \
d e f g
/ \ / \ / \ / \
h i j k l m n o
Relatii ntre numarul de noduri si structura unui
arbore binar
Lema 1
Numarul maxim de noduri de pe nivelul i al unui
arbore binar este egal cu 2
i-1
.
Demonstratia se face prin inductie:
La nivelul 1 avem 2
0
= 1 nod = rad (radacina
A). Presupunem conform metodei P(n): pe nivelul n
avem 2
n-1
noduri. Demonstram pentru P(n+1): se
observa ca toate nodurile de pe nivelul n+1 sunt
noduri descendente de pe nivelul n. Notnd niv(i)
numarul de noduri de pe nivelul i,
_________________________________________________________________________ 84
Structuri de date si algortitmi
_________________________________________________________________________
niv(n+1) 2niv(n) 2 2
n-1
= 2
n
.
Lema 2
Numarul maxim de noduri ale arborelui binar de
adncime h este egal cu 2
h
-1.
Demonstratie:
Numarul total de noduri este egal cu:
niv i
i
i
h
h
h
h
i
h
( ) ... + + +


2 2 2 2
2 1
2 1
2 1
1
1
0 1 1
1
(progresie geometrica)
Observatie: Numarul maxim de noduri pentru
arborele binar se atinge n situatia unui arbore binar
complet.
2
h
-1 = numarul de noduri n arborele binar complet
de adncime h
Lema 3
Notam cu:
n
2
numarul de noduri de grad 2 din arborele binar;
n
1
numarul de noduri de grad 1 din arborele binar;
n
0
numarul de noduri terminale (frunze) din
arborele binar;
n orice arbore binar, n
0
= n
2
+1 (nu depinde de
n
1
).
_________________________________________________________________________ 85
Structuri de date si algortitmi
_________________________________________________________________________
Demonstratie: fie n = n
0
+ n
1
+ n
2
(numarul total
de noduri); conform definitiei, fiecare nod are un
singur predecesor numarul de muchii E = n - 1.
Acelasi numar de muchii E = 2 n
2
+ n
1
.
Deci, n - 1 = 2n
2
+ n
1
, nlocuind, n
0
+ n
1
+n
2
-1 =
2n
2
+ n
1
n
0
= n
2
+ 1 ceea ce trebuia de
demonstrat.
Rezulta ca ntr-o expresie numarul de operatori binari
si unari este egal cu numarul de operanzi + 1.
Lemele se folosesc pentru calcule de complexitate.
Operatii asupra arborilor binari
Operatii curente:
selectia cmpului de date dintr-un nod si selectia
descendentilor;
inserarea unui nod;
stergerea unui nod.
Traversarea arborilor binari (A.B.)
Traversarea consta n "vizitarea" tuturor nodurilor
unui arbore ntr-un scop anume, de exemplu, listare,
testarea unei conditii pentru fiecare nod, sau alta
_________________________________________________________________________ 86
Structuri de date si algortitmi
_________________________________________________________________________
prelucrare. O traversare realizeaza o ordonare a
nodurilor arborelui (un nod se prelucreaza o singura
data).
Strategii de traversare:

traversare n preordine: prelucrare n ordinea: rad,
SAS, SAD;
traversare n inordine: prelucrare n ordinea: SAS,
rad, SAD;
traversare n postordine: prelucrare n ordinea: SAS,
SAD, rad.
rad
/ \
SAS SAD
Exemplu de traversare: a
/ \
b c
/ \ \
d e f
/ \
g h
preordine : A B D E G H C F
_________________________________________________________________________ 87
Structuri de date si algortitmi
_________________________________________________________________________
inordine : D B G E H A C F
postordine : D G H E B F C A
Functii de parcurgere (in pseudocod)
Facem notatiile:
p pointer la un nod
lchild(p) pointer la succesorul stng (p stg)
rchild(p) pointer la succesorul drept (p drt)
data(p) informatia memorata n nodul respectiv (p
data)
n C++ avem:
struct Nod{
Atom data;
Nod* stg;
Nod* dr;
};
Nod* p;
Procedurile de parcurgere sunt:
preorder(t)
{
if(t==0) return
_________________________________________________________________________ 88
Structuri de date si algortitmi
_________________________________________________________________________
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))
}
Binarizarea arborilor oarecare
Lema 1
_________________________________________________________________________ 89
Structuri de date si algortitmi
_________________________________________________________________________
Daca T este un arbore de grad k cu noduri de
dimensiuni egale (k pointeri n fiecare nod), arborele
avnd n noduri reprezentarea va contine n (k - 1) + 1
pointeri cu valoare zero (nuli).
Demonstratie: Numarul total de pointeri utilizati n
reprezentare este n k Numarul total de pointeri nenuli
este egal cu numarul de arce n k - (n - 1) = n (k -
1) + 1
nr. pointeri nuli
nr. total pointeri n k

n k n
n k
( ) 1 1
1
1
raportul este maxim pentru k = 2.
Rezulta ca cea mai eficienta reprezentare (n structura
nlantuita) este reprezentarea n arbori binari.
_________________________________________________________________________ 90
Structuri de date si algortitmi
_________________________________________________________________________
Curs 8
Arborele binar de cautare (BST)
Arborele binar de cautare reprezinta o solutie
eficienta de implementare a structurii de date numite
"dictionar". Vom considera o multime "atomi". Pentru
fiecare element din aceasta multime avem: a
atomi, este definita o functie numita cheie de cautare:
key(a) k cu proprietatea ca doi atomi distincti au
chei diferite de cautare: a
1
a
2
key(a
1
)
key(a
2
).
Exemplu: (abreviere, definitie) ("BST","Binary
Search Tree")
("LIFO","Last In First Out") key(a) =
a.abreviere
Un dictionar este o colectie S de atomi pentru
care se definesc operatiile:
insert(S,a) insereaza atomul a n S daca nu exista
deja;
_________________________________________________________________________ 91
Structuri de date si algortitmi
_________________________________________________________________________
delete(S,k) sterge atomul cu cheia k din S daca
exista;
search(S,k) cauta atomul cu cheia k n S si-l
returneaza sau determina daca nu este.
O solutie imediata ar fi retinerea elementelor din
S ntr-o lista nlantuita, iar operatiile vor avea
complexitatea O(n).
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)
Considernd r V (radacina arborelui), Ts
subarborele stng al radacinii si Td subarborele
drept al radacinii, atunci structura acestui arbore este
definita de urmatoarele proprietati:
1) un nod x Ts atunci key(data(x)) <
key(data(r));
_________________________________________________________________________ 92
Structuri de date si algortitmi
_________________________________________________________________________
2) x Td atunci key(data(x)) > key(data(r));
3) Ts si Td sunt BST.
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 stng sunt mai mici dect radacina si toate
nodurile din subarborele drept sunt mai mari dect
radacina.
Exemple: 15 15
/ \ / \
7 25 10
25
/ \ / \ / \ /
\
2 13 17 40 2 1 7
13
/ / \
9 27 99
este BST
nu este BST.
3) Inordine: viziteaza nodurile n ordine crescatoare a
cheilor: 2 7 9 13 15 17 25 27 40 99
_________________________________________________________________________ 93
Structuri de date si algortitmi
_________________________________________________________________________
Functii:
1) Search:
search(rad,k) // rad pointer la radacina
arborelui
{ // k cheia de cautare a
arborelui cautat
if (rad = 0) then return NULL
else
if key (data (rad)) > k then
return search (lchild (rad))
else
if key (data (rad)) < k then
return search (rchild (rad))
else return rad
}
2) Insert:
Se va crea un nod n arbore care va fi plasat la un nou
nod terminal. Pozitia n care trebuie plasat acesta este
unic determinata n functie de valoarea cheii de
cautare.
Exemplu: vom insera 19 n arborele nostru:
15
/ \
7 25
/ \ / \
_________________________________________________________________________ 94
Structuri de date si algortitmi
_________________________________________________________________________
2 13 17 40
/ \ /
\
9 19 27
99
insert(rad,a) // rad referinta la pointerul
la radacina // arborelui
{
if (rad= 0) then rad= make_nod(a)
else if key (data (rad)) > key(a)
then
insert(lchild(rad),a)
else if key (data(rad)) < key(a)then
insert (rchild (rad),a)
}
Functia make_nod creaza un nou nod:
make_nod(a)
{
p= get_sp() // alocare de memorie
pentru un nod nou
data(p)= a
lchild(p)= 0
rchild(p)= 0
return(p)
_________________________________________________________________________ 95
Structuri de date si algortitmi
_________________________________________________________________________
}
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
facnd apeluri de forma p= insert(p,a).
3) Delete:
delete(rad,k) // rad referinta la pointer la
radacina
{ // k - cheia de cautare a atomului
care trebuie sters de noi
if rad = 0 then
return // nodul cu cheia k nu se afla
n arbore
else if key(data(rad)) > k then
delete(lchild(rad),k)
else if key(data(rad)) < k then
delete(rchild(rad),k)
else delete_root(rad)
}
Stergerea radacinii unui BST.:
1) rad arbore vid
_________________________________________________________________________ 96
Structuri de date si algortitmi
_________________________________________________________________________
2) a) rad sau b) rad a) SAS
sau b) SAD
/ \
SAS SAD
delete_root(rad) // rad referinta la
pointer la radacina
{
if lchild(rad)=0 then
| p= rchild(rad)
| ret_sp(rad)
|_ rad= p
else if rchild(rad)= 0 then
| p= lchild(rad)
| ret_sp(rad)
|_ rad= p
else | p=
remove_greatest(lchild(rad))
| lchild(p)= lchild(rad)
| rchild(p)= rchild(rad)
| ret_sp(rad)
|_ rad= p
}
15
/ \
_________________________________________________________________________ 97
Structuri de date si algortitmi
_________________________________________________________________________
7 25
/ \ / \
2 13 17
40
/
/
9
27
/
\
26
33
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 adncime pe ramura
dreapta pna 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 stng (SAS).
Caz3: rad functia returneaza pointer la cel mai
mare nod din SAD, iar rezultatul va fi SAS arborele
_________________________________________________________________________ 98
Structuri de date si algortitmi
_________________________________________________________________________
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.
Demonstratia eficientei (complexitate)
Complexitatea tuturor functiilor scrise depinde de
adncimea arborelui. n cazul cel mai defavorabil,
fiecare functie parcurge lantul cel mai lung din arbore.
Functia de cautare are, n acest caz, complexitatea
O(n).
_________________________________________________________________________ 99
Structuri de date si algortitmi
_________________________________________________________________________
Structura arborelui BST este determinata de ordinea
inserarii.
De exemplu, ordinea 15 13 12 11 este alta dect 15 12
11 13 .
Studiem un caz de complexitate medie:
Crearea unui BST pornind de la secventa de atomi
(a
1
a
2
... a
n
)
gen_BST (va fi n programul principal)
| rad= 0
| for i= 1 to n
| insert (rad, a
i
)
Calculam complexitatea medie a generarii BST:
Complexitatea n cazul cel mai defavorabil este:

n
i
n O i
1
2
) ( ) (
Notam cu T(k) numarul de comparatii mediu
pentru crearea unui BST pornind de la o secventa de k
elemente la intrare. Ordinea celor k elemente se
considera aleatoare.
Pentru problema T(n) avem de creat secventa (a
1
a
2
... a
n
) cu observatia ca a
1
este radacina arborelui.
Ca rezultat, n urma primei operatii de inserare pe care
o facem, rezulta:
_________________________________________________________________________ 100
Structuri de date si algortitmi
_________________________________________________________________________
a1
/ \
a
1
a
i

(a
i
<a1)
(a
i
>a1)
Nu vom considera numararea operatiilor n
ordinea n care apar ele, ci consideram numarul de
operatii globale. Dupa ce am inserat a
1
, pentru
inserarea fiecarui element n SAS sau SAD a lui a
1
, se
face o comparatie cu a
1
. Deci:
T(n)= (n - 1) + val.med.SAS + val.med.SAD
val.med.SAS = valoarea medie a numarului de
comparatii necesar pentru a construi subarborele stng
SAS
val.med.SAD = valoarea medie a numarului de
comparatii necesar pentru a construi subarborele drept
SAD

+
+ +
n
i
n
i
i n T n i T n n n T
1 1
) ( ) / 1 ( ) 1 ( ) / 1 ( ) 1 ( ) (
Notam:
T
i
(n) = numarul mediu de comparatii necesar pentru
construirea unui BST cu n noduri atunci cnd prima
_________________________________________________________________________ 101
Structuri de date si algortitmi
_________________________________________________________________________
valoare inserata (a
1
) este mai mare dect i-1 dintre
cele n valori de inserat. Putem scrie:
T n n T i T n i
i
( ) ( ) ( ) ( ) + + 1 1

+
n
i
i
n T n n T
1
) ( ) / 1 ( ) (
Deci:

+
+ +
+ +
n
i
n
i
n
i
n
i
i T n n
i n T n i T n n
i n T i T n n n T
1
1 1
1
) 1 ( ) / 2 ( ) 1 (
) ( ) / 1 ( ) 1 ( ) / 1 ( ) 1 (
)) ( ) 1 ( ) 1 (( ) / 1 ( ) (
Deci:


1
0
) ( ) / 2 ( 1 ) (
n
i
i T n n n T
Complexitatea acestei functii este: O(n ln n) (vezi
curs 5 complexitatea medie a algoritmului Quick-
Sort)
Arbori binari de cautare dinamic echilibrati
(AVL)
Definitie
Un arbore binar este echilibrat daca si numai daca,
pentru fiecare nod din arbore, diferenta dintre
adncimile SAS si SAD n modul este 1.
Exemple:
_________________________________________________________________________ 102
Structuri de date si algortitmi
_________________________________________________________________________
a
a
/ \
/ \
b c
b c
/ \ / \ /
\ \
d e f g d
e f
/ \
g
h
arbore binar
arbore binar
complet echilibrat
echilibrat
Adncimea unui arbore echilibrat cu n noduri este
O(ln n).
Se completeaza operatiile insert si delete cu niste
prelucrari care sa pastreze proprietatile de arbore binar
echilibrat pentru arborele binar de cautare. Arborele
binar echilibrat este un BST echilibrat, proprietatea de
echilibrare fiind conservata de insert si delete. Efortul,
n plus, pentru completarea operatiilor insert si delete
nu schimba complexitatea arborelui binar echilibrat.
_________________________________________________________________________ 103
Structuri de date si algortitmi
_________________________________________________________________________
Transformarea structurii arborelui dupa inserare
pentru a conserva proprietatea de arbore
binar echilibrat
Modificarile care se vor face se vor numi rotatii.
Caz 1: Fie arborele echilibrat
A
/ \
B T
3
h = depth(T
1
) = depth(T
2
) = depth(T
3
)
/ \
T
1
T
2
Consideram arborii T
1
, T
2
, T
3
echilibrati.
Insernd un nod prin rotatie simpla, rezulta structurile
rotit simplu la dreapta si rotit simplu la stnga
imaginea oglinda a rotatiei dreapta:
A
A
/ \
/ \
B T
3
T
3
B
/ \
/ \
_________________________________________________________________________ 104
Structuri de date si algortitmi
_________________________________________________________________________
T
1
T
2
T
2
T
1

Caz 2: Inserarea se face prin rotatii duble:
A
A
/ \ /
\
B T
3
T
3
B
/ \
/ \
T
1
T
2
T
2
T
1

rotatie dubla
rotatie dubla
la dreapta
la stnga
Fie primul caz:
A
/ \
B T
3
/ \
_________________________________________________________________________ 105
Structuri de date si algortitmi
_________________________________________________________________________
T
1
T
2
este BST: T
1
< B < T
2
< A < T
3

Toti arborii care respecta n continuare aceasta


conditie vor fi BST.
Ridicnd pe B n sus, si notnd cu // legaturile
neschimbate, rezulta:
B
// \
// A
T
1
/ \\
____________T
2
_T
3
_________ pe aceeasi

linie
care este un BST , deci este arbore echilibrat, si are
aceeasi adncime (!!!) cu arborele initial (de dinainte
de inserare). Nodurile superioare nu sunt afectate.
Rationament analog pentru imaginea oglinda.
Fie cazul 2: Pentru rotatia dubla se detaliaza structura
arborelui T
2
. Nu se poate sparge arborele initial ca n
cazul 1.
A
_________________________________________________________________________ 106
Structuri de date si algortitmi
_________________________________________________________________________
/ \\
B \\
// \ T
3
// C
// / \
T
1
T
2S
T
2D

depth(T
1
) = depth(T
3
) = h
depth(T
2S
) = depth(T
2D
) = h - 1
n urma inserarii, unul dintre arborii T
2S
si T
2D
si mareste adncimea. Aplicam aceiasi tehnica:
T
1
< B < T
2S
< C < T
2D
< A < T
3
ncepem cu C:
C
/ \
B
A
// \ /
\\
// T
2S
T
2D
\\
_________________________________________________________________________ 107
Structuri de date si algortitmi
_________________________________________________________________________
____T
1
_________
T
3
_____________________
la acelasi
nivel
Rezulta un BST echilibrat, de aceeaai adncime
cu arborele initial. Rotatiile sunt duble, n sensul ca
s-a facut o rotatie simpla B la stnga 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.
_________________________________________________________________________ 108
Structuri de date si algortitmi
_________________________________________________________________________
Curs 9
Asadar, n inserarea prin rotatie se obtine un
arbore echilibrat cu adncimea egala cu adncimea
arborelui de dinainte de inserare. La inserarea unui
nod terminal ntr-un arbore AVL este necesara
aplicarea a cel mult o rotatie asupra unui nod. Trebuie,
deci sa gasim nodul asupra caruia trebuie aplicata
rotatia. Reprezentam ramura parcursa de la radacina la
nodul inserat:
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
_________________________________________________________________________ 109
Structuri de date si algortitmi
_________________________________________________________________________
S-a notat pentru fiecare nod bf balance factor
(factor de dezechilibrare):
bf(nod) = depth (lchild (nod)) depth
(rchild (nod))
adica este diferenta dintre adncimea subarborelui
stng si adncimea subarborelui drept.
Calculam factorii de balansare dupa inserare.
Observatie: Pentru nodul terminal s-a schimbat
adncimea si factorul de balansare; bf = -2 dupa
inserare devine nod dezechilibrat. Trebuie aplicata,
deci, echilibrarea.
Definitie:
Se numeste nod critic primul nod cu bf 0 ntlnit 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.
La nodul critic exista doua situatii:
1. Nodul critic va fi perfect balansat (bf = 0), daca
dezechilibrul creat de nodul inserat anuleaza
_________________________________________________________________________ 110
Structuri de date si algortitmi
_________________________________________________________________________
dezechilibrul initial al nodului. n acest caz nu este
nevoie de rotatie (el completeaza un gol n arbore).
2. Factorul de balansare devine bf = 2 sau bf = -2
atunci cnd nodul inserat mareste dezechilibrul
arborelui (s-a inserat nodul n subarborele cel mai
mare). n acest caz, se aplica o rotatie n urma careia
se schimba strucutra subarborelui, astfel nct noua
radacina capata bf = 0, conservndu-se adncimea.
Concluzie: Problema conservarii proprietatii de
echilibrare a arborelui se rezolva aplicnd o rotatie
asupra nodului critic numai atunci cnd inserarea
dezechilibreaza acest nod.
Costul suplimentar care trebuie suportat se
materializeaza prin necesitatea ca n fiecare nod sa se
memoreze factorul de dezechilibrare bf. Acesti factori
de dezechilibrare vor fi actualizati n urma operatiilor
de rotatie si inserare. Operatia de stergere ntr-un AVL
implica mai multe rotatii, ea nu se studiaza n acest
curs.
Exemplu: Se da arborele cu urmatoarea structura:
55
/ \
20 80
/ \ \
10 35 90
_________________________________________________________________________ 111
Structuri de date si algortitmi
_________________________________________________________________________
/ /
5 30
Sa se insereze nodurile 15, apoi 7 si sa se
echilibreze arborele.
Inseram prima valoare 15. Comparam mai nti cu
55 : e n stnga lui, s.a.m.d. pna cnd gasim locul ei
n pozitia de mai jos. Pentru a doua valoare de inserat,
7, nodul critic este 55. El este dezechilibrat stnga.
Deci, va fi echilibrat la valoarea 2. Este necesara
aplicarea unei rotatii asupra radacinii.
55
/ \
20 80
/ \ \
10 35
90
/ \ /
5 15 30
\
7
Facem o identificare cu unul din desenele de
echilibrare prezentate n cursul anterior. Se asociaza
nodurile: 55 A
20 B etc.
_________________________________________________________________________ 112
Structuri de date si algortitmi
_________________________________________________________________________

20
----------> noduri implicate
/ \
n
10 55
------> rotatie
/ \ / \
5 15 35
80
\ /
\
7 30
90
n urma unei rotatii simple, factorii de dezechilibru
implicati n rotatie devin zero.
Fie o a treia valoare de inserat 3, apoi a patra 9:
Nodul critic pentru 3 este 5, iar pentru 9 este este 10.
Dupa rotatia aplicata (T
2D
, T
2S
vizi), rezulta
arborele:
20
/
\
7
55
_________________________________________________________________________ 113
Structuri de date si algortitmi
_________________________________________________________________________
/ \
/ \
5 10
35 80
/ / \ /
\
3 9 15
30 90
La rotatia dubla, daca nodul 9 a fost inserat n
subarborele T
2S
,
B are bf = 0 |
A are bf = -1 | exceptie facnd nodul C
nodul de inserat
La rotatia dubla, daca nodul 9 a fost inserat n
subarborele T
2D
,
B are bf = 1 |
A are bf = 0 | exceptie facnd nodul C
nodul de inserat
Reprezentarea implicita a arborilor binari
n acest mod de reprezentare se reprezinta
arborele printr-un tablou. Fie un arbore binar complet:
a
/ \
_________________________________________________________________________ 114
Structuri de date si algortitmi
_________________________________________________________________________
b
c
/ \ /
\
d e f
g
/ \ / \ / \
/ \
h i j k l
m n o
care are 4 nivele, deci 2
4
- 1 = 15 noduri.
Asociem acestui arbore un vector V:
1
a
indici
V:
2 3 4 5 6 7 9 11 10 8 12 14 13 15
b c d e f g h i k m
j l n o
structurat astfel: radacina, nivelul 1 de la stnga la
dreapta, nodurile nivelului 2 de la stnga la dreapta,
etc, despartite de linii duble.
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 [2i];
_________________________________________________________________________ 115
Structuri de date si algortitmi
_________________________________________________________________________
2. right_child(x) este reprezentat n elementul de
vector V [2i + 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 completndu-se
structura arborelui cu noduri fictive pna la obtinerea
unui arbore binar complet.
Arbori heap (heap gramada)
Definitie:
_________________________________________________________________________ 116
Structuri de date si algortitmi
_________________________________________________________________________
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 dect cheile descendentilor).
Exemplu: 99
/ \
50 30
/ \ / \
45 20 25 23
Observatie: De obicei functia cheie reprezinta selectia
unui subcmp din cmpul de date memorate n nod.
Aplicatii ale arborilor heap
Coada cu prioritate;
Algoritmul Heap_Sort
Arborii heap nu se studiaza complet n acest curs.
_________________________________________________________________________ 117
Structuri de date si algortitmi
_________________________________________________________________________
_________________________________________________________________________ 118
Structuri de date si algortitmi
_________________________________________________________________________
Curs 10
Coada cu prioritati
Este o structura de date pentru care sunt definite
urmatoarele operatii:
insert (S,a) - insereaza un atom n structura,
remove (S) - extrage din structura atomul cu
cheia cea mai mare.
O coada cu prioritati poate fi implementata printr-
o lista. Implementarea cozii cu prioritati prin lista
permite definirea operatiilor insert si remove, n cazul
cel mai bun, una este de complexitate O(1) si cealalta
este de complexitate O(n).
Implementarea cozii cu prioritati prin heap face o
echilibrare cu complexitatea urmatoare: una este de
complexitate 0(log n) si cealalta de complexitate 0(log
n).
Operatia de inserare
/ \
heap: / \
/ 50 \
/ / \ \
_________________________________________________________________________ 119
Structuri de date si algortitmi
_________________________________________________________________________
/ 40 30 \
/ / \ / \ \
/ 33 37 12 2
\
/ / \ /
_________\
/ 10 15 7 | 42
-----------------|
Acelasi arbore n reprezentare implicita:
50 40 30 33 37 12 2 10 7 15 42
Operatiile insert si remove pentru arbori heap au o
forma foarte simpla cnd utilizeaza reprezentarea
implicita. Consideram, n continuare, arbori heap n
reprezentare implicita.
Exemplu: un arbore cu ultimul nivel avnd toate
nodurile aliniate la stnga:
Inseram valoarea 42 se adauga nodul la un nivel
incomplet;
n reprezentarea implicita se adauga nodul la sfrsit.
insert:
_________________________________________________________________________ 120
Structuri de date si algortitmi
_________________________________________________________________________
1) In reprezentarea implicita: V [N + 1] = a
N = N + 1
n continuare reorganizam structura arborelui astfel
nct 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
dect V[parent] (se schimba 42 cu 37)
3) naintare n sus: child = parent
parent = [child/2]
4) Se reia pasul 2) pna cnd 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:
_________________________________________________________________________ 121
Structuri de date si algortitmi
_________________________________________________________________________
insert(V, N, a) // V vectorul ce contine
reprezentarea
// implicita a heapu-lui;
// N numarul de noduri din
heap,
// ambele sunt plasate
// prin referinta (functia insert
// le poate modifica);
// a atomul de inserat;
{
N = N+1
V[N] = a
child = N
parent = [N/2]
while | parent 1 do
| if key(V [child]) > key(V
[parent]) then
| interchange (V [child],V
[parent])
| | child = parent
| |_ parent = [child/2]
|_ else break // parasim bucla
parent = 0
}
_________________________________________________________________________ 122
Structuri de date si algortitmi
_________________________________________________________________________
Operatia remove:
50
/ \
45 43
/ \ / \
33 40 40
20
/ \ / \ \
10 15 7 37
39
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 pna
cnd structura heap-ului este realizata.
parent
conditia de heap: / \
lchild rchild
parent = max(parent, lchild, rchild).
Exista trei cazuri:
1. conditia este ndeplinita deodata;
_________________________________________________________________________ 123
Structuri de date si algortitmi
_________________________________________________________________________
2. max este n stnga retrogradarea se face n stnga;
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)
}
_________________________________________________________________________ 124
Structuri de date si algortitmi
_________________________________________________________________________
Complexitatea celor doua operatii insert si remove:
n cazul cel mai defavorabil se parcurge o ramura
care leaga radacina de un nod terminal. La insert avem
o comparatie, iar la remove avem doua comparatii.
Rezulta, complexitatea este data de adncimea
arborelui. Daca N este numarul de noduri din arbore,
2
k
N 2
k+1
, si adncimea arborelui este k+1.
2
k
- 1 < N 2
k+1
- 1 k = [log
2
N]
| |
| |
| |
nr. de noduri nr. de noduri
ale arborelui ale arborelui
complet de complet de
adncime k adncime k+1
k log
2
N < k + 1 adncimea arborelui este k =
[log
2
N].
Deci complexitatea este O(log N).
A doua aplicatie a heap-urilor este algoritmul
Heap_Sort.
Algoritmul Heap_Sort
Heap_Sort(V, n)
{
_________________________________________________________________________ 125
Structuri de date si algortitmi
_________________________________________________________________________
heap_gen(V, n) N = n
for i = n downto 2 step -1 do
| N = i
|_ V[i] = remove(V, N)
}
Aceasta procedura sorteaza un vector V cu N
elemente: transforma vectorul V ntr-un heap si
sorteaza prin extrageri succesive din acel heap.
heap i
partea sortata
a vectorului
min max
Procedura Heap_Sort prin inserari repetate
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])
}
_________________________________________________________________________ 126
Structuri de date si algortitmi
_________________________________________________________________________
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 opera_ii insert n heap-uri cu
dimensiunea N n
Rezulta complexitatea acestor operatii nu
depaseste O(n log n). Facem un studiu pentru a vedea
daca nu cumva ea este mai mica dect O(n log 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 adaugnd
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 : 2
i-1
noduri i-1 comparatii
Considernd un arbore complet (nivel complet) N =
2
k
- 1 numarul total de comparatii pentru toate
nodurile este T(n) de la nivelul 2 la nivelul k. Vom
calcula:
_________________________________________________________________________ 127
Structuri de date si algortitmi
_________________________________________________________________________


k
i
i
i n T
2
1
2 ) 1 ( ) (
Sa aratam:


1
1
1
2 ) (
k
i
i n T
cu tehnica
) ( ) ( 2 ) ( n T n T n T
. Asadar:
2 2 ) 2 ( ) 1 2 ( 1 2 ) 1 (
2 2 2 2 2 ) 1 ( 2 2 ) 1 ( 2
2 ) 1 ( ... 2 3 2 2 2 1 2 ) 1 ( 2 ) 2 ( ... 2 3 2 2 2 1
2 2 2 2 2 ) (
1
0
1 0
1
2
1 3 2 1 1 4 3 2
1
1
1
1
1
1
1
1
1
+ +
+ + +
+ + + + +


k k k
k
i
i k
k
i
i k
k k k
k
i
k
i
k
i
i i i
k
i
i
k k
k k
k k k
i i i i n T
Rezulta:
k k n k k k n T
k k
+ + + + ) 2 ( 2 2 ) 1 2 ( ) 2 ( 2 2 ) 2 ( ) (
iar:
) 1 ( log
2
+ n k
,
din
1 2
k
n
Rezulta:
) 1 ( log ) 2 ) 1 ( (log ) (
2 2
+ + + n n n n T
------------------------
termen dominant
Facndu-se majorari, rezulta complexitatea
O(n log n).
Prezentam acum alta strategie de obtinere a
heap_gen cu o complexitate mai buna:
Construim heap-ul de jos n sus (de la dreapta spre
stnga). Cele mai multe noduri sunt la baza, doar
nodurile din vrf parcurg drumul cel mai lung.
_________________________________________________________________________ 128
Structuri de date si algortitmi
_________________________________________________________________________
noduri terminale
I
II
i
Elementele V[i+1,N] ndeplinesc conditia de structura
a heap-ului:
j >i avem: V[j] > V[2*j] , daca 2*j N
V[j] > V[2*j +1] , daca 2*j + 1 N
Algoritmul consta n adaugarea elementului V[i] la
structura heap-ului. El va fi retrogradat la baza heap-
ului (prelucrare prin retrogradare):
retrogradare(V, N, i)
{
parent = i
child = 2*i // fiu stnga al lui i
while child N do
| if child+1 N and
| key(V[child+1]) > key(V[child])
then
| child = child+1
_________________________________________________________________________ 129
Structuri de date si algortitmi
_________________________________________________________________________
| if key(V[parent]) < key(V[child])
then
| | interchange(V[parent],
V[child])
| | parent = child
| |_ child = 2*parent
|_ else break
}
n aceasta situatie, vom avea:
heap_gen
{
for i = [N/2] downto 1 step -1 do
retrogradare(V, n, i)
}
Complexitatea acestei operatii
Fie un arbore complet cu n = 2
k
- 1. Cazul cel
mai defavorabil este situatia n care la fiecare
retrogradare se parcurg toate nivelele:
nivel k : nu se fac operatii
nivel k-1 : 2
k-2
noduri o operatie de
comparatie
nivel k-2 : 2
k-3
noduri 2 operatii
_________________________________________________________________________ 130
Structuri de date si algortitmi
_________________________________________________________________________
-----------------------------------------------------------------
-
nivel i : 2
i-1
noduri k-i operatii
-----------------------------------------------------------------
--
nivel 2 : 2
1
noduri k-2 operatii
nivel 1 : 2
0
noduri k-1 operatii
Se aduna, si rezulta:


1
1
1
2 ) ( ) (
k
i
i
i k n T
Tehnica de calcul este aceeasi:
) ( ) ( 2 ) ( n T n T n T
1 2 3 1 ) 1 2 ( 2 ) 1 ( 2 2 2
2 2 ) 1 ( 2 2 ) 1 ( 2 2 2 2 ... 2 ) 3 ( 2 ) 2 ( 2 ) 1 (
2 2 2 3 ... 2 ) 3 ( 2 ) 2 ( 2 ) 1 ( 2 ) ( 2 ) 1 ( ) (
2 2 2 1
3
1
3
0
1 0 1 1 2 3 2 1 0
2
1
2
1
2 3 3 2 1 1
+ +
+ +
+ + + + +




k k k
k k k k k
k k k i k k n T
k k k k
k
i
k
i
k k k
k
i
k
i
k k i i
Rezulta:
1 ) 1 2 ( 3 1 2 3 ) (
2


k k n T
k k
1 ) 1 ( l o g 3 ) (
2
+ n n n T
-------
termen dominant
Rezulta complexitatea este O(n). Comparnd cu
varianta anterioara, n aceasta varianta (cu heap-ul la
baza) am cstigat un ordin de complexitate. Rezulta,
complexitatea algoritmului Heap_Sort este
_________________________________________________________________________ 131
Structuri de date si algortitmi
_________________________________________________________________________
determinata de functiile remove ce nu pot fi aduse la
mai putin de complexitate O(log n). Rezulta:
Heap_Sort = O(n) + O(nlog n)
---------------
termen ce determina complexitatea
Rezulta complexitatea alg. Heap_Sort = O(nlog
n)
_________________________________________________________________________ 132
Structuri de date si algortitmi
_________________________________________________________________________
Curs 11
Grafuri. Traversari pe grafuri
Definitie:
Fie G = (V, E) o multime, n care V este o multime
de noduri finita, V = n , si E o multime de arce,
E = {(i, j), i, j N }. G se numeste graf, E V V.
Avem: grafuri orientate n care (i, j) (j, i)
grafuri neorientate n care (i, j) E (j, i) E
Observatie: Un arbore este un caz particular de graf.
Definitii:
i se numeste predecesor al lui j;
j se numeste succesor al lui i;
(i, j) este adiacent cu i si j;
i, j adiacente;
E
i
= { k, (k, i) E} multimea de vecini la intrare;
E'
i
= { j, (i, j) E} multimea de vecini la iesire;
E
i
E'
i
multimea vecinilor.
grad_intrare(i) = in_degree(i) = E
i

grad_ie_ire(i) = out_degree(i) = E'


i

_________________________________________________________________________ 133
Structuri de date si algortitmi
_________________________________________________________________________
Pentru un graf neorientat (G - neorientat), E
i
E'
i
si
degree(i)= E
i
.
Definitie
Se numeste drum orientat ntr-un graf de la x la y
secventa de noduri D = (i
1
= x, i
2
, ... , i
n
= y),
(i
k
, i
k+1
) E, k n 1, .
Drumul D este neorientat, daca (i
k
, i
k+1
) E sau
(i
k+1
, i
k
) E
Definitie
Se numeste graf conex graful pentru care doua
noduri (x, y) V, D un drum de la x la y.
Un graf este complet daca fiecare nod este conectat cu
oricare din celelalte noduri: E = V V \ {(i, i), i V}
Definitie
Fie G = (V, E) un graf. Se numeste subgraf al
grafului G, un graf G' = (V', E'), astfel nct V' V,
E' (V' V') E
Reprezentari
1) Matrice de adiacenta
Fie G = (V, E) ,V = n. Se determina matricea de
adiacenta
_________________________________________________________________________ 134
Structuri de date si algortitmi
_________________________________________________________________________
astfel:
M M i j
i j E
i j E
n n

, ( , )
( , )
( , )
1
0


1
1
1
1
1
1
]
1

0 0 0 0 1
1 0 0 1 0
0 0 0 0 0
1 0 0 0 0
0 0 1 1 0
Un graf este etichetat daca o functie definita pe
E R, adica fiecarui arc i se asociaza o valoare.
Astfel de grafuri se reprezinta prin matrici de
colectivitate:
C i j
i j i j E
i j E
( , )
( , ), ( , )
, ( , )


+
0

Complexitate:
O(1), (i, j) E
O(n), E
i
, E'
i
, indegree(i), outdegree(i)
O(n
2
), spatiu
1) Liste de adiacenta directa
Definitie
Fie G = (V, E) un graf, V = n , V = {1, 2, ... , n}. Se
numeste lista de adiacenta asociata acestui graf o
colectie de n pointeri < (1, 2, ... , n), fiecare pointer
_________________________________________________________________________ 135
Structuri de date si algortitmi
_________________________________________________________________________
continnd adresa unei liste dupa regula urmatoare: L(i)
da adresa listei nlantuite care contine toti succesorii
lui i.
L(i) E
i
Exemplu:
0
0
0
0
3
5
1
2
2
3
0
1
2
3
4
5
3) Liste de adiacente inverse
(i,j) E
O(E) cardinal de E
L'(1, 2, ..., n), L'(i) E'
i
E
i
: O(E) sau O(maxout_degree)
E'
i
: O(E)
spatiu: O(E + n)
4) Liste de adiacenta dubla
_________________________________________________________________________ 136
Structuri de date si algortitmi
_________________________________________________________________________
(i,j) , E, (i,j, e(i,j))
Operatii pe grafuri, traversari
La grafuri se folosesc operatii ntre elemente ((i,j)
adiacente, etc), operatii de traversare pe grafuri. Se
numeste traversare pe graf o procedura de traversare,
pentru un scop bine definit, prin toate nodurile.
Exista: traversari n adncime depth first
traversari n latime bmedth first
Proceduri:
dfs(G, i) // mark(1... n) initializat cu
// (00 ... 0) ext
{ // procesare()
procesare(i)
mark(i) = 1
for fiecare k V cu (i,k) E
| if mark(k) = 0
|_dfs(k)
}
bfs(G, i)
{ // mark(1... n) initializat cu (0 ... 0)
ext
// o coada q isempty, a= pop(p),
put(q) ext
_________________________________________________________________________ 137
Structuri de date si algortitmi
_________________________________________________________________________
procesare(i)
mark(i) = 1
put(q, i)
while(isEmpty(q) = N
0
)) do
| k = pop(q)
| for fiecare j V , (k,j) E
| | if(mark(j)=0)
| | | procesare(j)
| | | mark(j) = 1
|_ |_ |_ put(q,j)
}
1) Matrice adiacenta
dfs(M, i)
{
procesare(i)
mark(i) = 1
for k = 1 to n
| if (M(i,k) = 1)
| | if mark(k) = 0
|_ |_ dfs(k)
}
2) Liste de adiacenta directa
_________________________________________________________________________ 138
Structuri de date si algortitmi
_________________________________________________________________________
dfs(L, i)
{
procesare(i)
mark(i) = 1
p = P(i)
while p 0
| if mark(data(p)) 0
| dfs(data(p))
|_ p = link(p)
}
Arbori de acoperire pe grafuri
Fie G(V, E) un graf.
Se numeste arbore de acoperire un arbore T = (V,
E') , cu E' E
Observatie: Algoritmii dfs, bfs produc arbori de
acoperire.
_________________________________________________________________________ 139