Sunteți pe pagina 1din 175

Academia de Studii Economice din Bucureti Facultatea de Cibernetic, Statistic i Informatic Economic Catedra de Informatic Economic

Structuri de date
- material didactic pentru ID -

Prof. univ. dr. Ion IVAN Cristian-Eugen CIUREA Mihai-Laureniu DOINEA

Acest material are la baz lucrarea Structuri de date, coordonatori Ion IVAN, Marius POPA, Paul POCATILU publicat la Editura ASE, Bucureti, 2008, premiat de Academia Romn cu diploma Tudor Tnsescu n data de 16 dec. 2010

Bucureti 2011

Titlul cursului: Structuri de date Introducere: Disciplina Structuri de date se studiaz n Academia de
Studii Economice din Bucureti, n cadrul Facultii de Cibernetic, Statistic i Informatic Economic i se adreseaz studenilor din anul 3, secia Informatic Economic. Disciplina Structuri de date are ca obiectiv prezentarea tuturor structurilor de date cu proprietile, avantajele i dezavantajele utilizrii lor, n vederea utilizrii lor eficiente. Cunoaterea structurilor de date le permite programatorilor s priveasc procesul de dezvoltare software ca proces de alocare i nivelare de resurse. Evoluia tehnicilor de programare determin realizarea unor produse program caracterizate prin complexitate ridicat i prin consumuri reduse de resurse. ntr-un context mai larg, structurile de date se constituie ca resurse la dispoziia programatorilor, care prin diversitate influeneaz hotartor calitatea programelor. n cadrul procesului de instruire, se utilizeaz ca resurse suplimentare materialele puse la dispoziie de biblioteca facultii, att n cadrul slii de lectur, ct i prin serviciul de mprumut. Laboratoarele Catedrei de Informatic Economic sunt disponibile pentru studiu individual, pe toat durata programului de lucru, atunci cnd nu se desfoar activiti didactice.

Evaluarea cunotinelor se realizeaz astfel:


se elaboreaz un proiect care are ca obiectiv utilizarea eficient a structurilor de date 20% din punctaj; studenii trebuie s ofere o serie de soluii pentru 5 probleme n timpul semestrului pentru care primesc 10% din punctaj; pentru activitatea depus n vederea realizrii de lucrri practice la disciplina Structuri de date se obine 20% din punctaj; examen 50% din punctaj. n vederea asimilrii ct mai eficiente i a unei uoare nelegeri a informaiilor prezentate n acest material, urmtorul graf de parcurgere a disciplinei reflect interconectarea tuturor structurilor de date prezentate. Obiectivul este reprezentat de conturarea nivelului conceptual, astfel nct s fie evideniat corelaia dintre structurile de date discutate i realizarea unei abordri progresive pentru a nlesni nelegerea i a sublinia utilitatea.

Articole

Fiiere

Masive unidimensionale

Liste simple i duble

Masive multidimensionale

Stive i cozi

Matrice rare

Arbori binari de cutare

Arbori B

Fig. 1. Graful de parcurgere a disciplinei Structuri de date

CUPRINS
1. Masivele structuri de date omogene i contigue
1.1. Masive unidimensionale 1.2. Masive bidimensionale 1.3. Operaii cu masive bidimensionale 1.4. Masive multidimensionale

2. Matrice rare
2.1. Concepte de baz 2.2. Memorarea matricelor rare 2.3. Determinarea gradului de umplere al unei matrice rare 2.4. Adunarea, scderea i transpunerea 2.5. nmulirea i inversarea matricelor rare

3. Articolul structur de date neomogen i contigu


3.1. Structuri de date poziionale 3.2. Structuri de date ierarhizate 3.3. Vectori de structuri i structuri de vectori

4. Fiiere
4.1. Structuri de date externe 4.2. Criterii de clasificare a fiierelor 4.3. Fiiere secveniale 4.4. Fiiere secvenial indexate 4.5. Fiierele bazei de date

5. Listele structuri dinamice necontigue


5.1. Consideraii privind structurile de date de tip list 5.2. Lista simplu nlnuit 5.3. Lista circular simplu nlnuit 5.4. Operaii cu liste liniare simplu nlnuite 5.5. Liste dublu nlnuite

6. Stive i cozi
6.1. Consideraii privind structurilor de date de tip stiv i coad 6.2. Caracteristicile structurilor de date de tip stiv 6.3. Operaii de baz cu stive 6.4. Evaluarea expresiilor matematice cu ajutorul stivei i cozii

7. Arbori binari i arbori de cutare


7.1. Structura de date de tip arborescent 7.2. Transformarea arborilor oarecare n arbori binari 7.3. Arbori binari de cutare 7.4. Aplicaii care utilizeaz structura de date de tip arbore binar de cutare

8. Arbori B
8.1. Arbori B. Definiie. Proprieti. 8.2. Operaii de baz ntr-un arbore B 8.3. Algoritmii C++ pentru inserarea unei valori de cheie ntr-un arbore B 8.4. Algoritmii C++ pentru tergerea unei valori de cheie intr-un arbore B

1. Masivele structuri de date omogene si contigue


Obiectivele unitii de nvare Dup studierea acestei uniti de nvare, studenii i vor nsui cunotine teoretice i vor deprinde abiliti practice pentru utilizarea masivelor unidimensionale si bidimensionale. Studenii vor aprofunda lucrul cu vectori i matrice, fiind capabili s defineasc, iniializeze i s refere elementele acestora.
1.1 Masive unidimensionale Sunt puine programele n care nu apar definite masive unidimensionale. Problemele de ordonare a irurilor, de calcul a indicatorilor statistici medie i dispersie, programele pentru gsirea elementului minim i multe altele presupun stocarea valorilor numerice ale irurilor n zona de memorie care n mod folcloric le numim vectori. Ceea ce de fapt se recunoate sub numele de vector este n realitate o structur de date omogene. Prin definirea: int x[10]; se specific: x elementul primul element al doilea element ... al zecelea element o dat compus din 10 elemente; de tip ntreg; x [0]; x [1]; ... x [9].

Lungimea zonei de memorie ocupat de variabila x se determin conform relaiei:

lg(x;.)
Dac funcia:

n i 1

lg (x[i] ; int)

(1.1)

lg(a; b) se definete pentru tipurile standard prin: lg (x; b) = lg(.; b) = k (1.2)

unde: k lungimea standard alocat la compilare pentru o dat de tipul b; lg(x0, x1, x2, . . . ., xn ; b) = lg (x0; b) + lg (x1; b) + . . . +lg(xn; b) = = lg(.; b) + lg (.; b) + lg (.; b) = kb + kb + . . . kb = n* kb rezult c: lg(x, int) = 10*2 bytes

Modul grafic de reprezentare a alocrii memoriei pentru elementele vectorului x, conduce la:

x[0] x[1] x[2] x[8] x[9]

Figura 1.1 Alocarea memorie pentru vectorul x Pentru c elementele sunt de acelai tip, respectiv: lg(x[0], . ) = lg(x[1], . ) = . . . = lg(x[9], .) dac se noteaz: = adr(x[i]) = adr (x[i+1]) - = lg (x[i],.) se observ c: dist (x[i], x[i+1]) = lg(x[i], int) = 2 bytes ntruct elementele ocup o zon de memorie contigu. Se definesc funciile: desemneaz succesorul unui element ntr-un ir; succ( ) desemneaz predecesorul unui element ntr-un ir astfel: pred( ) succ(x[i]) = x [i+1]; pred(x[i]) = x [i-1]. Aceste funcii au prioritile: succ(pred(x[i])) = succ(x[i-1]) = x[i] pred(succ(x[i])) = pred(x[i+1]) = x[i] Deci, funciile succ( ) i pred( ) sunt una invers celeilalte. Pentru extremitile vectorului: pred(x[0]) = succ(x[9]) = succ ( ) = pred ( ) = Direct, se observ c: succ 0 (x[i]) = x [i] pred 0 (x[i]) = x [i] succ m (x[i]) = x[i+m] pred n (x [i]) = x[i-n] succ m (pred n (x[i])) = x[i-n+m] pred n (succ m (x[i])) = x[i+m n] (1.4) (1.3)

Dac elementul x[i] este considerat reper, adresele celorlalte elemente se calculeaz folosind deplasarea fa de elementul reper. Funcia deplasare: depl(a;b) va permite calculul deplasrii lui a fa de b. Astfel: depl(x[i+k], x[i]) = (i+ k i) * lg (., int) = k * lg(., int) depl (x[i], x[i+k] = (i k i) * lg (., int) = -k * lg(., int) sau: depl(x[i+k], x[i]) = adr(x[i+k]) adr(x[i]) (1.5)

Dac x reprezint structura de date omogene n totalitatea ei, iar x[0], x[1], . . . , x[9] sunt prile care o compun, din reprezentarea grafic rezult c: adr(x) = adr(x[0]) Se definete: adr(a+b) = adr(a) + (b 1) * lg( ., int) (1.7) unde: a numele datei structurate; b poziia elementului a crui adres se dorete a fi calculat; adr(a+b) = adr(b+a). Contiguitatea este pus n eviden prin definirea funciei de distan DST(a, b), care indic numrul de bytes care nu aparin datelor a i b, separnd a de b. (1.6)

Fig. 1.2. Separarea zonelor de memorie alocate pentru variabilele a i b


Astfel, conform figurii 1.2, distana dintre a i b este DST(a, b)=5 bytes. n raport cu modul de definire a funciei DST( ), se observ c: DST(x[i], x[i+1]) = 0, i {0, 1, . . . , 9} DST(x[i], x[i]) = 0 DST(a, a) = 0 DST (a, b) 0

11

12

Fig. 1.3. Separarea zonelor de memorie alocate variabilelor a, b i c

DST (a, b) DST(a, c) + DST (c, b) 11 11 + 12 + lg (b) + 12 lg(b) + 2*12 0 DST (a, b) = DST (b, a) Reprezentat ca structur arborescent, vectorul are modelul grafic:

X0

X1

X2

X9

Fig. 1.4. Structura arborescent a vectorului x


n programe, se specific numrul maxim al componentelor vectorului, avndu -se grij ca n problemele ce se rezolv, numrul efectiv de elemente s nu depeasc dimensiunea declarat. De exemplu, n secvena: ......... int x[10]; int n; ......... cin >> n; for( i=0; i<n; i++) x[i] = 0; se identific urmtoarele inexactiti: - deoarece n nu este iniializat cu o valoare cuprins ntre 0 i 9, exist posibilitatea ca n cazul n=15, adresa calculat s fie: adr(x+15) = adr(x) + (15-1) * lg(x, int) (1.8) i s cuprind un cuvnt ce urmeaz cu mult mai departe de componenta x[9], cuvnt al crui coninut devine zero; - n limbajul Pascal, prin parametrii compilrii se controleaz expresiile indiciale, aa fel nct s fie incluse n intervalul definit pentru variaie, specificat n calificatorul tip[e1 .. e2], evitndu-se distrugerea necontrolat a operanzilor adiaceni masivului unidimensional. Exemplul dat arat c expresia indicial aparine intervalului [0, 9], ns limbajul C/C++ permite definirea unor limite ntr-o form mult mai flexibil. De exemplu, definirea: int y[11]; permite referirea elementelor: . . . ., y[-7], y[-6], . . . . y[0], y[1], y[2], y[3] adr(y) = adr(y[-7])

DST(y[ -7], y[-6]) = [-6-(7)-1] * lg(. , int) = 0 i acest vector rezult c este contiguu. Numrul de componente este dat de:

adr( y[3]) adr( y[ 7]) 1 11 lg(., int)

(1.9)

adr(y+4) = adr(y[-7] ) +[4-(-7) ]*lg(y, int) = adr(y[-7] ) + 11*lg(y, int) succ(y[ -5]) = y [-4] pred(y[-1]) = y[-2] depl(y[-7+2], y[-7]) = 2*lg(y, int) Deplasarea lui y[-6] fa de elementul y[2]: depl(y[-6], y[2]) = depl(y[2-8], y[2]) = (2-8-2)*lg(y, int) = -8*lg(y, int) depl(y[2], y[-6]) = [2-(-6)]*lg (y, int) = 8*lg(y, int) Se observ c: depl(a, b) = - depl (b, a) sau: depl(a, b) + depl(b, a) = 0 (1.11) (1.10)

Proprietile masivelor unidimensionale, conduc la ideea stocrii ntr-o zon de memorie a adresei unuia dintre termeni i prin adugarea sau scderea unei raii egale cu lungimea unui element, se procedeaz la baleierea spre dreapta sau spre stnga a celorlalte elemente.

x [0]

x[1]
...

x[i]

x[i+1]

adr

i+adr

i+1+adr

Fig. 1.5. Baleierea elementelor vectorului x


= adr(x[0]); = + i*lg(x[0], int); conduce la situaia n care: cont () adr (x[i]) (1.12)

Datorit contiguitii memoriei i a regulilor de regsire a elementelor, masivul unidimensional nu conine n mod distinct informaii privind poziia elementelor sale. Cunoscnd

10

numele masivului, referirea elementelor se realizeaz specificnd acest nume i poziia elementului cuprins ntre paranteze drepte sub forma unei expresii indiciale. Expresia indicial are un tip oarecare, ns nainte de efectuarea calculului de adresa are loc conversia spre ntreg, care este atribuit funciei int( ). Deci funcia int( ), realizeaz rotunjirea n minus a valorii expresiei indiciale. Astfel, pentru definirea: int z[100]; unde indicele pleac de la 6, adresa elementului: z [ ea + cos (b) / se calculeaz astfel:

c ] (1.13)

adr (z [ ea + cos (b) /


a

c ] ) = adr ( z[6] ) + + ( int (e + cos (b) / c )) * lg ( z[6] , int)


Funciile de validare a adreselor se vor defini astfel:

TRUE, dac adr(a) fp(a) =

i,

Af]

FALSE, n caz contrar


(1.14)

TRUE, dac gm(a) =

i,

Bf]

FALSE, n caz contrar

unde: fp( ) gm( ) Ai Af Bi Bf

funcia de validare adrese n raport cu programul; funcia de validare a adreselor n raport cu un masiv m; adresa se nceput a programului; adresa se sfrit a programului; adresa de nceput a masivului m; adresa de sfrit a masivului m.

Dac programul P este format din instruciunile I1, I2, , I100 atunci: adr (I1) = Ai adr (I100) = Af Dac n programul P este definit masivul: int x[10]; atunci:

11

