Você está na página 1de 38

UNIVERSITATEA OVIDIUS DIN CONSTANTA

FACULTEA DE MATEMATICA SI INFORMATICA

Algoritmi si structuri de
date II
Note de laborator

Ioana Pomparau

Versiunea: 28.04.2015
Cuprins

Prefata i

1 Programarea orientata pe obiecte n limbajul C++ 1


1.1 Programare orientata pe obiecte . . . . . . . . . . . . . . . . . 3
1.2 Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2 Liste simplu si dublu nlantuite. Liste circulare 5


2.1 Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

3 Stive si cozi. Standard Template Library (STL) 9


3.1 Stive si cozi . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.2 Standard Template Library (STL) . . . . . . . . . . . . . . . . 9
3.3 Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

4 Tablouri de dispersie 13
4.1 Tabele de dispersie . . . . . . . . . . . . . . . . . . . . . . . . 13
4.2 Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

5 Arbori binari. Arbori binari de cautare 17


5.1 Parcurgeri ale arborilor binari . . . . . . . . . . . . . . . . . . 18
5.2 Arbori binari de cautare . . . . . . . . . . . . . . . . . . . . . 18
5.2.1 Complexitatea operatiei de cautare . . . . . . . . . . . 19
5.2.2 Operatii specifice . . . . . . . . . . . . . . . . . . . . . 20
5.3 Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

6 Arbori rosu - negru 23


6.1 Proprietati ale arborilor rosu - negru . . . . . . . . . . . . . . 23
6.2 Operatia de inserare . . . . . . . . . . . . . . . . . . . . . . . 24
6.3 Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
7 B - arbori. Coduri Huffman 27
7.1 B-arbori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
7.1.1 Operatia de cautare . . . . . . . . . . . . . . . . . . . . 28
7.1.2 Operatia de inserare . . . . . . . . . . . . . . . . . . . 28
7.2 Coduri Huffman . . . . . . . . . . . . . . . . . . . . . . . . . . 31
7.3 Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

8 Recapitulare 34

2
Prefata

Aceste note de laborator se adreseaza studentilor de anul I, specializarea In-


formatica, ai Facultatii de Matematica si Informatica a Universitatii Ovidius
din Constanta.
La sfarsitul fiecarui laborator se recomanda un set de probleme. Prob-
lemele din tema sunt: exercitii simple (nemarcate), exercitii de dificultate
medie (marcate cu un asterisc), exercitii de dificultate ridicata sau care con-
situtie algoritmi elementari foarte importanti (marcate cu doua asteriscuri)
si probleme foarte dificile (trei asteriscuri).

i
Laboratorul 1

Programarea orientata pe
obiecte n limbajul C++

Mai jos sunt prezentate cateva aspecte n legatura cu diferentele ntre limba-
jele C si C++.

Exemplul 1.1. Comenzi de intrare si iesire. In limbajul C++ se pot folosi,


n plus fata de functiile scanf() si printf(), obiectele cin, respectiv cout. Aces-
tea doua din urma nu necesita specificarea formatului datelor. Pentru a uti-
liza cin si cout se va include fisierul header iostream.

Cod C Cod C++


#i n c l u d e < s t d i o . h> #i n c l u d e <i o s t r e a m >
#i n c l u d e < s t d l i b . h> u s i n g namespace s t d ;

i n t main ( ) { i n t main ( ) {
char nume [ 1 5 ] ; char nume [ 1 5 ] ;
char prenume [ 1 5 ] ; char prenume [ 1 5 ] ;
int varsta ; int varsta ;

p r i n t f ( " nume = " ) ; c o u t <<" n u m e = " ;


s c a n f ( " % s " , nume ) ; c i n >>nume ;
p r i n t f ( " prenume = " ) ; c o u t <<" p r e n u m e = " ;
s c a n f ( " % s " , prenume ) ; c i n >>prenume ;
p r i n t f ( " varsta = " ) ; c o u t <<" v a r s t a = " ;
s c a n f ( " % d " , &v a r s t a ) ; c i n >>v a r s t a ;

p r i n t f ( " % s % s are % d ani \ n " , c o u t <<nume<<" "<<prenume<<


nume , prenume , v a r s t a ) ; " a r e "<<v a r s t a <<" a n i "<<e n d l ;

system ( " pause " ) ; system ( " pause " ) ;


return 0 ; return 0 ;
} }

1
Exemplul 1.2. Transmiterea parametrilor prin referinta.

Cod C Cod C++


#i n c l u d e < s t d i o . h> #i n c l u d e <i o s t r e a m >
#i n c l u d e < s t d l i b . h> u s i n g namespace s t d ;

v o i d swap ( i n t a , i n t b ) { v o i d swap ( i n t &a , i n t &b ) {


i n t aux = a ; i n t aux = a ;
a = b ; a = b;
b = aux ; b = aux ;
} }

i n t main ( ) { i n t main ( ) {
int a , b ; int a , b ;

p r i n t f ( " a = " ) ; s c a n f ( " % d " , &a ) ; c o u t <<" a = " ; c i n >>a ;


p r i n t f ( " b = " ) ; s c a n f ( " % d " , &b ) ; c o u t <<" b = " ; c i n >>b ;
p r i n t f ( "\n %d %d \n" , a , b ) ; c o u t <<e n d l <<a<<" "<<b<<e n d l ;
swap(&a , &b ) ; swap ( a , b ) ;
p r i n t f ( "\n %d %d \n" , a , b ) ; c o u t <<e n d l <<a<<" "<<b<<e n d l ;

system ( " pause " ) ; system ( " pause " ) ;


return 0 ; return 0 ;
} }

