SCHEMA DE GRANTURI PENTRU UNIVERSITĂŢI – NECOMPETITIVE
Beneficiar: Universitatea „Vasile Alecsandri” din Bacău
Titlul subproiectului:
Matematică, Informatică și Biologie - educaţie şi profesii pentru viitor.
Rămâi Integrat şi Total Motivat (Mate-Info-Bio-RITM)
Acord de grant nr. 191/SGU/NC/II din 12.09.2019
STRUCTURI DE DATE
Lect. univ. dr. Iulian FURDU
2020
STRUCTURI DE DATE
2
CUPRINS
VIII. Recapitulare
Bibliografie
3
I. Recapitulare structuri de date cunoscute.
Clasificare structuri de date. Liste
De exemplu, un arbore binar este o structură de date, din care derivă heap-ul,
arborii de căutare binară BST, diagramele de decizie binară BDD etc. Unele structuri
de date sunt special create având anumite proprietăți care le fac potrivite pentru a
rezolva în mod eficient anumite probleme. (ex. BST: căutare după chei care pot avea
asociate valori, similar cu căutarea numerelor de telefon (valori) după numele
persoanei (chei))
Fig 1.1.BST(https://en.wikipedia.org/wiki/Binary_search_tree)
4
Un tip de date este o clasă (potențial infinită) de obiecte concrete care au
aceleași proprietăți. De exemplu, "integer" este un tip de date care conține toate
numerele întregi, "string" conține toate șirurile de caractere etc. Nu e necesar ca un
tip de date să fie neapărat fundamental (de bază, primitiv) într-un limbaj. De
exemplu:
struct MyComplex {
double Preala, Pimaginara;
};
5
Tipul datelor determină modul în care sunt reprezentate acestea în memoria internă
și operatorii permişi pentru prelucrarea lor.
Se poate testa lungimea în octeţi a fiecărui tip de dată folosind operatorul sizeof().
Acesta furnizează numărul de octeţi folosiţi pentru memorarea unei date,
precizată printr-o expresie sau prin tipul datei.
TypeBytes Range
--------------------------------------------------------------------
short int 2 -32,768 -> +32,767 (32kb)
unsigned short int 2 0 -> +65,535 (64Kb)
unsigned int 4 0 -> +4,294,967,295 ( 4Gb)
int 4 -2,147,483,648 -> +2,147,483,647 ( 2Gb)
long int 4 -2,147,483,648 -> +2,147,483,647 ( 2Gb)
signed char 1 -128 -> +127
unsigned char 1 0 -> +255
float 4
double 8
long double 12
Criteriul compunerii
Date simple sau date elementare
Sunt date independente unele de altele, din punct de vedere al reprezentării lor în
memorie.Localizarea unei date pe suportul de memorare nu se face în funcţie de
locaţia unei alte date pe suport.
Date compuse sau structuri de date.
Sunt colecţii de date între care există anumite relaţii (numite relaţii
structurale).Includem aici tablourile (vectori, matrici, masive), înregistrările și uniunile,
fișierele stringurile, tipul enumerat [2], liste, arbori, grafuri, etc.
Fiecare componentă a structurii are o anumită poziţie în cadrul ei. Pentru fiecare
tip de structură de date, în limbajul de programare trebuie să fie definiţi algoritmi de
localizare a componentelor în cadrul structurii de date.
Criteriul variabilităţii
Constante
Sunt date a căror valoare nu se modifică în timpul execuţiei programului.
Într-un program pot fi folosite constante, predefinite sau nu. Aceste constante au un
identificator, iar în program se va folosi, evident, identificatorul.
Variabile
Sunt date a căror valoare se modifică în timpul execuţiei programului, când pot avea
o valoare iniţială, mai multe valori intermediare şi o valoare finală.
7
Liste
Listele fac parte din cotidian, în fiecare zi construim liste ca o modalitate de a pune
lucrurile în ordine: lista cumpărăturilor, lista contactelor din telefon, lista examenelor
dintr-o sesiune, etc,
O listă liniară este un set finit de elemente ordonate liniar, adică există un
element considerat a fi primul în listă, un element considerat a fi ultimul, pentru
oricare alt element din listă existând un element precedent și un element următor.
Principalele operații cu liste sunt:
accesarea sau modificarea unui element din listă
inserarea unui element în listă
eliminarea unui element din listă
Modul în care aceste operatii sunt efectuate depinde de modul de reprezentare a
listelor. Există două tipuri de reprezentări: secventială (statică) și înlănțuită
(dinamică).
8
accesarea/ modificarea unui element se face prin intermediul indicelui
elementului (similar cu vectorii)
inserarea unui element se poate face într-o poziție dată, după sau înaintea unui
element dat. În continuare vom inserara un element numit elem_nou într-o poziție
datăk, deoarece celelalte se reduc la aceasta.
if n = N then OVERFLOW
else for i = n-1, k, -1
x[i+1] = x[i]
endfor
endif
x[k] = elem_nou
n = n +1
eliminarea elementului de indice k din listă.
Algoritm. Dacă n = 0 atunci se produce UNDERFLOW, respectiv lista este vidă
și deci eliminarea unui element din listă nu este posibilă. Presupunem 0 k n-1. Se
salvează informația conținută de elementul de indice k pentru eventuale prelucrări
ulterioare. Se mută elementele șirului x[k+1], ..., x[n-1], câte un bloc de memorie spre
stânga, începând cu x[k+1]. Se actualizează numărul de elemente al șirului:
if n = 0 then UNDERFLOW
else elem_sters = x[k]
for i = k, n-2,1
x[i] = x[i+1]
endfor
endif
n = n –1
De multe ori, memoria nu este ocupată consecutiv, zone de memorie libere alternând
cu zone de memorie ocupate. Alocarea secvențială a unei liste se poate face dacă
există blocuri de memorie suficient de mari pentru a o memora. Dacă, de exemplu,
vrem să alocam spațiu pentru o listă cu M elemente, dar nu există nici un bloc de
memorie de mărime M, deși există trei blocuri de memorie de mărime M/2, ar trebui
să găsim o modalitate de reorganizare a memoriei, altfel lista nu poate fi creată sau
să găsim o metodă alternativă de reprezentare a listei care să elimine neajunsul ca
elementele să fie memorate în locații consecutive. Acesta din urmă este cazul
alocării înlănțuite.
În acest caz, pentru a păstra ordinea listei va trebui să cunoaștem care este primul
element al listei, al doilea etc. fapt pentru care vom folosi un pointer la primul element
al listei (numit pointerul listei notat HEAD, ori TOP, FIRST, VÂRF etc). Pentru a ști
9
care este următorul element în listă fiecare element va trebui să dețină pe lângă
informația corespunzătoare lui și un pointer la elementul următor. Elementele
nemaifiind memorate în locații consecutive, acești pointeri ajută la reconstituirea
listei. Ultimul element al listei va avea drept componentă pointerul NULL. Din acest
motiv, fiecare element va fi de tip structură, cu două câmpuri: info (folosit pentru
memorarea informației elementului) și link (un pointer la elementul următor). Datorită
reprezentării structurate, elementele unei astfel de liste se mai numesc noduri.
HEAD
info link info info
...
10
HEAD
NU
...
p
info_nou
Obs. Algoritmul functionează chiar dacă lista este vidă și se poate folosi
pentru crearea unei liste prin introducerea pe rând, a elementelor listei.
ii) la sfârșitul listei:
Algoritm. Folosim variabilele info_nou ce conține valoarea informației nodului ce
trebuie introdus în listă, p pointer la un nod șiiter care inițial pointează la primul nod al
listei pe care o va parcurge până când acesta va pointa la ultimul nod.
Se alocă memorie pentru un nod nou și se returnează p - pointer la noul nod.
if p NULL then
p -> link = NULL
p -> info = info_nou
iter = HEAD
while (iter NULL and iter -> link NULL)
iter = iter -> link
endwhile
if iter = NULL then HEAD = p //lista vida, adaug primul elem
else iter ->link =p
endif
else OVERFLOW
endif
HEAD
NULL
iter
info_nou NULL
p
11
else OVERFLOW
endif
...
inainte
p info_nou
iter = HEAD
while (iter NULL and iter -> link sterge)
iter = iter -> link
endwhile
if iter = NULL then tipareste mesajul "Eroare. Lista este vida.
UNDERFLOW."
else recupereaza informatia nodului de sters sterge->info
iter ->link = sterge -> link
endif
... ...
iter sterge
Lab 1.
1. Scrieți un program care implementează algoritmii de mai sus pentru o listă liniară
alocată secvențial. 2. Scrieți un program care implementează algoritmii de mai sus
pentru o listă liniară alocată înlănțuit.
12
II. Stive (Implementări, interfeţe, demonstraţii)
Stiva este o listă liniarăîn care inserările și stergerile din listă se pot face numai pe la
un capat al listei, numit vârful stivei. Singurul element care poate fi accesat la un
moment dat este cel din vârful stivei. Se poate face o analogie între o stivă folosita în
programare și o stivă de caiete. Adăugarea unui caiet se poate face numai în vârful
stivei de caiete, peste cele existente și singurul caiet ce poate fi accesat, eventual
eliminat din stivă este cel din vârf.
stergere
inserare
...
Fig.2.1 Stiva
Stivele se mai numesc și liste push-down sau LIFO (last în/first out) deoarece primul
element care este extras din stivă este ultimul introdus în aceasta.
Cele două operații inserarea și ștergerea se mai numesc șipush respectiv
pop.
13
II.2 Alocarea înlănțuită
info_nou top
p info
top
.............
NULL
.......
NULL
Lab 2. Scrieți un program care implementează algoritmii de mai sus pentru o stivă.
14
III. Cozi (Implementări, interfeţe, demonstraţii)
O coadă este o listă liniară în care ștergerea/accesarea unui element se pot face
numai pe la un capăt al său, numit front, iar inserarea se face la celălalt capăt al
cozii, numit rear. Se poate face o analogie între o coadă folosită în programare și, de
exemplu, o coadă pentru tipărirea mai multor fișiere text. Pentru tipărirea unui nou
fișier va trebui să asteptăm până când toate fișierele sunt tipărite în ordinea în care
comenzile de tipărire au fost efectuate, exemplu ce ilustrează conceptul de coadă.
stergere
front
...
rear
inserare
Cozile se mai numesc și liste FIFO (first in/first out) deoarece primul element care
este extras din coadă este primul introdus.
15
3.2 Alocarea înlănțuită
struct nod
{
T info;
struct nod *link;
};
typedef struct nod NOD;
unde T este presupus definit anterior; notăm cu front, respectiv rear, pointerul la
primul nod al cozii, respectiv la ultimul nod.
NOD *front, rear;
Cu aceste notații, operațiile cu cozi se pot descrie astfel:
...........
.............
rear
rear NULL
info_nou
p NULL
Lab 3.
1. Scrieți un program care implementează algoritmii de mai sus pentru o coadă. 2.
Implementați o coadă cu priorități și una DEQUE, chei la alegere.
17
IV. Liste înlănţuite
(dublu, circulare: implementări, operaţii, demonstraţii)
Pentru folosirea mai ușoară a listelor liniare putem avea, pentru fiecare nod al listei
nu numai un pointer la elementul următor ci și unul la nodul precedent. O astfel de
reprezentare a unei liste se numește listă dublu înlănțuită. Astfel, orice nod va avea
forma:
Orice lista dublu înlănțuită va deține doi pointeri FIRST, către primul nod și LAST
care pointează la ultimul nod.
FIRST LAST
18
FIRST LAST
NULL
p
o la sfârșitul listei:
FIRST LAST
19
endif
inainte
p
eliminarea unui nod
o eliminarea primului nod:
Algoritm.
elem_sters = FIRST -> info
FIRST = FIRST -> rlink
FIRST ->llink = NULL
FIRST
NULL
o eliminarea ultimului nod:
Algoritm.
elem_sters = LAST -> info
LAST = LAST -> llink
LAST ->rlink = NULL
LAST
NULL
eliminarea unui nod dat din listă:
20
Algoritm. Folosim variabilele sterge,un pointer la nodul care trebuie eliminat din listă,
inainte, pointer la nodul dinainte și dupa, pointer la nodul după nodul ce trebuie șters.
elem_sters = sterge -> info
inainte = sterge -> llink
dupa = sterge -> rlink
dupa ->llink = inainte
inainte ->rlink = dupa
... ... llink info rlink llink info rlink llink info rlink ...
Într-o listă circulară ultimul element indică spre primul, atât în alocarea statică cât și
în cea înlănțuită. În alocarea statică va trebui sa folosim un tip struct pentru e reține
indexul începutului șirului de elemente.
struct Element
{ int info;
int link;
};
Element x[9];// similar cu inlantuirea, dar fara pointer catre
urmatorul nod
Primul nod din șir se va reține într-o variabilăfirst, iar parcurgerea poate fi:
int elem_crt;
first=2;
………………………
if (first!=null){
elem_crt = first;// alte prelucrari asupra lui x[]
while (x[elem_crt].link!=first) {
elem_crt = x[elem_crt].leg;//are ca val index nod urm
………………………… // alte prelucrari asupra lui x[]
}
}
21
Algoritm. Folosim variabilele info_nou ce conţine valoarea informaţiei nodului ce
trebuie introdus în listă, p pointer la un nod şi iter care iniţial pointează la primul nod
al listei şi va parcurge lista până când acesta va pointa la ultimul nod din listă.
Alocă memorie pentru un nod nou. Returneaza p, un pointer la noul nod.
if p !=NULL then
p -> link = HEAD
p -> info = info_nou
iter = HEAD
while (iter -> link != HEAD)
iter = iter -> link
endwhile
iter -> link =p
HEAD = p
else OVERFLOW
endif
...
info link info link info
iter = HEAD
while (iter != NULL and iter -> link &&HEAD and (iter -> link)
->link != HEAD)
iter = iter -> link
endwhile
if iter = NULL then UNDERFLOW
else if iter -> link = HEAD then elem_sters = iter ->info
22
else elem_sters = (iter - >link) ->info
iter -> link = HEAD
endif
endif
HEAD iter
....
o eliminarea altui nod din listă se face la fel ca în cazul listei liniare
Lab 4.
1. Scrieți un program care implementează algoritmii de mai sus pentru liste dublu
înlănțuite. 2. Analog pentru liste circulare.
23
V. Grafuri (conexe, orientate/neorientate, reprezentări, parcurgeri)
Două vârfuri ale unui graf neorientat între care există o muchie se numesc
adiacente. Dacă a, b sunt două vârfuri ale unui graf orientat astfel încât există un arc
de la a la b atunci spunem că b este adiacent vârfului a.
Gradul unui vârf x al unui graf neorientat, notat d(x), este dat de numărul de
vârfuri adiacente cu el. Un vârf de grad zero se numeste izolat.
24
x = vârf d(x) = grad
a 2
b 1
c 2
d 1
Pentru grafuri orientate se definesc gradul de intrare al unui vârf, notat d-(x),
ca fiind numărul de arce care au ca extremitate finală vârful respectiv iar gradul de
ieșire, notat d+(x), se definește ca numărul de arce ce au ca extremitate inițială
vârful respectiv. Pentru exemplele de mai sus, avem:
Tabelul 5.2. Gradele nodurilor într-un graf orientat (graf G2 din Fig. 5.2)
Un graf neorientat se numeste conex dacă între orice două vârfuri ale sale
există un drum. Componentele conexe ale unui graf sunt clasele de echivalentță ale
vârfurilor aflate în relația "exista drum intre". De exemplu, G1 este conex în timp ce
G2 nu este conex, dar are 2 componente conexe.
Un graf neorientat se numeste complet dacă există muchie între oricare două
vârfuri.
Un graf G' = (V', E') este subgraf al grafului G = (V, E) daca V' V si E' E.
Un graf G' = (V', E') este graf parțialal grafului G = (V, E) dacă V' = V și E' E.
Se numește graf ponderat (sau cu ponderi) un graf G = (V, E) pentru care fiecărei
muchii i se asociază o pondere dată de obicei de o funcție f: E R.
25
5.2 Reprezentarea grafurilor
1 3 4 NULL
2 3 NULL
3 1 2 NULL
1 NULL
4
5.3. Parcurgeri
26
de parcurgere a grafurilor: 1. Parcurgerea în adâncime (DFS – Depth First Search) 2.
Parcurgerea în lăţime (BFS – Breadth First Search).
Fie graful (obs. graful e format din trei componente conexe, între care un nod
izolat [3]):
Lab 5.
1. Parcurgeți graful figurat mai sus în lățime, indicând lista nodurilor, având ca nod de
start fiecare nod din cele rămase, pe rând (vezi exemplele de parcurgere). 2. Aceeași
cerință ca la 1, pentru parcurgerea în adâncime. 3. Scrieți un program care parcurge
în adâncime și în lățime un graf cu chei întregi (reprezentarea la alegere), indicând
parcursul. Pentru bonus: 4. Scrieți un program care adaugă și/sau șterge o muchie
dintr-un graf neorientat. 5. Scrieți un program care pentru un graf neorientat
determină dacă graful este conex.
27
VI. Tabele de dispersie (dicționare, hashing)
Pentru a adăuga elemente în tabelă astfel încât să le putem găsi ulterior rapid se
folosește o funcție numită funcție hash care face legătura între slot și elementul de
adăugat în slot. Funcția hash aplicată unui element întoarce poziția pe care acesta o
va ocupa în tabela hash, în intervalul 0..m-1 unde m este dimensiunea tabelei, uzual
un număr prim. Rațiunea pentru care este indicat ca mărimea tabelei hash să fie
număr prim o găsiți de exemplu la resursa [6]. Fie setul de elemente întregi 54, 26,
93, 17, 77 și 31 de adăugat în tabela de mai sus. O primă funcție hash, creată după
tehnica “metoda restului” ia un element și îl împarte la dimensiunea tabelului,
întorcându-se restul ca valoare hash (h (item) = item% 11). Astfel h(54)=54%11=10
(Fig.6.2 )
28
există o modalitate sistematică de a construi o funcție perfectă de hash. Din fericire,
nu avem nevoie ca funcția hash sa fie perfectă pentru a obtine eficiența.
O modalitate de a avea întotdeauna o funcție hash perfectă este de a mări
dimensiunea tabelei hash, astfel încât fiecare valoare posibilă din colecția de
elemente să poată fi inserată. Acest lucru garantează că fiecare articol va avea un
slot unic și este practic pentru un număr mic de articole, dar nu este eficient atunci
când numărul de articole este mare.
Scopul este crearea de funcții hash care să reducă la minimum numărul de
coliziuni, să fie ușor de calculat și să distribuie uniform articolele în tabelul hash.
Există o serie de moduri comune de a extinde metoda restului.
Metoda împachetării (sau plierii,indoirii, segmentării) constă în divizarea
itemului în bucăți de dimensiuni egale (asumând că e numeric, ultima parte poate să
nu fie de dimensiuni egale). Aceste bucăți sunt apoi sumate pentru a da valoarea
hash rezultată.
Exemplu: dacă articolul nostru este numărul de telefon 436-555-4601, am lua
cifrele și le-am împărți în grupuri de 2 (sau de mai mult de 2) : 43,65,55,46,01.
Sumând obținem 210. Dacă presupunem că tabelul nostru de hash are 11 sloturi,
atunci trebuie să efectuăm pasul suplimentar de divizare cu 11 și de păstrare a
restului. În acest caz, 210%11 este 1, astfel încât numărul de telefon 436-555-4601
este postat la slotul 1. Unele metode de pliere merg cu un pas mai departe și
inversează fiecare a doua bucată înainte de adăugare. Pentru exemplul de mai sus,
obținem 43 + 56 + 55 + 64 + 01 = 219 ceea ce dă 219% 11 = 10.
O altă tehnică numerică pentru construirea unei funcții hash se numește
metoda pătratului " din mijloc" (mid-square method). Mai întâi ridicăm elementul la
pătrat, apoi extragem o parte din cifrele rezultate, de regulă din mijloc.
Exemplu: dacă itemul ar fi 44, am calcula mai întâi 44^2 = 1.936,extragem
cifrele in mijloc (poate fi una, sau mai multe, depinde). Aici vom extrage 2, deci 93,
iar iar 93%11 dă 5. În final 44 va fi inserat la poziția 5. Procedând astfel, sunt șanse
mai mari ca distribuția elementelor să fie mai uniformă (v. Tabelul 6.1)
Tabelul 6.1. Distribuția elementelor pentru hash prin metoda restului vs mid-square
29
for pos in range(len(astring)):
sum = sum + ord(astring[pos])
return sum%tablesize
Deficiența acestei metode apare în cazul în care avem anagrame ale unui cuvintelor
deja hash-uite, respectiv aceleași litere dar în altă ordine. În aceste cazuri, apar
coliziuni cum, de exemplu h(cat)=h(tac)=h(atc) etc = 4. [5].
Pentru a evita acest lucru, putem lua în calcul ponderile elementelor, date de poziția
lor în șir, astfel: (Fig.6.3)
Un prim dezavantaj este faptul că în cazul căutării, dacă nu găsim elementul din
prima, va trebui să îl căutăm secvențial, începând cu poziția care îi succede și
30
reluând circular o dată (în cazul în care am ajuns la sfărșit). Astfel, pe 93 îl găsim pe
poziția 5, dar pentru 20, va trebui să parcurgem tabela începând cu poziția 9 pâna la
final și să reparcurgem de la început tabela. Un alt dezavantaj al adresării deschise
este clusterizarea, itemii au tendința de a se aglomera în zona valorii lor de hash,
așa cum se observă pentru 77, 44 și 55, care sunt toți lângă slotul 0.
O modalitate de a reduce clusteringulconstă în extinderea tehnicii de sondare liniară,
astfel încât în loc să căutăm secvențial următorul slot deschis, "sărim" sloturile,
distribuind astfel mai uniform elementele care au provocat coliziuni.
Exemplu: salt(skip) de 3 poziții. Aceasta înseamnă că, odată ce se produce o
coliziune, vom analiza fiecare al treilea slot până vom găsi unul care este gol (Fig.
6.5).
Se observă că 44, inițial în slotul 1, este acum în slotul 3, 44 fiind primul din lista
inițială pentru care obțineam coliziune. Această tehnică de ocupare prin salt se
numește rehashing, respectiv reevaluez hash-ul inițial prin alt hash [5], conform
newhashvalue=rehash(oldhashvalue)
Saltul peste 3 poziții, se poate defini ca rehash(pos)=(pos+3)%sizeoftable, iar în
general: rehash(pos)=(pos+skip)%sizeoftable, unde skip e pasul [5].
Pasul trebuie ales astfel încât să asigurăm vizitarea tuturor sloturilor, motiv în plus
pentru a alege dimensiunea tabelei hash număr prim.
O altă metodă constă în a folosi un salt pătratic [5] în loc de unul constant, în sensul
că în loc săavem salt de 3 (constant), vom face salt de 1, 4, 9, 16, respectiv dacă
prima valoare hash este h, următoarele vor fi h+1, h+4, h+9, h+16. Tabela finală,
folosind salt pătratic va arăta ca în Fig. 6.6
31
Fig. 6.7 Tabela hash obținută prin înlănțuire
Lab. 6.
1. Fie lista de numere:33, 112, 17, 29, 41, 120, 222, 101, 65. a) Figurati o tabela
hash de mărime 11 în care le introduceti folosind adresarea deschisă, funcția hash
fiind h(item)=item%11. b) Refigurați tabela redistribuind elementele prin folosirea unui
salt de 3. c) Refigurați tabela redistribuind elementele prin adresare pătratică. d)
Refigurați tabela utilizând liste înlănțuite în cazul coliziunilor. 2. Implementați pentru
lista de mai sus cerințele a)-d) cât și căutarea unei valori. 3. Implemenați și populați o
tabelă hash, pentru un set de 9 cuvinte la alegere, folosind funcția hash bazată pe
suma codurilor ascii ale caracterelor. Algoritmul trebuie să permită vizualizarea
tabelei hash și căutarea unei valori.
32
VII. Arbori (parcurgeri, aplicaţii).
Tipuri de arbori și aplicațiile lor (AVL, BST, etc.)
(1) (2)
Fig. 7.1. Arbori
iar nodul 2 este tatăl nodurilor 6, 1, 3, și 7 (care sunt urmașii lui 2); 5 este fiul lui 6; 4
este fiul lui 3; iar 8 este fiul lui 7. Nodurile 5, 4, 8, și 1 nu au nici un fiu. Nodurile care
nu au fii se mai numesc frunze sau noduri terminale, iar muchiile dintre noduri,
ramuri. Nodurile 6, 1, 3 și 7 sunt frați. De asemenea, nodurile 5, 4 și 8 sunt urmasii lui
2, iar rădăcina este singurul nod care nu are tată. În general, un nod al unui arbore
poate avea un numar arbitrar de fii.
33
Dacă orice nod al unui arbore nu are mai mult de n fii atunci arborele se
numeste arbore n-ar. Un arbore în care orice nod nu are mai mult de 2 fii se numeste
arbore binar.Se numește înălțime a unui arbore lungimea celui mai lung drum de la
rădăcinăla un nod terminal. Pentru arborele de mai sus înălțimea este 2. Observați
că între orice nod și rădăcinăexistă exact un singur drum.
2. Arbore care nu este arbore binar de căutare (9>4, iar 9 este nod în
subarborele stâng a lui 4).
34
Fig. 7.5 Contraexemplu - arbore binar de căutare
35
Fig. 7.6 Arbore binar de căutare. Căutare liniară
37
Fig. 7.8 Arbore binar dezechilibrat [4]
Cazul 2 - rotație simplă stânga – este simetric în oglindă față de cazul 1- rotație
simplă dreapta (Fig. 7.10)
38
Fig. 7.9 Rotație simplă dreapta [4]
Analog avem rotație dublă dreapta și rotație dublă stânga prin care un nod urcă două
nivele în rădăcină:
39
Fig. 7.11 Rotație dublă dreapta [4]
40
Exemplu: fie secvența de chei 4,5,7,2,1,3,6 care se inserează într-un
arboreAVL inițial vid. Evoluția arborelui și echilibrările sunt [4]:
-nodurile cu cheile 4,5 se vor insera ca și la arborii binari de căutare
-nodul cu cheia 7 ar fi de inserat în dreapta nodului cu eticheta 5, caz în care
arborele este dezechilibrat.
41
Fig. 7.15 Menținerea echilibrării la inserția valorii 3 [4]
-nodul cu cheia 6 s-ar insera în stânga nodului cu cheia 7, dar în acest caz arborele
ar fidezechilibrat:
42
Fig. 7.17 Menținerea echilibrării în cazul ștergerii [4]
Lab 7.
1. Scrieți o procedură iterativă pentru căutarea unei valori într-un BST. 2.
Implementați operațiile: STERGE, MINIM, MAXIM, PREDECESOR, SUCCESOR
pentru BST. 3. Parcurgeți arborele binar (exemplul 1. secțiunea 7.2) în preordine și în
postordine indicând lista cheilor nodurilor.
43
VIII. Recapitulare
Indicații. Parcurgeți întâi materialul rapid. Apoi parcurgeți din nou partea I și
încercați să răspundeți la întrebările din Test marcate cu 1. Ar fi bine ca, de fiecare
dată când nu reușiți să răspundeți la o întrebare să reparcurgeți întreg capitolul.
Treceți apoi la II și tot așa, până la final, răspunzând la intrebările ce încep cu un
număr identic cu al capitolului. Apoi faceți Exemple de subiecte, unul din numere
(timp de lucru 2 ore) pentru a vă verifica.
Orice alte surse bibliografice sunt binevenite.
44
7.4. Parcurgeți un arbore binar complet de înălțime 3 (chei naturale, la alegere, fără
duplicate) în inordine și în postordine. Indicați secvența de chei pentru fiecare
parcurgere după ce figurați arborele inițial
7.5 Ce caracterizează un BST?
7.6 Care sunt operațiile uzuale într-un BST?
7.7 Câte tipuri de rotiri există pentru arborii AVL? Care sunt acestea și cum
funcționează
7.8Se consideră secvența de chei: 5, 7, 4, 1, 3, 2, 6 care se inserează într-un arbore
AVL. Se cere: a) Evoluția arborelui și echilibrările (figurați pe pași, ce fel de
rotație s-a facut, acolo unde este cazul)b) Ștergeti un nod care să aibă minim 2 fii.
Figurați pe pași dezechilibrele, dacă apar și menționați prin ce tip de rotații le
corectați.
45
Exemple de subiecte de examen
Nr. 1.
I. Scrieți secvența relevantă de cod pentru parcurgerea grafurilor neorientate în
lățime
II. Cozi (alocări, operații)
III. Parcurgeți un arbore (Fig.7.4) în preordine
IV. Se consideră secvența de chei: 5, 7, 4, 1, 3, 2, 6 care se inserează într-un arbore
AVL. Se cere: a) Evoluția arborelui și echilibrările (figurați pe pași, ce fel de
rotație s-a facut, acolo unde este cazul) b) Ștergeti un nod care să aibă minim 2 fii.
Figurați pe pași dezechilibrele, dacă apar și menționați prin ce tip de rotații le
corectați.
Nr. 2
I. Scrieți secvența relevantă de cod pentru parcurgerea grafurilor neorientate în
adâncime
II. Stive (alocări, operații)
III. Parcurgeți un arbore (Fig.7.4) în postordine
IV. Se consideră secvența de chei: 1, 3, 2, 6, 4, 5, 7 care se inserează într-un arbore
AVL. Se cere: a) Evoluția arborelui și echilibrările (figurați pe pași, ce fel de
rotație s-a facut, acolo unde este cazul) b) Ștergeti un nod care să aibă minim 2 fii.
Figurați pe pași dezechilibrele, dacă apar și menționați prin ce tip de rotații le
corectați
46
Resurse disponibile online
[1].https://en.wikipedia.org/wiki/Binary_decision_diagram
[2]. http://www.tutorialspoint.com/ansi_c/c_basic_datatypes.htm
[3].http://webserv.lgrcat.ro/2010-
2011/Catedre/Informatica/11/Parcurgere_Conexitate.pdf
[4]. http://software.ucv.ro/~cmihaescu/ro/laboratoare/SDA/docs/avl.pdf
[5].https://runestone.academy/runestone/books/published/pythonds/SortSearch/Hash
ing.html#tbl-hashvalues1
[6]. https://cs.stackexchange.com/questions/11029/why-is-it-best-to-use-a-prime-
number-as-a-mod-in-a-hashing-function
47
Bibliografie
1. Tudor S. – Informatica. Curs pentru clasele a IX-a și a X-a, Editura L&S INFO-
MAT, 2008.
2. Andone R., Gârbacea I. - Algoritmi fundamentali o perspectivă C++, Editura
Libris, Cluj Napoca, 1995.
8. Calude C. - Complexitatea calcului, aspecte calitative, Editura Ştiinţifică și
Enciclopedică, Bucureşti, 1982.
9. Cormen T.H., Leiserson E.C., Rivest R.R. - Introducere în algoritmi, Editura Libris
Agora, 2000 (traducere în limba română).
10. Dahl O.J., Dijkstra E.W., Hoare C.A.R. - Structured Programing, Academic Press,
1972.
11. Knuth E. Donald - Arta programării calculatoarelor, orice ediție.
12. Livovschi L., Georgescu H. - Sinteza şi analiza algoritmilor, Editura Ştiinţifică și
Enciclopedică, Bucureşti, 1986.
13. E. Horowitz, S. Sahni, Fundamentals of Computer Algorithms – 1985
(Fundamentals of Data Structures).
14. R. Vişinescu, V. Vişinescu - Algoritmi și structuri de date - Teorie şi aplicaţii.
Probleme de concurs, Editura Edusoft, 2006.
48