adr (x[0]) = Bi adr (x[99]) = Bf Se spune c este corect folosit expresia indicial ea+cos(b)/ c dac: gm( x [ ea + cos (b) / sau dac: int (ea + cos (b) /

c ] ) = TRUE

(1.15)

c ) * ( lg ( z[6] , int ) < Bf - Bi

(1.16)

Dac se ia n considerare c: fp( x [ ea + cos (b) /

c ] ) = TRUE

(1.17)

pot apare situaii n care s fie modificate alte zone dect cele asociate masivului unidimensional. Dac se iau n considerare reprezentri simplificate, pentru localizarea unui element x[i] al unui masiv unidimensional, se folosete formula cunoscut: x[0] + x[i 0] * lungime_element (1.18)

Atunci cnd se construiesc programe n care se definesc masive unidimensionale, alegerea tipului, alegerea limitei inferioare i a limitei superioare pentru variaie, depind de contextul problemei dar i de formulele identificate prin inducie matematic pentru explorare, folosind structuri repetitive. De fiecare dat, trebuie avut grij ca numrul de componente ce rezult la definire s fie acoperitor pentru problemele ce se rezolv. Pentru stabilirea numrului maxim de componente ale vectorilor ce sunt definii, se consider: lungimea n baii a disponibilului de memorie; L Lp lungimea n baii a necesarului de memorie pentru program, respectiv instruciuni executabile i alte definiri; M numrul de masive unidimensionale de tip Ti, avnd acelai numr de componente care apar n program; numrul maxim de componente ale unui masiv. x x = int( ( L Lp ) / ( N * lg( . ;Ti ) ) ) (1.19)

n cazul n care cele N masive au dimensiuni variabile d1, d2, , dN, condiia de utilizare corect a resursei memorie este ca: d1 + d2 + +dN int( ( L Lp ) / lg( . ;Ti ) ) (1.20)

Astfel, se rezolv problema acoperirii resursei de memorie. 1.2 Masive bidimensionale Este aproape sigur c n activitatea de programare, matricele ocup ca importan un loc deosebit. i tot att este de adevrat c lucrul corect cu matrice ofer rezultate rapid e, cum la fel de adevrat este c utilizarea defectuoas reduce considerabil ansa de a obine rezultate corecte. Singura modalitate de a realiza programe corecte utiliznd matrice, este cunoaterea mecanismelor de implementare a acestora n diferite limbaje de programare, precum i studierea proprietilor ce decurg din ele.

12

Pornind de la ideea c o matrice este format din linii, iar liniile sunt formate din elemente, se ajunge la modelul grafic al matricei, care apare sub forma unei arborescene organizat pe trei nivele. Pentru definirea: int a[2][4]; rezult c matricea a este format din 2 linii, fiecare linie avnd cte 4 componente. Modelul grafic pentru matricea a este:
a

a0

a1

a00

a01

a02

a03

a10

a11

a12

a13

Fig. 1.6. Structura arborescent pe linii asociat matricei a


Modelul grafic presupune existena a trei nivele: - la nivelul cel mai nalt se afl ntregul, matricea a; - la nivelul imediat urmtor se afl primele pri n care se descompun matricea i anume liniile acesteia a[0] i a[1]; - la nivelul al treilea se afl elementele grupate pentru fiecare linie n parte. O alt definiie a matricei conduce la descompunerea n coloane i a coloanelor n elemente. a

C0

C1

C2

C3

a00

a10

a01

a11

a02

a12

a03

a13

Fig. 1.7. Structura arborescent pe coloane asociat matricei a


Deci n ambele cazuri se obin structuri arborescente, elementele de la baza arborescenelor reprezentnd dispunerea contigu a liniilor una n continuarea celeilalte, respectiv, a coloanelor una n continuarea celeilalte, regsind ambele situaii ca modaliti de liniarizare a matricelor. Dac se consider o matrice A, avnd m linii i n coloane, elementele fiind de tipul Ti, adresa elementului a[i][j] se calculeaz fie dup formula: adr(a[i][j]) = adr(a[0][0]) + ((i 0) * n + j ) * lg(a , Ti) dac liniarizarea se face linie cu linie, fie dup formula: adr(a[i][j]) = adr(a[0][0]) + (( j 0 ) * m + i ) * lg(a , Ti) (1.22) (1.21)

dac liniarizarea se face coloan dup coloan. Intuitiv, o matrice este privit lund n considerare numai primul nivel de descompunere, ca vector de linii sau ca vector de coloane.

13

Fiecare linie sau coloan, prin descompunerea la nivelul urmtor, este privit ca vector de elemente. Deci, n final o matrice este privit ca vector de vectori. Definirea: int a[2][4]; pune n eviden exact acest lucru. Din aceast descriere rezult c: adr(a) = adr(a[0]) = adr(a[0][0]) adr(a[i]) = adr(a[i][0]) Dac dispunerea elementelor este linie cu linie: DST(a[i][n], a[i+1][0]) = 0 Dac dispunerea elementelor este coloan dup coloan: DST(a[m][j] , a[0][j+1]) = 0 Deplasarea elementelor aij fa de elementul akh , se calculeaz dup relaia: depl (a[i][j], a[k][h]) = adr(a[k][h]) adr(a[i][j]) = = adr (a[0][0]) + ((k-0)n + h) * lg(a, Ti ) (adr(a[0][i])+ + ((i-0)n + j) *lg(a, Ti ) = = lg(a, Ti )*[(k-0)n (i-0)n + h j] = = lg(a, Ti)*[(k-i)n + h j] Numrul de elemente al matricei n se obine:

adr(a[m][n]) adr(a[0][0 ]) 1 m * n (1.23) lg(a, Ti )


n cazul dispunerii linie de linie: succ(a[i][n]) = a[i+1][0] pred(a[i][0]) = a [i-1][n] succ (a[i]) = a[i+1][0] pred (a[i]) = a[i-1][0] succ(a[i+k]) = succ (a[i]) pred (a[i]) = pred (a[i+k]) n mod asemntor: adr(a+i) = adr(a[0]) + (i-0)*lg(a[0]) adr (a[i] +j) = adr(a[i]) + (j-0)* lg (a[0][0]) lg(a[i]) = lg(a[i][0]) + . . .+lg(a[i][n]) sau: lg(a[i]) = n*lg(a[i][0]) (1.24) deci:

14

adr(a+i) = adr(a[0]) + (i-0)*n*lg(a[i][0])

(1.25)

Dac privim matricele ca vectori de vectori, n mod corespunztor se identific forma grafic:
a[0] a ... a[1] a[1][0] a[1][1] ... a[2] a[2][0] a[2][1] ... ... a[m] a[m][0] a[m][1] ... a[m][n] a[2][n] a[1][n] a[0][0] a[0][1] a[0][n]

Fig. 1.8. Reprezentarea matricelor ca vectori de vectori


unde a, a[i], a[i][j] se definesc n aa fel nct s reflecte urmtorul coninut: a[i] = adr(a[i][1]), i = 0, 1, . . . , m a = adr (a[0]) Definirea unei matrice ca avnd m linii i n coloane, deci n total m*n elemente, i explorarea elementelor prin includerea unui alt numr de linii i coloane conduce la o localizare a elementelor nsoit de un rezultat eronat, a crui provenien e neces ar a fi interpretat i reconstituit. Se consider matricea:

1 6 A

2 7

3 8

4 9

5 10

11 12 13 14 15 16 17 18 19 20 7 1 1 4 2

stocat n: int a[5][5]; definit i iniializat n programul principal, iar ntr-o procedur se consider c: int b[4][4];

15

Procedura realizeaz nsumarea elementelor diagonalelor matricei b. ntruct transmiterea parametrilor se face prin adres, punerea n coresponden este: a00 a01 a02 a03 1 2 3 4 a04 a10 a11 a12 a13 a14 a20 a21 5 6 7 8 9 10 11 12 b22 b23

b00 b01 b02

b03 b10 b11

b12 b13 b20 b21

a22 a23 a24 a30 a31 a32 a33 a34 a40 a41 a42 a43 a44 13 14 15 16 17 18 19 20 7 b30 b31 b32 b33 1 1 4 2

Fig. 1.9. Punerea n coresponden a matricei a cu matricea b

Prin specificaii se cere:

Sa
iar prin procedur se obine:

4 i 0

a[i, i ]

(1.26)

Sb

3 i 0

b[i, i ]

(1.27)

n loc de Sa = 42 se obine Sb = 34. n cazul n care structura repetitiv a procedurii efectueaz corect numrul de repetri: for (i = 0; i < 5; i + +) S = S + b[i][i]; i = 0 localizeaz termenul; adr (b[0][0] ) = adr(a[0][0]); cont (b[0][0] ) = cont (a[0][0] ) = 1; i = 1; adr(b[1][1]) = adr (b[1][1] ) + (1-0)*4* lg (int) + 1*lg (int) = = adr (b[0][0] ) +6*lg (int) cont (b[1][1]) = 6; i = 2; adr (b[2][2] ) = adr (b [0][0] ) + (2-0) * 4* lg (int) + 2*lg (int) = = adr (b[0][0]) + 11*lg (int); cont (b[2][2] ) = 11; i = 3; adr (b[3][3] ) = adr (b [0][0]) + 16*lg (int); cont (b [3][3] ) = 16; i = 4;

16

adr (b[4] ) = adr (b[0][0} + (4-0) 4* lg*(int) + 4*lg (int) = = adr (b[0][0] ) + 21* lg (int) cont (b [4][4] ) = 7; Valoarea obinut S = 1 + 6 + 11 + 16 + 7 = 41. Cunoaterea modului de adresare i a modului de transmitere a parametrilor, permite interpretarea cu rigurozitate a oricrui rezultat. Memorarea unei matrice numai ca form liniarizat, nensoit de informaia dedus din structura arborescent, implic efectuarea calcului de adresa ori de cte ori este accesat un element al matricei. n cazul n care nodului rdcin a i se rezerv o zon de memorie, iar nodurilor a[0], a[1], . . . , a[m] li se asociaz, de asemenea, zona ce se iniializeaz adecvat, regsirea elementelor se efectueaz folosind calcule de deplasri. De la relaia: adr (a[i][j] = adr (a[0][0] ) + [ (i 0)*n + j ]*lg (int) (1.28) se ajunge la: adr (a [i][j] ) = adr (a [i] ) + depl( a [i][j], a [i][0]) (1.29)

n cazul n care matricea este dens sau ncrcat sau are puine elemente nule sau este complet ncrcate, dar are elemente ce conin valori particulare, se efectueaz o tratare difereniat. Matricele simetrice, au proprietatea: cont (a [i][j]) = cont (a [j][i]) (1.30) ceea ce conduce la ideea memorrii numai a elementelor de pe diagonala principal i de sub ac easta sau de deasupra ei. Pentru o matrice simetric a[n][n], cu n*n elemente, se impune memorarea a n*(n+1) /2 dintre acestea. Dac sunt memorate elementele a[i][j], j i, i = 0, 1, 2, . . ., n, j=i, i+1, . . ., n, astfel:

a00

a01

...

a0n

a11

...

a1n

a22

...

a2n

...

ann

n-1

n-2

Fig. 1.10. Reprezentarea elementelor de deasupra diagonalei principale


adr(a [i][j]) = adr ([0][0]) + [(n-0) + (n-1) + . . . (n-i+1) + (j-0) ]*lg (int) adr (a [i][j] ) = adr (a [0][0] ) + f (i, j, n) * lg (int) f(i, j, n) reprezint funcia de calcul a deplasrii fa de elementul a[i][0] a elementului a [i][j]. Funcia f (i, j, n) se deduce prin inducie. Matricea cu linii sau coloane identice, sau cu elemente identice, permite o astfel de memorare care conduce la o economisire a spaiului de memorie. Astfel, pentru matricea:

1 1 1 1 1 1 1 1 1 1 1 1

17

se impun definirile: - nume matrice; - numr linii; - numr coloane; - valoarea elementului repetat, respectiv 1. n cazul n care matricea are coninutul:

1 2 3 4 5 0 1 2 3 4 5 0 6 8 3 2 6 1 2 2 2 2 2 2

aceasta are reprezentarea:

a[0]

a[1]

a[2]

a[3]

4 5

Fig. 1.11. Reprezentarea grafic a matricei A


Matricele diagonale sunt matricele n care elementele componente apar sub forma:

1 0 0 0 0 0 7 0 0 0 A 0 0 5 0 0 0 0 0 8 0 0 0 0 0 2
n acest caz, forma de memorie adecvat, n condiiile n care n urma prelucrrii matricei A, aceasta nu-i schimb structura, este vectorul. Un alt tip matrice, cu modaliti specifice de prelucrare, este matricea tridiagonal, care are forma:

1 4 0 0 0 0 2 8 1 0 0 0 T 0 2 7 3 0 0 0 0 6 5 9 0 0 0 0 0 8 1

18

Elementele nenule se memoreaz ntr-un masiv unidimensional, iar accesarea lor se efectueaz dup formula: adr (t[i][ j] ) = adr (t[0][0] ) + [2* (i 0 ) + j 0 ]* lg (int) unde: i = 0, 1, . . ., n; j =0, 1, pentru i = 1; j = i 1, i, i + 1, pentru 1 < i < n; j = n 1, n, pentru i = n. Matricea triunghiular are elementele nenule fie deasupra, fie sub diagonala principal, iar stocarea n memorie este asemntoare matricei simetrice. Pentru matricea triunghiular A cu elementele nenule sub diagonala principal, adresa elementului a[i, j] este: adr (a[i][j] ) = adr (a [0][0] ) + [i* (i 0) /2 + (j 0) ] * lg (int) (1.32) Construirea masivului unidimensional se realizeaz n secvena: k = 0; for (i=0; i<n; i++) for (j=i; j<n; j++) { k = k + 1; s [k] = T [i][j]; } dac elementele nenule sunt deasupra diagonalei principale sau n secvena: k = -n*(n+1)/2; for (i=n-1; i >= 0; i--) for (j=i; j>=0; j--) { s [k] = T[i][j]; k = k 1; } dac elementele nenule sunt sub diagonala principal. n toate cazurile, se urmrete att reducerea lungimii zonei de memorie ocupat, ct i creterea vitezei de acces la componente. Pentru aceasta, mai nti matricele sunt memorate n extenso, ca mai apoi dup analiza componentelor s se deduc dac matricele sunt simetrice sau dac au linii sau coloane constante, cazuri n care se fac alocri adecvate. Operaiile cu matrice se efectueaz cu algoritmi care iau n considerare forma efectiv n care s-a fcut memorarea, existnd posibilitatea trecerii rezultatului la un alt tip de stocare n memorie. De exemplu, dac A este o matrice triunghiular cu elemente nenule sub diagonala principal i B este o matrice triunghiular cu aceeai dimensiune ca matricea A, dar cu elementele nenule deasupra diagonalei principale, n cazul adunrii celor dou matrice, rezult o matrice cu toate elementele nenule, ce este stocat dup regulile acestui tip. Ca i n cazul masivelor unidimensionale, masivele bidimensionale se iniializeaz la definire, prin tastarea de valori i prin atribuire. n secvena: (1.31)

19

for (i=0; i<n; i++) for (j=0; j<n; j++) if (i = = j) a[i][j] = 1; else a[i][j] = 0; se construiete matricea unitate prin instruciuni de atribuire. n secvena: int a[10][10], m, n; for (i=0; i<n; i++) for (j=0; j<m; j++) { cout<<Element <<i<<,<<j<<:; cin>>a[i][j]); } se realizeaz iniializarea unei matrice de la tastatur. 1.4. Operaii cu masive bidimensionale Pentru masivele bidimensionale se definesc operaii precum: - inserarea unei linii sau unei coloane; - tergerea unei linii sau a unei coloane; - interschimbul a dou linii sau a dou coloane. Se consider clasa Matrix cu urmtoarele atribute: m numrul de linii ale matricei; n numrul de coloane ale matricei; a[ ][ ] valorile matricei. Metoda: void Matrix::interschimb (int i, int j) { int aux; for (int K=0; K<n; K++) { aux=a[i][K]; a[i][K]=a[j][K]; a[j][K]=aux; } } rezolv interschimbul dintre liniile i i j ale masivului bidimensional a. tergerea liniei K dintre un masiv bidimensional avnd m linii i n coloane este efectuat de funcia: void Matrix::stergem(int K) {

20

int i, j; for (i=K-1; i<m; i++) for (j=0; j<n; j++) a[i][j]=a[i+1][j]; m --; } Liniarizarea unui masiv bidimensional este realizat prin funcia: void Matrix::linerat(int b[ ]) { int i, j, K = 0; for (i=0; i<m; i++) for (j=0; j<n; j++) b[K++]=a[i][j]; } Operaia revine a pune n coresponden elementele unui masiv bidimensional cu elementele unui masiv unidimensional.

a
a [0] a [1] a [2]

Fig. 1.12. Punerea n coresponden a unei matrice cu un vector


Operaiile cu masive uni-, bi- i multidimensionale sunt necesare atunci cnd se lucreaz cu colectiviti omogene nzestrate cu caracteristici omogene, care se supun operaiilor de agregare prin nsemnare. Produciile nregistrate pe intervale de timp derivate unei mulimi de clieni pent ru un anumit produs se regsesc n modele sub forma unui masiv bidimensional cu attea linii ci clieni sunt i attea coloane cte intervale sunt luate n considerare. Se impune realizarea unei biblioteci de funcii pentru prelucrri cu masive multidime nsionale. n cazul matricelor care au mai puin de 30% elemente nenule se lucreaz cu matrice rare, crora le corespund reprezentri destinate care amelioreaz gradul de ocupare. Acest indicator este dat ca raport ntre elementele iniializate sau referite i totalul elementelor alocate dintr-un masiv.

21

1.5. Masive multidimensionale Utilizarea masivelor multidimensionale este impus de tendina unificrii gruprilor de date omogene, dup diferite criterii ntr-o singur mulime, cu posibilitatea reconstituirii apartenenei elementelor la fiecare grup. Dac ntr-o secie sunt 10 muncitori, iar ntreprinderea are 6 secii i se memoreaz salariile lunare ale acestora pentru 7 ani consecutivi, intuim stocarea volumului impresionant de salarii folosind un masiv cu 4 dimensiuni, definit prin: int salarii[10][6][12][7]; iar elementul: salarii [ i ][ j ][ k ][ h ] identific salariul muncitorului i, din secia j, obinut n luna k a anului h. Obinerea unui nivel mediu: salariu[.][j][k][h] salariu[.][.][k][h] salariu[.][.][.][h] salariu[.][.][.][.] sau a oricrei variante de grad de cuprindere a elementelor colectivitii, este o problem de: - definire a masivelor multidimensionale, cu numr de dimensiuni mai redus cu o unitate, cu dou uniti, cu trei i n final cu patru i apoi iniializarea lor; - efectuarea calculelor n cadrul unei secvene de structuri repetitive incluse. De exemplu, pentru calculele mediilor specificate mai sus se definesc variabilele: float mjkh_salariu[6][12][7]; float mmkh_salariu[12][7]; float mmmh_salariu[7]; float mmmm_salariu; Variabilele de control sunt definite pe domeniile: i [0, 9] j [0, 5] N N k [0,11] h [0, 6] N N (1.33) (1.34)

Problema iniializrii se simplific atunci cnd masivele multidimensionale se pun n coresponden cu un masiv unidimensional, acesta din urm fiind iniializat n cadrul unei singure structuri repetitive, n care variabila de control are ca domeniu : [1, 10 * 6 * 12 * 7] N (1.35)

Modelul grafic asociat masivului multidimensional rmne n continuare structura arborescent, cu deosebirea c numrul de nivele este mai mare i depinde de numrul de dimensiuni. Dac la variabila unidimensional numrul nivelelor este 2, la variabila bidimensional numrul nivelelor este 3, se deduce c la o variabila p-dimensional, numrul nivelelor este p+1. Dac se consider masivul a, p-dimensional, avnd pentru fiecare dimensiune definirea:

22

a[n1][n2][][np] modelul grafic este:


a

a1

a2

an1

a11

a12

...

a1,n2

a111

a112

...

a1,1,n3

. . . . . . . . . . . . . . . . . . . . a11 . . . 1 a11 . . . 2 a1,1, . . . np

Fig. 1.13. Modelul grafic al unui masiv p-dimensional Localizarea unui element oarecare din masivul p-dimensional de tipul Ti, se efectueaz dup formula: adr(a[i1][i2][ ][ip])= adr(a[l][l][ ][0])+f(i1,i2,...,ip; n1,n2,....,np)*lg(Ti) (1.36) Spre exemplificare, pentru p = 3 se obine: adr(a[i][j][k])=adr(a[0][0][0])+((i-0)*n2*n3+ (j-0)*n2+k)*1g(int) (1.37) Dac reprezentarea n memorie corespunde paralelipipedului:
a[n1][0][n3] a[n1][0][0] i a[i][n2][0] j a[n1][n2][n3] a[n1][n2][0]

Fig. 1.14. Reprezentarea geometric a unui masiv tridimensional


i masivele multidimensionale sunt tratate ca matrice rare, numrul vectorilor "informaionali" care conin indicii pentru corecta localizare a valorilor nenule, sunt n numr egal cu numrul dimensiunilor masivului de baz. Asocierea arborescenei i a posibilitii de adresare, respect aceleai reguli ca n cazul masivului bidimensional. Cnd se fac stocri de informaii referitoare la un grup de elemente, este corect abordarea n care se specific o serie de informaii precum: - poziia n memorie a primului element; - poziia n memorie a ultimului element; - numrul de elemente sau poziia elementelor n substructura real a masivului. De exemplu, dac ntr-un masiv tridimensional, n seciunea corespunztoare lui i= 3, avem pe linia a doua elementele:

23

(l 0 2 0 0 4) asociem arborescena:

a[0] ..

a[1]

a[2]

..

a[3]

a[2][0]

a[2][1]

0 2 5

a[2][n]

0 1 3 Fig. 1.15. Structura arborescent a unui masiv tridimensional


Variabila a[2][1] are o structur convenabil definit, care s poat memora adresele primei i respectiv ultimei componente de pe nivelul imediat inferior, precum i poziiile elementelor nenule din linie. Este convenabil i definirea: . 3 1 3 6 .

Fig. 1.16. Coninutul elementului a[2][1] din masivul a


n care, prima informaie de dup adresa primei componente indic numrul componentelor memorate la nivelul inferior. Este de dorit ca multitudinea de informaii suplimentare care vin s descrie structura, fie s menin un grad de ocupare a memoriei ct mai redus, fie s permit un acces mult mai rapid la informaiile dintr-un masiv multidimensional. Modul de a pune problema masivelor ca structuri de date omogene, prin folosirea de exemple, pentru programatori nu reprezint dificultate n nlocuirea valorilor particulare cu parametrii care conduc la formule generale pentru fiecare clas de probleme n parte.

Teme de control 1. S se citeasc informaiile dintr-un masiv unidimensional de numere ntregi i s se parcurg realizndu-se suma elementelor. 2. S se construiasc masivul bidimensional format din vector de vectori i s se iniializeze valorile elementelor coloan cu coloan. Bibliografie Ion IVAN, Marius POPA, Paul POCATILU - Structuri de date, Vol. 1 si 2, Editura ASE, Bucuresti, 2008.

24

2. Matrice rare
Obiectivele unitii de nvare Dup studierea acestei uniti de nvare, studenii i vor nsui cunotine teoretice asupra noiunii de matrice rar i vor deprinde abiliti practice de lucru cu acestea. Studenii vor fi capabili s realizeze implementarea acestor structuri n una din cele dou abordri, respectiv trei vectori alocai dinamic sau liste de liste. 2.1. Concepte de baz Matricele rare i gsesc aplicabilitatea n modelarea unor procese de natur industrial, economic, tehnic, social etc. Capitolul de fa i propune s trateze modalitile de reprezentare n structuri de date a matricelor rare, precum i principalele operaii matriceale implementate ntr-un limbaj orientat pe obiecte. n final este prezentat o aplicaie concret estimarea parametrilor unei regresii statistice. n rezolvarea multor probleme de natur economic, tehnic, social, a diverselor probleme de optimizare, precum i n modelarea unor procese industriale i tehnologice este necesar s se determine modelul matematic care descrie funcionarea procesului respectiv. Descrierea acestor sisteme fizice conduce la obinerea unor modele matematice care fie n mod direct, prin modelare, fie prin metoda de rezolvare implic sisteme de ecuaii algebrice liniare sau probleme de programare liniar a cror matrice a coeficienilor este rar (sparse), n sensul c ponderea elementelor nenule n totalul elementelor matricei este mic. Din punct de vedere practic trebuie remarcat faptul c analiza sistemelor mai sus amintite conduce la obinerea unor modele matematice de mari dimensiuni care implic sisteme de ecuaii algebrice liniare de mii de ecuaii, pentru a cror rezolvare sunt necesare resurse mari de memorie i timp de calcul. n multe cazuri practice, cum sunt sistemele n timp real, timpul de calcul este o resurs critic, nefiind permis s depeasc o valoare limit. Modelele matematice ale proceselor reale implic un numr foarte mare de variabile i restricii care prezint fenomenul de raritate ,sparsity, adic o slab interconectare a elementelor sale. Luarea n consideraie a fenomenului de raritate furnizeaz un nou mod de abordare foarte eficient, ce implic n dezvoltarea aplicaiilor informatice folosirea unor structuri de date speciale, care s conduc la reducerea resurselor de memorie i a timpului de calcul. n general, o matrice (n, n) - dimensional este rar atunci cnd conine un numr mic n 2 . Cantitativ, matricele rare sunt caracterizate de ponderea de elemente nenule , adic numrului de elemente nenule n totalul de elemente, pondere ce definete gradul de umplere al matricei. n aplicaiile curente se ntlnesc matrice rare cu grade de umplere ntre 0,15% i 3%. 2.2. Memorarea matricelor rare Se consider matricea:

25

1 0 0 0

0 0 0 1

0 0 0 2 0 4 0 0 0 0 0 0

(2.1)

Matricea A este un exemplu de matrice rar, ea coninnd 16 elemente nule din totalul de 20. Se definete gradul de umplere, densitatea, unei matrice prin raportul dintre numrul elementelor nenule i numrul total al elementelor sale:
G p n m 100 (%)

(2.2)

unde: p numrul de elemente nenule; n numrul de linii; m numrul de coloane. n general se accept c o matrice este rar dac densitatea sa este de cel mult 3%. Densitatea matricei A este G( A) 20% , ea fiind prezentat aici n scopul ilustrrii conceptului de matrice rar. Structura de date clasic folosit pentru manipularea matricelor, tabloul de dimensiune (n, m) alocat la compilare, se dovedete a fi ineficient n cazul n care matricea este rar. Un prim avantaj este legat de folosirea neeconomic a spaiului de memorie prin alocarea de zone mari pentru memorarea elementelor nule, care nu sunt purttoare de informaie. Ocuparea unor zone de memorie cu elemente nule nu se justific deoarece acestea nu contribuie la formarea rezultatului operaiilor cu matrice, adunare, nmulire etc., conducnd totodat i la mrirea duratei de realizare a acestor operaii prin ocuparea procesorului cu adunri i nmuliri scalare cu zero. Acest inconvenient se manifest cu att mai pregnant cu ct dimensiunea matricei este mai mare. Prin urmare, pentru probleme de dimensiuni mari, s-a cutat gsirea unor modaliti de reprezentare compact a matricelor rare, n care s se renune la memorarea elementelor nule. n acest caz este necesar ca tehnicile de memorare s ncorporeze pe lng elementele nenule i mijloacele de identificare a poziiilor acestor elemente n matrice. Sunt prezentate n continuare cteva posibiliti de memorare compact a matricelor rare MR. Se face, de asemenea, o analiz a oportunitii folosirii fiecrei tehnici n parte, n funcie de densitatea matricei. Memorarea prin identificare binar se bazeaz pe natura binar a sistemului de calcul, constnd n memorarea numai a elementelor nenule ale matricei ntr-o zon primar ZP avnd tipul de baz corespunztor tipului elementelor matricei i dimensiunea egal cu numrul elementelor nenule. Structura matricei este indicat printr-o secven binar memorat ntr-o zon secundar ZS. Matricea A prezentat anterior se memoreaz astfel: Zona primar: Locaie 1 2 3 4 Valoare 1 -2 4 -1 Fig. 2.1. Structura ZP pentru matricea A

26

Zona secundar: Locaie Valoare Locaie Valoare 1 1 11 0 6 0 0 0 0 0 0 1 15 16 0 0 0 0 0 1 0 Fig. 2.2. Structura ZS pentru matricea A 5 10 0 20 0 1 0

Matricea A a fost memorat n ordinea liniilor, o alt posibilitate de memorare fiind n ordinea coloanelor. Pentru a reduce spaiul ocupat de zona secundar se poate implementa soluia dat de memorarea la nivel de bit a valorilor acesteia. Dac matricea B cu dimensiunea (m, n) are densitatea G i dac tipul de baz al matricei, respectiv tipul fiecruia dintre elemente nenule ale matricei, este reprezentat printrun cuvnt de b octei, atunci zona primar va necesita m*n*G cuvinte de b octei iar zona secundar (m*n)/(8*b) cuvinte. Numrul total de cuvinte necesare memorrii matricei B prin intermediul celor dou zone este DMR1 = m*n*G + (m*n)/(8*b) (2.3)

ntruct pentru memorarea matricei n forma clasic sunt necesare DM = m*n cuvinte, raportul dintre cerinele de memorie ale structurii de mai sus i a celei standard este:
c1 DMR1 DM G 1 8b

(2.4)

n relaia anterioar s-a considerat c memorarea zonei secundare se face la nivel de bit. Considernd c elementele matricei A sunt reale i se reprezint pe 4 octei, rezult:
c1 A 0,2 1 32 0,23 (2.5)

ceea ce indic c memorarea matricei A conform acestei structuri ocup de aproximativ patru ori mai puin memorie dect cea standard. Egalnd c1 1 se determin limita superioar a densitii unei matrice pentru care aceast structur necesit mai puin memorie dect cea standard:
Glim 1 1 8b

(2.6)

Pentru matricea A:
Glim 1 1 32 0,96 96%

(2.7)

Aceast structur de memorare difer de abordri prin faptul c n zona secundar este alocat memorie i pentru elementele nule ale matricei. Structura este mai puin eficient

27

pentru matricele de mari dimensiuni foarte rare. Principala dificultate const n complexitatea programelor de implementare a operaiilor matriciale. O alt modalitate de memorare prin identificare binar se obine prin modificarea informaiilor din zona secundar. Aceast zon va conine pe jumti de cuvnt indicii de coloan a elementelor nenule din matrice, precum i anumite informaii de control pentru identificarea rapid a poziiei elementelor nenule n matrice. Structura ZS pe cuvinte este urmtoarea: Tabel 2.1. Structura ZS pe cuvinte Numrul Jumtatea stng Jumtatea dreapt cuvntului 1 Numrul de linii Numrul de coloane 2 Numrul de elemente nenule 3 Numrul de elemente nenule n linia Numrul de elemente nenule n linia 1 2 4 Numrul de elemente nenule n linia Numrul de elemente nenule n linia 3 4 k Numrul de elemente nenule n linia Numrul de elemente nenule n linia m-1 m k+1 Indicele de coloan al primului Indicele de coloan al celui de-al element memorat doilea element memorat k+2 Indicele de coloan al celui de-al etc. treilea element memorat j Indicele de coloan al ultimului element memorat Pentru matricea A, zona secundar ZS are structura din figura 2.3. Locaie Valoare 2 3 4 5 4 5 4 1 2 0 1 1 3 Fig. 2.3. Structura ZS pentru matricea A 1 6 5 2

n reprezentarea din figura 2.3 s-a considerat c elementele nenule sunt reprezentate pe 4 octei astfel c o jumtate de cuvnt n zona secundar se reprezint pe 2 octei. Prin structura de memorare prezentat mai sus se memoreaz matrice a cror dimensiune maxim este de 9999 de linii sau coloane cu numrul maxim de elemente nenule memorate egal cu 108 1. Se face observaia c n cazul matricelor ptrate n primul cuvnt din ZS se va memora dimensiunea matricei. Numrul total de cuvinte necesare zonei secundare este egal cu

(5 m m * n *G) / 2

(2.8)

valoarea fiind rotunjit la cel mai mare ntreg. Numrul total de cuvinte necesar memorrii unei matrice prin intermediul celor dou zone ZP i ZS este egal cu DMR2 = (5 m 3* m * n *G) / 2 (2.9)

28

Raportul dintre cerinele de memorie ale acestei structuri de identificare binar i a celei standard este:
c2 3 G 2 5 m 2*m*n

(2.10)

Pentru o matrice ptrat (m=n), egalnd c2 = 1 i trecnd la limit pentru m rezult valoarea maxim a densitii unei matrice rare pentru care structura prezentat este eficient:
Glim m 2 5 m 1 3 2m 2 0,666 66,6%

(2.11)

n relaia anterioar se ajunge la acelai rezultat n cazul unei matrice neptratic pentru care se trece la limit pentru n i m . Pentru o matrice rar de dimensiune (100, 100), cu o medie de 66 elemente nenule pe linie, structura de mai sus necesit un total de 6600 + (5 + 100 + 6600)/2 = 9952 cuvinte, cu 0,6% mai puin dect 10.000 cuvinte necesare pentru memorarea standard. ntruct densitatea elementelor nenule ale unei matrice rare este de obicei ntre 1% i 3%. Structura se dovedete a fi deosebit de eficient. Memorarea compact aleatoare const n utilizarea unei zone primare ZP, coninnd numai elementele nenule ale matricei i a dou zone secundare coninnd indicii de linie i de coloan corespunztoare elementelor nenule. Deoarece fiecare element nenul al matricei este identificat individual, este posibil ca matricea s fie memorat n ordine aleatoare. Matricea A se memoreaz astfel: Locaia 1 2 3 4 Valoare 1 -2 4 -1 Indice linie 1 2 2 4 Indice coloan 1 3 5 2 Fig. 2.4. Model de memorare compact aleatoare a matricei A Avantajele memorrii compacte aleatoare constau n faptul c noile elemente nenule ale matricei sunt adugate la sfritul zonelor de memorare fr a afecta celelalte elemente, precum i o manevrabilitate rapid a datelor. n cazul matricelor simetrice aceast structur de memorare este simplificat prin memorarea numai a elementelor nenule de deasupra diagonalei principale, precum i a elementelor nenule situate pe aceast diagonal. Numrul total de cuvinte necesare memorrii unei matrice de dimensiune (m, n) este n acest caz DMR3 = 3*m*n (2.12)

Raportul dintre cerinele de memorie ale acestei structuri i a celei standard este:

c3

3 G

(2.13)

Egalnd relaia anterioar cu unitatea se determin valoarea limit a densitii matricei rare pentru care aceast structur este eficient, Glim 33,3% .

29

n structura din figura 6.4, pentru identificarea elementelor nenule ale matricei rare au fost folosite dou zone secundare corespunztoare indicelui de linie i de coloan. Se prezint n continuare o alt posibilitate de memorare n care se va utiliza o singur zon secundar de dimensiune egal cu numrul de elemente nenule ale matricei, coninnd simultan informaii asupra indicilor de linie i de coloan. Astfel, fiecrui element din zona primar i se ataeaz n zona secundar un numr ntreg din care se determin indicii de linie i de coloan. Dac elementul a ij 0 este memorat n locaia k a zonei primare atunci n zona secundar se va memora un indice agregat ig a crui valoare este dat de relaia ig = i+(j-1)*n (2.14) unde n este numrul de coloane a matricei. Acest numr este suficient pentru identificarea elementului n matrice. Utiliznd acest artificiu, matricea A se memoreaz astfel: Locaia 1 2 3 4 Valoare 1 -2 4 -1 Indice agregat, ig 1 12 22 9 Fig. 2.5. Model derivat de memorare compact a matricei A Pentru a regsi indicele de linie i de coloan al oricrui element memorat n locaia k se utilizeaz urmtoarea tehnic de calcul: - coloana j este obinut prin relaia: j ig(k)/n (2.15)

linia i este determinat prin relaia: i = ig(k) ( j 1 ) n (2.16)

Avantajul acestei structuri de memorare const n faptul c necesit mai puin memorie dect cea precedent, fiind n schimb mai puin rapid n ce privete manevrarea datelor. Numrul total de cuvinte necesar memorrii matricei este DMR4 = 2*m*n*G (2.17)

Raportul dintre cerinele de memorie ale acestei structuri i a celei standard este:

c4

2G

(2.18)

Valoarea limit a densitii matricei pentru care aceast structur este eficient este G = 50%. Memorarea compact sistematic presupune c elementele nenule ale unei matrice rare sunt memorate ntr-o anumit ordine, respectiv pe linii sau pe coloane. n acest caz nu este necesar s se memoreze n zonele secundare indicii de linie, respectiv de coloan. Pentru o memorare n ordinea liniilor, ne mai sunt necesari indicii de linie, ns se cere specificarea nceputului fiecrei linii.

30

i n acest caz exist mai multe structuri de memorare. Cea prezentat n continuare este caracterizat prin faptul c utilizeaz o singur zon secundar ZS, care conine indicii de coloan ale elementelor nenule din matricea considerat, precum i elemente false care indic nceputul fiecrei linii i sfritul memorrii ntregii matrice. De exemplu, un element zero n ZS marcheaz prezena unui element fals i acesta specific n ZP numrul liniei elementelor de la dreapta locaiei. Sfritul matricei este marcat prin prezena n ZP a unui element fals cu valoarea zero. Pentru matricea A, memorarea n aceast form este urmtoarea: Locaia 1 2 3 4 5 6 7 8 ZP 1 1 2 -2 4 4 -1 0 ZS 0 1 0 3 5 0 2 0 Fig. 2.6. Model de memorare compact sistematic a matricei A Pentru aceast structur de memorare numrul maxim de cuvinte necesar pentru a reine o matrice rar de dimensiune (m, n) este DMR5 = 2*(m*n*r+m+1) Raportul de memorare este:
c5 2*G 2 * (m 1) m*n

(2.19)

(2.20)

Se constat c structura este eficient pentru memorarea matricelor rare cu o densitate a elementelor nenule de maximum 50%. Memorarea cu ajutorul listelor reprezint o extensie a memorrii compacte aleatoare. n timpul operaiilor de inversare a matricelor rare, noi elemente nenule sunt continuu generate, iar altele sunt anulate i deci structurile de memorare trebuie s fie capabile s execute aceste modificri ntr-un mod eficient. De aceea structurile de memorare bazate pe aceast tehnic sunt folosite pentru memorarea i manipularea matricelor rare de mari dimensiuni. Structura propus utilizeaz o zon principal ZP pentru memorarea elementelor nenule i trei zone secundare: ZSL ZSC ZSU memorarea indicilor de linie ale elementelor nenule; indicii de coloan; memorarea adresei urmtorului element al matricei. Matricea A se memoreaz dup cum urmeaz: Locaia 1 2 3 4 ZP 1 -2 4 -1 ZSL 1 2 2 4 ZSC 1 3 5 2 ZSU &2 &3 &4 NULL Fig. 2.7. Model de memorare cu ajutorul listelor a matricei A unde prin &2 se nelege adresa celei de-a doua locaii. Raportul dintre cerinele de memorare ale acestei structuri i a celei standard este: 31

c6

4*G

(2.21)

Prin urmare aceast structur de memorare este eficient pentru memorarea matricelor cu o densitate a elementelor nenule de maximum 25%. 2.3. Determinarea gradului de umplere al unei matrice rare Pentru a deduce dac o matrice este sau nu rar, se definete gradul de umplere al unei matrice, notat cu p. n cazul n care p < 0,3*m*n, se consider c matricea este rar. Problema matricelor rare comport dou abordri: - abordarea static, n care alocarea memoriei se efectueaz n faza de compilare; aceasta presupune ca programatorul s cunoasc cu o precizie bun numrul maxim al elementelor nenule; - abordarea dinamic, n care alocarea se efectueaz n timpul execuiei, caz n care nu este necesar informaia asupra numrului de elemente nenule; aceast abordare este dezvoltat n partea destinat listelor. Memorarea elementelor matricei rare, presupune memorarea indicelui liniei, a indicelui coloanei i, respectiv, valoarea nenul a elementului. Se consider matricea:

0 0 6 0 0 7 0 0 0 0 0 0 0 9 0 0 8 2 0 0

(2.22)

Gradul de umplere al matricei A cu numrul de linii m = 4, numrul de coloane, n= 5 i numrul elementelor nenule k = 5 este:
G 5 5 4 0.25

(2.23)

Se definesc 3 vectori: lin [ ] memoreaz poziia liniei ce conine elemente nenule; col [ ] memoreaz poziia coloanei ce conine elemente nenule; val [ ] memoreaz valoarea nenul a elementelor. Vectorii se iniializeaz cu valorile: Tabel 2.2. Valorile iniiale ale vectorilor LIN, COL i VAL LIN COL VAL 1 3 6 2 1 7 3 4 9 4 2 8 4 3 2

32

Pentru efectuarea calculelor cu matrice rare definite n acest fel, un rol important l au vectorii LIN, COL, iar pentru matricele rare rezultat se definesc vectori cu un numr de componente care s asigure i stocarea noilor elemente ce apar. Astfel, pentru adunarea matricelor rare definite prin: Tabel 2.3. Valorile matricei rare A LIN_A COL_A VAL_A 1 1 -4 2 2 7 4 4 8 i Tabel 2.4. Valorile matricei rare B LIN_B COL_B VAL_B 1 1 4 2 2 -7 3 2 8 4 1 5 4 3 6 rezultatul final se stocheaz n vectorii: Tabel 2.5. Valorile matricei rare rezultat C LIN_C COL_C VAL_C 1 1 0 2 2 0 3 2 8 4 1 5 4 3 6 4 4 8 ? ? ? ? ? ? Vectorii LIN_C, COL_C i VAL_C au un numr de componente definite, egal cu: DIM (LIN_A) + DIM (LIN_A) (2.24)

unde DIM() este funcia de extragere a dimensiunii unui masiv unidimensional: Astfel, dac: int a[n-m]; (2.25) atunci: DIM (a) = n - m+1 (2.26)

Fiind abordat problematica matricelor rare, n mod natural se produce eliminarea elementelor nenule, obinndu-se n final:

33

Tabel 2.6. Coninutul final al matricei rare C LIN_C COL_C VAL_C 3 2 8 4 1 5 4 3 6 4 4 8 ? ? ? ? ? ? ? ? ? ? ? ? Prin secvene de program adecvate, se face diferena ntre definirea unui masiv bidimensional i componentele iniializate ale acestora, cu care se opereaz pentru rezolvarea unei probleme concrete. Din punct de vedere al nivelului de umplere, tabelul 2.6 descrie o matrice rar cu un grad de umplere egal cu G=
12 *100 = 37.5 % 32

(2.27)

Situaia evideniaz ineficien n utilizarea spaiului de memorie alocat. De exemplu, vectorii LIN_A i LIN_B au 3, respectiv 5 componente n utilizare, dar la definire au rezervate zone de memorie ce corespund pentru cte 10 elemente. Rezult c vectorul LIN_C trebuie definit cu 20 componente nct s preia i cazul n care elementele celor dou matrice rare au poziii disjuncte. Din punct de vedere al criteriului minimizrii spaiului ocupat, aceast abordare nu este eficient deoarece presupune n cele mai multe situaii alocarea de spaiu care nu este utilizat. Pentru a atinge acest obiectiv, implementarea unei clase asociate matricei rare va defini vectori alocai dinamic, iar operaiile aritmetice vor genera vectori rezultat cu grad de umplere egal cu 100%. n cazul operaiilor de nmulire sau inversare, este posibil ca matricele rezultat s nu mai ndeplineasc cerina de matrice rar. n acest scop, se efectueaz calculele cu matrice rezultat complet definite i numai dup efectuarea calculelor se analizeaz gradul de umplere i dac acesta este redus, se trece la reprezentarea matricei complete ca matrice rar. Funciile full( ) i rar( ), au rolul de a efectua trecerea la matricea complet, respectiv la matricea rar. Funcia full( ) conine secvena: for( i = 0; i < n; i++) a [LIN_a[i]] [COL_a[i]] = val_a[i]; ce descrie iniializarea elementelor matricei pe baza valorilor din vectori, iar funcia rar( ) conine secvena: k =1; for( i = 0; i < m; i++) for( j = 0; j < n; j++) if (a[i][j] != 0) 34

{ LIN_a[k] = i; COL_a[k] = j; val_a[k] = a[i][j]; k = k + i; } n cazul n care gradul de umplere nu este suficient de mic astfel nct matricea s fie considerat rar, pentru memorare se utilizeaz o structur arborescent care conine pe nivelul al doilea poziiile elementelor nenule, iar pe nivelul al treilea valorile. Astfel matricei:

7 3 5 0 0 0 2 4 8 0 0 0 9 0 5 6 8 9 8 1

(2.28)

i corespunde reprezentarea din figura 2.8.


A

a[0] 1 2 3

a[1] 2 3 4

a[2] 3 5

a[3] -1

Fig. 2.8. Model grafic al matricei A Se elaboreaz convenii asupra modului de stabilire a lungimii vectorului de poziii, fie prin indicarea la nceput a numrului de componente iniializate, fie prin definirea unui simbol terminal. De asemenea. n cazul considerat s-a adoptat convenia ca liniile complete s fie marcate cu simbolul -1, fr a mai specifica poziiile elementelor nenule, care sunt de fapt termenii unei progresii aritmetice. Liniarizarea masivelor bidimensionale conduce la ideea suprapunerii acestora peste vectori. Deci, punnd n coresponden elementele unei matrice cu elementele unui vector, se pune problema transformrii algoritmilor, n aa fel nct opernd cu elementele vectorilor s se obin rezultate corecte pentru calcule matriceale. Astfel, considernd matricea:

1 6

2 7

3 8

4 5 9 10

(2.29)

11 12 13 14 15

35

prin punerea n coresponden cu elementele vectorului b, s se obin interschimbul ntre dou coloane oarecare k i j ale matricei. a00 1 b0 a01 2 b1 a02 a03 a04 a10 a11 a20 a21 a22 a23 3 4 5 6 7 11 12 13 14 b2 b3 b4 b5 b6 b10 b11 b12 b13 Fig. 2.9. Punerea n coresponden a matricei A cu vectorul b a24 15 b14

Dac matricea are M linii i N coloane i elemente de tip int, atunci adresa elementului a[i][j] este dat de relaia adr(a[i][j]) = adr(a[0][0] ) + ( (i-0 ) * N+j ) * 1g(int) (2.30)

iar din modul n care se efectueaz punerea n coresponden a matricei A cu vectorul b, rezult: adr(b[0]) = adr(a[0][0]) (2.31)

Pentru o matrice liniarizat, adresa elementului a[i][j] n cadrul vectorului este dat de relaia adr(a[i][j]) = adr(b[0] )+( (i-0) * N+j ) * lg(int) = adr(b[(i-0) * N+j]) (2.32)

Dac se consider problema interschimbrii valorilor coloanelor j i k pentru o matrice liniarizat atunci secvena de nlocuire a coloanelor for( i = 0; i < M; i++) { c = a[i][j]; a[i][j] = a[i][k]; a[i][k] = c; } este nlocuit prin secvena: for( i = 0; i < M; i++) { c = b[(i-0) * N+j]; b [(i-0) * N+j] = b[(i-0) * N+k]; b[(i-0) * N+k] = c; } Transformarea algoritmilor de lucru cu masive bidimensionale n algoritmi de lucru cu masive unidimensionale este benefic deoarece nu se mai impune cerina de transmitere ca parametru a dimensiunii efective a numrului de linii, dac liniarizarea se face pe coloane, respectiv a numrului de coloane, dac liniarizarea se face pe linii. n cazul matricelor rare, aceeai problem revine la interschimbarea valorilor de pe coloana a treia dintre elementele corespondente ale coloanelor k i j cu posibilitatea inserrii unor perechi i, respectiv, tergerii altora.

36

Pentru generalizare, un masiv n-dimensional rar, este reprezentat prin n + 1 vectori, fiecare permind identificarea coordonatelor elementului diferit de zero, iar ultimul stocnd valoarea acestuia. n cazul n care se construiete o matrice boolean ce se asociaz matricei rare, o dat cu comprimarea acesteia se dispun elementele nenule ntr-un vector. Punerea n coresponden a elementelor vectorului are loc n acelai moment cu decomprimarea matricei booleene i analiza acesteia. De exemplu, matricei :

8 0 0 0 4 0 3 3 0 0 0 0 0 0 0 1 7 0 5 6 9 0 0 0

(2.33)

se asociaz matricea boolean:

1 0 0 0 1 0 1 1 0 0 0 0 0 0 0 1 1 0 1 1 1 0 0 0

(2.34)

care prin compactare, ocup primii 3 octei ai unei descrieri, urmai de componentele vectorului: C = ( 8, 4, 3, 3, 1, 7, 5, 6, 9) (2.35) Compactarea este procedeul care asociaz un bit fiecrei cifre din forma liniarizat a matricei B. 2.4. Adunarea, scderea i transpunerea Prin prisma caracterului dinamic al modului de alocare a memoriei i a caracteristicilor unice ale matricelor rare, adunarea acestor structuri de date presupune parcurgerea unei serii pai: - determinarea numrului de elemente nenule ale matricei sum; din punctul de vedere al operanzilor sunt definite dou situaii de realizare a sumei, cu elemente comune i cu elemente distincte; n cazul sumei a dou elemente comune, se verific dac suma acestora este zero, caz n care rezultatul nu este reinut n matricea rar generat; - alocarea memoriei corespunztoare acestui numr pentru cele trei masive unidimensionale; - parcurgerea celor dou matrice pe linii sau pe coloane i determinarea sumei. Prin elemente comune au fost desemnate valorile caracterizate prin indici de linie i de coloan care sunt indentice n ambele matrice. Pentru implementare s-a folosit suprascrierea operatorilor, tehnic ce ofer o mai mare putere de sugestie operaiilor matriceale implementate. Este prezentat n continuare operatorul care implementeaz operaia de adunare, structurat conform pailor prezentai mai sus.

37

MatriceRara MatriceRara::operator +(MatriceRara & MR) { /* se determina dimensiunea matricei rezultat // // se simuleaza suma si se contorizeaza numarul de sume zero si numarul de sume nonzero

*/

MatriceRara rezMR; if((this->m!=MR.m)||(this->n!=MR.n)) return rezMR; int nrsz = 0, nrsnz = 0; int i = 0, j = 0; while((i<this->dim)&&(j<MR.dim)) { if(this->linii[i]<MR.linii[j]) i++; else if(this->linii[i]>MR.linii[j]) j++; else if(this->coloane[i]<MR.coloane[j]) i++; else if(this->coloane[i]>MR.coloane[j]) j++; else if(this->valori[i]+MR.valori[j]) { nrsnz++; i++; j++; } else { nrsz++; i++; j++; } } int rezdim = this->dim+MR.dim-nrsnz-2*nrsz; rezMR.dim = rezdim; rezMR.m = this->m; rezMR.n = this->n; rezMR.coloane = new int[rezdim]; rezMR.linii = new int[rezdim]; 38

rezMR.valori = new double[rezdim]; // se determina suma elementelor

int k=i=j=0; while((i<this->dim)&&(j<MR.dim)) { if(this->linii[i]<MR.linii[j]) { rezMR.linii[k] = this->linii[i]; rezMR.coloane[k] = this->coloane[i]; rezMR.valori[k] = this->valori[i]; i++; k++; } else if(this->linii[i]>MR.linii[j]) { rezMR.linii[k] = MR.linii[j]; rezMR.coloane[k] = MR.coloane[j]; rezMR.valori[k] = MR.valori[j]; k++; j++; } else if(this->coloane[i]<MR.coloane[j]) { rezMR.linii[k] = this->linii[i]; rezMR.coloane[k] = this->coloane[i]; rezMR.valori[k] = this->valori[i]; i++; k++; } else if(this->coloane[i]>MR.coloane[j]) { rezMR.linii[k] = MR.linii[j]; rezMR.coloane[k] = MR.coloane[j]; rezMR.valori[k] = MR.valori[j]; k++; j++; } else if(this->valori[i]+MR.valori[j]) { rezMR.linii[k] = MR.linii[j]; rezMR.coloane[k] = MR.coloane[j];

39

rezMR.valori[k] >valori[i]+MR.valori[j]; k++; j++; i++; } else { i++; j++; } } if(i<this->dim) for(;i<dim;i++,k++) { rezMR.linii[k] = this->linii[i]; rezMR.coloane[k] = this->coloane[i]; rezMR.valori[k] = this->valori[i]; } if(j<MR.dim) for(;j<MR.dim;j++,k++) { rezMR.linii[k] = MR.linii[j]; rezMR.coloane[k] = MR.coloane[j]; rezMR.valori[k] = MR.valori[j]; } return rezMR; }

this-

Pentru a minimiza numrul de parcurgeri ale celor dou matrice rare, n acest caz sunt necesare doar dou parcurgeri, n metoda prezentat se pornete de la ipoteza c matricea rar este generat prin parcurgerea pe linii a matricei iniiale. Acest lucru asigur o ordine ntre elementele matricei rare i permite identificare mai eficient a elementelor comune. De asemenea, este implementat o parcurgere simultan a celor dou matrice. Elementele curente din cele dou matrice sunt analizate n ordine prin prisma valorii liniei i a coloanei. n cazul n care elementele se gsesc pe linii diferite, elementul care are valoarea liniei mai mic este adugat la rezultat i se trece la urmtorul element din matricea respectiv. Dac elementele curente din cele dou matrice prezint aceeai valoare pentru linie, atunci se compar valoarea coloanelor. Pentru elementele comune, se analizeaz rezultatul sumei i se memoreaz doar valorile nenule. Implementarea operatorului de scdere este absolut similar celui de adunare, singura diferen fiind aceea c n cazul elementelor comune se calculeaz diferena lor, n locul adunrii. O alt abordare, este dat de utilizarea sumei, negnd anterior valorile matricei ce se scade. Prin utilizarea operatorului MatriceRara MatriceRara::operator *(double valoare) ce permite nmulirea matricei cu o valoare dat, scderea se realizeaz prin suprancrcarea operatorului -.

40

MatriceRara MatriceRara::operator *(double valoare) { MatriceRara rezMR; if(valoare) { rezMR = *this; for(int i=0;i<rezMR.dim;i++) rezMR.valori[i]*=valoare; } return rezMR; } MatriceRara MatriceRara::operator -(MatriceRara &MR) { return ((*this)+(MR*-1)); } Transpunerea matricelor rare, prin intermediul operatorului !, este similar celei efectuate pe structur tablou, constnd n inversarea indicilor de linie i coloan ntre ei. MatriceRara MatriceRara::operator !() { MatriceRara rezMR= *this; /* metoda transpune matricea */ for(int i=0;i<dim;i++) { int temp = rezMR.linii[i]; rezMR.linii[i] = rezMR.coloane[i]; rezMR.coloane[i] = temp; } return rezMR; } O alt metod de a realiza transpunerea este dat de inversarea pointerilor pentru masivele de ntregi reprezentnd liniile, respectiv coloanele elementelor nenule MatriceRara MatriceRara::operator !() { MatriceRara rezMR= *this; int * temp = rezMR.coloane; rezMR.coloane = rezMR.linii; rezMR.linii = temp; return rezMR; }

41

2.5. nmulirea i inversarea matricelor rare Pentru nmulirea matricei rare A, (m, l) dimensional, cu matricea rar B, (l, n) dimensional, se utilizeaz procedura standard, avnd n vedere c metoda getValoareElement i operatorul (i,j) permit accesul direct la elementele matricei rare. Pentru a genera matricea rezultat, ca i n cazul operaiilor de adunare i scdere, este nevoie s se determine, anterior efecturii produsului, numrul de elemente ale rezultatului. Acest lucru se realizeaz prin simularea produsului i contorizarea numrului de valori nenule. MatriceRara MatriceRara::operator *(MatriceRara &MR) { MatriceRara rezMR; if(this->n!=MR.m) return rezMR; /* se determina numarul de elemente ale rezultatului int rezdim=0; */

for(int i=0;i<this->m;i++) for(int j=0;j<MR.n;j++) { double val = 0; for(int k=0;k<this->n;k++) { val+=this->getValoareElement(i,k)*MR.getValoareElement(k,j); } if(val) rezdim++; } rezMR.dim = rezdim; rezMR.m = this->m; rezMR.n = MR.n; rezMR.coloane = new int[rezdim]; rezMR.linii = new int[rezdim]; rezMR.valori = new double[rezdim]; int l = 0; for(i=0;i<this->m;i++) for(int j=0;j<MR.n;j++) { double val = 0; for(int k=0;k<this->n;k++) { val+=this->getValoareElement(i,k)*MR.getValoareElement(k,j); } if(val) 42

{ rezMR.linii[l] = i; rezMR.coloane[l] = j; rezMR.valori[l] = val; l++; } } return rezMR; } Prin analiza acestui operator se constat c matricea rezultat pstreaz structura de matrice rar. Pentru implementarea operatorului de inversare s-a folosit algoritmul lui Krlov. Acesta const n parcurgerea unui numr de pai egal cu dimensiunile matricei: 1: A1 = A 2: A2 = A * B1 ... n-1: An-1 = A * Bn-2 p n n: An = A* Bn-1
pn

p1
p2

1 tr(A1 ) 1 1 tr(A 2 ) 2 1
1

B1

I p1 * A1

B2 = I p2 * A2

n 1
1 tr(A) n

tr(A n 1 ) B n
Bn

I pn 1 * An

I pn * An

Prin tr(A) se nelege urma matricei A, suma elementelor diagonale, iar I reprezint matricea unitate de aceeai dimensiune cu matricea A. Aceste elemente sunt implementate n clasa MatriceRar prin intermediul metodelor Unitate(int) i Urma(). MatriceRara MatriceRara::Unitate(int n) { MatriceRara rezMR; if(n>0) { rezMR.n=rezMR.m=rezMR.dim = n; rezMR.coloane = new int[n]; rezMR.linii = new int[n]; rezMR.valori = new double[n]; for(int i=0;i<n;i++) { rezMR.coloane[i] = i; rezMR.linii[i] = i; rezMR.valori[i] = 1; } } 43

return rezMR; } double MatriceRara::Urma() { double rez = 0; if(this->m==this->n) { for(int i=0;i<this->m;i++) rez+=this->getValoareElement(i,i); } return rez; } Krlov a demonstrat c dup parcurgerea celor n pai, Bn este o matrice identic nul. De aici rezult inversa matricei A: A-1 = pn * Bn-1 (6.36) Se prezint n continuare operatorul de inversare a matricelor rare care implementeaz algoritmul prezent. MatriceRara MatriceRara::Inversa() { MatriceRara tempMR, rezMR; MatriceRara unitateMR = MatriceRara::Unitate(this->m); MatriceRara initialaMR = *this; if(initialaMR.m==initialaMR.n) { double p = initialaMR.Urma() ; rezMR = initialaMR - (unitateMR*p); for(int k=2;k<initialaMR.m;k++) { tempMR = initialaMR*rezMR; p = (1.0/(double)k)*tempMR.Urma(); rezMR = tempMR - (unitateMR * p); } tempMR = initialaMR*rezMR; p = (1.0/(double)k)*tempMR.Urma(); rezMR = rezMR*(1.0/p); } return rezMR; }

44

Avantajele acestui algoritm constau n simplitatea implementrii i precizia rezultatelor, datorat folosirii unui numr redus de operaii de mprire. Teme de control 1. S se verifice dac o matrice ale crei dimensiuni i elemente au fost citite de la tastatur ndeplinete condiiile de memorare ca o matrice rar. 2. Pentru o matrice rar reprezentat cu ajutorul a trei masive unidimensionale s se realizeze conversia la un masiv bidimensional i s se justifice algoritmul. Bibliografie Ion IVAN, Marius POPA, Paul POCATILU - Structuri de date, Vol. 1 si 2, Editura ASE, Bucuresti, 2008.

45

3. Articolul structur de date neomogen i contigu


Obiectivele unitii de nvare Dup studierea acestei uniti de nvare, studenii i vor nsui cunotine teoretice i vor deprinde abiliti practice de lucru cu articole ca structuri de date neomogene si contigue.

3.1. Structuri de date poziionale Exist o diversitate de date cu care se caracterizeaz indivizii unei colectiviti. Dup efectuarea unei analize detaliate, se conchide c un numr q de caracteristici sunt suficiente pentru descrierea elementelor colectivitii. n continuare numim ablon de descriere o anumit ordine n care sunt nirate caracteristicile, ordine absolut necesar a fi respectat la descrierea fiecrui element al colectivitii. Fie caracteristicile C1, C2, ..., Cq dispuse n ordinea care se constituie n ablonul asociat descrierii indivizilor colectivitii considerate. De exemplu, pentru descrierea materialelor existente n stoc, se consider caracteristicile: C1 numele materialului; C2 unitatea de msur; C3 cantitatea existent n stoc la nceputul perioadei; C4 preul unitar; C5 data ultimei aprovizionri; C6 intrrile de materiale; C7 ieirile de materiale; C8 codul materialului; C9 stocul final. deci q = 8. ablonul pentru introducerea datelor are elementele: C8 C1 C2 C4 C3 C5 C6 C7 (3.1)

Caracteristica C9 nu e necesar pentru c rezult din calcule. Aceste caracteristici sunt poziionale, n sensul c C8 este prima caracteristic n ablon, C3 este a 5-a caracteristic n ablon, iar C7 este ultima caracteristic a ablonului. La introducerea datelor, irurile de constante se constituie n cmpuri. Astfel, se identific cmpul pentru codul materialului, format dintr-un numr fix de cifre, cmpul numele materialului, ce are un numr de caractere care nu depesc o valoare prestabilit etc. ablonul se descrie prin tabelul 3.1, unde lungimea este calculat ca numr de caractere. Tabel 3.1. ablon de descriere a cmpurilor Caracteristic Natur dat Lungimea C8 cod material numr 5 C1 nume material ir caractere 20 C2 unitate msur ir caractere 3 C3 cantitate existent n stoc numr 6 C4 pre unitar numr real 4+2

46

C5 data ultimei aprovizionri C6 intrri C7 ieiri C9 stoc final

ir caractere numr numr numr

9 6 6 9

Se observ c cele opt caracteristici difer ca natur, ca lungime a irului de simboluri ce compun cmpurile i ocup n cadrul ablonului poziii distincte. Datele grupate n ablon cu poziia fiecreia specificat, formeaz o structur de date de tip articol. Modelul grafic asociat articolului este prezentat n figura 3.1.
nume articol

primul element. din ablon

al 2-lea elem. din ablon

...

ultimul elem. din ablon

Fig. 3.1. Model grafic asociat ablonului de articol Se observ c pe ultimul nivel se afl date elementare specificate prin nume i tip. Structura, n ansamblul ei, are un nume i se definete distinct, ceea ce urmeaz reprezentnd componentele de la nivelul al doilea. typedef struct material { int cod; char nume[20]; char um[3]; float pre; int cont; int intrri; int ieiri; }; lg(material) = lg(cod) + lg(nume) + lg(um) + + lg(pret) + lg(cont) + lg(intrari) + lg(iesiri) + (3.2) n cazul n care compilatorul face o alocare neoptimal reprezint numrul biilor impui de alinierea cerut de fiecare cmp precedent care are un alt tip dect ntreg sau caracter. n cazul n care are loc optimizare n faza de compilare, devine nul.
n 1

adr(cmpi ) adr(cmp1 )
j 1

lg(cmp j)

(3.3)

unde:

47

1 reprezint baitul cerut de alinierea cmpului j 1 (3.4) 0 n cazul n care cmpj 1 nu cere alinierea

Dac datele de la nivelul al doilea le regsim sub numele de cmpuri elementare, la nivelul superior data este numit dat de grup. n unele situaii fiecrui nivel i se asociaz un numr, numit numr de nivel. Pentru nivelele inferioare se asociaz numere naturale mai mari dect cel asociat nivelelor superioare. Unui nivel i se ataeaz un numr sau un interval de numere. Descrierea n orice situaie rmne poziional. Astfel, pentru exemplul dat se folosesc descrieri cu numere de nivel: 01 material 02 cod 02 nume 02 um 02 pre 02 cantitate 02 intrri 02 ieiri 01 ................. Terminarea descrierii structurii, este marcat de apariia unui numr de nivel care delimiteaz nceputul altei structuri, sau o instruciune de program, proprie limbajului, care vine s marcheze nceputul unei alte secvene program. O alt descriere este: 7 material 8 cod 9 nume 10 um 8 pre 10 cantitate 9 intrri 9 ieiri 4 .......................... unde, pentru primul nivel se utilizeaz numerele 4, 5, 6 sau 7, iar pentru al doilea nivel, sunt utilizate numerele 8, 9 sau 10. La acest nivel de descriere a articolelor putem observa c o dat elementar este un articol cu cmpuri de acelai tip. Astfel: typedef struct a { int b; }; typedef struct c { 48

int c1; int c2; int c3; int c4; int c5; }; i a x; c w,y; conduc la aceleai rezervri de zone de memorie pentru x i y ca definiiile: int u; int t[5],v[5]; adr(c4) = adr(c1) + lg(c1) + lg(c3) = adr(c1) + (4-1) * lg(int) iar adr(v[4]) = adr(v[1]) + (4-1) * lg(int) (3.6) (3.5)

Cmpurile unui articol se numesc membrii articolului, iar referirea lor se face folosind operatorul de referire .. n exemplul considerat, w i y sunt variabile de tip c, iar t i v sunt masive, fiecare avnd 5 elemente. Prin w.C4 se refer cmpul C4 al variabilei w, iar prin y.C1 se refer cmpul C1 al variabilei y. Dac se ia n considerare tipul de variabil articol material i se face definirea: material m; m.cod, m.pre sunt referiri ale cmpurilor ce alctuiesc variabila m definit ca avnd tipul material. Se observ c articolul este un tip de dat generic, pe care prin forma de definire a cmpurilor l putem folosi pentru a obine tipuri derivate de date. Astfel, material i C sunt tipuri derivate de date corespunztoare tipului generic struct. Definirea acestui tip pune n eviden: - componentele, cmpurile care-l alctuiesc; - natura cmpurilor; - poziia cmpurilor. Altfel spus, se evideniaz structura sau ablonul datelor. Variabilele definite pentru un tip derivat ocup atta memorie ct rezult din lungimile cmpurilor nsumate i corectate cu alinierea i au n componen exact aceleai elemente folosite la descrierea tipului, ca membri ai fiecrei variabile. Descrierea unui tip derivat nu nseamn rezervare de memorie, ci stocare de informaie privind elementele care l definesc. Definirea variabilelor de tipul derivat descris anterior, efectueaz rezervare de memorie.

49

3.2. Structuri de date ierarhizate Datele de tip articol sunt ierarhizate pe dou sau mai multe nivele. Pentru a obine descrierea mai multor nivele este necesar ca la fiecare nivel inferior s fie posibil enumerarea de date elementare sau date de grup. Datele de grup e necesar s fie descrise deja nainte de a le folosi, dar n unele cazuri particulare descrierea lor se efectueaz ulterior folosirii. Astfel, se definesc tipurile: struct data { int zi; int luna; int an; }; struct adresa { data data_stabilirii; char strada[30]; int numar; char telefon[8]; char oras[30]; }; struct persoana { data data_nasterii ; adresa locul_nasterii; int varsta; data data_angajarii; adresa adresa_actuala; }; Definirea ulterioar specificrii datelor de grup, se face ca n exemplul: 01 persoana 02 data_stabilirii 03 zi 03 luna 03 an 02 locul_nasterii 03 data_nasterii 04 zi 04 luna 04 an 03 strada 03 numar 03 telefon 03 oras 50

02 varsta 02 data_angajarii 03 zi 03 luna 03 an 02 adresa_actuala 03 data_stabilirii 04 zi 04 luna 04 an 03 strada 03 numar 03 telefon 03 oras Oricare ar fi modalitatea de descriere a structurii arborescente, aceasta trebuie s rmn n continuare neambigu. Dac se accept ca definiie a lungimii elementului de la nivelul i:
ni

lg( x , i )
j 0

( y , j , i 1)

(3.7)

unde prin xj nelegem elementul yj de pe nivelul i+1, astfel nct: cont(x) adr(y1) (3.8)

rezult c adresa unui element yj de pe nivelul i aparinnd datei de grup k dintr-o structur de tip articol, se obine prin relaia:

adr y j , 0

adr y1 , i

k 1 k 1

j 1

lg y k , i

k h 1

lg y h , i

(3.9)

De exemplu, pentru articolul definit prin: struct student { char nume[21]; data data_nasterii; data data_admiterii; }; adresa cmpului data_admiterii.an se obine urmrind modelul grafic din figura 3.2

51

student

nume

data_nasterii

data_admiterii

zi

luna

an

zi

luna

an

Fig. 3.2. Modelul grafic al structurii articolului student i dup formula: adr(student.data_admiterii.an,3)=adr(student.nume,2)+ + lg(student.nume,2)+ 1+ + lg(student.data_nasterii,2)+ 2+ + lg(student.data_nasterii.zi)+ 3+ + lg(student.data_nasterii.luna)+ 4+ + lg(student.nume,2)+ 21+ 1+4+4+4+ + 2+4+ 3+4+ 4=41+1=42 (3.10) pentru c 2 = 3 = 4 = 0. Dac se consider punctul operator ca orice alt operator, atunci construcia: a.b.c.d (3.11) este o expresie care se evalueaz ca orice alt expresie aritmetic, logic sau raional. O astfel de expresie o numim expresie referenial. Expresia referenial se evalueaz de la stnga la dreapta, pentru c n final trebuie s se obin o adres. Referirea oricrui operand se face fie prin numele su, dac abordarea este la nivel de limbaj, fie prin adres, dac analiza este din punctul de vedere al programatorului interesat s cunoasc mecanismele proprii execuiei programelor. Interpretarea expresiei 3.11 este: d este membru n structura de tip articol c c este membru n structura de tip articol b b este membru n structura de tip articol a ceea ce ca model grafic i corespunde: adr(a.b.c.d) = adr(a.b) + adr(b.c) + adr(c.d) 2*adr(a) adr(a.b.c.d) = adr(a) + adr(a.b) + adr(b.c) + adr(c.d) 3*adr(a) adr(a.b.c.d) = adr(a) + [adr(a.b) - adr(a)] + [adr(b.c) adr(a)]+[adr(c.d)-adr(a)] (3.12) Punerea corect n coresponden a baiilor ocupai de o structur de tip articol cu cmpurile acesteia, mai ales n cazul n care exist diferene generate de alocarea neoptimizat a memoriei i de apariia variabilelor k, permite interpretarea riguroas a coninutului fiecrui

52

cmp. Problematica devine cu att mai important cu ct variabilele de tip articol se utilizeaz pentru partiionarea memoriei alocate unor buffere utilizate n operaii de intrare/ieire. Neoptimizarea prelucrrilor este benefic cu condiia ca interpretarea grupului de baii s fie controlat de programator, chiar dac citirea lor are un alt ablon dect cel utilizat la scriere. 3.3. Vectori de structuri i structuri de vectori Vectorii de structurii sunt masive omogene, n sensul c fiecare element al masivului nu difer de cellalt, chiar dac n alctuirea lor intr cmpuri de naturi diferite. Construcia: typedef struct student { char nume[30]; int varsta; char facult[20]; int an_studiu; }; student x[20]; definete un vector x avnd 20 de componente; fiecare component este un articol ce conine cmpurile nume, facult, an_studiu. Modelul grafic al acestui tip de dat derivat este reprezentat n figura 3.3.
x

x[0]

x[1]

x[2]

x[14]

x[19]

nume

varsta

facult

an_studiu

nume

varsta

facult

an_studiu

Fig. 3.3. Modelul grafic asociat structurii vectorului de articole x Masivul x este unul unidimensional omogen, pentru c toate cele 20 de componente au aceeai structur. Referirea vrstei corespunztoare studentului al cincisprezecelea dintr-o grup se realizeaz prin: x[14].varsta n cazul n care cmpurile unui articol sunt elemente ale unui masiv unidimensional, definim o structur de vectori. De exemplu: 53

typedef struct student { char nume[30]; int note[10]; }; student x; Dac se dorete aflarea notei a 4-a a studentului x, referirea se efectueaz prin: x.nota[3] Se definesc vectori de structuri care conin vectori, respectiv vectori de structuri de vectori: stud y[20]; Pentru a referi nota a 5-a a studentului al 17-lea se face referirea: y[16].nota[4] Lucrurile iau amploare dac se definesc structuri de matrice i matrice de structuri. Astfel, dac ntr-o secie sunt 20 de muncitori i ntreprinderea are 30 de secii i pentru fiecare muncitor trebuie cunoscut: timpul lucrat, salariul orar, numele, definind: typedef struct muncitor { char nume[20]; long int salariu; int ore; }; long int salariu_total; muncitor muncit[30][20]; se calculeaz salariul total pentru muncitorul 15 din secia a 4-a astfel: salariu_total: = muncit[3][14].salariu*muncit[3][14].ore; (3.13) Dac matricea este privit ca vectori de vectori, extensiile sunt fcute pentru masivele tridimensionale ca vectori de matrice sau matrice de vectori, iar pentru masivele cu patru dimensiuni, ca masive bidimensionale de masive bidimensionale, sau vectori de masive tridimensionale sau masive tridimensionale de vectori. Generalizrile decurg din posibilitatea de a construi structuri de structuri i uneori de a introduce aspectul recursiv al descrierii. Secvena: typedef int a[5]; typedef a b[5]; b c[5];

54

corespunde descrierii: int c[5][5][5]; iar: typedef int x[5][5]; x y[5][5]; corespunde descrierii: int z[5][5][5][5]; Referirea elementelor se efectueaz dup aceleai reguli, specificnd valori ntre paranteze ptrate, n numr egal cu dimensiunea atribuit masivului, ca de exemplu: c[i][j][k] y[i][j][k][h] (3.14) (3.15)

Atributele sunt comutative n raport cu operatorul de referire. Un element din vectorul de structur se refer prin: nume_vector[i].nume_membru iar un element din structura de vectori se refer prin: nume_structur.nume_membru[i] (3.17) (3.16)

Se consider spre exemplificare urmtorul program care gestioneaz acionarii unei societi comerciale. #include <windows.h> #include <process.h> #include <ctype.h> #include <stdio.h> #include <conio.h> #include <string.h> struct data { int da_year,da_day,da_mon; }; struct adresa { char str[25]; int nr; char bloc[3];

55

char sc; int ap; int sect; long codp; char loc[25]; char jud[20]; }; struct ID { char seria[3]; long nr; data dataE; int circa; }; struct actionar { char cod[6]; char nume[30]; struct adresa adr; data dataN; struct ID BI; char reprez[6]; int nr_act; }; struct ind { char cheie[6]; long poz; }; struct nod { char sir[6]; nod *st; nod *dr; }; ind index[1000]; char *mes[40]={ "Data trebuie sa fie numerica!", "Ziua trebuie sa fie intre 1 si 31!", "Luna trebuie sa fie intre 1 si 12!", "Anul trebuie sa fie intre 1900 si 2001!", "", "Data trebuie sa fie cuprinsa intre 0 si 1!", "Serie incorecta!"

56

}; long lg_fis() { long lung; FILE *pf; pf=fopen("actionar.dat","rb"); fseek(pf, 0, SEEK_END); lung=ftell(pf); fclose(pf); return lung; } void insnod(nod *& rad,char *s,int &vb) { if(rad==NULL) { rad=new nod; strncpy(rad->sir,s,6); rad->st=rad->dr=NULL; } else if(strncmp(s,rad->sir,6) < 0) insnod(rad->st,s,vb); else if(strncmp(s,rad->sir,6) > 0) insnod(rad->dr,s,vb); else vb=1; } void del_arb(struct nod *T) { if (T!=NULL) { del_arb(T->st); del_arb(T->dr); delete T; } } int indexare() { FILE *pf; actionar act; if ((pf=fopen("actionar.dat","rb"))==NULL) { printf("Fiserul cu date despre actionari nu exista!"); getch(); exit(1); } int k=0; long pozitie; while(fread(&act,sizeof(struct actionar),1,pf))

57

{ pozitie=ftell(pf)-sizeof(struct actionar); strncpy(index[k].cheie,act.cod,6); index[k].poz=pozitie; k++; } fclose(pf); return k; } void sortare(int n) { struct ind temp; for(int i=0;i<n-1;i++) for(int j=i;j<n;j++) if(strcmp(index[i].cheie,index[j].cheie)>0) { temp=index[i]; index[i]=index[j]; index[j]=temp; } } int cautare(int n,int &poz,char *key) { int s,m,d; s=0; d=n; while(s<=d) { m=(s+d)/2; if (!strncmp(key,index[m].cheie,6)) { poz=m; return m; } else if (strncmp(key,index[m].cheie,6)<0) d=m-1; else s=m+1; } return -1; } void sortareN() { struct actionar act,act1;

58

FILE *pf,*pt; if((pf=fopen("actionar.dat","rb+"))==NULL) { printf("Fisierul cu date despre actionari nu exista!"); getch(); return; } pt=fopen("temp.dat","wb"); int vb,na=lg_fis()/sizeof(struct actionar); for(int i=0;i<na;i++) { fread(&act,sizeof(struct actionar),1,pf); fwrite(&act,sizeof(struct actionar),1,pt); } fclose(pt); fclose(pf); pt=fopen("temp.dat","rb+"); do { vb=0; fseek(pt,0,0); for(int i=0;i<na-1;i++) { fread(&act,sizeof(struct actionar),1,pt); fread(&act1,sizeof(struct actionar),1,pt); if (stricmp(act.nume,act1.nume)>0 ) { fseek(pt,ftell(pt)-2*sizeof(struct actionar),0); fwrite(&act1,sizeof(struct actionar),1,pt); fwrite(&act,sizeof(struct actionar),1,pt); vb=1; } fseek(pt,ftell(pt)-sizeof(struct actionar),0); } } while(vb==1); fclose(pt); } void eroare(int cod) { printf("\a%s",mes[cod-1]); Sleep(500); } void valid_nr_l(long &val,int cod) { int i,VB=0; do {

59

VB=0; char tab[255]; fflush(stdin); gets(tab); if (strlen(tab)==0) VB=1; else { i=0; while(VB==0&&i<strlen(tab)) { if(isdigit(tab[i])) i++; else VB=1; } } if(VB==0) sscanf(tab,"%ld",&val); else eroare(cod); } while(VB!=0); } void valid_nr_f(float &val,int cod) { int i,VB=0; do { VB=0; char tab[255]; fflush(stdin); gets(tab); if (strlen(tab)==0) VB=1; else { i=0; while(VB==0&&i<strlen(tab)) { if(isdigit(tab[i])) i++; else VB=1; } } if(VB==0)

60

sscanf(tab,"%f",&val); else eroare(cod); } while(VB!=0); } void valid_nr_i(int &val,int cod) { int i,VB=0; do { VB=0; char tab[255]; fflush(stdin); gets(tab); if (strlen(tab)==0) VB=1; else { i=0; while(VB==0&&i<strlen(tab)) { if(isdigit(tab[i])) i++; else VB=1; } } if(VB==0) sscanf(tab,"%d",&val); else eroare(cod); } while(VB!=0); } void valid_ab_sb(char val[],int cod) { int i,VB=0; do { VB=0; char tab[255]; fflush(stdin); gets(tab); if (strlen(tab)==0||strlen(tab)!=2) VB=1; else

61

{ i=0; while(VB==0&&i<2) { if(isalpha(tab[i])) i++; else VB=1; } } if(VB==0) strncpy(val,tab,3); else eroare(cod); } while(VB!=0); } void sortareA() { struct actionar act,act1; FILE *pf,*pt; if((pf=fopen("actionar.dat","rb+"))==NULL) { printf("Fisierul cu date despre actionari nu exista!"); getch(); return; } pt=fopen("temp.dat","wb"); int vb,na=lg_fis()/sizeof(struct actionar); for(int i=0;i<na;i++) { fread(&act,sizeof(struct actionar),1,pf); fwrite(&act,sizeof(struct actionar),1,pt); } fclose(pt); fclose(pf); pt=fopen("temp.dat","rb+"); do { vb=0; fseek(pt,0,0); for(int i=0;i<na-1;i++) { fread(&act,sizeof(struct actionar),1,pt); fread(&act1,sizeof(struct actionar),1,pt); if ((unsigned)act.nr_act<(unsigned)act1.nr_act) { fseek(pt,ftell(pt)-2*sizeof(struct actionar),0); fwrite(&act1,sizeof(struct actionar),1,pt);

62

fwrite(&act,sizeof(struct actionar),1,pt); vb=1; } fseek(pt,ftell(pt)-sizeof(struct actionar),0); } } while(vb==1); fclose(pt); } void creare() { int v; char c,key[6]; FILE *pf; actionar act; nod *RAD=NULL; if ((pf=fopen("actionar.dat","rb"))!=NULL) { printf("ATENTIE\n"); printf("Fiserul cu date despre actionari exista!\n"); printf("Datele existente se vor pierde!\n"); printf("Doriti sa-l rescrieti?[D/N]: "); do c=getch(); while(c!='D'&&c!='N'); if (c=='N') { return; } } if ((pf=fopen("actionar.dat","wb"))==NULL) { printf("Eroare la creare!"); exit(1); } else { printf("Cod: "); while(scanf("%6s",key)!=0) { v=0; insnod(RAD,key,v); if (v) { printf("\n\aArticolul cu aceasta cheie exista!"); Sleep(500); fflush(stdin); continue; } strncpy(act.cod,key,6);

63

fflush(stdin); printf("Nume: "); gets(act.nume); fflush(stdin); printf("Adresa actionarului\n"); printf("\tStrada: "); gets(act.adr.str); printf("\tNumarul: "); valid_nr_i(act.adr.nr,1); printf("\tBloc: "); scanf("%s",act.adr.bloc); fflush(stdin); printf("\tScara: "); scanf("%c",&act.adr.sc); printf("\tApartament: "); valid_nr_i(act.adr.ap,1); printf("\tSector: "); valid_nr_i(act.adr.sect,1); printf("\tCod postal: "); valid_nr_l(act.adr.codp,1); fflush(stdin); printf("\tLocalitatea: "); gets(act.adr.loc); printf("\tJudetul: "); gets(act.adr.jud); do { printf("Data nasterii\n"); printf("\tZiua:"); valid_nr_i(act.dataN.da_day,1); if(act.dataN.da_day<1||act.dataN.da_day>31) eroare(2); } while(act.dataN.da_day<1||act.dataN.da_day>31); printf("\tLuna:"); do { valid_nr_i(act.dataN.da_mon,1); if(act.dataN.da_mon<1||act.dataN.da_mon>12) eroare(3); } while(act.dataN.da_mon<1||act.dataN.da_mon>12); printf("\tAnul:"); do { valid_nr_i(act.dataN.da_year,1); if(act.dataN.da_year<1900||act.dataN.da_year>2001) eroare(4); }

64

while(act.dataN.da_year<1900||act.dataN.da_year>2001); printf("Buletin de identitate\n"); printf("\tSeria:"); valid_ab_sb(act.BI.seria,7); printf("\tNumarul:"); valid_nr_l(act.BI.nr,1); printf("Data eliberarii\n"); printf("\tZiua:"); do { valid_nr_i(act.BI.dataE.da_day,1); if(act.BI.dataE.da_day<1||act.BI.dataE.da_day>31) eroare(2); } while(act.BI.dataE.da_day<1||act.BI.dataE.da_day>31); printf("\tLuna:"); do { valid_nr_i(act.BI.dataE.da_mon,1); if(act.BI.dataE.da_mon<1||act.BI.dataE.da_mon>12) eroare(3); } while(act.BI.dataE.da_mon<1||act.BI.dataE.da_mon>12); printf("\tAnul: "); do { valid_nr_i(act.BI.dataE.da_year,1); if(act.BI.dataE.da_year<1900||act.BI.dataE.da_year>2001) eroare(4); } while(act.BI.dataE.da_year<1900||act.BI.dataE.da_year>2001); printf("\tCirca de politie: "); valid_nr_i(act.BI.circa,1); printf("Numarul de actiuni detinute: "); valid_nr_i(act.nr_act,1); printf("\n"); fwrite(&act,sizeof(struct actionar),1,pf); printf("Cod: "); } } del_arb(RAD); fclose(pf); }

65

void creare_date() { char c; float ksoc,pfn,cota; long int val_A; FILE *pd; if ((pd=fopen("date.dat","rb"))!=NULL) { printf("ATENTIE\n"); printf("Fiserul cu date despre profit exista!\n"); printf("Datele existente se vor pierde!\n"); printf("Doriti sa-l rescrieti?[D/N]: "); do c=getch(); while(c!='D'&&c!='N'); if (c=='N') { return; } } if ((pd=fopen("date.dat","wb"))==NULL) { printf("Eroare la creare!"); getch(); return; } printf("DATE PRIVIND CAPITALUL SOCIAL SI PROFITUL\n"); printf("Valoarea unei actiuni:"); valid_nr_l(val_A,1); fwrite(&val_A,sizeof(long int),1,pd); printf("Capital Social:"); valid_nr_f(ksoc,1); fwrite(&ksoc,sizeof(float),1,pd); printf("Profit net:"); valid_nr_f(pfn,1); fwrite(&pfn,sizeof(float),1,pd); do { printf("Cota din profit repartizata la dividende [subunitara]:"); scanf("%f",&cota); if (cota>1||cota <0) eroare(6); } while(cota>1||cota <0); fwrite(&cota,sizeof(float),1,pd); fclose(pd); } void afis_date(const actionar &act) {

66

printf("\n\nCod: %s\n",act.cod); printf("Nume: "); puts(act.nume); printf("Adresa actionarului\n"); printf("\tStrada:"); puts(act.adr.str); printf("\tNumarul: %d\n",act.adr.nr); printf("\tBloc: %s ",act.adr.bloc); printf("Scara: %c Apartament: %d Sector: %d\n",act.adr.sc,act.adr.ap,act.adr.sect); printf("\tCod postal: %ld\n",act.adr.codp); printf("\tLocalitatea: ");puts(act.adr.loc); printf("\tJudetul: "); puts(act.adr.jud); printf("Data nasterii: %d/%d/%d\n",act.dataN.da_day,act.dataN.da_mon,act.dataN.da_year); printf("Buletin de identitate\n"); printf("\tSeria: %s",act.BI.seria); printf(" Numarul: %ld\n",act.BI.nr); printf("\tData eliberarii: %d/%d/%d\n",act.BI.dataE.da_day,act.BI.dataE.da_mon,act.BI.dataE.da_year); printf("\tCirca de politie: %d\n",act.BI.circa); printf("Numarul de actiuni detinute: %u\n",act.nr_act); printf("\n"); } void cons() { int n; char key[6]; int vb,i,c; actionar act; n=indexare(); sortare(n); FILE *pf=fopen("actionar.dat","rb"); while(printf("\nCod actionar:"),(c=scanf("%6s",key))!=0) { vb=cautare(n,i,key); if(vb==-1) { printf("\n\aArticolul cu aceasta cheie nu exista!\n"); Sleep(500); fflush(stdin); } else { fseek(pf,index[i].poz,0); fread(&act,sizeof(struct actionar),1,pf); afis_date(act); getch(); }

67

} fclose(pf); } void modif() { int n; char key[6]; int vb,i,nr; actionar act; n=indexare(); sortare(n); FILE *pf=fopen("actionar.dat","rb+"); while(printf("\nCod actionar:"),scanf("%6s",key)!=EOF) { vb=cautare(n,i,key); if(vb==-1) { printf("\n\aArticolul cu aceasta cheie nu exista!"); Sleep(500); } else { fseek(pf,index[i].poz,0); fread(&act,sizeof(struct actionar),1,pf); afis_date(act); printf("Noul numar:"); valid_nr_i(nr,1); act.nr_act=nr; fseek(pf,index[i].poz,0); fwrite(&act,sizeof(struct actionar),1,pf); } } fclose(pf); } void listA() { FILE *pt,*ptxt; unsigned int tot_a=0; long int val_a; float ksoc; actionar act; sortareA(); FILE *pd=fopen("date.dat","rb"); fread(&val_a,sizeof(long int),1,pd); fread(&ksoc,sizeof(float),1,pd); fclose(pd); pt=fopen("temp.dat","rb"); while(fread(&act,sizeof(struct actionar),1,pt)) tot_a+=act.nr_act;

68

rewind(pt); ptxt=fopen("lista1.txt","w"); fprintf(ptxt,"\n\n\n\t\tLista actionarilor dupa numarul de actiuni\n"); fprintf(ptxt,"\t\t******************************************\n\n"); fprintf(ptxt,"********************************************************** *************\n"); fprintf(ptxt,"* Cod * Nume Prenume * Numar * Ponderea actiunilor*\n"); fprintf(ptxt,"* * * actiuni * in capitalul social*\n"); fprintf(ptxt,"********************************************************** *************\n"); while(fread(&act,sizeof(struct actionar),1,pt)) { fprintf(ptxt,"*%6s *%-30s*%8u *%18.3f %%*\n",act.cod,act.nume,act.nr_act,(float)(unsigned)act.nr_act*val_a/ksoc*100); fprintf(ptxt,"********************************************************** *************\n"); } fprintf(ptxt,"********************************************************** *************\n"); fclose(pt); fclose(ptxt); } void listD() { FILE *pt,*ptxt; SYSTEMTIME data; float pfn,cota; float div; actionar act; sortareA(); unsigned int tot_a=0; FILE *pd=fopen("date.dat","rb"); fseek(pd,sizeof(long int)+sizeof(float),0); fread(&pfn,sizeof(float),1,pd); fread(&cota,sizeof(float),1,pd); fclose(pd); pt=fopen("temp.dat","rb"); while(fread(&act,sizeof(struct actionar),1,pt)) tot_a+=act.nr_act; div=(pfn*cota)/tot_a; rewind(pt); ptxt=fopen("lista2.txt","w"); GetSystemTime(&data); fprintf(ptxt,"\n\t\t\t\tDividende cuvenite actionarilor la data %d.%d.%d\n",data.wDay,data.wMonth,data.wYear); fprintf(ptxt,"\t\t\t\t************************************************\n\n") ; fprintf(ptxt,"Profit net:%.0f lei\n",pfn);

69

fprintf(ptxt,"Cota de profit repartizata la dividende:%.2f\n",cota); fprintf(ptxt,"Total actiuni:%u\n",tot_a); fprintf(ptxt,"Dividende per actiune:%.2f lei/act\n\n",div); fprintf(ptxt,"********************************************************** **************************************************************\n"); fprintf(ptxt,"* Nume Prenume * Data * Adresa *Buletin de * Numar * Dividende *\n"); fprintf(ptxt,"* * nasterii * *identitate * actiuni* *\n"); fprintf(ptxt,"********************************************************** **************************************************************\n"); while(fread(&act,sizeof(struct actionar),1,pt)) { fprintf(ptxt,"*%-30s*%2d.%2d.%d*Str:%-29s Nr:%-5d*%-2s %8ld*%8u*%11.2f *\n",act.nume,act.dataN.da_day,act.dataN.da_mon,act.dataN.da_year,act.adr.str,act.ad r.nr,act.BI.seria,act.BI.nr,act.nr_act,(unsigned)act.nr_act*div); fprintf(ptxt,"* * *Bl:%c%c%c Sc:%c Ap:%-3d Sect:%-2d Cod p:%-8ld * * * *\n",act.adr.bloc[0],act.adr.bloc[1],act.adr.bloc[2],act.adr.sc,act.adr.ap,act.adr.sect,act.a dr.codp); fprintf(ptxt,"* * *Loc:%-20s * * * *\n",act.adr.loc); fprintf(ptxt,"********************************************************** **************************************************************\n"); } fprintf(ptxt,"********************************************************** **************************************************************\n"); fclose(pt); fclose(ptxt); } void listAB() { FILE *pt,*ptxt; actionar act; sortareN(); pt=fopen("temp.dat","rb"); ptxt=fopen("lista3.txt","w"); fprintf(ptxt,"\n\n\n\t\t\tLista actionarilor in ordine alfabetica\n"); fprintf(ptxt,"\t\t\t***************************************\n\n"); fprintf(ptxt,"\t\t**************************************************\n"); fprintf(ptxt,"\t\t* Nr. * Nume Prenume * Numar *\n"); fprintf(ptxt,"\t\t* crt. * * actiuni *\n"); fprintf(ptxt,"\t\t**************************************************\n"); int i=0; while(fread(&act,sizeof(struct actionar),1,pt)) {

70

fprintf(ptxt,"\t\t*%6d *%-30s*%8u *\n",++i,act.nume,act.nr_act); fprintf(ptxt,"\t\t**************************************************\n"); } fprintf(ptxt,"\t\t**************************************************\n"); fclose(pt); fclose(ptxt); } void do_sterg() { int n,c; int vb,i; actionar act; n=indexare(); sortare(n); char key[6]; FILE *pf=fopen("actionar.dat","rb"); while(printf("\nCod actionar:"),scanf("%6s",key)!=0) { vb=cautare(n,i,key); if(vb==-1) { printf("\n\aArticolul cu aceasta cheie nu exista!\n"); Sleep(500); fflush(stdin); continue; } else { fseek(pf,index[i].poz,0); fread(&act,sizeof(struct actionar),1,pf); afis_date(act); printf("Doriti sa stergeti acest articol?[D/N]: "); do { c=getch(); } while(c!='D'&&c!='N'); if (c=='N') { continue; } } //rescriere fisier FILE *ptmp=fopen("tmp.dat","wb"); rewind(pf); for(int j=0;j<index[i].poz/sizeof(struct actionar);j++) { fread(&act,sizeof(struct actionar),1,pf); fwrite(&act,sizeof(struct actionar),1,ptmp);

71

} fread(&act,sizeof(struct actionar),1,pf); while(fread(&act,sizeof(struct actionar),1,pf)) fwrite(&act,sizeof(struct actionar),1,ptmp); fclose(pf); fclose(ptmp); unlink("actionar.dat"); rename("tmp.dat","actionar.dat"); n=indexare(); sortare(n); pf=fopen("actionar.dat","rb"); } fclose(pf); } void adaug() { int v; char key[6]; actionar act; nod *RAD=NULL; FILE *pf; if ((pf=fopen("actionar.dat","rb+"))==NULL) { printf("Fiserul cu date despre actionari nu exista!"); return; } else { strncpy(act.reprez," ",6); while(fread(&act,sizeof(struct actionar),1,pf)) insnod(RAD,act.cod,v); //fseek(pf,0,2); printf("Cod:\n"); while(scanf("%6s",key)!=0) { v=0; insnod(RAD,key,v); if (v) { printf("\n\aArticolul cu aceasta cheie exista!\n"); Sleep(500); fflush(stdin); continue; } strncpy(act.cod,key,6); fflush(stdin); printf("Nume: "); gets(act.nume); fflush(stdin);

72

printf("Adresa actionarului\n"); printf("\tStrada: "); gets(act.adr.str); printf("\tNumarul: "); valid_nr_i(act.adr.nr,1); printf("\tBloc: "); scanf("%s",act.adr.bloc); fflush(stdin); printf("\tScara: "); scanf("%c",&act.adr.sc); printf("\tApartament: "); valid_nr_i(act.adr.ap,1); printf("\tSector: "); valid_nr_i(act.adr.sect,1); printf("\tCod postal: "); valid_nr_l(act.adr.codp,1); fflush(stdin); printf("\tLocalitatea: "); gets(act.adr.loc); printf("\tJudetul: "); gets(act.adr.jud); do { printf("Data nasterii\n"); printf("\tZiua:"); valid_nr_i(act.dataN.da_day,1); if(act.dataN.da_day<1||act.dataN.da_day>31) eroare(2); } while(act.dataN.da_day<1||act.dataN.da_day>31); printf("\tLuna:"); do { valid_nr_i(act.dataN.da_mon,1); if(act.dataN.da_mon<1||act.dataN.da_mon>12) eroare(3); } while(act.dataN.da_mon<1||act.dataN.da_mon>12); printf("\tAnul:"); do { valid_nr_i(act.dataN.da_year,1); if(act.dataN.da_year<1900||act.dataN.da_year>2001) eroare(4); } while(act.dataN.da_year<1900||act.dataN.da_year>2001); printf("Buletin de identitate\n"); printf("\tSeria:");

73

valid_ab_sb(act.BI.seria,7); printf("\tNumarul:"); valid_nr_l(act.BI.nr,1); printf("Data eliberarii\n"); printf("\tZiua:"); do { valid_nr_i(act.BI.dataE.da_day,1); if(act.BI.dataE.da_day<1||act.BI.dataE.da_day>31) eroare(2); } while(act.BI.dataE.da_day<1||act.BI.dataE.da_day>31); printf("\tLuna:"); do { valid_nr_i(act.BI.dataE.da_mon,1); if(act.BI.dataE.da_mon<1||act.BI.dataE.da_mon>12) eroare(3); } while(act.BI.dataE.da_mon<1||act.BI.dataE.da_mon>12); printf("\tAnul: "); do { valid_nr_i(act.BI.dataE.da_year,1); if(act.BI.dataE.da_year<1900||act.BI.dataE.da_year>2001) eroare(4); } while(act.BI.dataE.da_year<1900||act.BI.dataE.da_year>2001); printf("\tCirca de politie: "); valid_nr_i(act.BI.circa,1); printf("Numarul de actiuni detinute: "); valid_nr_i(act.nr_act,1); printf("\n"); fwrite(&act,sizeof(struct actionar),1,pf); printf("Cod: "); } } del_arb(RAD); fclose(pf); } void men_fis() { int rasp,cont=1; while(cont) {

74

do { printf("MENIUL FISIERE\n\n"); printf("1.CREARE FISIER ACTIONARI\n"); printf("2.CREARE FISER CU DATE PRIVIND PROFITUL\n" ); printf("3.ADAUGARE ACTIONARI\n"); printf("4.MODIFICARE NUMAR DE ACTIUNI\n"); printf("5.STERGERE ACTIONARI DIN FISIER\n"); printf("6.REVENIRE LA MENIUL PRINCIPAL\n\n"); printf("Optiunea:"); valid_nr_i(rasp,5);//scanf("%d",&rasp); } while(rasp<1||rasp>6); switch(rasp) { case 1:creare();break; case 2:creare_date();break; case 3:adaug();break; case 4:modif();break; case 5:do_sterg();break; case 6:cont=0;break; } } } void men_sit() { int rasp,cont=1; while(cont) { do { printf("MENIUL SITUATII DE IESIRE\n\n"); printf("1.LISTA ACTIONARILOR IN ORDINEA NUMARULUI DE ACTIUNI\n"); printf("2.LISTA ACTIONARILOR IN ORDINE ALFABETICA\n"); printf("3.LISTA DIVIDENDELOR CUVENITE FIECARUI ACTIONAR\n"); printf("4.CONSULTARE FISIER DUPA COD ACTIONAR\n"); printf("5.REVENIRE LA MENIUL PRINCIPAL\n\n"); printf("Optiunea:"); valid_nr_i(rasp,5);//scanf("%d",&rasp); } while(rasp<1||rasp>5); switch(rasp) { case 1:listA();break; case 2:listAB();break; case 3:listD();break; case 4:cons();break;

75

case 5:cont=0;break; } } } void main() { int rasp,cont=1; while(cont) { do { printf("MENIUL PRINCIPAL\n\n"); printf("1.FISIERE\n"); printf("2.SITUATII DE IESIRE\n"); printf("3.TERMINARE PROGRAM\n\n"); printf("Optiunea:"); valid_nr_i(rasp,5);//scanf("%d",&rasp); } while(rasp<1||rasp>3); switch(rasp) { case 1:men_fis();break; case 2:men_sit();break; case 3:cont=0;break; } } } Programatorul trebuie s realizeze un echilibru ntre creterea numrului de dimensiuni, reducerea gradului de umplere i complexitatea expresiilor asociate calculelor de deplasare, pentru a localiza fiecare element al structurii pe care o definete. Acest echilibru conduce n final la reducerea duratei de prelucrare i la obinerea lizibilitii bune a programului. Teme de control 1. S se implementeze conceptul de matrice rar i operaia de adunare folosind un vector de structuri. 2. Pentru structura carte, s se defineasc atributele corespunztoare necesare reprezentrii unei biblioteci structurate pe categorii, folosind masive multidimensionale. Bibliografie Ion IVAN, Marius POPA, Paul POCATILU - Structuri de date, Vol. 1 si 2, Editura ASE, Bucuresti, 2008.

76

4. Fiiere
Obiectivele unitii de nvare Dup studierea acestei uniti de nvare, studenii i vor nsui cunotine teoretice i vor deprinde abiliti practice de lucru fiiere binare si text. Studenii vor nva lucrul cu funcii de acces la resursele memorate pe suporturile externe de date. 4.1. Structuri de date externe Masivele, listele i arborii binari, sunt structuri de date interne, prin faptul c se afl n memoria intern a unui sistem de calcul. n momentul n care aceste structuri sunt stocate n memoria extern pe discuri, benzi magnetice, dischete, compact-discuri acestea sunt numite structuri de date externe. Memoria intern a unui calculator este imaginat ca un ir ordonat de baii. Fiecare bait se definete prin adres i prin coninut. Memoria extern este imaginat, de asemen ea, ca un ir de baii, cu deosebirea c pentru calculul adresei sunt luate n considerare elementele specifice modului de structurare a suportului. Astfel, atunci cnd suportul este organizat n piste, cilindri i problematica accesului este rezolvat prin poziionarea pe aceste uniti de acces, adresarea efectundu-se lund n calcul elemente structurale. n plus, particularitile de desfurare n plan fizic a operaiilor de intrare/ieire determin analiza distinct a raportului dintre semnificaia instruciunilor I/O din programe i operaiile efective de citire/scriere de pe suportul extern. Presupunnd c pentru un sistem de calcul modulele care realizeaz citiri/scrieri opereaz cu coninutul unor buffere de lungime L, tot timpul aceste funcii furnizeaz informaii asupra nceputului zonei ce este explorat, lungimea acesteia fiind rezultatul logicii de organizare a datelor. Programatorul are acces direct sau indirect la zona de memorie tampon prin intermediul adresei sale de nceput sau prin intermediul unei alte zone de memorie definit n programul su, zon n care este copiat coninutul bufferului.
2 5 6
c i

adresa de nceput buffer

Memoria extern

Operaii de
t i r e / s c r i e r e

zona de memorie definit de


u t i l i z a t o r

Fig. 4.1. Operaii de intrare/ieire

77

Dinamica operaiilor de intrare/ieire determin actualizarea variabilei pointer care delimiteaz partea de nceput a subzonei din buffer ce este copiat n zona de memorie definit de utilizator. n cazul funciilor de citire/scriere a unui caracter, variabila pointer se modific cu o unitate, marcnd exact schimbul de informaii n dialogul om calculator . n cazul citirilor/scrierilor cu format delimitatorul acceptat este analizat, iar algoritmul de punere n coresponden este astfel proiectat nct acesta nu este integrat n setul informaiilor. 1 5 CR 1 2 0 CR 1 CR 1 CR

algoritm conversie alfanumericntreg

algoritm conversie alfanumericntreg

algoritm conversie alfanumericntreg

algoritm conversie alfanumericreal

variabila A

variabila B

variabila C

variabila D

Fig. 4.2. Punerea n coresponden a valorilor citite irurile de caractere sunt delimitate prin CR , iar algoritmul de parcurgere a bufferului determin un astfel de coninut al variabilei pointer nct este transmis ca parametru funciilor de conversie nceputul fiecrei subzone de buffer care ncepe dup CR . Modul cum este concretizat CR ca delimitator de sfrit de ir i modul cum este definit descriptorul de format imprim structurii de parametri ai funciilor de conversie anumite particulariti. Se observ c dinamica variabilei pointer este influenat de tipul operaiei de intrare/ieire. Acest aspect explic de ce este necesar efectuarea avansului acestuia, cnd se alterneaz citiri cu format cu citiri fr format. La operaii neomogene exist modaliti neomogene de definire i de tratare a delimitatorilor de sfrit de ir ca rezultat al activrii tastei CR . Ceea ce pare simplu n cazul structurilor de date interne, nu devine mai complicat n cazul structurilor de date externe, att timp ct sunt clarificate chestiunile legate de variabilele pointer asociate bufferelor i de faptul c o citire fizic efectiv nu nseamn neaprat o citire logic, iar la scriere se ntmpl acelai lucru. Prin operaia logic, nelegem aciunea ce corespunde unei apelri de funcie citire/scriere din program. De exemplu, pentru zona de lungime minim L = 256 octei, ce este scris/citit la o singur operaie fizic efectiv pe/de pe suport, la scrierea pe suportul extern a trei variabile de tip articol, A, B i C cu: lg(A) = 120 octei lg(B) = 110 octei lg(C) = 200 octei 78

variabila pointer permite preluarea datelor din structura A, se majoreaz cu 120, preia datele din structura B i realizeaz o scriere fizic pe suport. Variabila pointer este apoi reiniializat i va prelua datele structurii C dup care efectueaz a doua scriere fizic. n acest caz celor trei scrieri logice le-au corespuns dou scrieri fizice. Exist diferite modaliti de realizare a operaiilor de citire/scriere dup cum se folosesc sau nu factori de blocare, se definesc sau nu elemente de regsire a informaiilor. Structurile de date externe nu difer mult de structurile de date interne. Apar unele complicaii care nu sunt majore de altfel, prin aceea c volumul datelor este foarte mare i elementele repetitive abund, ceea ce conduce la ideea c structurile de date externe sun t privite ca structuri de structuri de date interne dispuse pe suport de memorie extern. Pentru a realiza regsirea ntr-un volum de date de dimensiuni remarcabile, este necesar organizarea, sistematizarea datelor i crearea acelor elemente indispensabile localizrii, adresrii. Structurile de date externe sunt contigue, formate din elemente dispuse n continuarea celorlalte i necontigue, distana dintre elemente fiind o variabil aleatoare a crei lege de repartiie este identificabil, dar care necesit memorarea distanelor, ntruct nu se construiete un mecanism de generare cu repetare a acestora. Structurile de date externe se regsesc n cele mai multe cazuri sub denumirea de fiiere. Cnd ating un nivel de structurare prin adrese suficient de dezvoltat, se formeaz fiiere interdependente, iar n cazul unor structuri mai complexe se regsesc sub denumirea de baze de date. n cazul n care coninutul de lungime L este tratat distinct, se ia n discuie conceptul de nregistrare fizic. Se pornete de la faptul c ntr-un buffer, de regul sunt stocate datele ce corespund unei structuri de tip articol, ce se recunosc sub denumirea de nregistrare logic sau articol logic. Scopul este de a face deosebirea ntre modul n care se dispun informaii le pe suportul fizic i modul n care sunt gndite organizrile de date n raport cu prelucrrile particulare. Introducerea factorilor de blocare vine s complice lucrurile, dar s mbunteasc indicele de folosire a zonelor de memorie. Dac: L = lg (structura de date de tip articol) i dac exist:
k L L'

L (4.1)

(4.2)

unde parantezele drepte nseamn partea ntreag a expresiei, raportul k reprezint o expresie mai simplificat a factorului de blocare. De exemplu, dac definim o structur de tip articol, ce conine cmpuri ce conduc la o lungime de 80 octei i L = 256 octei:
k 256 80 3,2 3

(4.3)

79

n cazul n care k = 1, pe cei 256 baii ai bufferului este ncrcat un articol, ce este n fiier. Deci fiierul conine n final n articole fizice i tot n articole logice, gradul de ncrcare cu informaie util fiind:
g1 n 80 100 n 256 10 100 32 30%

(4.4)

n cazul n care k = 2:
g2 n / 2 m 160 100 n 256

(4.5)

unde:

0, daca n e numar par 1, daca n e numar impar


Pentru k = 3:
g3 n 3 m 240 100 n 256

(4.6)

Se observ c:
g3 g2 g1

(4.7)

Se vorbete de factorul de blocare optim care se stabilete pentru fiecare tip de memorie extern i lungime de structur de date de tip articol, ce urmeaz a fi memorat n fiier. n continuare, lund n considerare numai aspectele care in de modul de stocare a informaiei, strict dependent de aplicaia programatorului, se fac urmtoarele specificaii: - se consider fiierul ca structur de date contigu dac informaiile utile sunt dispuse unele n continuarea celorlalte, fr baii care s le separe; - se consider fiierul ca structur de date regulat necontigu dac ntre toate articolele sau ntre grupuri de articole, avnd numr fix, exist baii nefolosii, n acelai numr; exist posibilitatea de a construi o formul de calcul a adresei articolului k, pornind de la adresa altui articol j; - se consider structuri de date necontigue fiierele ale cror articole sunt dispuse unele fa de celelalte, la distane care sunt variabile aleatoare. Trecerea de la memoria intern la memoria extern, ia n considerare modul de organizare al fiecrui suport. Dac la nivelul memoriei interne aceasta este privit ca un vector, n cazul suporturilor externe de informaie organizate pe piste baiii sunt privii ca avnd dispunerea asemeni elementelor unei matrice. Linia indic pista pe care se afl baitul, iar coloana indic poziia baitului pe pist. Organizarea pe sectoare determin luarea n considerare a unei matrice tridimensionale. Pentru fiecare suport realizatorii pun la dispoziie formulele de calcul ale adreselor cu luarea n considerare a elementelor de structur a suportului fizic. Problema fragmentrii informaiilor determin stocarea de date necesare localizrii prii ce se continu ntr-o alt zon a suportului.

80

Pentru simplificarea prezentrii se consider un suport extern S, cruia i se asociaz un model matriceal de dispunere a baiilor. Baitul bij reprezint baitul al j-lea, aflat pe pista i a suportului. Suportul are n piste, iar pe o pist se afl m baii. Pentru nceput se presupune c baiii au aceeai destinaie, respectiv de a memora informaii utile. Nu exist baii care stocheaz informaii privind structura suportului i modul de ocupare a acestuia. 4.2. Criterii de clasificare a fiierelor i n cazul clasificrii fiierelor, asemenea datelor interne, exist o multitudine de criterii, fiecare fiier fiind clasificat cu unul sau mai multe atribute ce corespund criteriilor de clasificare. a) Criteriul lungimii articolelor ce alctuiesc fiierul le mparte n: - fiiere cu articole de lungime fix sunt formate din elemente avnd aceeai lungime; fiierele sunt asemntoare vectorilor ca structuri de date interne; elementele sunt omogene i sunt dispuse unele n continuarea celorlalte; - fiierele cu articole de lungimi diferite, dar cunoscute se consider m tipuri de articole, avnd fiecare lungimea l1, l2, , lm; fiierul conine aceste elemente dispuse ntr-o anumit ordine sau n ordine oarecare; n ultima situaie este necesar memorarea de informaii care s permit identificarea tipurilor de articole i lungimea acestora; - fiiere cu articole de lungime diferit, dar necunoscut ceea ce se cunoate este legat de faptul c lungimile articolelor se afl cuprinse ntre dou limite: lungimile articolelor sunt variabile aleatoare, aparinnd unui interval definit; n mod obligatoriu, primul cmp conine lungimea articolului. b) Criteriul informaiilor ce definesc regulile referitoare la dispunerea elementelor, mparte fiierele n: - fiiere avnd ca singur mod de dispunere poziia articolelor; - fiiere cu elemente sortate dup un cmp numit cheie a articolului, n funcie de care se face reperarea n fiier. c) Criteriul informaiilor de localizare a articolelor care alctuiesc fiierul; - fiiere care nu conin informaii asupra poziiei articolelor singura modalitate de a selecta un articol este parcurgerea tuturor articolelor care l preced; - fiiere care au definite zone ce conin informaii referitoare la adresele unor grupe i subgrupe de articole pentru a identifica un anumit element se localizeaz grupul i apoi subgrupul de articole; o dat identificat subgrupul, selectarea elementului cutat este rezultatul parcurgerii articol de articol pn la gsirea respectivului element; - fiiere n care elementele conin informaii ce permit conturarea de liste nlnuite sau arbori pe supori de memorie extern complexitatea legturilor dintre articolele fiierului determin un volum de informaie privind adresele articolelor cu care un element intr ntr-o anumit relaie. d) Criteriul operaiilor ce sunt efectuate de fiiere determin gruparea acestora n: - fiiere destinate scrierii datelor; - fiiere destinate citirii datelor; - fiiere destinate efecturii operaiilor de actualizare. Oricare dintre fiierele unei grupe, n raport cu scopurile prelucrrii i modific atributele. De exemplu, un fiier care se creeaz este destinat scrierii datelor. La