Exemplul 1.3. Alocarea dinamica a memoriei. Se pot utiliza operatorii new


si delete, conform exemplului de mai jos.

Cod C Cod C++


#i n c l u d e < s t d i o . h> #i n c l u d e <i o s t r e a m >
#i n c l u d e < s t d l i b . h> u s i n g namespace s t d ;

i n t main ( ) { i n t main ( ) {
i n t p=( i n t ) m a l l o c ( s i z e o f ( i n t ) ) ; i n t p = new i n t ;
p = 10 ; p = 10 ;

i n t v =( i n t ) m a l l o c ( 3 s i z e o f ( i n t ) ) ; i n t v = new i n t [ 3 ] ;
v[2] = 5; v[2] = 5;

p r i n t f ( " % d % d \ n " , p , v [ 2 ] ) ; c o u t <<p<<" "<<v [ 2]<< e n d l ;

free (p ); delete p;
free (v ); delete [] v ;

system ( " pause " ) ; system ( " pause " ) ;


return 0 ; return 0 ;
} }

2
1.1 Programare orientata pe obiecte
Paradigma de programare orientata pe obiecte are la baza conceptul de
obiect. Obiectele descriu entitati, actiuni, obiecte, fenomene din viata reala.
Cu alte cuvinte, obiectele reprezinta structuri de date menite sa contina
informatii despre notiunea pe care o modeleaza.
Schema dupa care sunt create obiectele se numeste clasa. Clasele contin
caracteristicile (atributele) si actiunile (metodele) unui obiect. Acele atribute
si metode care pot fi accesate n afara clasei se vor declara cu specifica-
torul public. Crearea unui obiect dupa modelul clasei poarta denumirea de
instantiere.
In cadrul unei clase se pot defini doua tipuri speciale de metode: con-
structori si destructori. Astfel de metode nu au tip definit (nu sunt nici de
tip void) si sunt apelate implicit la crearea unui obiect (instantiere), respec-
tiv la distrugerea acestuia. Constructorul poarta numele clasei. Destructorul
are denumirea formata din simbolulsi denumirea clasei.
Mai jos este un exemplu n care se defineste o clasa menita sa modeleze
conceptul de figura geometrica de tip cerc. Caracteristicile unui obiect de tip
cerc sunt date centrul si raza sa. Acestea au fost declarate private pentru a
nu se putea accesa si modifica n afara clasei.
#i n c l u d e <i o s t r e a m >
u s i n g namespace s t d ;
struct P o i n t {
float x ;
float y ;
};

class Circle {
private :
struct P o i n t c e n t e r ;
float radius ;

public :
C i r c l e ( struct P o i n t c e n t e r C o o r d , f l o a t r a d i u s V a l u e ) {
center = centerCoord ;
radius = radiusValue ;
}

float getRadius (){


return r a d i u s ;
}

float getArea ( ) {
return r a d i u s r a d i u s 3 . 1 4 ;
}
};

3
i n t main ( ) {
struct P o i n t C ;
float r ;

cout<<" Coordinates : " ; c i n >>C . x ; c i n >>C . y ;


cout<<" Radius = " ; c i n >>r ;

C i r c l e m y C i r c l e (C, r ) ;
cout<<" The circle has radius "<<m y C i r c l e . g e t R a d i u s ( )
<<" and area "<<m y C i r c l e . g e t A r e a ( ) ;

system ( " pause " ) ;


return 0 ;
}

1.2 Tema
Exercitiul 1.1 . Descarcati fisierul Lab1 Circle.cpp si completati codul con-
form instructiunilor.

Exercitiul 1.2. Descarcati fisierul Lab1 VendingMachine.cpp si completati


codul conform instructiunilor.

4
Laboratorul 2

Liste simplu si dublu


nlantuite. Liste circulare

Listele simplu nlantuite reprezinta structuri de date liniare, cu propri-


etatea ca fiecare element retine adresa urmatorului. Prin urmare, un element,
numit si nod poate avea urmatoarea forma.

Iar o posibila implementare ar fi data de structura definita mai jos.


#i n c l u d e <i o s t r e a m >
struct Node{
i n t key ;
float info ;
Node n e x t ;
};

Imaginea urmatoare prezinta o lista simplu nlantuita.

In programul de mai jos se defineste o clasa corespunzatoare unei liste


simplu nlantuite si se implementeaza cateva operatii elementare.

5
#i n c l u d e <i o s t r e a m >
u s i n g namespace s t d ;

struct Node{
i n t key ;
float info ;
Node n e x t ;
};

class SingleLinkedList {
private :
struct Node head ;

public :
S i n g l e L i n k e d L i s t ( i n t keyValue , f l o a t i n f o V a l u e = 0 ) {
head = new Node ;
head>key = keyValue ;
head>i n f o = i n f o V a l u e ;
head>n e x t = NULL;
}

SingleLinkedList (){
while ( head != NULL) {
Node temp = head>n e x t ;
d e l e t e head ;
head = temp ;
}
}

void a d d F i r s t ( i n t keyValue , f l o a t i n f o V a l u e = 0 ) {
Node temp = new Node ;
temp>key = keyValue ;
temp>i n f o = i n f o V a l u e ;
temp>n e x t = head ;
head = temp ;
}

void addLast ( i n t keyValue , f l o a t i n f o V a l u e = 0 ) {


Node temp = head ;
while ( temp>n e x t != NULL) temp = temp>n e x t ;

Node newNode = new Node ;


newNode>key = keyValue ;
newNode>i n f o = i n f o V a l u e ;
newNode>n e x t = NULL;

temp>n e x t = newNode ;
}

void p r i n t ( ) {
cout<<" Current list : " ;
Node temp = head ;
while ( temp != NULL) {
cout<<temp>key<<" : "<<temp>i n f o <<" ; ";
temp = temp>n e x t ;
}
cout<<e n d l <<e n d l ;

6
}
};