81

consultare acelai fiier aparine grupei a doua, iar dac naintea consultrii se efectueaz actualizri, acelai fiier aparine celei de a treia grupe. e) Criteriul gestiunii fiierelor ia n considerare existena unui sistem de funcii de prelucrare care asigur efectuarea operaiilor specifice lucrului cu fiiere, numit sistem de gestiunea fiierelor. Acest criteriu mparte mulimea fiierelor n: - fiiere care sunt prelucrate prin apelarea de funcii ale unui sistem de gestiune rezolv ntreaga problematic legat de organizarea i accesul la date; funciilor sistemului de gestiune a fiierelor le corespund instruciuni; parametrii instruciunilor devin parametrii reali ai funciilor sistemului de gestiune; programatorul abordeaz ntreaga problematic numai din punct de vedere logic al rezolvrii problemei sale; - fiiere pentru care sunt definite funcii primitive de realizare a operaiilor elementare cu datele destinate suporturilor externe programatorului i revine sarcina s gestioneze totalitatea informaiilor necesare regsirii elementelor ce alctuiesc fiierul; pentru programator, fiierul este o mas amorf de baii, avnd coninut i poziii; el efectueaz transferuri de baii din zone de memorie spre suportul extern, indicnd adrese acestor zone, lungimea zonei i un mod de reparare a fiierului; exist situaii n care se efectueaz lucru direct n bufferul asociat fiierului cu care se lucreaz; n acest caz fiierul apare ca o resurs iar gestiunea acestei resurse este lsat integral la dispoziia programatorului; - fiiere care se construiesc i se utilizeaz folosind instruciunile limbajului de asamblare programatorul definete totalitatea condiiilor pentru efectuarea operaiilor cu fiiere; sunt definite i ncrcate etichetele care sunt scrise; se definesc bufferele i se gestioneaz; se calculeaz adresele de pe suportul extern unde vor fi scrise datele; programatorul preia n programele sale tot ceea ce efectueaz funciile primitive sau sistemul de gestiune a fiierelor; programul n care apar fiierele gestionate de programator nu apeleaz alte funcii dect cele scrise de acesta i folosete numai instruciuni ale limbajului de asamblare, cel mult seturi de macroinstruciuni. f) Criteriul organizrii fiierelor, ia n considerare existena sau inexistena unor informaii de regsire a datelor, dup cum urmeaz: - fiierele cu organizarea secvenial - succesiune contigu de articole fr existena unor elemente de informare, altele dect delimitatorii de nceput i de sfrit; dac se ia n considerare similitudinea acestui mod de organizare cu nregistrarea pe o caset a lagrelor unei formaii rock, avem imaginea clar a tuturor posibilitilor de lucru cu fiierele secveniale; accesul la un lagr presupune audierea celor care-l preced; tergerea unui lagr, altul dect cel de la sfrit presupune existena a dou casete; nregistrarea unui nou lagr, se face numai dup ultimul lagr anterior; - fiierele nsoite de informaii de tip index presupun sortarea articolelor dup chei i mprirea acestora n submulimi; punerea n coresponden a elementelor din submulimi cu adresele fizice determin realizarea ntr-un fiier a unei mulimi de subfiiere secveniale; cu ajutorul indecilor se identific subfiierul secvenial i articolul cutat este preluat dup ce a fost localizat prin parcurgerea articol dup articol a subfiierului; operaiile de actualizare, vizeaz tergerea de articole, adugarea de articole la sfritul fiierului sau subfiierelor, modificarea de cmpuri, rescrierea de articole i inserarea de articole; subfiierele sunt masive unidimensionale contigue, formate din articole; tergerea este o operaie de dezactivare a elementelor, fr realizarea deplasrii celorlalte elemente cu o poziie spre stnga, deci fizic tergerea unui articol nu se produce, operaia fiind

82

numai la nivel logic, n sensul c accesul la date este precedat de un test asupra caracterului activ sau neactiv al articolului; inserarea de articole, pentru a menine criteriul ordonrii elementelor care a determinat mprirea mulimii n submulimi de articole, presupune realizarea de liste nlnuite; articolul inserat este dispus ntr-o zon disponibil numit folcloric, zona de depire. - fiiere ale cror articole sunt dispuse aleator pe suport cunoscndu-se dimensiunile fiierului se aloc o zon pe suportul extern printr-o operaie numit preformare; datele sunt puse n coresponden printr-un procedeu oarecare cu adresele unde vor fi scrise; rezult c procedeul de punere n coresponden este cu att mai performant cu ct el realizeaz o dispunere mai uniform a articolelor n zona rezervat; exist posibilitatea ca folosind algoritmi de randomizare s se obin elemente ce intr n calculul adresei fizice a nceputului zonei din suportul extern n care se scrie un articol; pornind de la imposibilitatea s se genereze numere diferite care s ndeplineasc condiia de apartenen la subintervale, pe msur ce se scriu articole n fiiere, are loc frmiarea zonei rezervate; se construiesc funcii pentru gestionarea articolelor care au aceeai adres fizic prin calcul; aceste fiiere cu organizarea aleatoare, permit efectuarea ntregii game de operaii, cu condiia ca mecanismul de obinere a adresei fizice s se menin neschimbat; fiierul organizat aleator apare ca o structur necontigu n care fiecare element este localizat pe baza unei formule, avnd ca parametru valoarea unui cmp ce intr n structura articolului, cmp numit cheia articolului; numeroasele lucrri care prezint limbajul COBOL, redau imagini sugestive ale modului n care se implementeaz filozofia fiecrui mod de organizare a fiierelor, deci realitatea este alta, dac se are n vedere organizarea matriceal a suportului i modul static n multe cazuri de alocare a memoriei externe. g) Criteriul suportului unde este stocat fiierul mparte fiierele n: - fiiere pe cartele perforate; - fiiere pe band perforat; - fiiere pe band magnetic; - fiiere pe suport optic; - fiiere pe disc; - fiiere pe dischete; - fiier n memoria intern. Prelucrrile moderne, nengrdite de restriciile lipsei de memorie intern, au condus la citirea unui fiier ca un singur articol foarte mare i prelucrarea acestuia n memorie. Logic apare o singur instruciune de citire, deci apelarea o singur dat a funciei n realitate au loc:

cf

Lf L

(4.8)

citiri fizice, unde: cd - citiri fizice din fiier; Lf - lungimea fiierului, dat n baii; L - lungimea unei nregistrri fizice la o citire; m - variabila boolean ce este 0, dac Lf este divizibil prin L i 1 n caz contrar. h) Criteriul privind modul de efectuare a operaiilor de intrare/ieire grupeaz fiierele n: - fiiere de tip stream n care datele la scriere sau la citire sunt cmpuri elementare sau alte tipuri structuri de date, constituite ntr-un ir, cu indicarea formatului dup

83

care se fac mai nti conveniile i apoi se stabilesc lungimile irurilor care vor fi scrise pe suport; fiierul apare ca o succesiune de elemente oarecare ale cror poziii sunt deduse, dac se iau n calcul informaiile pe care le ofer descriptorii de format i locul pe care fiecare element l ocup n lista de parametri ai funciei apelate la scriere; este de dorit ca aceleai liste de variabile de la scriere s fie utilizate i la apelul funciilor de citire, iar descriptorii de format s se menin aceiai pentru a oferi succes prelucrrilor; - fiiere de tip record n care datele sunt caracterizate prin lungime i adres de nceput; articolele au lungime fix sau variabilitatea lungimilor este n cadrul unei mulimi formate din cteva elemente; operaiile de lucru cu fiierele de acest tip nu sunt n general precedate sau urmate de conversii; operaiile ce preced calculele sunt lsate la dispoziia programatorului; De exemplu, fiierul stocurilor de materiale este: - un fiier de tip record; - are organizare indexat; - se afl pe disc; - este gestionat cu funciile unui sistem de gestiune a fiierelor; - are articole de lungime fix; - se efectueaz toate operaiile de actualizare pe el; - conine informaii asupra poziiei anumitor articole; - articolele sunt identificate dup codul materialului care joac rol de cheie de articol; - fiecare material are o cheie unic; - fiierul este sortat cresctor. Astfel, un fiier oarecare este caracterizat nscriind oricare dintre informaiile ce rezult din criteriile specificate. 4.3. Fiiere secveniale Fie mulimea de elemente E1, E2, , En oarecare ce corespund structurilor de date SD1, SD2, , SDn definite ntr-un program P. Elementele Ei, i = 1, 2, , n, sunt iruri de baii caracterizate printr-un coninut rezultat din iniializarea structurilor de date SDi, i = 1, 2, , n. Pe un suport extern se aloc elementelor E1, E2, , En zone de memorie de lungime:

lg SDi
fiierul avnd n final lungimea:
Lf n lg

lg

(4.9)

n i 1

lg SDi

(4.10)

De obicei, lg() = 2. Cei doi baii ataai fiecrui element vor conine lungimea articolului:

cont

lg SDi

(4.11)

84

Fiierul este caracterizat printr-o adres a articolelor. Astfel:

adr E1

A lg

(4.12)

unde, A reprezint adresa fizic a primului bait a primului articol cruia i s-a ataat n fa lg() baii pentru a memora lungimea articolului respectiv:
i 1

adr Ei
sau:

A
j 1

cont

lg

(4.13)

i 1

adr Ei

A
j 1

lg SDi

i lg

(4.14)

unde, k reprezint grupul de baii de lungime lg() ataat elementului Ek n fiier. Dac:

lg SD1
atunci:

lg SD2

... lg SDn

(4.15)

cont

cont

... cont

(4.16)

caz n care, n eticheta de nceput a fiierului se specific tipul articolului lungime fix. De asemenea, se memoreaz i lungimea, iar lg(k) devine zero. Adresa unui element oarecare Ei, este obinut prin relaia:
i 1

adr Ei

A
j 1

lg SD j

(4.17)

Cu fiierele secveniale se efectueaz urmtoarele proceduri: - crearea unui fiier secvenial const n dispunerea elementelor E1, E2, , En pe suport n aceast ordine; - consultarea integral sau parial a fiierului baleiaz din aproape n aproape elementele fiierului i se prelucreaz cele care prezint interes; - adugarea elementului Em se efectueaz la adresa:

adr Em
-

adr En

lg S n

lg

(4.18)

ceea ce pune n eviden c adugarea se face numai la sfritul fiierului; interclasarea a dou fiiere; sortarea fiierelor, care const n obinerea unui fiier n care articolele au o astfel de dispunere nct pentru un cmp x exist relaia: cont (E1.x) > cont (E2.x) > > cont (En.x) (4.19)

dac sortarea s-a fcut descresctor, sau: 85

cont (E1.x)

cont (E2.x)

cont (En.x)

(4.20)

dac sortarea s-a fcut cresctor. ntruct se lucreaz n cele mai multe cazuri cu fiierul n totalitatea lui, nu este justificat memorarea de informaii pentru anumite articole. Explorarea fiierelor secveniale corespunde unei structuri de date contigue, asemeni unui vector de structur sau a unei niruiri de diferite structuri, cu posibilitatea calculului adresei unui element oarecare: adr (suc (Ei)) = adr (Ei) + lg (SDi) + lg ( ) adr (pred (Ei)) = adr (Ei) lg (SDi-1) lg ( ) Se spune c fiierul este nchis dac:
m

(4.21) (4.22)

lim sucm Ei