i n t main ( ) {
/* Static memory allocation - the memory is freed at the end
of the block , in this case , of main ()
*/
S i n g l e L i n k e d L i s t myList ( 1 , 6 4 . 6 ) ;
myList . p r i n t ( ) ;
myList . a d d F i r s t ( 2 , 1 2 8 . 7 ) ;
myList . addLast ( 3 , 2 5 6 . 8 ) ;
myList . p r i n t ( ) ;

// Dynamic memory allocation - the memory is freed with delete


S i n g l e L i n k e d L i s t pToMyList = new S i n g l e L i n k e d L i s t ( 1 , 8 1 ) ;
pToMyList>p r i n t ( ) ;
pToMyList>a d d F i r s t ( 2 ) ;
pToMyList>addLast ( 3 , 2 4 3 ) ;
pToMyList>p r i n t ( ) ;
d e l e t e pToMyList ;

system ( " pause " ) ;


return 0 ;
}

Intr-o lista dublu nlantuita, fiecare element retine atat adresa urmatorului
nod, cat si a precedentului.

Listele circulare au proprietatea ca ultimul nod al listei pointeaza catre


primul. Acestea pot fi simplu sau dublu nlantuite.

7
2.1 Tema
Exercitiul 2.1. Descarcati fisierul Lab2 SingleLinkedList.cpp si completati
codul conform instructiunilor.

Exercitiul 2.2. Modificati programul anterior astfel ncat sa se imple-


menteze o lista dublu nlantuita.

Exercitiul 2.3. Problema lui Josephus. Folosind o lista circulara (sim-


plu sau dublu nlantuita), rezolvati urmatoarea problema: n persoane sunt
asezate n cerc (si numerotate de la 0 la n 1); pornind de la persoana cu
numarul 0, la fiecare pas, a k-a persoana este executata; ultima persoana care
ramane este gratiata; afisati ordinea n care au loc executiile.

Exercitiul 2.4
. Descarcati fisierul Lab2 CardsGame.cpp si completati codul
conform instructiunilor.

8
Laboratorul 3

Stive si cozi. Standard


Template Library (STL)

3.1 Stive si cozi


Structurile de date de tip stive si cozi sunt liste cu restrictii la adaugarea si
la stergerea de elemente.
Stivele, supranumite LIFO (Last In, First Out), sunt caracterizate prin
doua operatii principale: pop - se extrage elementul din varful stivei si push -
se adauga un element n varful stivei. Ambele operatii au complexitate O(1).
In cazul structurilor de date de tip Coada, numite si FIFO (First In,
First Out), operatiile principale sunt: dequeue - se extrage primul element
al cozii si enqueue - se adauga un element la sfarsitul cozii. Cele doua
operatii au, de asemenea complexitate O(1).
Aceste structuri de date sunt folosite atat n aplicatii pentru a stoca
date propriu-zise, cat si n cadrul unor algoritmi, pentru a gestiona temporar
diverse informatii.

3.2 Standard Template Library (STL)


Biblioteca STL este compusa din clase de tip container, algoritmi si iteratori.
Scopul acestora este sa implementeze structuri de date frecvent utilizate,
precum si operatii si algoritmi specifici acestor structuri de date.

9
Prin container ntelegem clase care gestioneaza structuri de date precum
vectori, liste, stive, cozi etc. Acestea ar trebui sa poata stoca orice tip de
data. Din acest motiv utilizeaza sabloane (templates). Mai jos este prezentat
un exemplu de utilizare al clasei stack.
#i n c l u d e <i o s t r e a m >
#i n c l u d e <s t a c k >

u s i n g namespace s t d ;

i n t main ( )
{
s t a c k <int> myStack ;
int i ;

f o r ( i = 1 ; i <= 1 0 ; i ++) myStack . push ( i ) ;

cout<< " Popping out elements ... " ;


cout<<e n d l ;
while ( ! myStack . empty ( ) )
{
cout<<myStack . top ()<<" " ;
myStack . pop ( ) ;
}
cout<<e n d l ;

system ( " pause " ) ;


return 0 ;
}

Pentru a accesa obiectele din cadrul containerelor se utilizeaza iteratori.


Acestia generalizeaza notiunea de pointer, indicand un obiect din cadrul unui
container. In exemplul urmator este creat un vector cu 10 elemente. Cu
ajutorul unui iterator sunt afisate valorile acestora.
#i n c l u d e <i o s t r e a m >
#i n c l u d e <v e c t o r >

u s i n g namespace s t d ;

i n t main ( )
{
v e c t o r <int> myVector ;
int i ;
f o r ( i = 0 ; i < 1 0 ; i ++) myVector . p u s h b a c k ( i i ) ;

v e c t o r <int > : : i t e r a t o r i t ;
f o r ( i t = myVector . b e g i n ( ) ; i t != myVector . end ( ) ; i t ++)
cout <<i t <<" " ;

cout<<e n d l ;

system ( " pause " ) ;


return 0 ;
}