En

(4.23)

Se spune c fiierul este deschis dac:


m

lim pred m Ei

E1

(4.24)

O astfel de abordare determin continuarea prelucrrii chiar dac exist tentative de a nchide un fiier deja nchis sau de a deschide un fiier deja deschis. Apare problema privind parametrii funciei de deschidere. Dac sunt luai n considerare parametrii primei deschideri, problema este rezolvat, tentativele de deschidere a unor fiiere deja deschise fiind inefective. ntruct exist formule de calcul pentru adresele fiecrui element din submulimea E1, E2, , En nu se justific construirea unui vector a adreselor n fiier a1, a2, , an pentru aceste elemente. Sistemele de operare evoluate gestioneaz nchiderea fiierelor la nchiderea execuiei programelor. 4.4. Fiiere secvenial indexate Se consider o mulime de elemente E1, E2, , En generate dup structura SD i un cmp x astfel nct: cont (E1.x) cont (E2.x) cont (En.x) (4.25)

deci, irul elementelor este sortat cresctor dup cmpul x, care joac rol de cheie a articolelor n structura SD de generare. Elementele de sortare vor fi memorate, fiecare ocupnd o zon de lungime:

lg SD

lg

(4.26)

86

unde, reprezint o zon n care este memorat o adres fizic de pe suport, innd seama de structurarea contigu a suportului. Fiierul are un fond informaional de lungime:
Lf n lg lg SD

(4.27)

Se construiete mulimea perechilor: (cont(Ei.x).ai) (4.28)

a cheilor i adreselor articolelor ce intr n componena fiierului. Se calculeaz:


n 2 i 1

cont Ei. x n

(4.29)

i rezult o mprtiere suficient de mare care s reduc ansele gsirii unei formule de calcul a adresei fizice a unui articol, folosind strict ca informaie cheia acestuia. Dac se accept ideea utilizrii unui arbore binar cu 4 noduri terminale, mulimea elementelor E1, E2, , En este mprit n 4 subiruri, avnd un numr aproape identic de elemente, reinnd adrese i cheile elementelor ce delimiteaz fiecare subir de articole: (cont(Ekj.x), akj), j= 1, 2, 3, 4; k = 1, 2, , n (4.30)

unde k1 k2 k3 k4. Perechile de delimitare ale nceputului de subir, se obin astfel: - pentru primul subir:

Q1
- pentru al doilea subir:

cont E1.x , a1

(4.31)

Q2
- pentru al treilea subir:

cont E1 m .x , am

(4.32)

Q3

cont E1

2m

.x , a 2 m

(4.33)

- pentru al patrulea subir:

Q4

cont E1

3m

.x , a3m

(4.34)

Perechile pentru delimitarea sfritului pentru fiecare din cele 4 subiruri sunt:

87

P1 P2 P3 P4

cont Em .x , am cont E2 m .x , a2 m cont E3m .x , a3m cont En .x , an


(4.35)

S-a considerat c fiecare subir are m elemente, cu excepia ultimului ir care are 1, 2 sau 3 elemente mai puine. Setului de date i se asociaz arborele binar:
(Q1, P4)

(Q1, P2)

(Q3, P4)

(Q1, P1)

(Q2, P2)

(Q3, P3)

(Q4, P4)

Fig. 4.3. Structura setului de date i Dac, de exemplu, fiierul sortat este organizat secvenial i se dorete citirea articolului penultim, care are cheia 7233, n mod normal trebuie s fie citite cele n-2 articole care l preced. Dac ns fiierul este nzestrat cu aceast informaie pe care o conine arborele binar cu 4 noduri terminale, inspectarea nodului rdcin permite vizualizarea faptului c articolul cutat cu cheia 7233 este n fiier, adic: cont (E1.x) 7233 cont (En.x) ntruct:
7233 cont (E n .x) cont (E i .x) 2

(4.36)

(4.37)

rezult c se parcurge pe nivelul inferior, nodul din dreapta. ntruct:


7233 cont (E 2n 1 .x) cont (E n .x) 2

(4.38)

rezult c se parcurge pe nivelul inferior, nodul din dreapta. Odat ajungnd pe ultimul nivel al arborescenei, se rein adresele a3m+1 i an i se baleiaz secvenial subfiierul delimitat astfel. Deci, se vor baleia mai puin din totalul elementelor fiierului. Pentru construirea arborilor binari asociai exist numeroi algoritmi de partiionare a fondului informaional. Important este ca numrul cutrilor secveniale s fie ct mai redus. 88

Trebuie realizat un echilibru ntre numrul de nivele ale arborelui binar i numrul subfiierelor, deoarece creterea numrului de subfiiere conduce la creterea duratei de acces la acestea din cauza parcurgerii unui numr prea mare de noduri n arborele binar. n cazul n care se dorete inserarea unui articol Ej ntr-un astfel de fiier se identific poziia lui, astfel nct: cont (Ek.x) cont (Ej.x) cont (Ek+1.x) (4.39)

n acest caz, articolul ca atare este memorat ntr-o zon rezervat a fiierului, legtura cu celelalte articole efectundu-se prin:

cont cont

k j

adr E j adr E k
1

(4.40)

ce corespunde unei inserri de elemente ntr-o list. La un numr mare de inserri, parcurgerea listelor reduce performana programului de exploatare a fiierului secvenial indexat. Acestea justific trecerea la reorganizarea fiierului. Prin reorganizare se nelege crearea unui nou fiier, n care elementele se dispun ntr-o aceeai zon fr a mai exista informaii de legtur, ca n cazul n care ar fi dispersate pe suport. La reorganizare, se construiete un nou binar al indecilor. Utilizarea arborilor binari are aici numai un caracter exemplificativ. Tipul de arbore depinde n principal de mprtierea cheilor n intervalul pe care sunt definite. Informaiile privind arborele asociat tabelei de indeci se stocheaz pe suport i face parte din fiier. Pentru o parcurgere mai rapid, n cazul n care este posibil, informaiile aferente structurii arborescente a indecilor se ncarc n memoria intern i se lucreaz cu ele folosind funciile de parcurgere ale unui arbore. n limbajele precum C i C++, fiecare programator implementeaz algoritmi proprii pentru organizarea secvenial indexat, iar prin comparaia comportamentului lor statistic cu ali algoritmi existeni, sunt dezvoltai sau abandonai. 4.5. Fiierele bazei de date Din punctul de vedere al modului de structurare a datelor, bazele de date reprezint o form mai general de reprezentare a datelor, care reflect caracteristici ale elementelor omogene ce definesc mai multe mulimi. Fie mulimile M1, M2, ..., Mk, formate fiecare din n1, n2, ..., nk elemente, aa fel alese nct caracterizarea complet a unui element xj (M1) este efectiv dac sunt prezentate datele dj1, dj2, ..., djk, unde djk (Mk). n plus, fiecare mulime are elementele structurare dup o anumit regul. Punerea n coresponden a elementelor celor k mulimi, conduc n numeroase situaii, ca unui element din mulimea Mi s-i corespund mai multe elemente din mulimea Mj sau mai multor elemente din mulimea Mi s le corespund un singur element din mulimea Mh. Se observ c necesitatea de a separa informaiile n fiiere distincte este dat n principal din dorina de a diminua redundana dintr-un fiier, pe de o parte, i pentru a permite noi faciliti de exploatare a structurilor de date, pe de alt parte.

89

n continuare, se consider un exemplu clasic, foarte frecvent utilizat n descrierea principiilor i fundamentelor de realizare a bazelor de date. Fie mulimea M1 a persoanelor dintr-o localitate, care se afl ntr-una din relaiile: x este vecin cu y x este fiul lui y x este tatl lui y x este soia lui y x este soul lui y Un individ al colectivitii, este descris prin: - nume; - adres; - raport cu ali indivizi, respectiv vecini, rude; - tip automobil/marca; - culoare automobil, - an cumprare; - loc de munc. Fie mulimea M2 mulimea automobilelor, n care elementele se afl n relaiile: marca x este produs de y marca x are capacitate z capacitatea z are consumul specific w Un autoturism este descris prin: - nume productor; - model; - culoare; - an fabricaie; - capacitate; - tip combustibil; - caracteristici motor; - consum la 100 km. Fie M3 mulimea locurilor de munc, format din elemente caracterizate prin: - nume instituie; - capital social; - tip instituie; - numr salariai; - nume compartiment; - nume meserii acceptate la compartimentul respectiv; - nume lucrtori din compartiment. Fie M4 mulimea impozitelor care se aplic mijloacelor de transport, format din elementele: - limita inferioar a capacitii cilindrice; - limita superioar a capacitii cilindrice; - impozit; - taxa CASCO pentru primii 3 ani de funcionare; - taxa CASCO pentru mainile cu vechime cuprins ntre 4 10 ani; - taxa CASCO pentru mainile cu vechime mai mare de 10 ani.

90

Dei se discut foarte mult, cea mai costisitoare etap din activitatea de manipulare a bazelor de date o reprezint ncrcarea acestora. n cazul de fa, datele nu sufer o uzur moral rapid pentru c vizeaz indivizii dintr-un cartier sau bloc. n cazul n care fenomenul are o dinamic accelerat, se observ c la terminarea ncrcrii 60 80% din date sunt uzate moral i baza de date practic este inoperant. n cazul considerat, cele patru mulimi conduc la date ce alimenteaz baza de date i se trage concluzia c n continuare ea este complet ncrcat. Pentru a vedea puterea de prelucrare pe care software-ul bazei de date o are, exemplificm cteva cereri: - listarea locurilor de munc ale membrilor familiei lui x; - listarea proprietarilor de autoturisme cu culoarea z; - listarea tuturor celor care lucreaz la locul de munc w i au autoturisme pentru care pltesc taxa CASCO cuprins n intervalul [a, b]; - exist o relaie ntre capitalul social al firmelor i uzura moral a autoturismelor pe care le au salariaii lor? - listarea proprietarilor care pltesc un impozit mai mare dect C lei i taxa CASCO mai mare dect D lei; - exist meseriai de tipul z care au maini de tipul u absolut noi i care au vecini n aceeai situaie? Dac toate datele despre un individ, sunt nregistrate ntr-o structur cuprinztoare, care conine elementele de descriere ale celor patru mulimi, se observ o multitudine de repetri, ceea ce conduce de fapt la regruparea informaiilor. Cele 4 mulimi nu sunt un dat, ci sunt rezultatul unei analize a modului n care sunt sistematizate i concentrate datele. Pentru elementele din mulimea M1 se asociaz arborescena:
persoana x

familie

vecini

soie

copii

vecinul din fa

vecinul din spate

vecinul din stnga

vecinul din dreapta

copilul Y1

copilul Y10

Fig. 4.9. Arborescena asociat elementelor mulimii M1 Pentru elementele mulimii M2 se asociaz arborescena din figura 4.10.

91

Productor

Marca 1

Marca 2

Marca n

Model

Culoare

An fabric.

Fig. 4.10. Arborescena asociat elementelor mulimii M2 i pentru elementele mulimii M3 i M4 se asociaz, de asemenea, arborescene. Deci, dac fiecare mulime este concretizat n cte un fiier, articolele n cadrul fiecrui fiier sunt legate ntre ele, funcie de structura arborescenei asociate. Baza de date presupune att legturi ntre articolele fiecrui fiier, ct i legturi ntre fiiere. Se ridic ntrebarea: pentru a rspunde la toate solicitrile, este necesar constituirea unor structuri de adrese; toate structurile de adrese sunt create de la nceput sau acestea se creeaz pe msur ce necesitile de prelucrare impun acest lucru? n fiierul persoanelor, cmpul corespunztor autoturismului conine i adresa articolului ce corespunde mrcii i culorii autoturismului. Dac intereseaz listarea proprietarilor de maini de culoare z se, procedeaz astfel: - se construiete irul: S = (s1, s2 sn) (4.41)

unde si este adresa articolului pentru descrierea autoturismului al crui proprietar este persoana ai din fiierul de persoane. - din fiierul autoturismelor se extrage subirul: P = (p1, p2, pn) (4.42)

al adreselor articolelor ce corespund autoturismelor de culoare z: P S = (sk1, sk2, skm) (4.43)

ceea ce nseamn c persoanele ale cror nregistrri ocup poziiile k1, k2, km cu maini de culoare z i afiarea: cont (ref (ki) . nume), i = 1, 2, , m (4.44)

conduce la tabelarea numelor de persoane care posed maini de culoare z. Interogarea unei baze de date revine la constituirea mai multor iruri, din fiecare mulime cte unul. Numrul de iruri maxim este de regul egal cu numrul mulimilor de articole definite.

92

Astfel, dac se dorete listarea tuturor celor care au locul de munc w i au autoturism pentru care pltesc o tax CASCO cuprins n intervalul [a, b] , se procedeaz astfel: - se construiete irul adreselor persoanelor care au locul de munc w: ( j = const (w.adresa_persoanaj)), j = 1, 2, n se construiete irul capacitilor cilindrice: (C1, C2, Ch) (4.46) al autoturismelor, nsoite de adresele articolelor din fiierul asociat mulimii M2, ce corespund autoturismelor pentru capacitile identificate: m1 m2 me (4.47) (4.45)

De regul, e h, ntruct exist mai multe mrci de maini cu aceeai capacitate cilindric. Prin analiza coninutului fiierului M4, rezult c autoturismele pentru care se pltete o tax CASCO cuprins n intervalul [a, b] , sunt cele care au capacitile cuprinse ntre: [ 1, [ 2,
1] 2]

(4.48) (4.49)

Aceste limite conduc la filtrarea elementelor mulimii C1, C2, Ch, astfel nct rezult submulimea adreselor: m = {m1, m2, mp} de regul p < e. Se construiete irul de adrese:
u

(4.50)

= (i 0 1

k; cont (.i.adresa_autoturism)

m)

(4.51)

Scrierea elementelor irului: (cont (u->nume_persoana)), u = 1, 2, , w (4.52)

rezolv cererea formulat iniial. n exemplul dat, s-a procedat la utilizarea de variabile pointer. Pentru a face deosebire ntre pointerii folosii la referirea elementelor din memoria intern i pentru a evita confuziile ce apar folosind conceptul de pointer spre fiier, ntruct este deja concentrat tipul de dat FILE. n continuare se utilizeaz conceptul de pointer extern. Fiind dat un suport extern al cror baii au adresa cuprins ntre valorile [A,B]N, A B i A, B N, variabila v se spune c este pointer extern dac i numai dac: cont (v) [A, B] N (4.53)

Poziionarea citirii este de fapt atribuirea sau modificarea coninutului unei variabile de tip pointer extern, aa fel nct ea s conin adresa baitului de unde ncepe citirea/scrierea. 93

n enunul problemei, M1, M2, M3 i M4 reprezint fiiere, adic variabile pointer care sunt iniializate cu adresele baiilor de unde ncepe alocarea memoriei externe pentru fiecare din cele patru fiiere. Dac se are n vedere c fiierul are i o etichet de nceput de lungime, cont(Mi) + reprezint adresa primului articol din fiierul Mi. Presupunnd c fiierului Mi i se aloc o zon contigu, cuprins ntre adresele [Ai, Bi] , rezult c: cont (Mi) = Ai (4.54) (4.55)

cont ( i) = Bi lg (SDi)

unde i reprezint adresa ultimului element de structur SDi al fiierului Mi care are o etichet de sfrit de fiier de lungime . Revenind la fiierele interdependente, fiierele nlnuite, se observ c structurile de date ce se asociaz fiierelor, pe lng informaiile utile date de programator, se definesc nc multe cmpuri de tip pointer extern care asigur traversrile prin structura extern pe timpul execuiei. Folosind definirea pointerilor extern, locul de munc w este identificat prin pointerul extern pw ce corespunde adresei articolului respectivului loc de munc. Mulimea m1, m2, me, reprezint valori ale variabilei pointer extern r, asociat fiierului M2: m = rj cont (rj->capacitate) C1 , C2 , C h , i j nrr (M2) (4.56)

unde nrr( ) este o funcie care definete numrul de articole existent ntr-un fiier: nrr : F1, F2, Fk N (4.57)

unde F este mulimea fiierelor, date prin valoarea iniial a pointerilor lor externi: m = mi cont (mj-> capacitate) [ 1,
1]

U [ 2,

2]

(4.58)

Selectarea elementelor reprezint construirea de iruri de adrese i apoi prin operaii de reuniune, intersecie, se obin irurile de elemente care prezint rezolvarea problemei. Utilizarea bazelor de date reprezint un domeniu al informaiei aplicate. Construirea de sisteme de gestiune a bazelor de date este o preocupare de mare importan pentru toi realizatorii de software, iar elementele prezentate definesc doar o serie de trsturi generale ale filozofiei bazelor de date. Fiecrui mecanism particular i corespund reguli precise de definire, iniializare i modificare a pointerilor externi ce se asociaz fiecrui articol. De menionat c n spatele oricrei cereri de informaie furnizat de utilizatorul bazei de date, se afl pointeri externi, care n unele cazuri sunt deja iniializai i se folosesc ca atare, iar n alte cazuri, sunt numai definii i i ncarc coninutul n funcie de cerere. Exist situaii cnd pentru a rezolva o problem, se definesc noi pointeri externi i se activeaz proceduri de iniializare, n concordan cu problema de rezolvat. n acest caz se creeaz noi legturi ntre elemente, cu noi posibiliti de selectare a informaiilor din baza de date. Statistic, diversitatea de fiiere face ca utilizarea unora s fie n anumite cazuri mai eficiente dect a altora. Experiena fiecrui programator este cea care hotrte tipul de fiier cu care se lucreaz pentru fiecare aplicaie. 94

La alegerea tipului de fiier cu care se lucreaz, concur o serie de factori din care se enumr: - numrul de elemente care alctuiesc fiierul; - tipul prelucrrilor: integrale, dup o cheie, prin selecie; - frecvena i tipul operaiilor de actualizare; - durata impus de prelucrare i necesitatea sortrii datelor; - timpul disponibil pentru elaborarea programelor; - costuri de elaborare i utilizare; - sistemul de calcul disponibil; - sistemele de gestiune a fiierelor cunoscute i disponibile. Alegerea tipului de fiier i comportamentul algoritmilor implementai pentru regsirea informaiilor, sunt rezultatul unei analize statistice a datelor, nregistrate prin urmrirea n timp a comportamentului la prelucrare. Teme de control 1. S se memoreze ntr-un fiier text informaiile necesare ncrcrii a dou matrice rare i s se listeze rezultatul nmulirii dintre acestea. 2. Pentru un masiv unidimensional de elemente articol de tip pies, s se listeze ntr-un fiier binar toate piesele care nu sunt n conformitate cu anumite caracteristici citite de la tastatur. Bibliografie Ion IVAN, Marius POPA, Paul POCATILU - Structuri de date, Vol. 1 si 2, Editura ASE, Bucuresti, 2008.

95

5. Listele structuri dinamice necontigue


Obiectivele unitii de nvare Dup studierea acestei uniti de nvare, studenii i vor nsui cunotine teoretice i vor deprinde abiliti practice de lucru cu liste simplu i dublu nlnuite. Studenii vor nva lucrul cu pointeri i operaiile pe liste simple i duble, liniare i circulare. 5.1. Consideraii privind structurile de date de tip list O list liniar (numit i list nlnuit -Linked List) este o colecie de n>=0 elemente x[1], x[n] toate de un tip oarecare, numite noduri ntre care exist o relaie de ordine determinat de poziia lor relativ. Ea este deci o mulime ealonat de elemente de acelai tip avnd un numr arbitrar de elemente. Numrul n al nodurilor se numete lungimea listei. Dac n=0, lista este vid. Dac n>=1, x[1] este primul nod iar x[n] este ultimul nod. Pentru 1<k<n, x[k] este precedat de x[k-1] i urmat de x[k+1]. Acest tip de structur de date se aseamn cu o structur standard: tipul tablou cu o singur dimensiune (vector), ambele structuri coninnd elemente de acelai tip iar ntre elemente se poate stabili o relaie de ordine. Una dintre deosebiri const n numrul variabil de elemente care constituie lista liniar, dimensiunea acesteia nu trebuie declarat i deci cunoscut anticipat (n timpul compilrii) ci se poate modifica dinamic n timpul execuiei programului, n funcie de necesiti. Astfel utilizatorul nu trebuie s fie preocupat de posibilitatea depirii unei dimensiuni estimate iniial, singura limit fiind mrimea zonei heap din care se solicit memorie pentru noile elemente ale listei liniare. Un vector ocup n memorie un spaiu continuu de memorie, pe cnd elementele unei liste simplu nlnuite se pot gsi la adrese nu neaprat consecutive de memorie. O alt deosebire avantajeaz vectorii, deoarece referirea unui element se face prin specificarea numrului de ordine al respectivului element, pe cnd accesul la elementele unei liste liniare se face secvenial, pornind de la capul listei (adresa primului nod al listei) pn la ultimul element al ei, ceea ce mrete uneori considerabil timpul de acces la un anumit element. Pentru o list liniar este obligatoriu s existe o variabil, declarat n timpul compilrii, denumit cap de list care s pstreze adresa primului element al listei. Pierderea acestei valori va duce la imposibilitatea accesrii elementelor listei liniare. Pentru implementarea dinamic a unei liste liniare, folosind pointeri, nodurile listei vor fi structuri ce conin dou tipuri de informaie: - cmpurile ce conin informaia structural a nodului - cmpurile ce conin informaia de legtur, ce vor conine pointeri la nodurile listei. nlnuirea secvenial a elementelor unei liste se face utiliznd variabile de tip pointer, care specific adresa de memorie a elementelor adiacente. Fiecare nod are un predecesor i un succesor, mai puin elementele prim i ultim dac lista nu este circular. Listele nlnuite cu un singur cmp de legtur se numesc liste simplu nlnuite (legtura indic urmtorul element din list). Fiecare nod conine un pointer ce conine adresa nodului urmtor din list.

96

INFO
URM

NULL

Fig. 5.1. Lista simplu nlnuit Ultimul element poate conine ca adres de legtur fie constanta NULL fie constanta 0 (indicnd astfel c ultimul nod nu are nici un succesor). Tipul unui nod ntr-o list simplu nlnuit se poate defini folosind o declaraie de forma: class Lista; class ElementLista { TINFO info; ElementLista *urm; public: ElementLista(int val=0); friend class Lista; }; Listele nlnuite cu 2 cmpuri de legtur se numesc liste dublu nlnuite (o legtur indic nodul precedent iar cealalt nodul succesor).
NULL
PREC

INFO
URM

NULL

Fig. 5.2. Lista dublu nlnuit Nodurile unei liste dublu nlnuite au tipul definit dup cum urmeaz, pointerul urm definind relaia de succesor pentru nodurile listei, iar prec pe cea de predecesor: class Lista; class ElementLista { ElementLista *prec; TINFO info; ElementLista *urm; public: ElementLista(int val=0); friend class Lista; }; n situaia n care se realizeaz o nchidere a nlnuirilor, pierzndu-se astfel noiunile de nceput i de sfrit ale unei liste liniare, se obine o list circular simplu sau dublu nlnuit. ntr-o list circular simplu nlnuit toate nodurile sunt echivalente, fiecare nod avnd un urmtor i fiecare la rndul su constituind urmtorul unui alt nod. Acest considerent este valabil i pentru listele circulare dublu nlnuite extinzndu-se i asupra relaiei de preceden care exist ntre nodurile listei. 97

Relativ la o poziie K din cadrul unei liste, putem defini urmtoarele tipuri de operaii: - accesul la cel de-al K-lea nod al listei pentru examinare sau modificare; - tergerea nodului aflat pe poziia K din cadrul listei; - inserarea unui nou nod nainte de, respectiv dup nodul K. Limitnd efectuarea acestor trei operaii la primul sau la ultimul nod al unei liste liniare simplu nlnuite se obin structurile de date derivate de tip stiv i coad. n ceea ce privete stiva, aceasta constituie o list simplu nlnuit n care toate inserrile i suprimrile de noduri se efectueaz la un singur capt al acesteia, numit vrful stivei. Stivele se mai numesc structuri list de tip LIFO, Last-In-First-Out, adic ultimul-introdusprimul-suprimat sau liste de tip pushdown. Relativ la structura de date de tip coad, trebuie specificat c elementele noi vor fi inserate la un capt al acesteia, numit spate, iar suprimrile de noduri vor avea loc la cellalt capt, numit fa. Din acest motiv, cozile se mai numesc i liste de tip FIFO, first-in-first-aut, adic liste de tip primul-venit-primul-servit. Prezena mai multor nlnuiri ntr-un acelai nod confer mai mult flexibilitate structurilor de date de tip list, acestea devenind aa-numitele multiliste, nodurile acestora aparinnd n acelai timp la mai multe liste nlnuite simple. 5.2. Lista simplu nlnuit ntr-o astfel de list exist ntotdeauna un nod care nu mai are succesor, precum i un nod care nu este succesorul nici unui alt nod, aceste noduri constituind capetele listei simplu nlnuite. ntr-o prim variant de gestionare a acestui tip de list vom apela la doi pointeri prim i ultim pentru a referiri nodurile terminale ale listei.
INFO
URM

NULL

prim

ultim

Fig. 5.3. Lista simplu nlnuit marcat de pointerii prim i ultim Dac specificarea nceputului listei prin pointerul prim este obligatorie, utilizarea pointerului de sfrit ultim este opional, ns eficient atunci cnd se dorete crearea listei n ordine natural printr-o inserie a noilor noduri la sfritul acesteia. n practic, parcurgerea ntregii liste pentru a determina ultima poziie a acesteia este o soluie ineficient, convenabil fiind cunoaterea, cu ajutorul pointerului ultim, a nodului terminal al acesteia. n prezena lui ultim, secvena de program care insereaz un nod la sfritul unei liste liniare i concomitent l actualizeaz pe ultim este urmtoarea: void Lista::inserare_sfarsit(TINFO val) { ElementLista* ptr=new ElementLista(val); if (ptr==NULL) { cout<<"Eroare alocare spatiu la inserare"; return; } Ultim->urm=ptr; 98

Ultim=ptr; if (Prim==NULL) Prim=Ultim; cout<<"Inserare la sfarsitul listei cu succes!\n"; } Se observ c dup adugarea unui nou element la sfritul listei, se actualizeaz pointerul Ultim, care va pstra adresa noului element ultim al listei. Dac elementul adugat la sfritul listei este primul element al listei (caz n care pointerul Prim conine valoarea NULL), vom actualiza i valoarea pointerului Prim, elementul adugat fiind n acelai timp primul i ultimul element al listei. Dac se dorete crearea unei liste prin inseria noilor noduri la nceputul acesteia, se va apela metoda inserare_inceput, aceasta fiind valabil i n cazul unei liste vide. void Lista::inserare_inceput(TINFO val) { ElementLista* ptr=new ElementLista(val); if (ptr==NULL) { cout<<"Eroare alocare spatiu la inserare"; return; } if(Prim==NULL) Ultim=ptr; ptr->urm=Prim; Prim=ptr; cout<<"Inserare la inceputul listei cu succes!\n"; } Dac elementul adugat la nceputul listei este primul element al listei (caz n care pointerul Prim conine valoarea NULL), vom actualiza i valoarea pointerului Ultim, elementul adugat fiind n acelai timp ultimul i primul element al listei. Operaia de inserare a unui nod ntr-o list nu se reduce numai la cele dou situaii prezentate, ci implic de asemenea posibilitatea inserrii acestuia ntr-o poziie oarecare a listei, i anume, dup sau naintea unui anumit nod specificat prin referina sa spec. Schematic cele dou situaii sunt prezentate n figurile urmtoare, observndu -se o oarecare dificultate la operaia de inserie a noului nod naintea celui specificat.
spec

Fig. 5.4. Inseria unui nod dup un nod specificat

99

void Lista::inserare_dupa_un_element(TINFO X, TINFO cheie) { ElementLista* q, *spec; for(spec=Prim;spec&&spec->info!=cheie;spec=spec->urm); if(spec) { q=new ElementLista(X); if (q==NULL) { cout<<"Eroare alocare spatiu la inserare"; return; } if(spec==Ultim) Ultim=q; q->urm=spec->urm; spec->urm=q; cout<<"Inserare dupa element cu succes!\n"; } else cout<<"Nu exista cheia specificata!\n"; }
spec X

INFO

Fig. 5.5. Inserarea unui nod naintea unui nod specificat Inserarea noului nod naintea celui referit prin spec se implementeaz ca o inserare dup acesta, noul nod prelund coninutul cmpului info al acestuia, n care apoi se va memora informaia X.

void Lista::inserare_inaintea_unui_elem(TINFO X,TINFO cheie) { ElementLista* q, *spec; for(spec=Prim;spec&&spec->info!=cheie;spec=spec->urm); if(spec) { q=new ElementLista(X);

100

if (q==NULL) { cout<<"Eroare alocare spatiu la inserare"; return; } if(spec==Ultim) Ultim=q; q->info=spec->info; q->urm=spec->urm; spec->urm=q; spec->info=X; cout<<"Inserare inaintea unui element cu succes!\n"; } else cout<<"Nu exista cheia specificata!\n"; } Se pune problema crerii unei liste nlnuite ordonate dup cmpul info, caz n care inserarea unui nod nou nu trebuie s afecteze relaia de ordonare existent. Vom traversa lista utiliznd doi pointeri consecutivi i vom identific astfel poziia de inserare a noului nod. Inserarea noului nod se va face dupa nodul referit de pointerul q1. Cei doi pointeri q1 i q2 avanseaz simultan de-a lungul listei pn cnd valoarea q1 info devine mai mare sau egal cu valoarea de inserat X. Inserarea noului nod va avea loc ntre nodurile referite de q1 i q2. void Lista::inserare_ordonata(TINFO X) { ElementLista *q1=NULL,*q2; ElementLista* ptr=new ElementLista(X); if (ptr==NULL) { cout<<"Eroare la alocare spatiu pentru inserare"; return; } for(q2=Prim;q2&&q2->info<X;q1=q2,q2=q2->urm); if(q2==Prim) { if(Prim==NULL) Ultim=ptr; ptr->urm=Prim; Prim=ptr; } else { if(q2==NULL) Ultim=ptr; q1->urm=ptr; ptr->urm=q2; } cout<<"Inserare ordonata cu succes!\n"; }

101

Identificarea unui nod n cadrul unei liste se poate efectua aplicnd metoda de cutare liniar, aceasta presupunnd parcurgerea elementelor listei, nod cu nod, fie pn se localizeaz nodul dorit, fie pn la sfritul listei dac elementul cutat este inexistent.

/* caut nodul X n cadrul listei i returnez pointerul spre nodul identificat i 0 n caz contrar */ ElementLista *Lista::cautare(TINFO X) { ElementLista *q; for(q=Prim;q&&q->info!=X;q=q->urm); if(q) return(q); else return(0); } S se ofere o implementare a problemei concordanei, inserarea unui nou nod realizndu-se la nceputul listei. n final, lista va conine toate cuvintele X distincte i numrul de apariii ale acestora. #include<iostream.h> class Lista; class ElementLista { int info,contor; ElementLista *urm; public: ElementLista(int val=0); friend class Lista; }; class Lista { protected: ElementLista *Ultim, *Prim; public: Lista() { Prim = NULL; Ultim = new ElementLista(); } ~Lista(); void traversare(); void inserare(int); }; ElementLista::ElementLista(int val) {

102

info=val; contor=1; urm=NULL; } Lista::~Lista() { ElementLista* ptr=Prim; while (Prim) { Prim=Prim->urm; delete ptr; ptr=Prim; } } void Lista::traversare() { ElementLista* p=Prim; if (p==NULL) cout << "\n Lista este vida! \n"; else while (p!=NULL) { cout<<p->info<<" numar de aparitii: "<<p->contor<<"\n"; p=p->urm; } cout<<"\n"; } void Lista::inserare(int X) { ElementLista *p; for(p=Prim;p&&p->info!=X;p=p->urm); if(p) p->contor++; Else { p=new ElementLista(X); p->urm=Prim; Prim=p; } } void main() { Lista Listamea; int cheie; cout<<"Introduceti cheia de inserat:"; cin>>cheie;

103

while(cheie) { Listamea.inserare(cheie); cout<<"Introduceti cheia de inserat:"; cin>>cheie; } cout<<"Inserare incheiata!\n"; cout<<"\nTraversare lista:\n"; Listamea.traversare(); } O mbuntire substanial a procesului de cutare poate avea loc prin aplicarea aanumitei metode de cutare cu reordonare. Ori de cte ori un cuvnt se caut i se localizeaz n list, el va fi mutat la nceputul acesteia, astfel nct la proxima apariie s fie gsit imediat. Cu alte cuvinte, lista se reordoneaz dup fiecare cutare finalizat cu succes. Dac un nod nu este gsit n list, atunci el va fi inserat la nceputul acesteia.
q2 q1 prim 3 X
NULL

INFO
URM

prim

fanion

Fig. 5.6. Mutarea nodul X la nceputul listei void Lista::inserare(int X) { ElementLista *q1, *q2=NULL, *p; for(q1=Prim;q1&&q1->info!=X;q2=q1,q1=q1->urm); if(q1) { q1->contor++; if(q2) { q2->urm=q1->urm; q1->urm=Prim; Prim=q1; } } else { p=new ElementLista(X); p->urm=Prim; Prim=p; } }

104

Se propune eliminarea unui anumit nod, referit prin q1, din cadrul unei liste liniare nlnuite. Pentru aceasta vom folosi un pointer auxiliar, q2, care indic predecesorul elementului ce urmeaz s fie suprimat . void suprima (TNOD *q); { ElementLista *q1=NULL,*q2; for(q2=Prim;q2&&q2->info!=X;q1=q2,q2=q2->urm); if(q2) { if(q2==Prim) Prim=q2->urm; if(q2==Ultim) Ultim=q1; if(q1) q1->urm=q2->urm; delete(q2); cout<<Suprimare cu succes!\n"; } Else cout<<"Nodul nu exista!\n"; } 5.3. Lista circular simplu nlnuit Listele circulare sunt liste nlnuite ale cror nlnuiri se nchid, n aceste condiii disprnd noiunea de nceput i de sfrit al listei. Pentru gestiunea unei astfel de lista, vom pstra un pointer spre ultimul element al listei. # include <iostream.h> # define TINFO int class Lista; class ElementLista { TINFO info; ElementLista *urm; public: ElementLista(int val=0); friend class Lista; }; class Lista { protected: ElementLista *Ultim; public: Lista() { Ultim = NULL;} ~Lista(); void traversare(); void inserare_sfarsit(TINFO); void inserare_inaintea_unui_elem(TINFO,TINFO); 105

ElementLista *cautare(TINFO); void suprimare(TINFO); }; ElementLista::ElementLista(TINFO val) { info=val; urm=NULL; } Lista::~Lista() { ElementLista* ptr; if(Ultim!=NULL) { while (Ultim->urm!=Ultim) { ptr=Ultim->urm; Ultim->urm=Ultim->urm->urm; delete ptr; } delete Ultim; } } void Lista::traversare() { ElementLista* ptr=Ultim; if (Ultim==NULL) cout << "\n Lista este vida! \n"; else { while (ptr->urm!=Ultim) { ptr=ptr->urm; cout << ptr->info<<" "; } cout << ptr->urm->info<<" "; cout<<"\n"; } } void Lista::inserare_sfarsit(TINFO val) { ElementLista* ptr=new ElementLista(val); if (ptr==NULL) { cout<<"Eroare la alocare spatiu pentru inserare"; return; } if(Ultim) {

106

ptr->urm=Ultim->urm; Ultim->urm=ptr; Ultim=ptr; } else { Ultim=ptr; ptr->urm=ptr; } cout<<"Inserare sfarsit cu succes!\n"; } void Lista::inserare_inaintea_unui_elem(TINFO X,TINFO cheie) { ElementLista* ptr, *q1=Ultim, *q2; if(Ultim==NULL) { cout<<"Lista vida! Inserare fara succes!\n"; return; } for(q2=Ultim->urm;q2!=Ultim&&q2->info!=cheie;q1=q2,q2=q2>urm); if(q2->info==cheie) { ptr=new ElementLista(X); if (ptr==NULL) { cout<<"Eroare alocare spatiu la inserare"; return; } q1->urm=ptr; ptr->urm=q2; cout<<"Inserare cu succes!\n"; } else cout<<"Nu exista cheia specificata!\n"; } ElementLista *Lista::cautare(TINFO X) { ElementLista *ptr; if(Ultim==NULL) return(0); for(ptr=Ultim->urm;ptr!=Ultim&&ptr->info!=X;ptr=ptr>urm); if(ptr->info==X) return(ptr); else return(0); } void Lista::suprimare(TINFO X)

107

{ ElementLista *q1=Ultim, *q2; if(Ultim==NULL) { cout<<"Lista vida! Suprimare fara succes!\n"; return; } for(q2=Ultim->urm;q2!=Ultim&&q2->info!=X;q1=q2,q2=q2>urm); if(q2->info==X) { if(q2->urm==q2) { Ultim=NULL; delete(q2); } else { if(q2==Ultim) Ultim=q1; q1->urm=q2->urm; delete(q2); } cout<<"Suprimare cu succes!\n"; } else cout<<"Nodul nu exista!\n"; } void main() { Lista Listamea; int opt; TINFO valoare,cheie; do { cout<<"\n Optiuni de lucru cu lista:"; cout<<"\n 1 - Afisare lista"; cout<<"\n 2 - Inserare element la sfarsitul listei"; cout<<"\n 3 - Inserare element inaintea unui elem specificat"; cout<<"\n 4 - Cautarea unui element specificat"; cout<<"\n 5 - Suprimarea unui element"; cout<<"\n 9 - Terminare lucru \n\n"; cout<<"Introduceti optiunea dorita:"; cin>>opt; switch(opt) { case 1: { cout<<"Traversare lista:"; Listamea.traversare();

108

break; } case 2: { cout<<"Introduceti elementul de inserat:"; cin>>valoare; Listamea.inserare_sfarsit(valoare); break; } case 3: { cout<<"Introduceti elementul de inserat:"; cin>>valoare; cout<<"Introduceti elem inaintea caruia inseram:"; cin>>cheie; Listamea.inserare_inaintea_unui_elem(valoare,cheie); break; } case 4: { cout<<"Introduceti elementul cautat:"; cin>>valoare; if(Listamea.cautare(valoare)) cout<<"Elementul a fost gasit!\n"; else cout<<"Elementul nu exista in lista!\n"; break; } case 5: { cout<<"Introd elem pe care doriti sa-l suprimati:"; cin>>valoare; Listamea.suprimare(valoare); break; } case 9: break; default: cout<<"\n Nu exista optiunea! \n "; } }while (opt!=9); } 5.4. Operaii cu liste liniare simplu nlnuite Parcurgerea listei liniare simplu nlnuite corespunde funciei de extragere a adresei elementului succesor i de referire a membrilor acestuia.

109

Pentru tiprirea coninutului elementelor matricei rare, cu numr necunoscut de elemente nenule, funcia de parcurgere, construit recursiv este: parcurgere (pg) { while (pg->poz != NULL) { tipareste (pg->i, pg->j, pg->val); parcurgere (pg->poz); } } Considernd lista ca mulimea de perechi de forma (Z, conine adresele de regsire a elementelor listei. parcurgere (adr (w)) { while (w != NULL) { tipareste (w, z); parcurgere (ref (w), ); } } tergerea unui element ( Zk, nainte de tergere lista este:
Zk-1
k
k)

) de tip TL, w = adr( Z1 )

al listei

Zk

k+1

Zk+1

k+2

....

Fig. 5.7. Lista nainte de tergere Dup efectuarea tergerii:

Zk-1

k+1

Zk

k+1

Zk+1

k+2

....

Fig. 5.8. Lista dup de tergerea elementului stergere ( w, w1, Zk ) { while (ref (w). != NULL || ref (w).Z != Zk) { w1 = ref (w, ); stergere (w1, w, Zk) }

110

if( ref (w1).Z = =Zk) ref (w). = ref (w1). ;

Concatenarea a dou liste Concatenarea a dou liste (Z, ) i (U, ), reprezint ca ultimul element al primei liste s-i schimbe valoarea din NULL a lui n cu adresa elementului (U1, 1). Deci: ref( Zn ). n = adr[ (U1, 1)] Funcia care efectueaz concatenarea listelor este: concatenare((Z, ), (U, )) { while (ref(Z). != NULL) { concatenare ((ref(Z). , ),(U,Z)); } ref(Z). = adr((U, )) } Modelul grafic al concatenrii:

( Z1,

1)

( Z2,

2)

( Z3,

3)

( Zn, ...

n)

... ( U1,
1)

NULL

( U2,

2)

( U3,

3)

( Um,

m)

Fig. 5.9. Modelul grafic al concatenrii listelor Lista concatenat are ca prin element (Z1, 1), iar ca ultim element (Um, m). Fizic, lista (U, ) nu s-a modificat. Conservnd (Z, 1) i (U, 1) se lucreaz cu cele dou liste, ca i cum concatenarea nu s-a executat. Totui lista concatenat, pentru operaia de parcurgere se comport ca o list cu m + n componente. Modificarea unui element al listei Fie lista (Zi, i), i = 1, 2, ..., n. Pentru nlocuirea unei valori a cu valoarea b, trebuie mai nti gsit elementul k pentru care: cont (Zk) = a, dup care se realizeaz atribuirea: Zk = b; n toate cazurile, parcurgere, tergere, concatenare, modificare, disciplina de parcurgere este de la primul element ctre ultimul, First In First Out. modificare (w) {

111

if (ref (w). z != a) modificare (ref (w). ); else ref (w). z = b; } Copierea unei liste Fie lista (Zj, j), j = 1, 2, ..., n. Se pune problema obinerii unei liste: (Zj, ... , n, astfel nct: cont(Zj) = cont(Zj), pentru j = 1, 2, ..., n. copiere_lista (w, u) { while (ref (w). != NULL) { ref (u).Z = ref (w).Z; alocare (v); ref (u). = v; copiere_lista (ref (w). ), v); } ref (u). = NULL; } Inserarea unui element n list
j j Se spune c o list este ordonat cresctor dac: , pentru orice j = 1, 2, , n 1. A insera un element ntr-o list, nseamn mai nti a gsi o valoare k {1, 2, , n}

j),

j = 1, 2,

cont ( Z )

cont ( Z

1)

astfel nct: cont ( Zk ) a cont ( Zk 1 ) sau cont ( Zk ) a cont ( Zk 1 ) , dup cum lista este ordonat cresctor sau descresctor. n aceste condiii, inserarea elementului a, nseamn conform modelului grafic, a trece de la configuraia:

(Zk, ...

k)

(Zk+1,

k+1)

...

a ( , )

Fig. 5.10. Configuraia nainte de inserare nodului n interiorul listei la configuraia:

112

(Zk, adr( )) ...

(Zk+1,

k+1)

...

a ( , adr (Zk+1))
Fig. 5.11. Configuraia dup inserarea nodului n interiorul listei Exist cazuri particulare de inserare n care elementul este poziionat fie la nceputul listei, fie la sfritul acesteia, operaia numindu-se adugare. Dac elementele a i b vor fi inserate la nceputul, respectiv, la sfritul listei, se trece de la configuraia: (Zn, n) (Z1, 1)
... NULL ...

a ( , )

a ( , )

Fig. 5.12. Configuraia nainte de inserarea nodului la un capt al listei la configuraia:


(Z1,
1)

(Zn, adr( )) ... ...

b ( ,adr ( Z1) ( ,

b )

NULL

Fig. 5.13. Configuraia dup inserarea nodului la un capt al listei Interschimbul ntre dou elemente ale listei Interschimbul nu se realizeaz fizic, zonele ce corespund celor dou elemente modificndu-i doar adresele de referire a elementelor. Modelul grafic al listei nainte de interschimbul elementelor (Zk, k) i (Zj, j) este:

113

(Zk-1, ...

k-1)

(Zk,

k)

(Zk+1,

k+1)

... (Zj-1, ...


Fig. 5.14. Modelul grafic al listei nainte de interschimbul nodurilor
j-1)

(Zj, j)

(Zj+1,

j+1)

...

Dup efectuarea interschimbului, legturile dintre componente sunt:

(Zk, ... (Zk-1,


k-1)

k)

(Zk+1,

k+1)

... (Zj, j) ... (Zj-1,


j-1)

... (Zj+1,
j+1)

Fig. 5.15. Modelul grafic al listei dup interschimbul nodurilor Funcia pentru efectuarea interschimbului, realizeaz atribuirile:
k-1 = j-1 = j= k=

adr( Zj ) (10.41) adr( Zk ) (10.42)

adr( Zk+1 ) (10.43) adr( Zj+1 ) (10.44)

ceea ce nseamn c la un moment dat sunt gestionate ase adrese de variabile de tip TL, ale elementelor ce se interschimb, precum i a elementelor adiacente. Sortarea elementelor unei liste Fiind dat o structur de date de tip list (Zj, j), j = 1, 2, ..., n, funcia de sortare transform aceast structur de date ntr-o nou list (Zk, k), k = 1, 2, ..., n astfel nct oricrui k [1, n] N i corespunde un j [1, n] N i numai unul aa nct:cont( Zk ) =
cont ( Zk 1 ) pentru orice k = 1, 2, , n 1. cont( Zj ) i cont ( Zk ) Funcia de sortare apeleaz la rndul ei funcia de interschimb a dou elemente adiacente, pn cnd n final se obine ordinea cresctoare sau descresctoare a termenilor Zi din lista iniial. Un exemplu simplu de sortare, fr a lua n considerare multitudinea de tehnici este:

114

sortare (w) { k = 1; while (k != 0) { k = 0; while (ref (w). != NULL) { if (ref (w).Z > ref (w).ref( ).Z) { k = 1; interschimb (w, w. ); } } } } 5.5. Liste dublu nlnuite Spre deosebire de listele simplu nlnuite care permit parcurgerea de la primul element spre ultimul alocat dinamic, listele dublu nlnuite realizeaz i drumul invers, permind i parcurgerea de la ultimul element ctre primul element. Modelul grafic al listei dublu nlnuite este:

(Zj-1, ...

j-1, j-1)

(Zj , j,

(Zj+1,

j+1, j+1)

...

Fig. 5.16. Model grafic al listei dublu nlnuite sau:

...

...

Fig. 5.17. Model grafic al listei dublu nlnuite Lista dublu nlnuit este de fapt format din dou liste (Zj, proprietile:
adr ( Z j ) adr (
j

j)

i (Uj,

j)

cu

adr ( U j ) adr (
j j j

cont ( Z j ) lg (
j

cont ( U j )

) ) )

cont ( Z j , cont ( Z j , cont (


j 1

adr ( Z j 1 ) adr ( Z j 1 ) cont (


j 1

115

i cu listele dublu nlnuite se efectueaz operaii precum: - inserarea unui element; - adugarea unui element; - tergerea unui element; - inversarea a dou elemente; - tergerea listei; - parcurgerea ntr-un sens i n sensul opus; - transformarea listei n list circular dublu nlnuit. Un exemplu de creare, inserare, cutare, parcurgere i tergere a unei liste dublu nlnuite, este urmtorul program:

# include <iostream.h> # define TINFO int class ListaDubluInlan; class ElementLista { TINFO info; ElementLista *pred, *suc; public: ElementLista(int val=0); friend class ListaDubluInlan; }; class ListaDubluInlan { protected: ElementLista *Prim; public: ListaDubluInlan() { Prim = NULL;} ~ListaDubluInlan(); void traversare_inainte(); void inserare_inceput(TINFO); void inserare_inaintea_unui_elem(TINFO,TINFO); void inserare_dupa_elem(TINFO,TINFO); ElementLista *cautare(TINFO); void suprimare(TINFO); }; ElementLista::ElementLista(TINFO val) { info=val; pred=suc=NULL; } ListaDubluInlan::~ListaDubluInlan() { ElementLista* ptr; 116

while (Prim) { ptr=Prim; Prim=Prim->suc; delete ptr; } } void ListaDubluInlan::traversare_inainte() { ElementLista* ptr=Prim; if (Prim==NULL) cout << "\n Lista este vida!"; else while (ptr) { cout << ptr->info<<" "; ptr=ptr->suc; } cout<<"\n"; } void ListaDubluInlan::inserare_inceput(TINFO val) { ElementLista* ptr=new ElementLista(val); if (ptr==NULL) { cout<<"Eroare la alocare spatiu pentru inserare"; return; } ptr->pred=NULL; ptr->suc=Prim; if(Prim) Prim->pred=ptr; else Prim=ptr; cout<<"Inserare la inceput cu succes!\n"; } void ListaDubluInlan::inserare_inaintea_unui_elem(TINFO X,TINFO cheie) { ElementLista* ptr, *p; if(Prim==NULL) { cout<<"Lista vida! Inserare fara succes!\n"; return; } for(p=Prim;p&&p->info!=cheie;p=p->suc); if(p) {

117

ptr=new ElementLista(X); if (ptr==NULL) { cout<<"Eroare alocare spatiu la inserare"; return; } if(p==Prim) { ptr->pred=NULL; ptr->suc=p; p->pred=ptr; Prim=ptr; } else { ptr->pred=p->pred; ptr->suc=p; p->pred->suc=ptr; p->pred=ptr; } cout<<"Inserare inainte element cu succes!\n"; } else cout<<"Nu exista cheia specificata!\n"; } void ListaDubluInlan::inserare_dupa_elem(TINFO X,TINFO cheie) { ElementLista* ptr, *p; if(Prim==NULL) { cout<<"Lista vida! Inserare fara succes!\n"; return; } for(p=Prim;p&&p->info!=cheie;p=p->suc); if(p) { ptr=new ElementLista(X); if (ptr==NULL) { cout<<"Eroare alocare spatiu la inserare"; return; } ptr->suc=p->suc; ptr->pred=p; if(p->suc) p->suc->pred=ptr; p->suc=ptr; cout<<"Inserare dupa element cu succes!\n"; } else cout<<"Nu exista cheia specificata!\n";

118

} ElementLista *ListaDubluInlan::cautare(TINFO X) { ElementLista *ptr; for(ptr=Prim;ptr&&ptr->info!=X;ptr=ptr->suc); if(ptr) return(ptr); else return(0); } void ListaDubluInlan::suprimare(TINFO X) { ElementLista *ptr; if(Prim==NULL) { cout<<"Lista vida! Suprimare fara succes!\n"; return; } for(ptr=Prim;ptr&&ptr->info!=X;ptr=ptr->suc); if(ptr) { if(ptr==Prim) { Prim=Prim->suc; if (Prim) Prim->pred=NULL; } else { ptr->pred->suc=ptr->suc; if(ptr->suc) ptr->suc->pred=ptr->pred; } delete(ptr); cout<<"Suprimare cu succes!\n"; } else cout<<"Nodul nu exista!\n"; } void main() { ListaDubluInlan Listamea; int opt; TINFO valoare,cheie; do { cout<<"\n Optiuni de lucru cu lista:"; cout<<"\n 1 - Afisare lista";

119

cout<<"\n 2 - Inserare element la inceputul listei"; cout<<"\n 3 - Inserare element inaintea unui elem specificat"; cout<<"\n 4 - Inserare dupa un element specificat"; cout<<"\n 5 - Cautarea unui element specificat"; cout<<"\n 6 - Suprimarea unui element"; cout<<"\n 9 - Terminare lucru \n\n"; cout<<"Introduceti optiunea dorita:"; cin>>opt; switch(opt) { case 1: } cout<<"Traversare lista:"; Listamea.traversare_inainte(); break; } case 2: { cout<<"Introduceti elementul de inserat:"; cin>>valoare; Listamea.inserare_inceput(valoare); break; } case 3: { cout<<"Introduceti elementul de inserat:"; cin>>valoare; cout<<"Introduceti elem inaintea caruia inseram:"; cin>>cheie; Listamea.inserare_inaintea_unui_elem(valoare,cheie); break; } case 4: { cout<<"Introduceti elementul de inserat:"; cin>>valoare; cout<<"Introduceti elementul dupa care inseram:"; cin>>cheie; Listamea.inserare_dupa_elem(valoare,cheie); break; } case 5: { cout<<"Introduceti elementul cautat:"; cin>>valoare; if(Listamea.cautare(valoare)) cout<<"Elementul a fost gasit!\n"; else cout<<"Elementul nu exista in lista!\n"; break;

120

} case 6: { cout<<"Introd elem pe care doriti sa-l suprimati:"; cin>>valoare; Listamea.suprimare(valoare); break; } case 9: break; default: cout<<"\n Nu exista optiunea! \n "; } }while (opt!=9); } Teme de control 1. Sa se realizeze un program C++ pentru crearea si afisarea unei liste circulare simplu nlnuite. 2. Sa se realizeze un program C++ pentru crearea unei liste simplu nlnuite i tergerea elementului de pe poziia k. Bibliografie Ion IVAN, Marius POPA, Paul POCATILU - Structuri de date, Vol. 1 si 2, Editura ASE, Bucuresti, 2008.

121

6. Stive i cozi
Obiectivele unitii de nvare Dup studierea acestei uniti de nvare, studenii i vor nsui cunotine teoretice i vor deprinde abiliti practice de lucru cu stive i cozi, reprezentnd cazuri particulare de liste simple. 6.1. Consideraii privind structurilor de date de tip stiv i coad O stiv, stack n limba englez, este o structur de tip LIFO (Last In First Out = ultimul intrat primul ieit) i este un caz particular al listei liniare n care toate inserrile (depunerile n englez push) i tergerile (sau extragerile -n englez pop) (n general orice acces) sunt fcute la unul din capetele listei, numit vrful stivei. Acest nod poate fi citit, poate fi ters sau n faa lui se poate insera un nou nod care devine cap de stiv.

push

pop

Fig. 6.1. Mecanismul de stiv Pentru nelegerea mecanismului unei stive, se poate folosi reprezentarea manevrrii ntr-un depou a vagoanelor de cale ferat sau a unui vraf de farfurii din care putem depune i extrage doar n i din vrful vrafului. Stivele sunt legate i de algoritmii recursivi, la care salvarea variabilelor dintr-o funcie care se apeleaz recursiv se face avnd la baz un mecanism de tip stiv. O stiv poate fi implementat ca o list liniar pentru care operaiile de acces (inserare, tergere, accesare element) sunt restricionate astfel: - inserarea se face doar n faa primului element al listei, capul listei; - accesarea respectiv tergerea acioneaz doar asupra primului element al listei. O stiv poate fi implementat folosind o structur de date static sau dinamic. n abordarea static, stiva poate fi organizat pe un spaiu de memorare de tip tablou unidimensional. n abordarea dinamic, implementarea unei stive se poate face folosind o structur de tip list liniar simplu nlnuit n care inserarea se va fac e tot timpul n capul listei iar tergerea de asemenea se va face n capul listei. 122

O coad (n englez queue) este o structur de tip FIFO (First In First Out = Primul venit primul servit), n care toate inserrile se fac la un capt al ei (numit capul cozii) iar tergerile (extragerile) (n general orice acces) se fac la cellalt capt (numit sfritul cozii). n cazul cozii, avem nevoie de doi pointeri, unul ctre primul element al cozii (capul cozii), iar altul ctre ultimul su element (sfritul cozii). Exist i o variant de coad circular, n care elementele sunt legate n cerc, iar cei doi pointeri, indicnd capul i sfritul cozii, sunt undeva pe acest cerc.

push

pop

Fig. 6.2. Mecanismul de coad Se face o analogie cu o cale ferat pe un singur sens sau cu obinuita coad la un ghieu oarecare la care primul venit este i primul servit. Pentru gestiunea unei cozi sunt necesari doi indicatori: - un indicator care indic primul element ce urmeaz a fi scos; - un indicator care indic unde va fi pus urmtorul element adugat la coad. ntr-o abordare static, o coad poate fi implementat folosind un spaiu de memorare de tip tablou unidimensional. n acest caz, adugri i tergeri repetate n coad deplaseaz coninutul cozii la dreapta, fa de nceputul vectorului. Pentru evitarea acestei deplasri, o soluie este ca fiecare operaie de extragere din coad s fie acompaniat de deplasarea la stnga a coninutului cozii cu o poziie. n acest caz, primul element care urmeaz s fie eliminat din coad va fi ntotdeauna pe prima poziie, indicatorul care s indice elementul ce urmeaz s fie scos pierzndu-i utilitatea. Dezavantajul acestei soluii l reprezint necesitatea deplasrii ntregului set de elemente din coad rmase cu o poziie. ntr-o abordare dinamic, o coad poate fi implementat folosind o list liniar simplu nlnuit la care operaiile de acces sunt restricionate corespunztor. Exist dou abordri: - una n care adugarea se face tot timpul la nceputul listei liniare simplu nlnuit e iar extragerea se face de la sfritul listei - cea de a doua, n care adugarea se face la sfritul listei iar extragerea se face din capul listei. Pentru implementarea unei cozi, vom folosi aspectele tratate n capitolul de liste liniare. 6.2. Caracteristicile structurilor de date de tip stiv Caracteristicile unei stive sunt puse n eviden prin comparaie cu structura de date de tip list: - n timp ce pentru gestionarea listei se vehiculeaz cu variabile de tip Tp, care stocheaz adresa componentei (Z1, 1) a listei, componenta numit cap de list, n cazul stivei este memorat adresa ultimei componente (Z'n, 'n), numit vrful stivei; - n timp ce cont cont(n) = NULL n cazul listei, cont ('1) = NULL n cazul stivei; - n timp ce succ (Zj) = Zj+1 n cazul listei, n cazul stivei succ(Zj)=Zj-1; - n timp ce pred (Zj) = Zj-1 n cazul listei, n cazul stivei pred(Zj)=Zj+1;

123

n tip ce parcurgerea n cazul listei este de la (Z1, 1) spre (Zn, m), n cazul stivei, parcurgerea este de la (Z'n, 'n) spre ( Z'1, '1 ); - n timp ce disciplina lucrului cu elementele listei este primul venit primul servit (FIFO), n cazul stivei regula de vehiculare a elementelor, este ultimul venit primul servit (LIFO). Se observ c stiva este o list invers. Totui, multe dintre tehnicile de rezolvare a problemelor vehiculeaz stive i nu liste, datorit disciplinei de gestionare a elementelor. Regulile de creare i accesare la nivel de stiv, prevd c fiecrui element care se adaug, i se aloc o zon de memorie i acest element devine vrf al stivei. Exist procese n care informaiile se stocheaz n ordinea opus momentului prelucrrii, iar odat folosit, aceasta devine necesar. Stocnd informaiile ntr-o stiv, dup utilizarea informaiilor coninute n vrful stivei, zona de memorie alocat este eliberat (dealocat), vrful stivei mutndu-se cu o component spre baza stivei. Baza stivei este considerat elementul (Z'1, '1). n funcie de cerinele de prelucrare, se obine o fluctuaie a poziiei vrfului stivei, ceea ce creeaz o imagine mai bun pentru dinamica prelucrrii datelor. Astfel, stivele stau la baza gestionrii adreselor parametrilor ce se transmit n funcii, implementrii mecanismelor de recursivitate din limbaje de programare. n general, ideea de stiv sugereaz prelucrri care au un caracter simetric. De exemplu, se consider o prelucrare complet privind: A. ncrcarea componentelor C1 pentru disponibilizarea de resurse ale unui sistem de calcul; B. ncrcarea componentei C2 care este destinat lansrii n execuie a programului utilizatorului; C. efectuarea deschiderii de fiiere n programul utilizatorului C3; D. prelucrri de date i afiri de rezultate C4; E. nchiderea fiierelor; F. ieirea din programul utilizatorului sub supravegherea componentei C3; G. ieirea din lucru sub componenta C2; H. dezactivarea resurselor sistemului de calcul prin componenta C1. Se construiete stiva din figura 11.3.
vrf de stiv adr (C4) (Z'4, '4)

adr (C3)

(Z'3, '3)

adr (C2)

(Z'2, '2)

adr (C1) NULL

(Z'1, '1) baza activ

Fig. 6.3. Structura unei stive

124

Dup parcurgerea pasului D, vrfului stivei coboar la (Z'3, '3) i componenta (Z'4, '4) este dealocat, deci distrus. Folosind informaiile stocate n pasul C, se efectueaz tergerea fiierelor (pasul E). Vrful stivei se coboar la componenta (Z'2, '2), iar componenta (Z'3, '3) este eliberat.
vrf de stiv e

b a NULL

baza activ

Fig. 6.4. Structura unei stive pentru inversarea caracterelor unui ir Se execut pasul F, care este simetricul pasului C i apoi vrful coboar spre (Z'1, '1) i se activeaz componentele C1 pentru eliberarea resurselor pas simetric pasului A. Astfel, de exemplu, dac dorim s inversm poziia elementelor unui ir {a, b, c, d, e}, prin crearea unei stive care s conin aceste elemente, cu modelul grafic din figura 11.4 i prin apelarea unei funcii de parcurgere cu imprimarea elementelor identificate, se obine exact irul { e, d, c, b, a }. n cazul calculrii lui n!, se folosete formula de recuren: P(n) = P(n-1) * n, cu P(1) = 1, se creeaz o stiv care conine expresiile ce sunt evaluate cu valorile descresctoare ale lui n. Se construiete stiva care are n informaia util, de la baz spre vrf: n, n 1, n 2, , 3, 2, 1. Valoarea iniial a funciei recursive P(1) = 1, permite prin parcurgerea stivei de la vrf spre baz, obinerea rnd pe rnd a rezultatelor: P(2), P(3),,P(n 2), P(n 1 ), P(n) i ntlnindu-se NULL la baza stivei, procesul se ncheie. 6.3. Operaii de baz cu stive tergerea unei stive, revine la dealocarea componentelor care formeaz stiva. Se realizeaz prin parcurgerea integral a stivei, cu dealocarea fiecrui vrf. Adugarea unui element n stiv, se face de regul cu mutarea poziiei vrfului stivei pe componenta adugat. tergerea unui element din stiv, se efectueaz de regul asupra elementului din vrful stivei, aceasta cobornd pe componenta ce l precede. Parcurgerea stivei, se efectueaz de la vrf ctre baz. Operaiile de inserare, de concatenare, de sortare a stivelor, nu difer mult de aceleai operaii din interiorul unei liste, dar frecvena lor de utilizare este mult mai redus.

125

Programul urmtor, exemplific unele dintre operaiile care se execut cu structuri de date de tip stiv.

#include <iostream.h> #include <malloc.h> class elementstiva { public: int info; elementstiva *prec; elementstiva() { prec=NULL; } }; class stiva { public: elementstiva *vs; //varful stivei stiva() { vs=NULL; } void sterg() { elementstiva* aux=vs; if(vs == NULL) cout<<"\n Stiva este vida!"; else { aux = vs->prec; free(vs); while(aux !=NULL) { vs=aux; aux=aux->prec; delete vs; } vs = NULL; } }// void tipar() { elementstiva* aux; if(vs == NULL) cout<<"\n Stiva este vida!"; 126

else { aux = vs; while(aux !=NULL) { cout<<aux->info; aux=aux->prec; } } } void inserare(int el) { elementstiva *aux; aux = new elementstiva(); aux->info = el; aux->prec = vs; vs = aux; }// int extrag() { elementstiva* aux; int el; if (vs == NULL) return -1; else { aux = vs->prec; el = vs->info; delete vs; vs = aux; return el; } } }; void main() { stiva st; char opt; int x,el; do { cout<<"\n Optiuni de lucru cu stiva "; cout<<"\n P - Tiparire stiva";

127

cout<<"\n A - Adaugare element n stiva"; cout<<"\n E - Extragere element din stiva"; cout<<"\n T - Terminare lucru"; cin>>opt; switch(opt) { case 'a': cout<<"element:";cin>>el; st.inserare(el); break; case 'e': { x = st.extrag(); if (x==-1) cout<<"\n Stiva este vida!"; else cout<<"\n Element extras"<<x; } break; case 's': st.sterg(); break; case 'p': st.tipar(); break; default: cout<<"\n Nu exista optiunea!"; } }while (opt!='t'); } Pe baza meniului afiat prin program, se activeaz funciile n orice ordine, iar situaia de stiv vid, este marcat n mod corespunztor. Crearea stivei se realizeaz prin adugri succesive, care presupun alocri dinamice de memorie pentru elementele componente. Procedura de tergere elibereaz toat zona de memorie alocat stivei, iar funcia de extragere, elibereaz zona de memorie ocupat de vrful stivei i iniializeaz cu adresa noului vrf al stivei, variabila VS. 6.4. Evaluarea expresiilor matematice cu ajutorul stivei i cozii Pentru evaluarea expresiilor matematice exist diferii algoritmi. Unul dintre acetia folosete ca structur principal de date stiva. Acest algoritm presupune rearanjarea expresiei ntr-o anumit form astfel nct ordinea operaiilor s fie clar i evaluarea s necesite o singur parcurgere a expresiei. Pentru aceasta se poate folosi forma polonez, inventat de matematicianul de origine polonez Jan Lukasiewicz. Acesta presupune scrierea operatorilor naintea operanzilor. Aceast form mai are o variant numit scrierea polonez invers n care operatorii sunt scrii n urma operanzilor.

128

Tabel 6.1. Forme ale scrierii unei expresii matematice Expresia n forma Expresia n forma Expresia matematic polonez (scriere polonez invers (scriere infixat) prefixat) (scriere postfixat) 4+5 +45 45+ 4+5*5 4*2+3 4+2+3 4 * (2 + 3) +4*55 +*423 ++423 *4+23 455*+ 42*3+ 42+3+ 423+*

Dup cum se vede din tabelul 6.1, ordinea operanzilor nu se schimb, ei gsindu-se n aceeai ordine ca n expresia matematic. Forma polonez invers are avantaje fa de scrierea prefixat sau infixat: - ordinea n care se efectueaz operaiile este clar; - parantezele nu mai sunt necesare; - evalurile sunt uor de efectuat cu ajutorul calculatorului. Un algoritm de transformare din expresie matematic n scriere postfixat a fost dezvoltat de ctre Edsger Dijkstra (algoritmul macazului al lui Dijkstra Dijkstra Shunting Algorithm). Acest algoritm utilizeaz o stiv n care sunt pstrai operatorii i din care sunt eliminai i transferai n scrierea postfixat. Fiecare operator are atribuit o ierarhie dup cum este prezentat n tabelul 6.2. Tabel 6.2. Ierarhia operatorilor Operator Ierarhie ([{ )]} +*/ 1 2 3 4

Algoritmul este: se iniializeaz stiva i scrierea postfixat; att timp ct nu s-a ajuns la sfritul expresiei matematice: - se citete urmtorul element din expresie; - dac este valoare se adaug n scrierea postfixat; - dac este ( se introduce n stiv; - dac este ) se transfer elemente din stiv n scrierea postfixat pn la (; - altfel: att timp ct ierarhia operatorului din vrful stivei este mai mare ierarhia operatorului curent, se trece elementul din vrful stivei n scrierea postfixat; se introduce operatorul curent n stiv. se trec toi operatorii rmai pe stiv n scrierea postfixat. Avnd expresia sub aceast form, se face evaluarea ei. Algoritmul de evaluare folosete tot o stiv pe care sunt pstrai de aceast dat operanzii. Algoritmul este: se iniializeaz stiva; 129

att timp ct nu s-a ajuns la sfritul scrierii postfixate: - se citete urmtorul element; - dac este valoare se depune pe stiv; - altfel (este operator): se extrage din stiv elementul y; se extrage din stiv elementul x; se efectueaz operaia x operator y; se depune rezultatul pe stiv; ultima valoare care se afl pe stiv este rezultatul expresiei. De exemplu, pentru expresia n scriere postfixat: 4 8 2 3 * - 2 / +, algoritmul se execut ca n figura 6.5:
Elementul citit 4 8 Stiva 4 8 4 2 8 4 3 2 8 4 6 8 4 2 4 2 2 4 1 4 5

Fig. 6.5. Execuia algoritmului Programul care realizeaz evaluarea expresiilor matematice prin algoritmii prezentai a fost realizat folosind programarea orientat pe obiecte. Astfel exist o clas numit stiva care are rolul de a implementa operaiile cu stiva necesare att la generarea expresiei n scriere postfixat ct i la evaluarea expresiei scris n aceast form. Acest obiect are la baz clasa deque (double ended que coad cu 2 capete list dublu nlnuit) care se gsete n biblioteca standard de C++. Fiind nevoie de dou tipuri de stiv (una care pstreaz operanzi i una care pstreaz valori reale) s-a construit o clas stiv template. Pentru pstrarea formei poloneze inverse se utilizeaz o structur de date numit coad care este implementat tot printr-un obiect care are la baz clasa deque. n cadrul acestei clase sunt implementate funcii care ajut la programarea algoritmului. Exist funcii care verific dac primul element din coad reprezint o valoare (eNumar()), extrag primul element ca valoare (getNumar()).

130

Mai exist o clas fisier care are rolul de a parcurge fiierul n care se afl expresie aritmetic ce trebuie evaluat. Teme de control 1. S se creeze si traverseze o stiva ale carei informatii utile sunt pointeri la un articol de tip carte. 2. S se creeze si traverseze o coada ale carei informatii utile sunt articole student. 3. S se realizeze conversia din stiva de carti in lista dubla de carti. Bibliografie Ion IVAN, Marius POPA, Paul POCATILU - Structuri de date, Vol. 1 si 2, Editura ASE, Bucuresti, 2008.

131

7. Arbori binari i arbori de cutare


Obiectivele unitii de nvare Dup studierea acestei uniti de nvare, studenii i vor nsui cunotine teoretice i vor deprinde abiliti practice de lucru cu arbori binari i arbori de cutare. 7.1. Structura de date de tip arborescent Construirea unei structurii de date de tip arborescent pornete de la problema pe care o avem de rezolvat. Pentru gsirea soluiei ecuaiei f ( x ) 0 pentru x [ a, b ] se efectueaz cu metode de calcul numeric, dintre care cea mai simpl este aceea a njumtirii intervalului. Se calculeaz: c (a b) / 2 Dac: f ( a ) *f ( c ) 0 nseamn c rdcina x [ a, c ) , n caz contrar x (c, b ] . Noul subinterval este i el divizat. Astfel, avem imaginea unei modaliti de lucru ierarhizat pe attea niveluri cte sunt necesare obinerii unei precizii pentru soluia ecuaiei f ( x ) 0 . Se asociaz acestor niveluri reprezentarea grafic din figura 12.1.
[ a, b ]

[a ,

a+b 2

a+b 2

,b]

[a ,

3a + b 4

3a + b 4

a+b 2

a+b 2

a + 3b 4

a + 3b 4

,b]

Fig. 7.1. Reprezentarea grafic asociat divizrii intervalelor Pentru evaluarea expresiei aritmetice: (a b c d ) ( x y ) e c d prin modul de efectuare a calculelor conduce la reprezentarea din figura 7.2.

132

* / * c x + a b c + d y d e

Fig. 7.2. Reprezentarea arborescent asociat evalurii expresiei Pentru regsirea crilor ntr-o bibliotec, n fiierul tematic cotele se structureaz pe nivele, ca n exemplul din figura 7.3.
biblioteca 1xxxxx : 7xxxxx

matematica 1xxxxx : 23xxxx algebra 1xxxxx / 197xxx geometrie 198xxx / 23xxxx

calculatoare 24xxxx : 51xxxx hardware 24xxxx / 419xxx software 420xxx / 51xxxx

economie 52xxxx : 7xxxxx teorie 52xxxx / 680xxx software 681xxx / 7xxxxx

Fig. 7.3. Structura asociat fiierului de cote ale crilor Reprezentarea n memorie a informaiilor care au multiple legturi ntre ele, trebuie s fie astfel fcut, nct s poat realiza parcurgerea complet a zonelor sau localizarea unui element din structur. Se observ c n arborescen exist un nod numit rdcin sau printe. Acesta are descendeni. Fiecare descendent poate fi la rndul su printe i n acest caz are descendeni. Arborele binar este caracterizat prin aceea c, orice nod al su are un singur printe i maxim doi descendeni. Fiecare nod este reprezentat prin trei informaii i anume: informaia util care face obiectul prelucrrii, ea descriind elementul sau Z fenomenul asociat nodului; informaia care localizeaz nodul printe; informaia (informaiile) care localizeaz nodul (nodurile) descendent (descendente). Alocarea dinamic determin ca i s fie variabile aleatoare, ale cror legi de repartiie sunt necunoscute. Valorile lor sunt stocate n zona de memorie, formnd mpreun 133

cu variabila Z structura de date asociat unui nod, structur ce corespunde tripletului (Zj, j, j) asociat nodului j dintr-un graf de form arborescent binar. Volumul informaiilor destinate localizrii nodurilor, depinde de obiectivele urmrite i de rapiditatea cu care se dorete localizarea unui anumit nod. Cu ct diversitatea prelucrrilor este mai mare, cu att se impune stocarea mai multor informaii de localizare.

Zj-1

j-1

j-1

Zj Zj + 1

j j

adr (Z j - 1 ) adr (Z j 1 )

j+1

j-1

Fig. 7.4. Modelul grafic asociat tripletului (Zj,

j,

j)

Dac tripletul (Zj, j, j) ce corespunde modelului grafic din figura 7.4 permite baleierea de sus n jos, sau de jos n sus, pe o ramur a arborelui. Pentru a parcurge pe orizontal arborele, este necesar att o informaie suplimentar de localizare j , care corespunde nodului vecin din dreapta de pe acelai nivel, ct i informaia j pentru localizarea nodului din stnga de pe acelai nivel. Un astfel de arbore se descrie prin: ( Z j , j, j, j, j ) Descrierea complet a unui arbore, presupune crearea mulimii tripletelor sau cvintuplelor asociate nodurilor. De exemplu, arborescenei asociate evalurii expresiei: e = a + b + c prezentate n figura 7.5 i corespunde alocarea i iniializarea zonelor de memorie din figura 7.6. =

+ e c a + b

Fig. 7.5. Structura arborescent asociat evalurii expresiei e

134

= +

null e null

null

null

null

Fig. 7.6. Alocarea i iniializarea zonelor de memorie pentru implementarea structurii arborescente Acestor reprezentri grafice, trebuie s le corespund algoritmi pentru ncrcarea adreselor, ntruct a construi forma liniarizat a structurii arborescente, nseamn n primul rnd, a aloca zone de memorie i n al doilea rnd, a iniializa membrii structurii cu adrese care s permit reconstruirea corect a nlnuirii. Se observ c pentru iniializarea corect, este necesar existena la un moment dat a pointerilor spre trei zone de memorie alocate dinamic i anume: pointerul spre zona alocat nodului precedent (printe); pp pointerul spre zona asociat nodului curent; pc pointerul spre zona asociat primului descendent; pd Construirea nodului ( Zj, j, j ) revine la efectuarea iniializrilor:
ref (pc) . Z ref (pc) . ref (pc) . pp pc pc; pd;
j j j

valoare_citita; pp; pd;

new (pd); Dup efectuare secvenei se face trecerea la pasul urmtor. Folosind simbolurile, orice structur arborescent se reprezint prin: N mulimea nodurilor A mulimea arcelor De exemplu, arborescena dat pentru calculul expresiei se reprezint prin: N = { e , = , a, +, b, + , c } i A = { (a+) , (b+) , (c+) , (+, =) , (=, e) } Se observ c pentru fiecare element al mulimii N, se aloc dinamic zona de memorie care conine i informaiile j, j . La alocare este posibil numai iniializarea zonei Zj. Odat cu alocarea se iniializeaz i un vector de pointeri, cu adresele corespunztoare zonelor de memorie puse n coresponden cu nodurile. n continuare, prelund elementele mulimii A, are loc completarea cmpurilor j i j . De exemplu, pentru arborele binar din

135

figura 12.7 construirea se efectueaz prin alocarea a 7 zone de memorie cu structura ( Z j , j, j ) pentru nodul din mulimea N { A, B, C, D, E, F, G } i se iniializeaz vectorul de pointeri pp[i] dup cum urmeaz: pp [1] adr (ZA) pp [2] adr (ZB) pp [3] adr (ZC) pp [4] adr (ZD) pp [5] adr (ZE) pp [6] adr (ZF) pp [7] adr (ZG) Baleierea mulimii: A { (AB), (AC), (BD), (BE), (CF), (CG) } revine la efectuarea atribuirilor: ref (pp[1] ). pp[2] ref (pp[2] ). pp[4] ref (pp[3] ). pp[6] ref (pp[4] ). ref (pp[5] ). ref (pp[6] ). ref (pp[7] ). NULL (12.11) ref (pp[2] ). pp[1] ref (pp[4] ). pp[2] ref (pp[3] ). pp[1] ref (pp[6] ). pp[3] ref (pp[7] ). pp[3] ref (pp[1] ). ref (pp[1] ). NULL

A B D C

Fig. 7.7. Arbore binar Pentru arborii binari echilibrai, exist posibilitatea ca dup alocarea memoriei s se descrie elementele mulimii A, dup care iniializrile cmpurilor j i j s se fac prin apelarea unei funcii. Problematica devine mai simpl, dac arborele binar este complet, adic are n niveluri la baz 2(n-1) elemente descendente, fr a fi printe, noduri terminale. n programele C/C++, pentru implementarea structurilor de date necontigue, arborele binar se definete prin urmtorul tip de baz derivat.

struct arbore { int valoare; arbore *precedent; arbore *urmator;

136

}; class arb { arbore *rad; }; struct arbore_oarecare { int valoare; arbore_oarecare **fii; //lista fiilor unui nod int nrfii; //nr de fii ai nodului }; class arb_oarecare { arbore_oarecare *rad; }; struct arbore_binar { int valoare; arbore_binar *stanga; arbore_binar *dreapta; }; class arb_binar { arbore_binar *rad; }; 7.2. Transformarea arborilor oarecare n arbori binari Aplicaiile concrete, asociaz unor obiecte, subansamble sau procese, structuri de tip arborescent care nu sunt binare, astfel nct se contureaz ideea ca arborii binar sunt cazuri particulare de arbori, mai ales prin frecvena cu care sunt identificai n practic. Mecanismele de realizare i de utilizare a arborilor binar i fac practice, dei n realitate au frecvena d e apariie sczut. Apare problema transformrii unei structuri arborescente oarecare ntr-o structur arborescent binar, problema rezolvabil prin introducerea de noduri fictive. Astfel, fiind dat arborescena din figura 7.8, transformarea ei n structur arborescent binar, revine la introducerea nodurilor fictive, x, y, u, v, w, rezultnd arborele din figura 7.9.

137

g
A

Fig. 7.8. Structur arborescent oarecare

Fig. 7.9. Structur arborescent cu noduri fictive Arborele oarecare, are un numr de noduri mai mic dect arborele binar, nodurilor fictive corespunzndu-le zone de memorie structurate (NULL, j, j). Alocarea dinamic presupune, ca n zona [Di, Df] prin dealocare s apar goluri, adic zone libere ce sunt realocate altor variabile. Este necesar o gestionare a acestor zone i periodic trebuie s se efectueze o realocare prin reorganizare, aa fel nct s dispar golurile rezultate n procesul de alocare-dealocare multipl. De exemplu, pentru un program P, este necesar alocarea a 3 zone de memorie de 1500, 2000, 4000 baii, ce corespund arborilor binar A, B i C. Alocarea este efectuat iniializnd variabilele pointer pA, pB i pC prin apelul succesiv al funciei alocare( ), (pas 1). Dealocarea dup un timp a arborelui binar B, determin apariia unui gol ntre zonele ocupate de variabilele A i C, (pas 2).

138

Alocarea unei zone de memorie pentru arborii binari D (3000 baii) i E (1000 baii), revin la a dispune pe D n continuarea lui C i a intercala arborele E ntre A i C, n golul rezultat din dealocarea lui E, rmnnd ntre E i C un gol de 300 baii, (pas 3). Dac se pstreaz informaiile privind modul de iniializare a variabilelor pointer care stocheaz adresele nodurilor rdcin a arborilor A, E, C i D, este posibil glisarea informaiilor n aa fel nct s dispar golul dintre E i C. Nu s-a luat n considerare nsi necontiguitatea din interiorul fiecrui arbore. n practic, apare problema optimizrii dispunerii variabilelor dinamice, dar i cea a alegerii momentului n care dispersia elementelor atinge un astfel de nivel, nct zona pentru alocare dinamic este practic inutilizabil i trebuie reorganizat. 7.3. Arbori binari de cutare Un arbore binar de cutare este un arbore binar care are proprietatea c prin parcurgerea n inordine a nodurilor se obine o secven monoton cresctoare a cheilor (cheia este un cmp ce servete la identificarea nodurilor n cadrul arborelui). Cmpul cheie este singurul care prezint interes din punct de vedere al operaiilor care se pot efectua asupra arborilor de cutare. Principala utilizare a arborilor binari de cutare este regsirea rapid a unor informaii memorate n cheile nodurilor. Pentru orice nod al unui arbore de cutare, cheia acestuia are o valoare mai mare dect cheile tuturor nodurilor din subarborele stng i mai mic dect cheile nodurilor ce compun subarborele drept.
20

10

25

22

26

Fig. 7.10. Arbore binar de cutare Structura de date folosit pentru descrierea unui nod al unui arbore binar de cutare va fi urmtoarea: struct arbore_binar { int cheie; arbore_binar *stanga; arbore_binar *dreapta; }; class arb_binar { arbore_binar *rad; };

139

Rdcina arborelui binar de cutare va fi definit n felul urmtor: arb *Radacina = NULL; Se observ ca fiecare nod este compus din cheia asociat i din informaiile de legtur care se refer eventualii fii. Aa cum le spune i numele, arborii binari de cutare sunt folosii pentru regsirea rapid a informaiilor memorate n cheile nodurilor. De aceea cutarea unui nod cu o anumit valoarea a cheii este o operaie deosebit de important. Cutarea ncepe cu nodul rdcin al arborelui prin compararea valorii cheii cutate cu valoarea cheii nodului curent. Dac cele dou valori coincide, cutarea s-a ncheiat cu succes. Dac informaia cutat este mai mic dect cheia nodului, cutarea se continu n subarborele stng. Dac cheia cutat este mai mare dect valoarea cheii nodului, cutarea se reia pentru subarborele drept. Crearea unui arbore binar de cutare presupune adugarea cte unui nod la un arbore iniial vid. Dup inserarea unui nod, arborele trebuie s rmn n continuare ordonat. Din acest motiv, pentru adugarea unui nod se parcurge arborele ncepnd cu rdcina i continund cu subarborele stng sau drept n funcie de relaia de ordine dintre cheia nodului i cheia de inserat. Astfel, dac cheia de inserat este mai mica dect cheia nodului, urmtorul nod vizitat va fi rdcina subarborelui stng. n mod similar, dac cheia de inserat este mai mare dect cheia nodului, traversarea se va continua cu nodul rdcin al subarborelui drept. Aceast modalitate de traversare se continu pn n momentul n care se ajunge la un nod fr descendent. Acestui nod i va fi adugat un nod fiu cu valoarea dorit a cheii. Aplicaiile care utilizeaz arbori binari de cutare pot permite sau pot interzice, n funcie de filozofia proprie, inserarea n cadrul arborelui a unei chei care exist deja. Inserarea n cadrul arborelui anterior a unui nou nod cu valoarea cheii egal cu 12 conduce ctre urmtorul arbore, figura 7.11.
20

10

25

12

22

26

Fig. 7.11. Arbore binar de cutare dup inserarea unui nod Maniera uzual de inserare a unui nod ntr-un arbore binar de cutare este cea recursiv. n practic, de cele mai multe ori cutarea i inserarea se folosesc mpreun. Astfel, n cazul n care cutarea unei chei s-a efectuat fr succes, aceasta este adugat la arborele binar de cutare. O alt operaie care se poate efectua asupra unui arbore binar de cutare este tergerea unui nod care are o anumit valoare a cheii. Dac valoarea cheii este gsit n cadrul arborelui, nodul corespunztor este ters. Arborele trebuie s rmn arbore de cutare i dup tergerea nodului. n ceea ce privete nodul care va fi ters, acesta se va ncadra ntr-una din variantele urmtoare: a) nodul nu are subarbori (fii); b) nodul are doar subarbore stng; c) nodul are doar subarbore drept; 140

d) nodul are att subarbore stng ct i subarbore drept. n cazul n care nodul nu are nici subarbore stng dar nici subarbore drept (varianta a) este necesar doar tergerea nodului. Nu sunt necesare alte operaiuni de actualizare a arborelui.