10
Sunt implementati algoritmi clasici sub forma unor functii care prelu-
creaza datele memorate n containere. Modificand codul de mai sus, ele-
mentele vectorului sunt inversate nainte de a fi afisate.
#i n c l u d e <i o s t r e a m >
#i n c l u d e <v e c t o r >
#i n c l u d e <a l g o r i t h m >

u s i n g namespace s t d ;

i n t main ( )
{
v e c t o r <int> myVector ;
int i ;
f o r ( i = 0 ; i < 1 0 ; i ++) myVector . p u s h b a c k ( i i ) ;

r e v e r s e ( myVector . b e g i n ( ) , myVector . end ( ) ) ;

v e c t o r <int > : : i t e r a t o r i t ;
f o r ( i t = myVector . b e g i n ( ) ; i t != myVector . end ( ) ; i t ++)
cout <<i t <<" " ;

cout<<e n d l ;

system ( " pause " ) ;


return 0 ;
}

Mai multe informatii puteti gasi la urmatoarele adrese:


http://www.cplusplus.com/reference/stl/
http://www.cplusplus.com/reference/algorithm/
http://www.cplusplus.com/reference/iterator/
https://www.sgi.com/tech/stl/
http://www.cprogramming.com/tutorial/stl/stlintro.html

3.3 Tema
Exercitiul 3.1 . Descarcati fisierele Lab5 Stack.cpp si Lab5 Queue.cpp si
completati codul conform instructiunilor.

Exercitiul 3.2 . Se citeste de la tastatura o expresie aritmetica cu paranteze


rotunde. Sa se verifice, folosind o stiva, daca parantezele sunt nchise corect.

Exercitiul 3.3. Fie un labirint reprezentat cu ajutorul unei matrice de


ordin n. Spatiile libere, pe unde se poate trece, sunt marcate cu 0, iar cele
blocate, cu 1. Se doreste gasirea celei mai scurte cai de la punctul de plecare,
marcat cu 1, la punctul de sosire, marcat cu 2.

11
Exemplu de labirint pentru n = 4

1 0 0 1
0 1 1 2
0 0 1 0
0 0 0 0

Indicatie. Se adauga ntr-o coada pozitia de plecare; cat timp nu s-a


ajuns la punctul de sosire se extrage un element din coada si se adauga toate
celulele adiacente cu acesta prin care se poate trece; noile celule adaugate sunt
marcate cu valoarea incrementata a celulei extrase. Matricea din exemplu
devine:
1 2 3 1
2 1 1 2
3 4 1 8
4 5 6 7
si cel mai scurt drum este de dimensiune 8.

Exercitiul 3.4 . Implementati solutii ale celor doua probleme anterioare


(paranteze si labirint) astfel ncat sa utilizati STL.

12
Laboratorul 4

Tablouri de dispersie

4.1 Tabele de dispersie


Ne punem problema analizarii unor structuri de date din punct de vedere al
operatiilor de inserare si de cautare. Elementele structurii de date sunt
caracterizate prin chei (naturale sau artificiale) si valori.
Pentru nceput consideram o structura de date de tip lista nlantuita.
Aceasta are avantajul de a folosi eficient memoria. De asemenea, operatia
de inserare a unui element nou n lista poate fi de complexitate O(1). Insa
accesul la al k-lea element al unei liste nlantuite se face n timp O(k), iar
cautarea unui element este de complexitate O(n), unde n reprezinta dimen-
siunea listei.
O alta varianta este de a folosi un vector. In cazul acestei structuri de
date accesul la un element pentru care se cunoaste pozitia, numita si index
este constant, O(1), la fel ca si insertia. Dar fara a avea cunostinte apriori
despre datele memorate, cautarea are tot complexitate liniara.
Structura de date Insertie Cautare Observatii
Lista nlantuita O(1) O(n) Foloseste eficient memoria
Vector O(1) O(n) Accesul la un element pentru
care se cunoaste pozitia are com-
plexitate O(1)
Vector ordonat O(log n) O(log n) Accesul la un element pentru
care se cunoaste pozitia are com-
plexitate O(1)

13
Apare atunci ntrebarea: putem construi un vector n care cheia
unui element sa ne dea pozitia acestuia? Daca acest lucru ar fi real-
izat, atunci cautarea unui element ar presupune calcularea indexului sau si
verificarea existentei n vector pe pozitia obtinuta. In acest caz si cautarea
ar avea complexitate constanta.
O idee initiala ar fi sa se construiasca un vector ai carui indecsi sa fie chiar
posibilele chei ale elementelor (adresare directa). Aceasta abordare nu este
nsa practica ntotdeauna, deoarece universul cheilor ar putea fi mult prea
mare relativ la memoria care poate fi folosita.
Pentru a evita astfel de situatii se folosesc tabele de dispersie. Acestea
reprezinta structuri de date care implementeaza tablouri asociative: cheii
unui element i se asociaza un cod din plaja de valori ale indecsilor unui
vector; insertia si cautarea se vor face la pozitia data de acest cod.
Maparea ntre cheile valorilor de memorat si universul de chei ale struc-
turii de date folosite se realizeaza cu ajutorul unor functii numite functii de
dispersie (vezi http://en.wikipedia.org/wiki/Hash_function). Aceste
functii transforma valori de dimensiuni variabile n coduri de dimensiune
fixata ce pot fi apoi folosite cu rol de indecsi, realizand practic o criptare
ntr-un singur sens.
Cum n general universul cheilor este mult mai mare decat multimea de
indecsi ai tabloului de dispersie, o functie de dispersie va asocia mai multor
chei acelasi cod. Acest fenomen poarta numele de coliziune. O posibila
rezolvare a acestei probleme se face prin nlantuire: la fiecare pozitie a
tabelului se memoreaza o legatura catre o lista nlantuita de elemente. Atunci
un element va fi inserat sau cautat n lista corespunzatoare codului hash al
acestuia.
Eficienta acestei abordari depinde de cat de buna este o functie de
dispersie. Aceasta trebuie sa aiba urmatoarele proprietati: sa fie usor de
calculat, sa evite coliziunile si sa distribuie uniform cheile n tabel.
Daca aceste conditii sunt respectate, atunci operatia de insertie are com-
plexitate O(1), iar cea de cautare are complexitate medie O(n/m), unde n
reprezinta numarul de elemente memorate n tabel, iar m reprezinta numarul
de pozitii ale acestuia.

14
O aplicatie a functiilor de dispersie apare n autentificarea utilizatorilor.
Pentru a asigura o buna securitate a parolelor, acestea nu sunt memorate
n clar. Unei parole i se aplica o serie de functii de dispersie si ceea ce se
memoreaza n baza de date este valoarea obtinuta. Atunci cand un utilizator
introduce o parola pentru a se autentifica, acesteia i se aplica aceeasi serie
de functii si rezultatul se compara cu valoarea memorata pentru respectivul
utilizator. Daca sunt egale se considera ca autentificarea a fost realizata
cu succes. Important n aceasta situatie este ca fiind dat un cod hash si
cunoscandu-se functia cu care a fost obtinut sa fie dificil de calculat valoarea
initiala.
Tabele de dispersie sunt folosite n implementarea tablourilor asociative,
a unei memorii de tip cache etc.

4.2 Tema
Exercitiul 4.1. 1. h(x) = x mod 32;
2. h(x) = x mod 31;
M 2 
3. h(x) = W (x mod W ) , unde W = 26 si M = 25 ;
M 
4. h(x) = W (ax mod W ) , unde W = 26 , M = 25 si a = 101;
5. h(x) = [m (Ax mod 1)], unde prin (Ax mod
1) se noteaza partea
( 51)
fractionara a lui Ax, iar m = 1000 si A = 2
.
calculati numarul de valori posibile generate (reprezentand indecsii unor
tabele de dispersie).
Exercitiul 4.2 . Generati aleator 1000 de numere ntregi cu valori cuprinse
ntre 0 si 9999. Aplicati functiile de dispersie de la exercitiul anterior acestor
valori si, fara a construi efectiv un tabel de dispersie, pentru fiecare functie
1. numarati coliziunile de pe fiecare cheie;
2. calculati urmatoarea suma
m1 n 2

X ci m
n ,
i=0 m

unde n reprezinta numarul de elemente (n = 1000), m numarul de


indecsi ai tabelului, iar ci reprezinta numarul de coliziuni ale cheii i.

15
Exercitiul 4.3. Scrieti o clasa care implementeaza un tabel de dispersie.
Se va utiliza functia de dispersie de la 1. Coliziunile vor fi rezolvate prin
nlantuire. Realizati si un exemplu de utilizare al clasei.

Exercitiul 4.4 . Implementati operatiile de creare a unui utilizator nou,


autentificare si schimbare a parolei folosind tabele de dispersie. Parolele nu
vor fi memorate n clar si vor fi folosite pentru a genera chei ale tabelului.
Functia de dispersie va calcula suma codurilor ASCII ale caracterelor din
parola modulo 31.
Exemplu: datele unui utilizator avand parola asd vor fi memorate n
tabelul de dispersie la pozitia (97 + 115 + 100) mod 31 = 2.

16
Laboratorul 5

Arbori binari. Arbori binari de


cautare

Structurile de date de tip arbore sunt n general folosite pentru implementarea


unor date ierarhice. Un arbore reprezinta o multime de noduri ntre care
exista relatia de parinte - fiu. Nodul care nu are parinte se numeste radacina.
Nodurile care nu au nici un fiu se numesc frunze.
Un tip particular de arbori l reprezinta arborii binari. Acestia se disting
prin faptul ca orice nod are cel mult doi fii. De cele mai multe ori se stabileste
o ordine ntre fiii unui nod: fiu stang, respectiv drept.

17
5.1 Parcurgeri ale arborilor binari
Parcurgerile specifice arborilor binari sunt n latime (breadth-first) si n
adancime (recursive, depth-first): preordine (RSD), inordine (SRD) si
postordine (SDR).

Latime Se viziteaza nodurile n ordinea naltimii, ntai radacina, apoi nodurile


de la nivelul 1, de la stanga la dreapta, apoi de la nivelul 2 de la stanga
la dreapta si asa mai departe.
Pentru exemplul de mai sus parcurgerea n latime viziteaza nodurile n
ordinea: A, B, D, C, E, H, F, G, I.
Preordine Se viziteaza radacina;
Se viziteaza n preordine subarborele stang;
Se viziteaza n preordine subarborele drept.
Pentru exemplul de mai sus parcurgerea n preordine viziteaza nodurile
n ordinea: A, B, C, D, E, F, G, H, I.
Inordine Se viziteaza n inordine subarborele stang;
Se viziteaza radacina;
Se viziteaza n inordine subarborele drept.
Pentru exemplul de mai sus parcurgerea n inordine viziteaza nodurile
n ordinea: B, C, A, F, E, G, D, I, H.
Postordine Se viziteaza n postordine subarborele stang;
Se viziteaza n postordine subarborele drept;
Se viziteaza radacina.
Pentru exemplul de mai sus parcurgerea n postordine viziteaza nodurile
n ordinea: C, B, F, G, E, I, H, D, A.

5.2 Arbori binari de cautare


Arborii binari de cautare sunt un tip particular de arbori binari definiti prin
faptul ca fiecarui nod x i se atribuie o cheie care ndeplineste urmatoarele
proprietati:
1. cheie(x) cheie(y), oricare ar fi y un nod din subarborele stang al
nodului x;

18
2. cheie(x) < cheie(z), oricare ar fi z un nod din subarborele drept al
nodului x.

Asa cum sugereaza si denumirea, acesti arbori reprezinta o solutie buna


de structurare a datelor atunci cand apare frecvent necesitatea efectuarii
operatiei de cautare.
In exemplul de mai jos cheile arborelui sunt litere ale alfabetului, prin
urmare ordinea cheilor este data de ordinea alfabetica.

Observatia 5.1. 1. Este necesar sa se parcurga ntreg arborele pentru a


se gasi cheia E? (Nu)

2. Pentru a raspunde la ntrebarea exista cheia J? cate noduri trebuie


vizitate? (3)

3. Ce se poate spune despre rezultatul parcurgerii n inordine? (Nodurile


vor fi vizitate n ordine alfabetica)

5.2.1 Complexitatea operatiei de cautare


Datorita structurii specifice a arborilor binari de cautare, complexitatea
operatiei de cautare depinde de naltimea h a acestora. Mai exact, n cazul
nefavorabil cautarea are complexitate O(h).
O ntrebare naturala este atunci: care este naltimea unui arbore binar cu
n noduri? Raspunsul difera n functie de structura arborelui. In cel mai rau
caz arborele este liniar (vezi imaginea de mai jos) si n consecinta h = n 1,
iar n cel mai bun caz arborele este binar complet si atunci h = [log2 (n)].

19
5.2.2 Operatii specifice
Inserarea unui nou nod. Se procedeaza ntr-un mod asemanator operatiei
de cautare: se porneste de la radacina si prin comparatii succesive se merge
pe ramura din stanga sau din dreapta, daca nodul de inserat are cheia mai
mica sau egala decat a nodului curent, respectiv mai mare; cand se ajunge
la un subarbore vid se face insertia la acea locatie. Prin aceasta metoda se
pastreaza structura de arbore binar de cautare.
Exemplul 5.1. Sa presupunem ca dorim sa adaugam noduri cu cheile J,
respectiv A n arborele din figura de mai jos. Nodul cu cheia J se va insera
ca fiu drept al nodului de cheie I, iar cel cu cheia A se va insera ca fiu stang
al nodului deja existent de cheie A.

20
Stergerea unui nod. Daca nodul de sters este o frunza se poate elimina
direct din arbore, fara a-i fi afectata structura de arbore binar de cautare.
In caz contrar, pana cand nodul de sters devine frunza se fac interschimbari
ale acestuia cu nodul de cheie minima din subarborele drept sau de cheie
maxima din subarborele stang. Acest procedeu asigura mentinerea structurii
de arbore binar de cautare (de ce?).

Exemplul 5.2. Se doreste stergerea nodului G din arborele de mai sus.


Cheia cea mai mica din subarborele drept este H si arborele devine cel din
imaginea de mai jos, stanga. Deoarece nodul cu cheia G este acum frunza se
poate elimina, vezi imaginea din dreapta.

5.3 Tema
Exercitiul 5.1. Sa se scrie parcurgerile n preordine, inordine si postordine
pentru fiecare din arborii de mai jos.

21
Exercitiul 5.2. Sa se insereze ntr-un arbore binar de cautare, initial vid,
urmatoarele chei, n ordinea data: 20, 5, 40, 1, 23, 15, 7, 6, 9, 0, 33, 20, 3,
10, 5, 6, 26, 4, 41.
Sa se stearga nodul cu cheia 10.

Exercitiul 5.3. Descarcati fisierul Lab8 BinarySearchTree.cpp si completati


codul conform instructiunilor.

22
Laboratorul 6

Arbori rosu - negru

6.1 Proprietati ale arborilor rosu - negru


Un arbore rosu - negru este un arbore binar de cautare care ndeplineste
urmatoarele proprietati.

RN1 fiecare nod este fie rosu, fie negru;

RN1 orice subarbore vid al unui nod este tratat ca un nod frunza nil;

RN2 fiecare frunza nil este neagra;

RN3 daca un nod este rosu atunci ambii fii sunt negri;

RN4 fiecare drum simplu de la un nod la un descendent care este frunza


contine acelasi numar de noduri negre.

Operatiile de cautare si de parcurgere pentru acest tip de arbori sunt


cele de la arbori binari de cautare. Apar modificari la operatiile de inserare,
respectiv stergere.
Prin aceste reguli care definesc arborii rosu - negru (ARN) se garanteaza
o proprietate foarte importanta a acestora: drumul de la radacina la cea mai
ndepartata frunza este de cel mult de doua ori mai mare decat drumul de
la radacina la cea mai apropiata frunza. Spunem ca un arbore rosu - negru
este aproximativ echilibrat.

23
Pentru a justifica afirmatia de mai sus este suficient sa consideram pro-
prietatea RN4 si sa notam cu b numarul de noduri negre continute de orice
drum de la radacina la o frunza. Atunci orice astfel de drum are cel putin b
noduri negre si, conform proprietatii RN3 cel mult b noduri negre si b noduri
rosii.
Nodurile cu chei se mai numesc si noduri interne, iar frunzele (nodurile
nil) se numesc noduri externe.

Lema 6.1. Un arbore rosu - negru cu n noduri interne are naltimea cel mult
egala cu 2 log2 (n + 1).

O consecinta a rezultatului de mai sus este ca operatia de cautare are


complexitate O(log2 n).

6.2 Operatia de inserare


Orice nod de inserat se va colora cu rosu si apoi se va proceda ca la arbori
binari de cautare. In urma insertiei unui nod rosu este posibil ca proprietatea
RN3 sa nu mai fie ndeplinita. Arborele este adus ntr-o forma corecta, care
respecta toate conditiile RN1 - RN4, cu ajutorul unei serii de operatii bazate
pe rotatii. Aceste operatii conserva proprietatea de arbore binar de cautare.

24
Sunt folosite doua tipuri de rotatii: la stanga si la dreapta. Acestea sunt
descrise n imaginea de mai jos:
Introducem n continuare urmatoarea terminologie: se numeste Bunic
acel nod cu proprietatea ca este parintele parintelui nodului curent; se numeste
Unchi acel nod cu proprietatea ca are acelasi parinte ca si parintele nodului
curent.

Algoritm de insertie
- se coloreaza nodul de inserat cu rosu;
- se insereaza nodul procedand ca la arbori binari de cautare; nodul
inserat devine nod Curent;
- se efectueaza urmatorii pasi cat timp arborele nu este rosu negru
[1] daca nodul Curent este radacina se coloreaza cu negru;
[2] daca si Parintele si Unchiul sunt rosii, atunci amandoi pot fi
colorati cu negru iar Bunicul este colorat cu rosu; nodul Curent este
acum Bunicul si se reia de la pasul [1];
[3] daca Parintele este rosu, Unchiul este negru, nodul Curent este
fiu drept (stang) si Parintele este fiu stang (drept) se executa o rotatie la
stanga (dreapta) a Parintelui; se continua cu urmatorul pas cu Parintele
ca nod Curent;
[4] daca Parintele este rosu, Unchiul este negru si atat nodul Curent
cat si Parintele sunt fii stangi (drepti), atunci culorile Parintelui si ale
Bunicului sunt interschimbate si se executa o rotatie la dreapta (stanga)
a Bunicului.

6.3 Tema
Exercitiul 6.1. Fiind dat urmatorul arbore rosu - negru sa se adauge noduri
cu cheile 4, 9, 20, 6, 3 si 12.

25
Exercitiul 6.2 . Pornind de la clasa care implementeaza un arbore binar de
cautare, scrieti o clasa noua care modeleaza o structura de date de tip arbore
rosu - negru.

26
Laboratorul 7

B - arbori. Coduri Huffman

7.1 B-arbori
Structurile de date de tip B-arbore generalizeaza notiunea de arbore binar de
cautare, asigurand totodata complexitatea logaritmica a operatiei de cautare.
Pentru un m numar natural nenul fixat arbitrar, spunem ca un arbore
m-ar (cu proprietatea ca orice nod are cel mult m fii) este b-arbore daca au
loc urmatoarele:

1. pentru fiecare nod, daca t este numarul de fii, atunci nodul are t 1
chei;

2. n fiecare nod cheile sunt asezate n ordine crescatoare si au rol de a


separa fiii;

3. fiul cel mai din stanga al unui nod are toate cheile mai mici decat prima
cheie a parintelui; fiul aflat ntre doua chei k1 si k2 are cheile cu valori
cuprinse n intervalul [k1 , k2 ]; fiul cel mai din dreapta are cheile mai
mari decat ultima cheie a parintelui;

4. toate frunzele sunt la acelasi nivel

5. toate nodurile interne cu exceptia radacinii au cel putin [ m2 ] fii nevizi;

6. fiecare frunza trebuie sa contina cel putin [ m2 ] 1 chei si maxim m 1


chei.

27
7.1.1 Operatia de cautare
Se realizeaza analog celei de la arbore binar de cautare. Se porneste de la
radacina si la fiecare pas se compara cheia cautata cu cheile nodului curent.
Cat timp nu s-a gasit sau nodul curent nu este frunza, daca nodul curent se
cauta n nodul curent doua chei de pe pozitii consecutive cu proprietatea ca
intervalul determinat de acestea doua contine cheia cautata. Se continua cu
nodul cuprins ntre aceste chei.

7.1.2 Operatia de inserare


Pentru a insera o cheie noua (si informatia pe care o identifica, in functie
de contextul problemei) se procedeaza ca la operatia de cauare pana cand
se gaseste o frunza. In cazul n care cheia are loc se adauga. Daca nu,
cheia mediana se muta n nodul parinte si frunza se separa n doua noduri
dupa pozitia pe care se gasea mediana. Daca n nodul parinte nu era loc se
continua algoritmul de divizare.

Exemplul 7.1. Sa se adauge ntr-un B-arbore initial vid, cu m = 5, urma-


toarele chei: C, N, G, A, H, E, K, Q, M, F, W, L, T, Z, D, P, R, X, Y,
S.
Rezolvare. Primele 4 noduri se introduc n radacina n ordine crescatoare
si rezulta urmatoarea configuratie:

28
Inserand cheia H n nodul radacina se depaseste numarul admis de chei.
Cheia mediana se muta ntr-un nod nou care devine radacina si nodul curent
(care este n particular si frunza si radacina) se divide n doua noduri:

Cheile E, K si Q se insereaza fara a fi nevoie de diviziuni:

Pentru a se insera M este nevoie sa se faca o diviziune a fiului drept al


radacinii si mediana, M, se muta n radacina. Arborele devine:

29
In urma efectuarii tuturor inserarilor arborele final rezultat este urmatorul:

30
7.2 Coduri Huffman
Codurile Huffman reprezinta o modalitate optima de arhivare a unui text
dat. Ideea de baza este ca fiecare caracter va fi nlocuit cu un cod format
din biti de 0 si 1 astfel ncat un caracter cu frecventa mare de aparitie sa
aiba un cod cat mai mic posibil fata de unul cu frecventa mica. Codurile
asociate sunt coduri cu prefix (nici un cod valid nu reprezinta prefixul unui
alt cod valid). In codarea Huffman textul dat este analizat pentru a se calcula
frecventa de aparitie a fiecarui caracter. Se construieste apoi un arbore binar
format din noduri frunze (asociate caracterelor) si noduri interne, astfel: se
porneste cu n noduri libere (frunze), unde n reprezinta numarul de caractere
distincte din text; fiecare nod contine un caracter si frecventa sa de aparitie;
se creeaza apoi un nod nou avand ca fii 2 noduri cu frecventa minima de
aparitie; se continua analog cu multimea de noduri din care s-au eliminat
cele doua noduri folosite si la care s-a adaugat noul nod creat; procesul se
ncheie atunci cand se ramane cu un singur nod, acesta devenind si radacina
unui arbore binar Huffman. Codul unui caracter se calculeaza parcurgand
lantul de la radacina la nodul frunza care l contine si considerand 0 daca
se merge pe directia fiului stang, respectiv 1 pentru fiul drept. Pentru a se
dezarhiva un text codat Huffman trebuie sa se cunoasca codurile caracterelor
(date de arborele Huffman).

Exemplul 7.2. Pentru textul this is an example of a huffman tree se


calculeaza urmatoa-rele frecvente de aparitie si se creeaza arborele Huffman
de mai jos
(vezi http: // commons. wikimedia. org/ wiki/ File: Huffman_ tree.
svg )
Textul este astfel codat si rezulta arhivarea 011010101000101111110001 -
0111110100010111000100100100111100111100100011100110110111101011 -
11010001111101110101110100010111011011000000000 pe 135 de biti, repre-
zentand o compresie de 53% fata de numarul de 288 de biti folositi pentru
memorarea codurilor ASCII corespunzatoare caracterelor din text (36 de car-
actere 8 biti de caracter).

31
32
7.3 Tema
Exercitiul 7.1
. Scrieti o clasa care modeleaza un B-arbore.

Exercitiul 7.2. Fisierul date.in contine un text format numai din literele
mici ale alfabetului latin.

1. Calculati frecventa de aparitie a fiecarui caracter in text.

2. Construiti arborele Huffman corespunzator textului dat.

3. Calculati codul fiecarui caracter (sirul de valori 0 si 1 va fi memorat cu


ajutorul unui sir de ntregi, nu de biti).

4. Arhivati textul.

5. Dezarhivati un text codat Huffman cunoscand arborele Huffman aso-


ciat.

33
Laboratorul 8

Recapitulare

Exercitiul 8.1. Fie un tabel de dispersie cu 8 locatii, initial vid, caruia i se


asociaza functia de dispersie
h(x) = x mod 8.
Stiind ca rezolvarea coliziunilor se face prin nlantuire, reprezentati grafic
tabelul de dispersie rezultat n urma inserarii unor elemente cu urmatoarele
chei: 13, 6, 54, 9, 1, 35, 28, 188, 42, 14, 75, 3, 86, 4 si 90.
Exercitiul 8.2. 1. Inserati ntr-un arbore binar de cautare, initial vid,
elemente avand urmatoarele chei: 200, 45, 600, 68, 92, 35, 79, 119,
301, 72, 506, 1, 54, 211, 9, 43, 192, 40, 302, 29, 0, 13 si 251. Inserarea
se va face n ordinea data.
2. Scrieti listele de noduri rezultate n urma parcurgerii n latime, preor-
dine, inordine si postordine.
3. Descrieti etapele necesare stergerii elementelor avand cheile 92 si 301.
Exercitiul 8.3. Inserati ntr-un arbore rosu - negru, initial vid, elemente
avand urmatoarele chei: 200, 45, 600, 68, 92, 35, 79, 119, 301, 72, 506, 1,
54, 211, 9, 43, 192, 40, 302, 29, 0, 13 si 251. Inserarea se va face n ordinea
data.
Exercitiul 8.4. Inserati ntr-un B-arbore de ordin 4 (fiecare nod are maxim
4 fii), initial vid, elemente avand urmatoarele chei: 200, 45, 600, 68, 92,
35, 79, 119, 301, 72, 506, 1, 54, 211, 9, 43, 192, 40, 302, 29, 0, 13 si 251.
Inserarea se va face n ordinea data.

34

Você também pode gostar