Fig. 7.12. Arbore binar de cutare nainte de tergerea unui nod n figura 7.12 este prezentat un fragment al unui arbore binar de cutare din care dorim s tergem nodul din dreapta, iar n figura 7.13 se prezint acelai fragment de arbore dar dup tergerea nodului dorit.

Fig. 7.13. Arbore binar de cutare dup tergerea unui nod Pentru cazurile b i c (nodul pe e dorim s-l tergem are subarbore stng sau drept), pe lng tergerea nodului este necesar i actualizarea legturilor dintre nodurile arborelui. n figurile 7.14 i 7.16 se prezint cte un fragment dintr-un arbore binar de cutare din care dorim s tergem nodul evideniat. n figura 7.14, nodul pe care dorim s-l tergem are numai subarbore stng iar cel din figura 7.16 are doar subarbore drept. n figurile 7.15 i 7.17 se pot observa efectele operaiei de tergere.

Fig. 7.14. Subarbore numai cu descendent stng

141

Fig. 7.15. Subarbore dup efectuarea tergerii

Fig. 7.16. Subarbore numai cu descendent drept

Fig. 7.17. Subarbore dup efectuarea tergerii Pentru aceste prime trei cazuri, actualizarea arborelui se face n felul urmtor: fiul nodului care va fi ters, dac exist, va deveni fiul tatlui acestuia. Actualizarea arborelui este urmat de tergerea nodului din memorie. Cazul n care nodul ce se dorete a fi ters are att subarbore stng ct i subarbore drept necesit o tratare special. Astfel, mai nti se localizeaz fie cel mai din stnga fiu al subarborelui drept fie cel mai din dreapta fiu al subarborelui drept. Cheile acestor noduri sunt valoarea imediat urmtoare cheii nodului ce se dorete a fi ters i valoarea precedent. Dup suprimarea nodului dorit, arborele va trebui s rmn n continuare arbore de cutare ceea ce nseamn c relaia de ordine dintre nodurile arborelui va trebui s se pstreze. Pentru aceasta, unul din nodurile prezentate anterior va trebui adus n locul nodului care se dorete a fi ters dup care are loc tergerea efectiv a nodului dorit. Determinarea celui mai din dreapta fiu al subarborelui stng se face parcurgnd subarborelui stng prin vizitarea numai a fiilor din dreapta. Primul nod care nu are subarbore drept este considerat ca fiind cel mai din dreapta nod al subarborelui stng.

142

n figura 7.18 se prezint un fragment de arbore binar de cutare din care dorim s suprimm nodul evideniat care are doi descendeni. Presupunem ca nodul haurat este cel mai din dreapta fiu al subarborelui stng. Acest nod va lua locul nodului care se va terge. n figura 7.19 este reprezentat fragmentul de arbore dup efectuarea operaiei de tergere a nodului dorit.

Fig. 7.18. Subarbore cu doi descendeni

Fig. 7.19. Subarbore dup efectuarea tergerii 7.4. Aplicaii care utilizeaz structura de date de tip arbore binar de cutare Asupra arborilor binari de cutare pot fi efectuate o serie de operaii, dintre care o parte sunt specifice tuturor structurilor de date compuse (adugare element, tergere elemente) iar altele sunt specifice acestui tip de structur de date. De asemenea se remarc operaii la nivel de element (nod), precum i operaii care implic ntregul arbore. Programul urmtor, exemplific o modalitate de creare a unui arbore binar de cutare, stergere noduri, traversare, numarare noduri i de tiprire a acestuia:

#include <iostream.h> #include <conio.h> #include <stdio.h>

143

#include <malloc.h> #include <string.h> struct nod { int info; nod *stg,*drt; }; class arbbin { nod *rad; int stergere(nod *&); void stergere_nod(nod*&,int); public: arbbin() {rad=NULL;}; ~arbbin() {rad=NULL;}; void traversare_srd(nod*); void srd(); void traversare_rsd(nod *); void rsd(); void traversare_sdr(nod *); void sdr(); int sumaFrunze(nod*); int sFrunza(); int numara(nod *); int numara_nod(); void print(nod *); void tiparire(); void salvare(); nod *inserare_nod(nod *,int ); void operator + (int); void operator - (int); arbbin &operator >(FILE *); arbbin &operator <(FILE *); void inserare_cuv(nod *& ,char*); void insert(char *); nod *operator [] (int); }; nod *arbbin::inserare_nod(nod *rad,int k) { if (rad) { if (k<rad->info) rad->stg=inserare_nod(rad->stg,k); else if (k>rad->info) rad->drt=inserare_nod(rad->drt,k); else printf("\nNodul exista in arbore!");

144

return rad; } else { nod *p=new nod; p->stg=NULL; p->drt=NULL; p->info=k; return p; } } void arbbin::operator +(int k) { rad=inserare_nod(rad,k); } void arbbin::traversare_srd(nod *rad) { if (rad) { traversare_srd(rad->stg); printf(" %d",rad->info); traversare_srd(rad->drt); } } void arbbin::srd() { nod *p; p=rad; traversare_srd(p); } void arbbin::traversare_rsd(nod *rad) { if (rad) { printf(" %d",rad->info); traversare_rsd(rad->stg); traversare_rsd(rad->drt); } } void arbbin::rsd() { nod *p; p=rad; traversare_rsd(p); }

145

void arbbin::traversare_sdr(nod *rad) { if (rad) { traversare_sdr(rad->stg); traversare_sdr(rad->drt); printf(" %d",rad->info); } } void arbbin::sdr() { nod *p; p=rad; traversare_sdr(p); } void arbbin::print(nod *rad) { if (rad) { printf("%d",rad->info); if((rad->stg)||(rad->drt)) { printf("("); print(rad->stg); printf(","); print(rad->drt); printf(")"); } } else printf("-"); } void arbbin::tiparire() { nod *p; p=rad; print(p); } int arbbin::sumaFrunze(nod *rad) { if (rad) if(!rad->stg&&!rad->drt) return rad->info; else return sumaFrunze(rad->stg)+sumaFrunze(rad->drt);

146

else return 0; } int arbbin::sFrunza() { nod *p; p=rad; return sumaFrunze(p); } int arbbin::numara(nod *rad) { if (rad) return 1+numara(rad->stg)+numara(rad->drt); else return 0; } int arbbin::numara_nod() { nod *p; int nr; p=rad; nr=numara(p); return nr; } void arbbin::stergere_nod(nod *&rad,int inf) { nod *aux; if (!rad) printf("\nNodul nu exista in arbore!"); else if (inf<rad->info) stergere_nod(rad->stg,inf); else if (inf>rad->info) stergere_nod(rad->drt,inf); else { aux=rad; if (!aux->stg) { rad=aux->stg; delete(aux); } else if(!aux->drt) { rad=aux->drt; delete(aux); } else

147

rad->info=stergere(rad->stg); } } int arbbin::stergere(nod *&p) { if (p->stg) return stergere(p->stg); else { nod *q=p; int inf=q->info; p=p->stg; delete(q); return inf; } } void arbbin::operator -(int inform) { nod *nou; nou=rad; stergere_nod(nou,inform); } nod *arbbin::operator [] (int inf) { nod *aux; aux=rad; while(aux&&aux->info!=inf) { if (inf<aux->info) aux=aux->stg; else if (inf>aux->info) aux=aux->drt; }; if (aux&&aux->info==inf) cout<<"\nNodul cautat exista in arbore!"; else cout<<"\nNodul cautat nu exista in arbore!"; return aux; } void meniu() { cout<<"\n 1.ADAUGARE NOD DE LA TASTATURA"; cout<<"\n 2.STERGERE NOD DE LA TASTATURA"; cout<<"\n 3.CAUTARE NOD IN ARBORE";

148

cout<<"\n cout<<"\n cout<<"\n cout<<"\n cout<<"\n }

4.TRAVERSARI ARBORE-SRD(),RSD(),SDR()"; 5.SUMA INFORMATIILOR DIN FRUNZE "; 6.NUMARUL NODURILOR DIN ARBORE"; 7.AFISARE ARBORE"; 0.TERMINARE";

void main() { arbbin arb; int informatie; char optiune; arb+10;arb+7;arb+15;arb+9;arb+3;arb+8;arb+25; meniu(); } } Teme de control 1. S se scrie funcia pentru numrarea elementelor dintr-un arbore binar de cutare. 2. S se scrie funcia pentru determinarea elementului minim i elementului maxim dintr-un arbore binar de cutare. Bibliografie Ion IVAN, Marius POPA, Paul POCATILU - Structuri de date, Vol. 1 si 2, Editura ASE, Bucuresti, 2008.

149

8. Arbori B
Obiectivele unitii de nvare Dup studierea acestei uniti de nvare, studenii i vor nsui cunotine teoretice i vor deprinde abiliti practice de lucru cu arbori B. 8.1. Arbori B. Definiie. Proprieti. n cazul sistemelor de gestiune a bazelor de date relaionale (SGBDR) este important ca pe lng stocarea datelor s se realizeze i regsirea rapid a acestora. n acest scop sunt folosii indecii. Un index este o colecie de perechi <valoare cheie, adresa articol>. Scopul primar al unui index este acela de a facilita accesul la o colecie de articole. Un index se spune ca este dens dac el conine cte o pereche <valoare cheie, adresa articol> pentru fiecare articol din colecie. Un index care nu este dens uneori este numit index rar. Structura de date foarte des folosit pentru implementarea indecilor este arborele de cutare. Articolele memorate pot fi orict de complexe, dar ele conin un cmp numit cheie ce servete la identificarea acestora. S notm cu C mulimea cheilor posibile ce vor trebui regsite cu ajutorul arborelui de cutare. Dac arborele de cutare este astfel construit nct folosete o relaie de ordine total pe C, atunci vom spune c arborele de cutare este bazat pe ordinea cheilor. Arborii de cutare, bazai pe ordinea cheilor, sunt de dou feluri: arbori binari de cutare (au o singur cheie asociat fiecrui nod) sau arbori multici de cutare (au mai multe chei asociate fiecrui nod). Performanele unui index se mbuntesc n mod semnificativ prin mrirea factorului de ramificare a arborelui de cutare folosit. Arborii multici de cutare sunt o generalizare a arborilor binari de cutare. Astfel, unui nod oarecare, n loc s i se ataeze o singur cheie care permite ramificarea n doi subarbori, i se ataeaz un numr de m chei, ordonate strict cresctor, care permit ramificarea n m + 1 subarbori. Numrul m difer de la nod la nod, dar n general pentru fiecare nod trebuie s fie ntre anumite limite (ceea ce va asigura folosirea eficient a mediului de stocare). Cele m chei ataate unui nod formeaz o pagin. Determinarea poziiei cheii cutate n cadrul unui nod se realizeaz secvenial n cazul paginilor cu numr mic de chei sau prin cutare binar. Un exemplu de arbore multici de cutare de ordin 3 este dat n figura 8.1.

50

100

20

40

70

120

140

15

30

42

43

60

65 0

110

130

136

150

18

26

36

58

62

69

145

Fig. 8.1. Arbore multici de cutare de ordin 3 Un arbore multici de cutare care nu este vid are urmtoarele proprieti: fiecare nod al arborelui are structura dat n figura 8.2;

150

P0

K0

P1

K1

P2

Pn - 1

Kn - 1

Pn

Fig. 8.2. Structura de nod pentru un arbore multici de cutare de ordin n unde P0, P1, , Pn sunt pointeri ctre subarbori i K0, K1, , Kn 1 sunt valorile cheilor. Cerina ca fiecare nod s aib un numr de ramificaii mai mic sau egal dect m conduce la restricia n m 1. valorile cheilor ntr-un nod sunt date n ordine cresctoare: Ki < Ki + 1, i 0, n 2 toate valorile de chei din nodurile subarborelui indicat de Pi sunt mai mici dect valoarea cheii K , i 0, n 1 .
i

toate valorile de chei din nodurile subarborelui indicat de Pn sunt mai mari dect valoarea de cheie Kn 1. subarborii indicai de P , i 0, n sunt de asemenea arbori multici de cutare. Prima oar arborele B a fost descris de R. Bayer i E. McCreight n 1972. Arborii B rezolv problemele majore ntlnite la implementarea arborilor de cutare stocai pe disc: au ntotdeauna toate nodurile frunz pe acelai nivel (cu alte cuvinte sunt echilibrai dup nlime); operaiile de cutare i actualizare afecteaz puin blocuri pe disc; pstreaz articolele asemntoare n acelai bloc pe disc; garanteaz ca fiecare nod din arbore va fi plin cu un procent minim garantat. Un arbore B de ordin m este un arbore multici de cutare i are urmtoarele proprieti: i. ii. iii. iv. toate nodurile frunz sunt pe acelai nivel; rdcina are cel puin doi descendeni, dac nu este frunz; m fiecare pagin conine cel puin 2 chei (excepie face rdcina care poate avea mai puine chei, dac este frunz); nodul este fie frunz, fie are n + 1 descendeni (unde n este numrul de chei m din nodul respectiv, cu 2 n m-1 );
i

fiecare pagin conine cel mult m-1 chei; din acest motiv, un nod poate avea maxim m descendeni. Proprietatea i menine arborele balansat. Proprietatea ii foreaz arborele s se ramifice devreme. Proprietatea iii ne asigur c fiecare nod al arborelui este cel puin pe jumtate plin. nlimea maxim a unui arbore B d marginea superioar a numrului de accese la disc necesare pentru a localiza o cheie. Se consider h nlimea maxim a unui arbore B cu N chei, unde valoarea indicatorului este dat de relaia: N 1 h log m 2 2 Ca exemplu, pentru N = 2.000.000 i m = 20, nlimea maxim a unui arbore B de ordin m va fi 3, pe cnd un arborele binar corespondent va avea o nlime mai mare de 20.

v.

151

8.2. Operaii de baz ntr-un arbore B Procesul de cutare ntr-un arbore B este o extindere a cutrii ntr-un arbore binar. Operaia de cutare n arborele B se realizeaz comparnd cheia cutat x cu cheile nodului curent, plecnd de la nodul rdcin. Dac nodul curent are n chei, atunci se disting urmtoarele cazuri: c < x < c , i 1, n se continu cutarea n nodul indicat de P ;
i i +1 i

cn < x se continu cutarea n nodul indicat de Pn; x < c0 se continu cutarea n nodul indicat de P0. Lungimea maxim a drumului de cutare este dat de nlimea arborelui. Fiecare referire a unui nod implic selecia unui subarbore. Arborele B suport cutarea secvenial a cheilor. Arborele este traversat secvenial prin referirea n inordine a nodurilor. Un nod este referit de mai multe ori ntruct el conine mai multe chei. Subarborele asociat fiecrei chei este referit nainte ca urmtoarea cheie sa fie accesata. Arborii B sunt optimi pentru accesul direct la o cheie. Pentru accesul secvenial la o cheie nu se obin performane satisfctoare. Condiia ca toate frunzele s fie pe acelai nivel duce la un comportament caracteristic arborilor B: fa de arborii binari de cutare, arborilor B nu le este permis s creasc la frunze; ei sunt forai sa creasc la rdcin. Operaia de inserare a unei chei n arborele B este precedat de operaia de cutare. n cazul unei cutri cu succes (cheia a fost gsit n arbore) nu se mai pune problema inserrii ntruct cheia se afla deja n arbore. Dac cheia nu a fost gsit, operaia de cutare se va termina ntr-un nod frunz. n acest nod frunz se va insera noua cheie. Funcie de gradul de umplere al nodului frunz afectat, se disting urmtoarele cazuri: nodul are mai puin de m 1 chei; inserarea se efectueaz fr s se modifice structura arborelui ; nodul are deja numrul maxim de m 1 chei; n urma inserrii nodul va avea prea multe chei, de aceea el va fisiona. n urma fisionrii vom obine dou noduri care se vor gsi pe acelai nivel i o cheie median care nu se va mai gsi n nici unul din m cele dou noduri. Cele 2 chei din stnga rmn n nodul care fisioneaz. Cele din dreapta vor forma cel de-al doilea nod. Cheia median va urca n nodul printe, care la rndul lui poate s fisioneze. Dac i rdcina fisioneaz, atunci cheia median va da natere la o nou rdcin. Se observ c procesul de inserare a unei chei garanteaz c fiecare nod intern va avea cel puin jumtate din numrul maxim de descendeni. n urma operaiilor de inserare arborele va deveni mai nalt i mai lat, figura 8.3.
m 2

Fig. 8.3. Modificrile dimensionale ale unui arbore B dup efectuarea inserrii 152

S considerm un arbore B de ordin 5 (deci numrul maxim de chei dintr-un nod va fi 4). n urma inserrii valorilor de cheie 22, 57, 41, 59 nodul rdcin va fi cel din figura 8.4. 22 41 57 59 Fig. 8.4. Structura nodului rdcin dup inserarea cheilor n urma inserrii cheii 54, nodul rdcin va conine prea multe chei, aa c el va fisiona, figura 8.5. cheia 22 41 54 57 59 22 41 Fig. 8.5. Fisionarea nodului rdcin mediana 57 59

54 va n urma promovrii cheii mediene 54 se va forma o nou rdcin, arborele crescnd promova n nlime cu 1, figura 8.6.
a b
22 41 57 59 54

Fig. 8.6. Formarea noii rdcini a arborelui Inserarea cheilor 33, 75, 124 nu ridic probleme, figura 8.7.
a b
22 33 41 57 59 75 54

c
124

Fig. 8.7. Structura arborelui dup inserarea cheilor 33, 75, 124 Inserarea cheii 62 ns duce la divizarea nodului c, figura 13.8.
Cheia mediana 62 va promova

c
57 59 62 75
124

c
57 59

75

124

Fig. 8.8. Fisionarea nodului c Cheia 62 va promova n nodul rdcin, figura 8.9.
a b
22 41 54

62

c
57 59 75

124

Fig. 8.9. Promovarea cheii 62 n rdcin

153

n urma inserrii cheilor 33, 122, 123, 55, 60, 45, 66, 35 configuraia arborelui va fi cea din figura 13.10.
a
35 54 62
122

b
22 33 41

f
45

c
55 00 Structura 57

d
59 60 66 75 00 arborelui dup inserri succesive
123

e
124

Fig. 8.10.

Inserarea cheii 56 se va face n nodul c i acesta va fisiona, figura 8.11.


c
55 00 56 57 00 59 60

c
55 56

Cheia mediana 57 va promova

59

60

Fig. 8.11. Fisionarea nodului c

Oricum, nodul printe a este deja plin i nu poate primi noua cheie 57 i pointerul ctre nodul nou format f. Algoritmul de fisionare este aplicat din nou, dar de data aceasta nodului a, figura 8.12.
a
35 54 57 00 62
122

a
35 54

Cheia mediana 57 va promova

62

122

Fig. 8.12. Fisionarea nodului a

Cheia median promovat 57 va forma noua rdcin a arborelui B, avnd subarborele stng a i subarborele drept h, figura 8.13.
i
57

a i b
22 33

h
54 62
122

35

f
41 45

c
55 56

g
59 60

d
66 75

e
123 124

Fig. 8.13. Noua configuraie a arborelui B Uzual, n special pentru arbori B de ordin mare, un nod printe are suficient spaiu disponibil pentru a primi valoarea unei chei i un pointer ctre un nod descendent. n cel mai ru caz algoritmul de fisionare este aplicat pe ntreaga nlime a arborelui. n acest mod arborele va crete n nlime, lungimea drumului de cutare crescnd cu 1. Sintetizat, algoritmul de inserare a unei valori de cheie n arbore este prezentat n figura 8.14.

154

Adaug noua valoare


de cheie n nodul frunz corespunzator

OVERFLOW?

Divide nodul n dou noduri


aflate pe acelai nivel i promoveaz cheia median

Fig. 8.14. Algoritmul de inserare a unei chei n arbore Algoritmul de inserare ntr-un arbore B (pseudocod): insereaz noua valoare de cheie n nodul frunz corespunztor; nodul_curent = nodul_frunza; while( starea pentru nodul_curent este OVERFLOW ): - divide nodul_curent n dou noduri aflate pe acelai nivel i promoveaz cheia median n nodul printe pentru nodul_curent; - nodul_curent = nodul printe pentru nodul_curent. n cel mai ru caz, inserarea unei chei noi duce la aplicarea algoritmului de fisionare pe ntreaga nlime a arborelui, fisionndu-se h 1 noduri, unde h este nlimea arborelui nainte de inserare. Numrul total de fisionri care au aprut cnd arborele avea p noduri este de p 2. Prima fisionare adaug dou noduri noi; toate celelalte fisionri produc doar un m 1 singur nod nou. Fiecare nod are un minim de 2 chei, cu excepia rdcinii, care are cel

p 1

puin o cheie. Deci arborele cu p noduri conine cel puin chei. Probabilitatea ca o fisionare s fie necesar dup inserarea unei valori de cheie este mai mic dect : p 2 m divizari 1 p 1 1 2 chei
1 m 1 care este mai mic dect 1 divizare per 2 inserri de chei (deoarece p 2 tinde ctre 0 p 1 pentru o valoare mare a lui p i p 2 este aproximativ 1). De exemplu, pentru m = 10,

m 2

probabilitatea apariiei unei divizri este de 0.25. Pentru m = 100, probabilitatea divizrii este 0.0204. Pentru m = 200, probabilitatea divizrii este 0.0101. Cu alte cuvinte, cu ct ordinul arborelui este mai mare, cu att este mai mic probabilitatea ca inserarea unei valori de cheie sa duc la divizarea unui nod. Operaia de tergere dintr-un arbore B este ceva mai complicat dect operaia de inserare. Operaia de tergere se realizeaz simplu dac valoarea de cheie care urmeaz a fi tears se afl ntr-un nod frunz. Dac nu, cheia va fi tears logic, fiind

155

nlocuit cu o alta, vecin n inordine, care va fi tears efectiv. n urma tergerii se disting urmtoarele cazuri: m dac nodul conine mai mult de 2 chei, tergerea nu ridic probleme; m dac nodul are numrul minim de chei 2 , dup tergere numrul de chei din nod va fi insuficient. De aceea se mprumut o cheie din nodul vecin (aflat pe m acelai nivel n arbore) dac acesta are cel puin 2 chei, caz n care avem de-a face cu o partajare. Dac nu se poate face o partajare cu nici unul din nodurile vecine (fiecare nod vecin are numrul minim de chei), atunci cele dou noduri vecine vor fuziona, mprumutndu-se o cheie i din nodul printe. Partajarea sau fuzionarea trebuie eventual repetate i pentru nivelurile superioare. n cazul cel mai nefavorabil, dac partajarea sau fuzionarea parcurg ntreaga nlime a arborelui, se va forma o nou rdcin, nlimea arborelui scznd cu unu. Probabilitatea de fuzionare a nodurilor scade o dat cu creterea ordinului arborelui B. S considerm urmtoarea configuraie de arbore B de ordin 5 din figura 8.15.
a
57

b
35 54 62

c
122

d
22 33 22

f
55 56

g
59 60

h
66 75

i
123 124

e
40 41 45 50

Fig. 8.15. Arbore B de ordin 5 tergerea valorilor de cheie 40 i 45 din nodul e nu ridic probleme, figura 8.16
a
57

b
35 54 62

c
122

d
22 33 34

e
41 50

f
55 56

g
59 60

h
66 75

i
123 124

Fig. 8.16. Structura arborelui dup tergerea cheilor 40 i 45 tergerea valorii de cheie 50 necesit ns partajarea ntre nodurile d i e, figura 8.17.

156

a
57

valoarea de cheie 35 coboara; valoarea de cheie 34 urca d


22 33 34

b
35 54 62

c
122

e
41 50

f
55 56

g
59 60

h
66 75

i
123 124

Fig. 8.17. Partajarea ntre nodurile d i e


a
57

b
34 54 62

c
122

d
22 33

e
35 41

f
55 56

g
59 60 66

h
75

i
123 124

Fig. 8.18. Structura arborelui dup partajare tergerea valorii de cheie 22 va necesita fuzionarea nodurilor d i e, figura 8.19.
a
57

fuzionare

b
34 54 62

c
122

d
22 33

e
35 41

f
55 56

g
59 60 66

h
75

i
123 124

Fig. 8.19. Fuzionarea nodurilor d i e ns n urma fuzionrii nodurilor d i e, nodul b va conine prea puine valori de cheie, aa c vor fuziona i nodurile b i c, figura 8.20.
a
57

fuzionare c

b
54 62

122

d
33 34 35 41

f
55 56

g
59 60 66

h
75

i
123 124

Fig. 8.20. Fuzionarea nodurilor a, b i c

157

Astfel, n final, arborele B va arta astfel ca n figura 8.21.


54 57 62
122

33

34

35

41

55

56

59

60

66

75

123

124

Fig. 8.21. Structura final a arborelui B Algoritmul de tergere dintr-un arbore B, n pseudocod: if (valoarea de cheie care se terge nu este ntr-un nod frunz) then: nlocuiete valoarea de cheie cu succesor / predecesor; nodul_curent = nodul_frunza; while (starea pentru nodul_curent este UNDERFLOW ): - ncearc partajarea cu unul din nodurile vecine aflate pe acelai nivel, via nodul printe; - if (nu este posibil ) then: 1. fuzioneaz nodul_curent cu un nod vecin, folosind o valoare de cheie din nodul printe; 2. nodul_curent = printele pentru nodul_curent. 8.3. Algoritmii C++ pentru inserarea unei valori de cheie ntr-un arbore B nainte de a prezenta algoritmii pentru cutare i inserare ntr-un arbore B, s ncepem mai nti cu declaraiile necesare pentru implementarea unui arbore. Pentru simplicitate vom construi ntregul arbore B n memoria heap, folosind pointeri pentru a descrie structura arborescent. n majoritatea aplicaiilor, aceti pointeri vor trebui nlocuii cu adrese ctre blocuri sau pagini stocate ntr-un fiier pe disc (deci accesarea unui pointer va nsemna un acces la disc). Un nod al arborelui B va fi descris de clasa btree_node. Pentru a fi ct mai general n ceea ce privete tipul valorii de cheie folosit va fi implementat ca fiind o clas template. Oricum, tipul valorii de cheie folosit va trebui s permit ordonarea cheilor; n cazul implementrii de fa cerina este de a suporta testele de inegalitate strict (<) i de egalitate (= =). Pentru generalitate se vor folosi funcii pentru testele de egalitate i inegalitate strict. Declaraiile acestora sunt urmtoarele:

template <typename FIRST_ARGUMENT_TYPE, typename SECOND_ARGUMENT_TYPE, typename RETURN_TYPE > struct binary_function { /** empty */ } ; template <typename TYPE> struct less_than : public binary_function<TYPE, TYPE, bool> { inline bool operator()( const TYPE& first, const TYPE& second ) { return first < second ;

158

} } ; /** struct less_than

*/

template <> struct less_than<char*> : public binary_function<char*, char*, bool> { inline bool operator()( const char* first, const char* second ) { return strcmp( first, second ) < 0 ; } } ; /** struct less_than<char*> */ template <typename TYPE> struct equal_to : public binary_function<TYPE, TYPE, bool> { inline bool operator()( const TYPE& first, const TYPE& second ) { return first == second ; } } ; /** struct equal_to */ template <> struct equal_to<char*> : public binary_function<char*, char*, bool> { inline bool operator()( const char* first, const char* second ) { return strcmp( first, second ) == 0 ; } } ; /** struct equal_to<char*> */ Declaraia clasei btree_node este:

template <typename KEY_NODE_TYPE> class btree_node { friend class btree<KEY_NODE_TYPE> ; public: typedef KEY_NODE_TYPE key_node_type ; typedef key_node_type* key_node_ptr ; typedef btree_node<KEY_NODE_TYPE> btree_node_type ; typedef btree_node_type* btree_node_ptr ; typedef struct { key_node_type key_value ; btree_node_ptr node_ptr ; } btree_node_tuple ; public: enum BTREE_NODE_STATUS { EMPTY = 0, UNDERFLOW, MINIMAL, OPERATIONAL, FULL, OVERFLOW 159

}; public: btree_node( int ncapacity ) ; virtual ~btree_node( void ) ; BTREE_NODE_STATUS status( void ) const ; int capacity( void ) const ; int size( void ) const ; key_node_type& key_value( int position ) ; btree_node<KEY_NODE_TYPE>* child( int position ) ; int push( const key_node_type& value, btree_node_ptr child ) ; bool find( const key_node_type& value, int& position ) ; int remove_at( const int position ) ; bool is_leaf( void ) const ; int split( btree_node_tuple* tuple ) ; protected: static int initialize( btree_node_ptr node ) ; int shift2right( const int start ) ; int shift2left( const int start ) ; protected: btree_node( const btree_node<KEY_NODE_TYPE>& ) ; btree_node<KEY_NODE_TYPE>& operator =( const btree_node<KEY_NODE_TYPE>& ) ; protected: less_than<key_node_type> _less_than ; equal_to<key_node_type> _equal_to ; protected: key_node_ptr node_keys ; btree_node_ptr* node_childs ; int node_capacity ; int node_size ; }; /** class btree_node

*/

Structura btree_node_tuple va fi folosit de algoritmul de fisionare (divizare) a unui nod. Valorile din cadrul enumerrii BTREE_NODE_STATUS nseamn: nodul nu conine nici o cheie ; EMPTY nodul conine prea puine chei ; UNDERFLO W nodul conine numrul minim de chei ; MINIMAL OPERATION numrul de chei coninut de nod respecta cerinele de arbore B; AL 160

FULL OVERFLOW

nodul conine numrul maxim de chei ; nodul conine prea multe chei.

Ordinea n care cmpurile din enumerare apar este important (a se vedea funciile de introducere i tergere a unei chei din arbore). Constructorul clasei btree_node primete ca parametru numrul maxim de chei care pot fi pstrate ntr-un nod (capacitatea nodului). Operaiile de copiere a unui nod nu sunt permise (este dezactivat constructorul de copiere i operatorul de atribuire). Cmpurile _less_than i _equal_to sunt folosite n testele de egalitate i inegalitate strict. Cmpul node_capacity pstreaz numrul maxim de chei dintr-un nod (capacitatea nodului). Cmpul node_size menine numrul de chei aflate la un moment dat ntr-un nod. Cmpul node_keys este un vector n care vor fi pstrate valorile de chei din nod (n implementarea de fa, pentru implementarea mai uoara a algoritmului de divizare a unui nod, numrul de chei pstrate ntr-un nod va fi cu unu mai mare dect numrul maxim de chei). Cmpul node_childs va pstra pointeri ctre descendeni (numrul de descendeni ai unui nod este cu unu mai mare dect numrul de chei din acel nod). Constructorul btree_node<KEY_TYPE>::btree_node() apeleaz funcia initialize() care iniializeaz un nod al arborelui (aloc memoria necesar pentru pstrarea valorilor de chei i a pointerilor ctre descendeni, seteaz numrul de chei prezente n arbore la 0):

template <typename KEY_NODE_TYPE> btree_node<KEY_NODE_TYPE>::btree_node( int capacity ) { this->node_capacity = capacity ; initialize( this ); } template <typename KEY_NODE_TYPE> int btree_node<KEY_NODE_TYPE>::initialize( btree_node_ptr node ) { if( 0 = = node ) return -1 ; node->node_keys = new key_node_type[ node->capacity() + 1 ]; if( 0 = = node->node_keys ) return -1 ; node->node_childs = new btree_node_ptr[ node->capacity() + 2 ]; if( 0 = = node->node_childs ) { delete []node->node_keys ; node->node_keys = 0; return -1 ; } memset(node->node_childs,0,sizeof(btree_node_ptr)* (node->capacity() + 2 ) ); node->node_size = 0; return 0; } Destructorul btree_node<KEY_TYPE>::~btree_node() elibereaz memoria alocat:

template <typename KEY_NODE_TYPE>

161

btree_node<KEY_NODE_TYPE>::~btree_node( void ) { delete []this->node_keys ; delete []this->node_childs ; } Pentru a se afla numrul de chei aflate la un moment dat ntr-un nod se folosete funcia size():

template <typename KEY_NODE_TYPE> inline int btree_node<KEY_NODE_TYPE>::size( void ) const { return this->node_size ; } Pentru a se determina numrul maxim de chei care pot fi pstrate ntr-un nod se folosete funcia capacity():

template <typename KEY_NODE_TYPE> inline int btree_node<KEY_NODE_TYPE>::capacity( void ) const { return this->node_capacity ; } Un nod poate fi interogat pentru starea n care se afl folosind funcia status():

template <typename KEY_NODE_TYPE> inline btree_node<KEY_NODE_TYPE>::BTREE_NODE_STATUS btree_node<KEY_NODE_TYPE>::status( void ) const { if( 0 == size() ) return EMPTY ; else if( size() < ( capacity() / 2 ) ) return UNDERFLOW ; else if( size() == ( capacity() / 2 ) ) return MINIMAL ; else if( size() == capacity() ) return FULL ; else if( size() > capacity() ) return OVERFLOW ; return OPERATIONAL ; } Se observ c starea nodului este funcie de numrul de chei aflate la un moment dat n nod. Mai exact, dac avem un arbore B de ordin m, atunci numrul maxim de chei dintr m 1 un nod va fi m 1, iar numrul minim de chei va fi 2 . Parametrul primit de constructor va fi m 1, deci capacitatea nodului va fi m 1. Cum numrul de chei aflate la un moment dat ntr-un nod se obine folosind funcia size(), vom avea: dac size() returneaz zero, atunci n nod nu se gsete nici o cheie starea nodului este EMPTY ; numrul minim de chei din nod este capacity()/2; deci dac size()==capacity()/2, atunci nodul are numrul minim de chei starea nodului este MINIMAL ;

162

dac size() < capacity() / 2, atunci nodul are prea puine chei starea nodului este UNDERFLOW ; dac size() == capacity(), atunci nodul are numrul maxim de chei permise starea nodului este FULL ; dac size() > capacity(), atunci nodul are prea multe chei starea nodului este OVERFLOW. Pentru a se determina dac nodul este un nod frunz se folosete funcia is_leaf(), care va returna true dac nodul este o frunz (un nod este nod frunz dac nu are nici un descendent):

template <typename KEY_NODE_TYPE> bool btree_node<KEY_NODE_TYPE>::is_leaf( void ) const { assert( 0 != this->node_childs ) ; // return 0 == this->node_childs[0] ; for( int idx = 0; idx <= size(); idx++ ) if( 0 != this->node_childs[idx] ) return false ; return true ; } Ca funcii pentru interogarea valorii unei chei i unui descendent dintr-o anume poziie se folosesc funciile key_value() i child():

template <typename KEY_NODE_TYPE> inline KEY_NODE_TYPE& btree_node<KEY_NODE_TYPE>::key_value( int position ) { if( position < 0 || position >= size() ){ /** signal out of bounds*/ assert( false ) ; } return this->node_keys[position] ; } template <typename KEY_NODE_TYPE> inline btree_node<KEY_NODE_TYPE>* btree_node<KEY_NODE_TYPE>::child( int position ) { if( position < 0 || position > size() ){ /** signal out of bounds */ assert( false ) ; return 0 ; }

163

return this->node_childs[position] ; } Cutarea unei valori de cheie ntr-un nod se face folosind funcia find(). Primul parametru primit de funcie este valoarea de cheie care se caut n nod. Dup cum valoarea de cheie cutat se gsete sau nu n nod, find() va returna true sau false, cu meniunea ca al doilea parametru al funciei (position, care este un parametru de ieire) va fi setat dup cum urmeaz: daca valoarea de cheie cutat se gsete n nod, atunci position este setat la indexul la care se gsete cheia n nod; dac valoarea de cheie cutat nu se gsete n nod, position va indica indexul subarborelui n care s-ar putea gsi valoarea de cheie cutat.

template <typename KEY_NODE_TYPE> bool btree_node<KEY_NODE_TYPE>::find( const key_node_type& value, int& position ) { bool ret_value ; position = -1 ; if( _less_than( value, this->node_keys[0] ) ) { position = 0 ; ret_value = false ; } else { for( position = size() - 1 ; _less_than( value, key_value( position ) ) && position > 0; position-); ret_value = _equal_to( value, this->node_keys[position] ) ; if( !ret_value ) position++ ; } return ret_value ; } Inserarea unei valori de cheie n nod se realizeaz folosind funcia push(). n implementarea de fa inserarea valorii de cheie ntr-un nod se face ntotdeauna specificnd i pointerul ctre subarborele din dreapta valorii de cheie (subarbore care conine toate valorile de cheie mai mari dect valoarea de cheie inserat). Dac nodul este n starea OVERFLOW sau valoarea de cheie exist deja n nod, funcia va returna 1, fr a face nimic altceva. n urma determinrii poziiei pe care va fi inserat valoarea de cheie, ar putea fi necesar o deplasare ctre dreapta a valorilor de cheie din nod mai mari dect cea care se insereaz (deplasarea se face mpreun cu pointerii ctre descendeni; funcia folosit este shift2right()).

164

template <typename KEY_NODE_TYPE> int btree_node<KEY_NODE_TYPE>::push( const key_node_type& value, btree_node_ptr child ){ if( OVERFLOW == status() ) return -1 ; if( EMPTY == status() ) { this->node_keys[0] = value ; this->node_childs[1] = child ; this->node_size = 1 ; return 0 ; } int key_position = -1 ; if( find( value, key_position ) ) { /** duplicate key value return -1 ; }

*/

if( key_position < size() ) shift2right( key_position ) ; this->node_keys[key_position] = value ; this->node_childs[key_position + 1] = child ; this->node_size++ ; return 0 ; } Funcia shift2right() este:

template <typename KEY_NODE_TYPE> int btree_node<KEY_NODE_TYPE>::shift2right( const int start ) { if( EMPTY == status() || start < 0 || start >= size() ) return -1 ; for( int idx = size(); idx > start; idx-- ) { this->node_keys[idx] = this->node_keys[idx - 1] ; this->node_childs[idx + 1] = this->node_childs[idx] ; } return 0 ; }

165

Pentru eliminarea unei chei dintr-o anumit poziie se va folosi funcia remove_at() (practic, eliminarea presupune diminuarea cu unu a numrului de chei coninute de nod i o deplasare ctre stnga a valorilor de cheie i a subarborilor aflai n dreapta poziiei din care se terge cheia).

template <typename KEY_NODE_TYPE> int btree_node<KEY_NODE_TYPE>::remove_at( const int position ) { if( -1 == shift2left( position ) ) return -1 ; this->node_size-- ; return 0 ; } Funcia shift2left() este:

template <typename KEY_NODE_TYPE> int btree_node<KEY_NODE_TYPE>::shift2left( const int start ) { if( EMPTY == status() || start < 0 || start >= size() ) return -1 ; for( int idx = start + 1; idx < size(); idx++ ) { this->node_keys[idx - 1] = this->node_keys[idx] ; this->node_childs[idx] = this->node_childs[idx + 1] ; } return 0 ; } Atunci cnd starea unui nod este de OVERFLOW, acesta se va diviza. n urma divizrii se va obine un nod nou. Funcia de divizare a unui nod este split(). Parametrul funciei split() este de ieire, fiind de tipul btree_node_tuple:

typedef struct { key_node_type key_value ; btree_node_ptr node_ptr ; } btree_node_tuple ; n urma procesului de divizare a unui nod va urca n nodul printe cheia median i un pointer ctre nodul nou format. Cheia median va fi cmpul key_value al structurii btree_node_tuple. Pointerul ctre nodul nou format va fi cmpul node_ptr al structurii btree_node_tuple. Noul nod va conine valorile de chei i subarborii din dreapta cheii mediane.

166

template <typename KEY_NODE_TYPE> int btree_node<KEY_NODE_TYPE>::split( btree_node<KEY_NODE_TYPE>::btree_node_tuple* tuple ){ if( 0 == tuple ) return 0 ; if( OVERFLOW != this->status() ) return 0; int median_position = this->size() / 2; tuple->key_value = this->key_value( median_position ); btree_node_ptr new_node = new btree_node_type( this->capacity()); if( 0 == new_node ) return -1; for( int idx = median_position + 1; idx < this->size() ; idx++ ) new_node->push( this->key_value( idx ), this->child( idx + 1 ) ); new_node->node_childs[0] = this->child( median_position + 1 ); this->node_size = median_position; tuple->node_ptr = new_node; return 0 ; } n continuare, va fi dat declaraia clasei de arbore B:

template <typename KEY_TYPE> class btree { public: typedef KEY_TYPE key_type ; typedef btree_node<KEY_TYPE> btree_node_type ; typedef btree_node_type* btree_node_ptr ; typedef btree_node_type::btree_node_tuple btree_node_tuple ; typedef btree<KEY_TYPE> btree_type ; typedef btree_type* btree_ptr ; public: btree( int order ) ; virtual ~btree( void ) ; int push( const key_type& value ) ; int remove( const key_type& value ) ; protected: int push_down( btree_node_tuple* tuple, btree_node_ptr current ) ; 167

int remove_down( const key_type& value, btree_node_ptr current ) ; int replace_with_predecessor( btree_node_ptr node, int position ) ; void restore( btree_node_ptr current, const int position ) ; void move_left( btree_node_ptr current, const int position ) ; void move_right( btree_node_ptr current, const int position ) ; void combine( btree_node_ptr current, const int position ) ; private: btree_node_ptr int } ; /** class btree root ; order ; */

Clasa btree este o clas template dup tipul valorii de cheie. La fel ca la clasa btree_node au fost folosite o serie de typedef-uri n cadrul clasei (codul este mai uor de scris / citit dac tipul btree_node<KEY_TYPE> se redefinete ca fiind btree_node_type). Nodul rdcin al arborelui B este dat de cmpul root. Ordinul arborelui B este dat de cmpul order. Constructorul btree<KEY_TYPE>::btree() primete ca parametru ordinul arborelui i seteaz rdcina arborelui la 0.

template <typename KEY_TYPE> btree<KEY_TYPE>::btree( int order ) { this->order = order ; this->root = 0 ; } Destructorul clasei btree<KEY_TYPE>::~btree() va elibera spaiul de memorie ocupat de arbore.

template <typename KEY_TYPE> btree<KEY_TYPE>::~btree( void ) { clear( this->root ) ; this->root = 0 ; } template <typename KEY_TYPE> void btree<KEY_TYPE>::clear( btree_node_ptr node ) { if( 0 == node ) return ; if( !node->is_leaf() ) for( int idx = 0 ; idx <= node->size(); idx++ ) clear( node->child( idx ) ) ; delete node ; } Funcia de inserare a unei valori de cheie n arbore este push(). Dac arborele nu are nici o valoare de cheie inserat (adic rdcina arborelui este 0 arborele este gol), atunci inserarea valorii de cheie este simpl: se construiete rdcina arborelui cu valoarea de cheie

168

care se insereaz. Daca arborele nu este gol, atunci se insereaz cheia n arbore recursiv folosind funcia push_down(), pornind de la nodul rdcin. Funcia push_down() va returna 1 dac n urma procesului de inserare recursiv a valorii de cheie nodul rdcin a fost divizat (cheia i un pointer ctre subarborele drept al cheii vor fi coninute de cmpurile variabilei tuple). n acest caz nlimea arborelui crete cu unu, formndu-se o nou rdcin cu valoarea de cheie dat de cmpul kei_value al variabilei tuple; subarborele stng va fi vechea rdcin a arborelui, iar subarborele drept va fi dat de cmpul node_ptr al variabilei tuple.

template <typename KEY_TYPE> int btree<KEY_TYPE>::push( const key_type& value ) { if( 0 == this->root ) { this->root = new btree_node_type( this->order - 1 ) ; if( 0 == this->root ) return -1 ; this->root->push( value, (btree_node_ptr)0 ) ; return 0 ; } btree_node_tuple tuple ; tuple.key_value = value ; tuple.node_ptr = 0 ; if( push_down( &tuple, this->root ) ) { btree_node_ptr new_root = new btree_node_type( this->order - 1 ); if( 0 == new_root ) return -1 ; new_root->push( tuple.key_value, tuple.node_ptr ) ; new_root->node_childs[0] = this->root ; this->root = new_root ; } return 0 ; } Funcia push_down() de inserare recursiv n arbore primete ca parametri nodul curent current n care se ncearc inserarea, i, mpachetat n variabila tuple, valoarea de cheie care se insereaz. Iniial, la primul apel, cmpul node_ptr al variabilei tuple va avea valoarea 0. n cadrul algoritmului de inserare se va cuta valoarea de cheie n nodul curent. Dac aceasta exist deja n nod (arbore), funcia de inserare nu va face nimic i va returna 1. Altfel, variabila key_position va indica: dac nodul curent este frunz, key_position indic locul n care se va insera valoarea de cheie; dac nodul curent nu este o frunz, key_position va indica subarborele n care va trebui s se fac inserarea. Dac nodul curent este frunz, inserarea este simpl: se apeleaz doar metoda push() de inserare a unei chei ntr-un nod (la inserarea ntr-un nod frunz ntotdeauna cmpul node_ptr al parametrului tuple va fi 0). Dac nodul curent nu este frunz, atunci va trebui urmrit dac n urma inserrii recursive n nodul curent a urcat o valoare de cheie. Daca n nodul curent a urcat o valoare de cheie (metoda push_down() a returnat valoarea 1) atunci: 169

cmpurile parametrului tuple vor conine valoarea de cheie care a urcat i un pointer ctre subarborele drept corespunztor cheii ; n nodul curent vor trebui inserate key_value i node_ptr indicate de cmpurile parametrului tuple. n final, se verific starea n care se afl nodul curent. Dac starea acestuia este de OVERFLOW, atunci nseamn ca acesta conine prea multe chei i va trebui divizat. Pentru aceasta se folosete metoda split() a nodului care va primi ca parametru adresa lui tuple. n urma apelului metodei split() coninutul cmpurilor key_value i node_ptr ale variabilei tuple vor fi actualizate pentru a indica valoarea de cheie care va urca n nodul printe al nodului curent i pointerul ctre subarborele drept; n acest caz metoda push_down() va returna 1 pentru a indica faptul c n nodul printe vor trebui folosite cmpurile variabile tuple. Dac starea nodului nu este OVERFLOW, metoda push_down() va returna 0.

template <typename KEY_TYPE> int btree<KEY_TYPE>::push_down( btree_node_tuple* tuple, btree_node_ptr current ){ if( 0 == current ) return 0 ; int key_position ; bool duplicate_key = current->find( tuple->key_value, key_position) ; if( duplicate_key ) { /** signal duplicate value */ return -1 ; } if( current->is_leaf() ) { current->push( tuple->key_value, tuple->node_ptr ); } else { if( push_down( tuple, current->child( key_position ) ) ) current->push( tuple->key_value, tuple->node_ptr); } if( btree_node_type::OVERFLOW == current->status() ) { current->split( tuple ) ; return 1 ; } return 0 ; } 8.4. Algoritmii C++ pentru tergerea unei valori de cheie intr-un arbore B Funcia de tergere a unei valori de cheie dintr-un arbore B este remove(). Intern este folosit funcia recursiv remove_down(). Dac n urma tergerii valorii de cheie nodul rdcin nu mai are nici o valoare de cheie (starea nodului rdcin este EMPTY), atunci noua

170

rdcin a arborelui este subarborele stng al vechii rdcini. Vechea rdcin se elimin din memorie.

template <typename KEY_TYPE> int btree<KEY_TYPE>::remove( const key_type& value ) { remove_down( value, this->root ) ; if( btree_node_type::EMPTY == this->root->status() ) { btree_node_ptr old_root = this->root ; this->root = this->root->child( 0 ) ; delete old_root ; } return 0 ; } Funcia de tergere recursiv a unei valori de cheie din arbore este remove_down(). Parametrii primii de funcie sunt valoarea de cheie care se terge i un pointer ctre nodul curent. Dac la un moment dat nodul curent este 0, atunci nseamn c valoarea de cheie solicitat a fi tears nu se gsete n arbore. Altfel, funcie de tipul nodului unde cheia este gsit avem urmtoarele cazuri: dac valoarea de cheie se gsete ntr-un nod frunz, atunci tergerea nseamn apelarea metodei remove() a nodului frunz pentru eliminarea cheii aflat n poziia dat de variabila position ; dac valoarea de cheie a fost gsit ntr-un nod care nu este nod frunza, atunci valoarea de cheie care trebuie tears va fi nlocuit, n implementarea de fa, cu valoarea de cheie care o precede (se poate demonstra c valoarea de cheie care o precede se gsete ntr-un nod frunz). Dup ce se face nlocuirea folosind funcia replace_with_predecessor(), se va continua algoritmul de tergere, dar de data aceasta se va solicita tergerea valorii de cheie cu care s-a fcut nlocuirea. Se observ c dac nodul curent este valid (nu este 0) i valoarea de cheie care se caut nu se gsete n nodul curent, atunci variabila position va fi poziionat de metoda find() a nodului ca fiind poziia subarborelui care este posibil s conin valoarea de cheie. n finalul funciei, dac nodul curent nu este frunz (este un nod printe care are descendeni care au fost afectai), se testeaz starea nodului rdcin pentru subarborele pe care s-a efectuat coborrea n procesul de tergere. Dac starea acestui nod este UNDERFLOW (conine prea puine valori de cheie), atunci va fi apelat funcia restore() care va restaura proprietile de arbore B.

template <typename KEY_TYPE> int btree<KEY_TYPE>::remove_down( const key_type& value, btree_node_ptr current ){ if( 0 == current ) { /** signal value not found */

171

return 0 ; } int position ; if( current->find( value, position ) ) { if( current->is_leaf() ) current->remove_at( position ) ; else { replace_with_predecessor( current, position ) ; remove_down(current->key_value(position ), current->child( position)) ; } } else remove_down( value, current->child( position ) ) ; if( !current->is_leaf() && btree_node_type::UNDERFLOW = = current->child(position)->status() ) restore( current, position ) ; return 0 ; } Funcia de nlocuire a unei valori de cheie cu valoarea de cheie care o precede este replace_with_succesor(). Parametrii primii de funcie sunt: node, un pointer ctre nodul care conine valoarea de cheie care se nlocuiete i position care d poziia subarborelui (n nodul indicat de node) care conine valoarea de cheie care precede valoarea de cheie care se nlocuiete.

template <typename KEY_TYPE> int btree<KEY_TYPE>::replace_with_predecessor( btree_node_ptr node, int position ){ if( position > node->size() ) return 0 ; btree_node_ptr leaf = node->child( position ) ; while( !leaf->is_leaf() ) leaf = leaf->child( leaf->size() ) ; node->node_keys[position] = leaf->key_value( leaf->size() - 1 ) ; return 1 ; } Funcia care asigur restaurarea proprietilor de arbore B este restore(). Parametrii funciei sunt: un pointer ctre un nod printe (current) i o poziie (position) care indic un subarbore al nodului current (nodul rdcin al acestui subarbore conine prea puine valori de cheie). Funcia folosit n implementarea de fa este cumva orientat ctre stnga, n sensul c mai nti se uit la nodul vecin din stnga pentru a lua o valoare de cheie, folosind nodul vecin din dreapta numai dac nu gsete suficiente valori de cheie n nodul din stnga. Paii care sunt necesari sunt ilustrai n figura 8.22.

172

move_right()

move_left()
x z w x
y

combine()

Fig. 8.22 Paii parcuri n funcia de restaurare a proprietilor unor arbore B

template <typename KEY_TYPE> void btree<KEY_TYPE>::restore( btree_node_ptr current, const int position ){ if( 0 == position ) { if( current->child( 1 )->status() > btree_node_type::MINIMAL ) move_left( current, 1 ) ; else combine( current, 1 ) ; } else if( position == current->size() ) { if(current->child(current->size()-1)->status()> btree_node_type::MINIMAL) move_right( current, position ) ; else combine( current, position ) ; } else if( current->child( position - 1 )->status() > btree_node_type::MINIMAL ) move_right( current, position ) ; else if( current->child( position + 1 )->status() > btree_node_type::MINIMAL ) move_left( current, position + 1 ) ; else combine( current, position ); } Funciile move_left(), move_right() i combine() sunt uor de dedus din figura 13.22.

173

template <typename KEY_TYPE> void btree<KEY_TYPE>::move_left( btree_node_ptr current, const int position ){ btree_node_ptr node = current->child( position - 1 ) ; node->push( current->key_value( position - 1 ), current->child( position )->child( 0 ) ); node = current->child( position ) ; current->node_keys[position - 1] = node->key_value( 0 ); node->node_childs[0] = node->node_childs[1] ; node->shift2left( 0 ) ; node->node_size-- ; } template <typename KEY_TYPE> void btree<KEY_TYPE>::move_right( btree_node_ptr current, const int position ){ btree_node_ptr node = current->child( position ) ; node->shift2right( 0 ) ; node->node_childs[1] = node->child( 0 ) ; node->node_keys[0] = current->key_value( position - 1 ) ; node->node_size++ ; node = current->child( position - 1 ) ; current->node_keys[position - 1] = node->key_value(node->size()-1); current->child(position)->node_childs[0]=node->child(node->size()); node->node_size-- ; } template <typename KEY_TYPE> void btree<KEY_TYPE>::combine( btree_node_ptr current, const int position ) { btree_node_ptr rnode = current->child( position ) ; btree_node_ptr lnode = current->child( position - 1 ) ; lnode->push(current->key_value( position - 1 ), rnode->child( 0 )); for( int idx = 0; idx < rnode->size(); idx++ ) lnode->push( rnode->key_value( idx ), rnode->child( idx + 1 ) ); current->remove_at( position - 1 ) ; delete rnode ; } 174

Teme de control 1. S se scrie funcia pentru crearea unui arbore B de ordinul n. 2. S se scrie funcie pentru tergerea unei chei dintr-un nod frunz al unui arbore B. Bibliografie Ion IVAN, Marius POPA, Paul POCATILU - Structuri de date, Vol. 1 si 2, Editura ASE, Bucuresti, 2008.

175