Sunteți pe pagina 1din 504

2.

TIPURI DE DATE I DATE ELEMENTARE


2.1 Date i informaii
n practic se face deosebire ntre o dat i o informaie. Exemplele
oferite n cele mai multe cazuri sunt edificatoare. Exist i tendine de a oferi
definiii pentru date i pentru informaii. Dilemele cnd o informaie este
considerat dat i cnd o dat este o informaie, sunt rezolvate pentru muli
specialiti, dar rmn dileme pentru o alt categorie de specialiti.
Din punct de vedere al programatorului, ceea ce face obiectul prelucrrii
sunt de fapt iruri de bii care reprezint date sau informaii, funcie de
contextul n care sunt generate i de modul n care se interpreteaz
rezultatele. Pentru a nu complica i mai mult problematica, se consider c n
activitatea de programare se opereaz cu date. Toate intrrile i ieirile
programelor sunt date. Sistemele de prelucrare, ns sunt intitulate n
continuare sisteme informaionale sau sisteme informatice, n mod ornamental
din punctul de vedere al programatorilor.
n realitate, atunci cnd acestea funcioneaz corect, prelucreaz ntradevr informaii. Atunci cnd, ns, fluxurile sunt greoaie i determin un
nivel de istorism costisitor, prelucrrile sunt ale unor date certe.
Pentru ca n literatura de specialitate capitolul deinut descrierii
operanzilor informaii sau date se numete STRUCTURI DE DATE, n
continuare, nu se mai face deosebirea dintre informaie i dat. Utilizatorii sunt
aceia care decid dac ofer spre prelucrare informaii sau date i dac
rezultatele prelucrrii sunt date sau sunt informaii.

2.2 Clasificri ale datelor


Exist numeroase puncte de vedere de abordare a gruprii datelor,
fiecare constituindu-se ntr-un criteriu. Ceea ce este ns adevrat, este legat
de faptul c fiecrei date i se ataeaz totalitatea atributelor ce rezult din
multitudinea de clasificri care se iau n considerare.
a) Criteriul variabilitii grupeaz datele n:
- date constante, care nu se modific ntr-un interval de timp sau
pe durata execuiei programului; n cazul n care pentru a face un
program lizibil constantele sunt puse n coresponden cu anumii
identificatori, n programe sunt vehiculai acetia din urm,
formnd constantele simbolice.
- date variabile, ale cror niveluri se modific fie ntr-un interval de
timp, fie pe parcursul execuiei unui program; ntotdeauna se
vorbete de o valoare iniial, valori intermediare i o valoare
final; numrul valorilor intermediare determin mecanismele
necesare prelucrrilor, includerea n structuri repetitive sau
stocarea lor n fiiere;
b) Criteriul compunerii difereniaz datele astfel:

date simple sau elementare, fiecare avnd o anumit semnificaie


i fiind independente de celelalte date care apar ntr-un context
specificat; datele elementare se mai numesc atomi;
- date compuse sau structurate, formate din date elementare sau
date la rndul lor structurate; fiecare component are o anumit
poziie n cadrul structurii i mpreun cu celelalte formeaz un
ntreg; ntre prile care alctuiesc o dat compus exist legturi
n primul rnd de coninut i numai toate la un loc caracterizeaz
un fenomen, un proces sau un individ dintr-o colectivitate:
apartenena i poziia fiecrei componente se precizeaz explicit
la descrierea datei structurate;
c) Criteriul semnificaiei coninutului conduce la:
- date care fac obiectul operaiilor de prelucrare, adic particip ca
operanzi n expresii, se iniializeaz prin atribuiri sau operaii de
intrare, se stocheaz pe supori, se afieaz sau se transmit ca
parametri;
- date care permit adresarea operanzilor i care au valori cuprinse
ntre limite precizate, care prin calcule de adrese localizeaz
corect fie operanzi, fie alte date de adresare, fie funcii de
prelucrare;
- date care efectueaz prelucrarea, care apar ca succesiuni de
instruciuni direct executabile dac fiierul care le aparine este
ncrcat n memoria unui calculator i se comand lansarea n
execuie a acestuia;
d) Criteriul naturii datelor genereaz tipurile de date urmtoare:
- date de tip ntreg, ale cror elemente aparin mulimii Z;
- date de tip real, ale cror elemente aparin mulimii R;
- date de tip complex, ale cror elemente aparin mulimii C, iar
coeficienii care desemneaz partea real i partea imaginar
aparin mulimii R;
- date de tip boolean, ale cror elemente aparin mulimii {TRUE,
FALSE} sau {0, 1};
- date de tip caracter, ale cror elemente aparin mulimii
caracterelor ce sunt definite prin combinaie de bii la nivelul unui
bait; din cele 256 de combinaii unele sunt grupate pentru litere,
altele pentru cifre, altele pentru caractere speciale i pentru
caractere de control; corespunztor, sunt definite date de tip
alfabetic, de tip numeric, date de tip caractere de control etc.;
aceste date au cte un singur element din mulimea care i
definete tipul;
- date de tip ir de caractere reprezint o compunere prin
concatenare a datelor de tip caracter; datele acestea au un
delimitator al sfritului de ir, fie o constant de tip ntreg la
nceput, preciznd numrul de caractere care intr n alctuirea
irului;
e) Criteriul construirii tipurilor conduce la:
- date de tip fundamental care aparin unui tip implementat n
fiecare limbaj de programare, precum tipurile ntreg, real,
-

f)

g)

h)

i)

caracter, boolean, complex; programatorul are posibilitatea


definirii constantelor simbolice i variabilelor proprii specificnd
tipurile fundamentale i alege prelucrrile compatibile acestora;
- date de tip derivat care se obin prin includerea n cadrul unor
structuri a componentelor avnd unul din tipurile fundamentale
implementate n limbaj; rezultatul obinut este un tip de dat
derivat care se pune n coresponden cu un identificator i care
este folosit de programator pentru a defini variabilele n program
avnd respectivul tip;
Criteriul dispunerii n memoria intern, grupeaz datele n:
- date dispuse n zone contigue care permit localizarea uneia
dintre ele cunoscnd o adres i o deplasare; n cazul n care
zonele de memorie ocupate au aceeai lungime, adresa fiecrei
date se constituie ca termen al unei progresii aritmetice i este
calculat cunoscnd adresa primei date i poziia n irul datelor
contigue a elementului cutat;
- date dispersate n memoria intern - se obin n cazul alocrii
dinamice a memoriei necesare, ceea ce impune stocarea i
conservarea adresei zonei de memorie asociat fiecrei date;
dac datele dispuse n zone contigue, au realizat proiectarea
alocrii n faza de compilare, datelor dispersate li se aloc
memorie efectiv n faza de execuie i nu exist posibilitatea ca n
mod direct s se construiasc modele de calcul a adreselor fizice
pe care datele le ocup, mai ales dac alocarea memoriei este un
proces ce depinde de testarea unor condiii din program;
Criteriul cmpului de aciune, mparte datele n:
- date cu caracter global care se definesc o singur dat, dar care
sunt utilizate din orice punct al programului sau a funciilor i
procedurilor care intr n componena lui; aceste date se definesc
i li se aloc memorie o singur dat i au cmpul de aciune cel
mai cuprinztor;
- date cu caracter local sunt n fiecare procedur i li se aloc
memorie dinamic, automat, la apelarea fiecrei proceduri sau
funcii; odat cu revenirea n secvena apelat deci la ieirea din
funcie sau din procedur - are loc eliberarea memoriei alocate
(dealocarea memoriei); variabilele locale nu sunt folosite dect n
procedura sau funcia unde au fost definite;
- date de tip registru au rolul de a pune la dispoziie
programatorului n limbaje evoluate, accesul la registrele
calculatorului; n cazul unei folosiri judicioase exist posibilitatea
creterii vitezei de prelucrare, iar n cazul folosirii abuzive a
registrelor se obine fenomenul invers;
Criteriul definirii domeniului presupune:
- date al cror domeniu este specificat prin limita inferioar, limita
superioar i forma de prezentare generic a elementelor;
- date al cror domeniu este definit odat cu enumerare
elementelor care i formeaz.
Criteriul alocrii memoriei, grupeaz datele n:

date statice calcule de alocare a memoriei se efectueaz n faza


de compilare, iar nainte de execuie, alocarea este efectiv;
- date dinamice a cror memorie este alocat i dealocat n timpul
execuiei programului, prin funcii de bibliotec apelate.
ntr-un program, o anumit dat este astfel definit nct se ncadreaz
ntr-una din subgrupele fiecrui criteriu. Astfel, definirea:
-

// PROGRAM definire:
#include<.>
#include<.>
................................
int k;
main( )
{
................................
}

dintr-un
-

program C/C++ se interpreteaz astfel:


k este o dat variabil (criteriul variabilitii);
k este o dat elementar (criteriul compunerii);
k este o dat de tip operand (criteriul semnificaiei);
k este o dat de tip ntreg (criteriul naturii datelor);
k este o dat de tip fundamental (criteriul construirii tipurilor);
k este o dat dispus ntr-o zon contigu (criteriul dispunerii
n memoria intern);
- k este o dat global (criteriul cmpului de aciune).
Deci, k este un operand, variabila elementar, global, de tipul
fundamental ntreg, dispus ntr-o zon contigu.
2.3 Modele de prezentare a datelor
ntre forma de reprezentare natural sau extern a datelor i forma de
reprezentare intern a acestora, exist mari diferene.
Reprezentarea intern a datelor, se realizeaz utiliznd algoritmi de
codificare, care pun n coresponden datele cu iruri de bii. Pentru fiecare tip
de dat se definete lungimea zonei de memorie i algoritmul de codificare,
precum i codurile operaiilor care utilizeaz operanzii n concordan cu
caracteristicile de tip ale acestora.
a) Modelul de verificare a concordanei tip-coninut-adres
Modele de reprezentare a datelor au n componena lor:
LSUPi,LIMFi valori care precizeaz limita inferioar i limita superioar a
intervalului creia i aparine data de tip i specificat;
Ai
indicatorul algoritmului de realizare a reprezentrii interne
pentru datele de tip i;
f( )
funcia de apartenen a datei la un anumit tip;

M
n
k

mulimea funciilor de conversie;


numrul de tipuri de date;
cerine de aliniere a adresei de nceput a zonei de memorie, k
{1, 2, 4, 8};
natura i a datei;
funcia de determinare a adresei unei zone de memorie pus
n coresponden cu un identificator specificat:

Ti
adr( )

adr : J -> N

(2.1)

unde:
J mulimea identificatorilor;
N submulime a numerelor naturale cu care se localizeaz fiecare bait al
zonei de memorie la dispoziia programatorului;

N [ a, b] N
cont( )

delimiteaz posibiliti hardware de dispunere n memorie a


programului, unde a, b N, a < b;
funcia coninut a zonei de memorie:
cont : J T i

tip( )

U Di
i 1

(2.2)

funcia de identificare a tipului variabilei:


tip : J -> T

(2.3)

Mulimea Tj a naturii datelor fundamentale implementate n limbajul de


programare Lj, se definete prin:
Tj = { T1, T2, ...,Tn }

(2.4)

Dac j este C, atunci:


TC = {int, bool, float, char, string}

(2.5)

deci n = 5, fr a fi luate n considerare variantele pentru datele ntregi, reale


i posibilitatea de a specifica seturi de valori.
Pentru datele de natur boolean, LSUP2 este 1 sau TRUE i LINF2 este 0
sau FALSE.
Pentru datele de natur real A3, corespunde modului de construire a
mantisei i caracteristicii precum i dispunerea acestora pe cei 6 baii.
Pentru datele ntregi H, mulimea funciilor de conversie are elementele:
H = {f11, f12, f13, f14, f15}

(2.6)

Dintre acestea numai f11 i f14 sunt inversabile, deci tabloul funciilor:
fij ( ), i = 1, 2, 3, 4, 5 i j =1, 2, 3, 4, 5

(2.7)

demonstreaz c se efectueaz conversii n toate direciile cu o anumit


pierdere a unor simboluri din descrierea iniial.
Cerinele de aliniere sunt specifice particularitii hardware a sistemelor
de calcul. n cazul n care la compilare nu este realizat optimizarea alocrii
memoriei, apar zone neutilizabile cu efecte ce sunt interpretate mai dificil.
Declararea:
...
char a;
float b;
char c;
char d;
...

n absena optimizrii alocrii de memorie, conduce la rezervarea:

A:8

A+16

A+8

A+20

Figura 2.1 Alocarea n memorie a variabilelor a, b, c i d


n cazul optimizrii, secvenei de program i corespunde:

A: 8

A+8

Figura 2.2 Alocarea optimizat n memorie a variabilelor a, b, c i d


Este posibil optimizarea datorit comunicativitii dispunerii operanzilor
ntr-o secven, atunci cnd acetia sunt elementari i nu apare problema
redefinirii.
Funcia de apartenena a datelor la un anumit tip se definete prin:
f : U x Di -> { FALSE, TRUE}

(2.8)

unde:
U - mulimea irurilor ce se genereaz cu simbolurile alfabetului
nstruit pentru un limbaj;
Di

intervalul sau mulimea elementelor specifice tipului de date i.

FALSE
f (x , i )
TRUE

daca x Di
daca x Di

(2.9)

De exemplu:
f(13, int) =TRUE

(2.10)

pentru c 13 [-32768, 32767] Z, Dint fiind domeniul ntregilor, n timp ce


f( - 4, bool) = FALSE

(2.11)

pentru c 4 nu aparine mulimii {FALSE, TRUE}.


n secvena:
...
int x;
...
x = 20;
...

presupunnd c n compilare i editare de legturi, variabilei x i se asociaz


zona de memorie:
x

00

00

00

14

14

00

00

00

07AA0

07AA0

a
b
Figura 2.3 Zona de memorie asociat variabilei x
(a microprocesoare MOTOROLA, b microprocesoare INTEL)
adr(x) = 07AA0
cont(x) = (00000014)16
f(cont(x), int) = TRUE
tip (x) = int
Deci:
f(cont(x), tip(x)) = TRUE

(2.12)

Funcia de aliniere:
K: adresa x tipi -> TRUE

(2.13)

TRUE daca adresa M k i


K(adresa, Ti )
FALSE daca adresa k i

(2.14)

unde ki este factorul de aliniere cerut prin construcie pentru tipul de date Ti .
K(07AA0, int) = TRUE

(2.15)

pentru c 07AA0 4
K(adr(x), tip (x)) = TRUE

(2.16)

Se spune c variabila x este:


- corect alocat;
- corect iniializat;
dac i numai dac:
f (cont(x), tip(x)) AND K(adr(x), tip(x)) = TRUE

(2.17)

b) Modelul de generare a constantelor pentru un tip specificat de


date
Folosind convenii i simboluri, se definesc reguli, mecanisme de
generare a constantelor. Astfel, se noteaz:

c
a

- cifra
- a sau b

b
[]

- construcie opional

Figura 2.4 Notaiile utilizate n generarea constantelor


Constantelor ntregi li se asociaz modelul de generare:

+
-

ccccc. . . c

Figura 2.5 Modelul de generare a constantelor


Putem verifica dac irurile:
0
- 125
.
3

+- 60
44
sunt sau nu constante ntregi. Cu uurin ne dm seama c irurile . 3 + - 60
i 44 - nu ndeplinesc cerinele impuse de ablonul model.
Pentru constantele de tip real se prezint modelul de generare:


E
[cc ...c ] [.] [cc ...c ] e [cc ]


Figura 2.6 Modelul de generare a constantelor reale
irurile:

+ 1 . 2e-4
- . 3 E2
2 . e-1
1e + 4

sunt constante reale ntruct respect regulile de generare incluse n ablon.


c) Modele de descriere a datelor folosind grafuri.
ntruct grafurile permit punerea n eviden a interdependenelor dintre
elemente omogene sau neomogene, se consider utilizarea lor ca fiind
sugestiv n cazul structurilor de date.
Prin convenie, se stabilesc c nodurile care prezint numai arce
incidente spre interior corespund datelor elementare, iar nodurile care au arce
incidente spre interior i spre exterior corespund datelor de grup.
Astfel, graful:

b
c

Figura 2.7 Graful de reprezentare a datei compuse a


este interpretat ca: data compus a are n alctuirea ei datele elementare b, c
i d.
Graful:
0 - ->0 - ->0- ->0
a
b
c
d

(2.18)

corespunde datelor interdependente, n care b urmeaz lui a, c urmeaz lui b i


d urmeaz lui c. Nodurile a, b, c, d sunt fie date elementare, fie date compuse,
iar pentru stabilirea relaiei de preceden este necesar memorarea unor
adrese.
Pentru realizarea n cadrul programelor a definirii structurilor complexe
de date este necesar reprezentarea acestora folosind grafuri i dup aceea
scrierea n program a unei forme liniarizate neambigue.
De exemplu, pentru structura de tip arborescent, se utilizeaz scrierea
parantetica, ce presupune ca elementele de pe acelai nivel s fie separate prin
virgul, iar pentru trecerea la nivel inferior, utilizarea unei paranteze rotunde
deschise.
Revenirea la nivelul precedent se marcheaz cu o parantez nchis.
Astfel grafului:
a

g
e

Figura 2.8 Graful de reprezentare a datei complexe a


i corespunde liniarizarea:
a(b(d,e,f,g), c(h(i,j)))

(2.19)

d) Modele care permit implementarea recursivitii n descrierea


datelor
Se face deosebire ntre modelele recursive de descriere a datelor
precum:
<semn> : : = + <cifra> : : = 0 1 2 3 4 5 6 7 8 9
<ntreg fr semn> : : = <cifra> <ntreg fr semn><cifra>
<cifra><ntreg fr semn>
<ntreg> : : = <ntreg fr semn> <semn><ntreg fr semn>

i modelele care implementeaz, recursivitatea n descrierea datelor. Astfel,


construcia:
tip_de_dat = ( _ntreg,_)

(2.20)

pune n eviden ca dat conine dou date elementare i anume care are
tipul ntreg i care are tipul .
Aceste modele permit descrierea structurilor de date autoreferite: liste,
stive i arbori.
e) Modele grafice
Sunt utilizate reprezentri grafice pentru locaiile de memorie asociate
variabilelor i constantelor unui program. Prin arce se stabilesc legturile
dintre locaii. Acest model de descriere a datelor este sugestiv i fr
ambiguitate.
Construcia:
1

10

ni1

Figura 2.9 Modelul locaiilor de memorie asociate variabilelor i constantelor


reprezint o list, fiecare component avnd dou elemente: primul reprezint
informaia util avnd valorile 1, 7 i 10, iar al doilea conine adrese. Arcele
orientate indic locaia a crei adresa este memorat n componenta
precedent.
f) Modelul vectorial
Se consider un vector avnd un numr dat de componente. Fiecare
component are o semnificaie precizat, iar componentele luate n ansamblu
lor descriu complet i corect structurile de date.
Se observ c pentru datele elementare, multe dintre componentele
vectorului sunt nule. Zerourile, arat lipsa dependenelor n aval i n amonte
sau mrimea distanei dintre dou componente.
Funcia distant se definete astfel:
d(x,y) = adr (y) adr (x)

(2.21)

Definim lg(x,Ti), funcia lungime a zonei de


operandului x. De exemplu, pentru secvena de program:
...
x :
y :
z :
w :
...

extended;
comp;
shortint;
word;

memorie

asociat

lg(x, extended) = 10 baii


lg (y, comp) = 8 baii
lg (z, shortint) = 1 baii
lg(w, word) = 4 baii
unde:
lg : I x T -> {1,2,4,6,8,10}

(2.22)

Programul care pune n eviden lungimile tipurilor de date ale limbajului


C/C++ este:
//program dimensiune_tip
#include <iostream.h>
#include <malloc.h>
main()
{
cout<<"\n - - - - - - - - - - - - - - - - - - - - - - - - ";
cout<<"\n Reprezentarea datelor de tip ntreg";
cout<<"\n - - - - - - - - - - - - - - - - - - - - - - - - ";
cout<<"\n char: "<< sizeof(char)<<" octet";
cout<<"\n unsigned char: "<< sizeof(unsigned char)<<" octet";
cout<<"\n int: "<< sizeof(int)<<" octet";
cout<<"\n unsigned int: "<< sizeof(unsigned int)<<" octet";
cout<<"\n signed int: "<< sizeof(signed int)<<" octeti";
cout<<"\n short int: "<<sizeof(short int)<<" octeti";
cout<<"\n long int: "<< sizeof(long int)<<" octeti";
cout<<"\n - - - - - - - - - - - - - - - - - - - - - - - - ";
cout<<"\n Reprezentarea datelor de tip real";
cout<<"\n - - - - - - - - - - - - - - - - - - - - - - - - ";
cout<<"\n float: "<< sizeof(float)<<" octeti";
cout<<"\n double: "<<sizeof(double)<<" octeti";
cout<<"\n long double: "<<sizeof(long double)<<" octeti";
cout<<"\n - - - - - - - - - - - - - - - - - - - - - - - - ";
cout<<"\n Reprezentarea datelor de tip caracter";
cout<<"\n - - - - - - - - - - - - - - - - - - - - - - - - ";
cout<<"\n char: "<<sizeof(char)<<" octet";
cout<<"\n - - - - - - - - - - - - - - - - - - - - - - - - ";
cout<<"\n Reprezentarea datelor de tip logic";
cout<<"\n - - - - - - - - - - - - - - - - - - - - - - - - ";
cout<<"\n boolean: "<< sizeof(bool)<<" octet";
}

Dac variabilele x i y sunt contigue:


d(x,y) = lg (x,Ti)

(2.23)

n cazul vectorilor i matricelor, apare posibilitatea punerii n eviden a


contiguitii. Pentru un vector x cu n componente:
d(x[j], x [j+1]) = 1g (x[1],Ti )

(2.24)

oricare j aparine mulimii {1, 2, . . ., n}.


n cazul n care variabilele ocup zone de memorie necontigue:

d(x,y) > lg (x,Ti )

(2.25)

inegalitatea depinznd de modalitatea n care s-a fcut alocarea memoriei.


Redefinirile sau reacoperirile, corespund unor distane fie nule, fie mai
mici dect lungimea cmpului considerat reper.
x

l1

12
y
Figura 2.10 Modelul de reacoperire a locaiilor de memorie
d(x,y) < lg (x) = l1
lg (y) = 12

(2.26)
(2.27)

adr(x) < adr(y) < adr(x) + 11

(2.28)

Uniunile de date apar drept cazuri particulare n acest model de


descriere a datelor.
union a: T1 , b:T2 ,c :T3;

(2.29)

dist(a,b) = dist(a,c) = dist(b,c) = 0

(2.30)

Lungimea zonei de memorie ocupat de variabilele union este:


1= max {lg(a,T1),lg(b,T2),lg (c,T3))

(2.31)

g) Modelul obiectelor generice


Datele aparinnd unui anumit tip apar n expresii precedate sau urmate
de anumii operatori. De asemenea, ele sunt parametrii pentru anumite funcii.
Modelul include:
- forma generic de construire a datei de un tip specificat
- operatorii i funciile care utilizeaz datele definite pentru tipul
respectiv, precum i excepiile de utilizare.
Acest model permite descrierea corect a secvenelor de program, cu
construirea de obiecte acolo unde limbajele de programare implementeaz
cerinele programrii orientate pe obiect.
Exist multe alte modaliti de descriere riguroas a datelor, toate ns
se subordoneaz unor obiecte dintre care cel mai important este crearea
premiselor analizei semantice, pentru punerea n eviden mai nti a
corectitudinii descrierilor de date i mai apoi a corectitudinii programelor.

2.4 Cerine de definire a datelor


n toate programele scrise apar variabile simple. Acestea definesc fie
variabile de control, fie variabile n care se regsesc totaluri sau rezultate
independente de alte evaluri ale programului. Fiecare dat pe care un
programator o specific are caracteristici proprii, care determin tipul i locul n
care este definit, modul de alocare a memoriei, modalitatea de iniializare i
felul n care este folosit n final coninutul su.
Vom considera spre exemplificare un program P n care se utilizeaz
variabilele i de tip ntreg i s de tip real.
Variabila i este o variabil de control folosit n regsirea elementelor
vectorului definit prin:
float x[100];

Variabila s este definit astfel:


float s;
Pentru variabilele independente, definirea n secven este comutativ
fr a influena rezultatul prelucrrii.
Secvenele:

variabile

S1. int i;
float s;

variabile

S2. float s;
int i;

(2.32)

sunt echivalente, rezultatele prelucrrii unui program care conine una din cele
dou secvene sunt identice.
Dac programului P i se ataeaz o secven S la stnga, se obine
programul P1:
P1 = S || P

(2.33)

unde || este operatorul de concatenare.


Dac programului P i se ataeaz o secven S la dreapta, se obine
programul P2:
P2 = P || S

(2.34)

Se spune c secvena S de instruciuni este comutativ n raport cu


operatorul || dac:
rez (S || P; d) = rez (P || S; d)

(2.35)

unde rez( ) este funcia rezultat definit:


rez: P X C -> C

(2.36)

unde:
P
mulimea programelor;
C
mulimea constantelor elementare, vectoriale, matricele i de alte
structuri.
n cazul secvenelor S1, S2 dac:
rez(S1 || P;d) = rez(S2 || P; d)

(2.37)

se spune c cele dou secvene sunt echivalente, adic:


(S1 U S2) || P

(2.38)

Se observ c n programul P, variabila de control i trebuie iniializat cu


valoarea 1 i atinge cel mult valoarea 100, domeniul acesteia fiind:
Dom (i) = [1,100] N

(2.39)

Definirea cu tipul integer determin:


Dom(int) = [-37768, 32767] N

(2.40)

deci Dom (i) Dm (int) unde Dom este funcia domeniu:


Dom : J -> D

(2.41)

Aceast funcie permite tratarea tipurilor fundamentale int, float, bool ca


identificatori cu caracteristici prefixate prin construcia limbajului C/C++.
Dac presupunem c cele 100 de componente ale vectorului x au valori
cuprinse ntre 1 i 200 suma lor nu depete 200 * 100 = 20000. Deci:
Dom (s) = [100,20000] N

(2.42)

Dom (float) = [2.9E 39,1.7E38]


Dom (s) Dom (float)

(2.43)

(2.44)

Faptul c domeniile variabilelor i i s, sunt incluse n domeniile definite


tipurilor, determin excluderea situaiei obinerii de rezultate trunchiate i deci
de pierdere a controlului coninutului lor.
Oportunitatea alegerii tipului ntreg sau real, este pus n eviden de
ponderea funciilor de conversie care se activeaz la execuie.
Pentru un programator care a lucrat ntr-un limbaj de asamblare,
secvenele:

int i;
float s;
. . .
i = 1.;
s = 0;

int i;
float s;
. . .
i = 1;
s = 0.;

sunt diferite pentru c:


- n prima secven se genereaz constanta 1, ca avnd tip float; deci
ocup o zon de memorie de 4 bytes (constante de maxim 7 - 8
cifre). Zona este structurat pentru caracteristic i mantis.
Constanta 0 se genereaz ntr-o zon de memorie de 2 sau 4 bytes
corespunztoare tipului int;
- n modul obiect generat la compilare, i = 1 i s = 0, se concretizeaz
prin copieri (mutri) ale coninutului zonelor de memorie care
corespund operanzilor din dreapta semnului egal, n alte zone de
memorie care corespund operanzilor din stnga semnului egal;
- instruciunile de copiere (mutare) presupun operanzi omogeni; dac
operandul receptor este de tip ntreg, atunci operandul emitor
trebuie s fie tot de tip ntreg; dac operandul receptor este de tip
real, atunci i operandul emitor trebuie s fie de tip real; n caz de
neomogenitate, compilatorul genereaz secvene de apelare a
funciilor de conversie;
- secvena S1 necesit 2 apeluri de funcii de conversie i anume:
conversie de la real la ntreg, f13 i conversie de la real la ntreg f31;
rez1 = f13(1.)
copiere rez1 -> i
rez2 = f31 (0)
copiere rez2 -> s
ntruct n secvena S2, exist concordana ntre tipurile constantelor
generate ca operanzi emitori i operanzi receptori, nu mai sunt
necesare apelri ale funciilor de conversie.
Ca o cerin n alegerea tipului, este realizarea unui nivel ct mai redus
al apelurilor funciilor de conversie.
Posibilitatea definirii la utilizare a variabilelor elementare, conduce
uneori la realizarea unei ocupri a memoriei cu operanzi cu grad redus de
folosire.
Exist limbaje, ca de exemplu Fortran i Basic, care nu necesit
definirea explicit a variabilelor, ci acestea se definesc la prima utilizare, tipul
fiind precizat odat cu respectarea unei reguli de construire a identificatorilor.
Gradul de utilizare este marcat prin numrul de instruciuni n care
variabilele apar, sau prin frecvena de modificare a coninutului lor. Un
program devine cu att mai bun cu ct mprtierea variabilelor elementare
este mai redus.
-

Astfel, dac n secvena S1 apare variabila i, iar n secvena S2 apare


variabila j i programul:
P = S1 || S2

(2.45)

se observ c domeniile celor dou variabile care au acelai tip sunt disjuncte,
deci este definit o singur variabil care este utilizat de ambele secvene.
Dac:
rez (S2; rez (S1; i), j) = rez (S2; rez(S1;i), i)

(2.46)

secvena:
int i;
int j;
...
S1 (i);
...
S2 (j);

va fi modificat obinndu-se secvena:


int i;
...
S1 (i);
...
S2 (i);

Caracterul local sau global, dinamic sau static, este dat de contextul n
care se utilizeaz fiecare variabil. Important este ca programul s realizeze
pentru un exemplu de test din specificaiile de programare, acelai coninut
pentru toate punctele de control.
Dac pentru valorile 1, 2, 3, 4, 5 ale vectorului x de 5 componente, la
iteraia a treia, n specificaiile de programare se indic pentru variabila s
valoarea 6 i dac prin:
rez (S1 S1 S1; s, i)

(2.47)

cont(s) este 6, nseamn c definirea s este corect. Dac ns n locul asociat


structurii repetitive, este definit s i este iniializat:
rez (S1 S1 S1; s, i)

(2.48)

conduce la cont (s) cu valoarea 3, pentru c celelalte valori se pierd la fiecare


activare a blocului, se conchide c definirea local determin erori asupra
rezultatului.

2.5 Cerine de iniializare i utilizare


Variabilele elementare se iniializeaz sub control de ctre programator.
Ele au semnificaii precum:
- definesc dimensiunile problemei de rezolvat;
- definesc precizia rezultatelor;
- definesc opiuni ale utilizatorului care determin funcii care se
activeaz;
- conin rezultate cu grad de cuprindere difereniat;
- controleaz execuia repetitiv a secvenelor;
- specific limite de valori pe care le iau unele variabile;
- conin niveluri puse n coresponden cu tipuri de erori, tipuri de
rezultate sau evenimente n prelucrare.
Compilatoarele moderne pun n eviden situaiile n care se definesc i
se utilizeaz variabile elementare, fr ca n prealabil s fie iniializate.
Iniializarea unei variabile elementare se efectueaz:
- la definire; exist limbaje care permit definirea i iniializarea
variabilei (de exemplu, n limbajul C, construciile int s = 0, i = 0;
sunt frecvente);
- printr-o funcie de citire;
- prin atribuire, variabila elementar aflndu-se n membrul stng.
O variabil elementar se definete pentru a i se utiliza coninutul cel
puin o singur dat ntr-o expresie, ca parametru ntr-o funcie sau ntr-o
expresie indicial.
Afiarea rezultatului coninut de o variabil elementar apare ca o
utilizare a acesteia sub form de parametru n funcii precum writeln( ),
println().
Se spune c variabila elementar i este corect definit i corect utilizat
dac:
cont_spcf (i,n) = = cont_prg (i,m)
unde:
cont_spcf ( )
cont_prg ( )

(2.49)

funcia de coninut a variabilei i dup efectuarea pasului n al


algoritmului precizat n specificaiile de programare;
funcia de coninut a variabilei i dup executarea
instruciunii m din program, instruciune care delimiteaz
sfritul pasului n al algoritmului descris n specificaii.

Dac:
cont_prg (i, m) = = cont_prg(i, m+1) (2.50)
oricare ar fi m [1, M] N, unde M este numrul de instruciuni executabile
care formeaz programul, se spune c i nu i modific coninutul, este deci o
constant i ori este defectuos utilizat, ori trebuia definit nu ca variabil ci ca
o constant simbolic.
Urma programului se obine prin:

cout<<m<< <<cont_prg(i, m);

unde m = 1, 2, . . . , M sau numai pentru valori modificate:


if (cont_prg (i,m) != cont_prg(i,m+1))
cout<<M+1<< <<cont_prg(i,m+1);

n cazul n care datele elementare se definesc numai pentru seturi de


valori dintr-o mulime, se utilizeaz funcia de apartenen, care pune n
eviden corectitudinea reiniializrii unei variabile sau a rezultatelor din
calcule.
Dac:
f (cont_prg (x, m), Ti ) = = FALSE

(2.51)

nseamn c la instruciunea a m a a programului, valoarea variabilei


elementare x nu corespunde setului de valori T ataat acesteia.
De exemplu, dac pentru marcarea erorilor de execuie se atribuie
codurile:
0, dac execuia s-a desfurat normal;
1, dac exist tentativa mpririi prin zero;
2, dac matricea este singular;
3, dac valorile unei expresii indiciale depesc limitele pentru care
este definit operandul de tip masiv:
Ti = {0, 1, 2, 3}

(2.52)

Dac ntr-o funcie de inversare a matricei, variabila ierr este definit pe


mulimea Ti i dac ntr-un punct k al funciei i se atribuie valoarea 7, atunci:
f (cont_prg (ierr, k), Ti ) = = FALSE

(2.53)

nseamn c s-a nregistrat o eroare, o ndeprtare de specificaiile de


programare. Este posibil ca uneori specificaiile de programare s suporte
modificri, care reflect cerine de finee realizate n program. Mulimea
tipurilor de erori este diversificat i atunci se obine:
Ti = Ti U {4, 5, 6, 7}

(2.54)

unde codurile 4, 5, 6, 7 corespund unor noi situaii care conduc la ntreruperea


execuiei. n acest caz:
f(cont_prg(ierr, k), Ti ) = = TRUE

(2.55)

Cerinele de iniializare pentru date de acelai tip, conduc la trecerea de


la variabile elementare, la variabile de tip masiv.
Construciei:

int
...
a =
b =
c =
d =
e =
f =
...

a, b, c, d, e, f;
0;
0;
0;
0;
0;
0;

i corespunde secvena compact:


int
int
. .
for

x[6];
i;
.
(i=0; i<6; i++)
x[i] = 0;

sau secvena:
int x[6] = {0, 0, 0, 0, 0, 0};

Alegerea dintre date elementare i date compuse, este legat n primul


rnd de modul de calcul a adreselor i de obiectivul urmrit prin prelucrare iar
n al doilea rnd, de compactitatea programului.
Datele elementare, se constituie ca o mulime de noduri ale unui graf n
care mulimea arcelor este vid. Adresele variabilelor elementare au caracter
aleator. n general, nu se stabilete o relaie de calcul a adreselor unor
elemente din mulimea de date elementare, avnd ca reper un element
aparinnd de asemenea acestei mulimi.

2.6 Cerine de lizibilitate a programului


Datele elementare sunt puse n coresponden cu identificatori sugestivi.
Astfel, pentru calculul volumului unei prisme paralelipipedice se definesc:
. . .
int lungime, latime, inaltime, volum;
cin>>lungime>>inaltime>>latime;
volum=lungime*latime*inaltime;
cout<<volum = << volum;
. . .

n cazul definirii unei variabile compuse omogene, secvena echivalent


este:

. . .
int x[4];
cin>>x[0]>>x[1]>>x[2];
x[3]=x[0]*x[1]*x[2];
cout<< volum = <<x[3];
. . .

Lizibilitatea programului n acest caz, este crescut numai ca posibilitate


de urmrire a sintaxei acestuia. n prima form se nelege exact semnificaia
prelucrrii. n plus, dac se accept utilizarea variabilelor de stare globale, care
sunt n totalitate variabile elementare, dup apelarea funciilor, se fac teste i
se continu prelucrarea numai dac acestea au nivelul pus n coresponden cu
execuia cerut.
Aceasta este de fapt cauza necesitii standardizrii rspunsului pe care
l ofer funciile n domeniul valorilor de stare pe care le returneaz.
Secvena:
. . .
int stare;
. . .
stare = f1(p1 . . . pn );
if (stare != 0)
return(stare);
stare=f2 (p1. . . . .pn );
if (stare != 0)
return(stare);
. . .

ilustreaz controlul permanent al programatorului


prelucrrii folosind variabila elementar stare.

asupra

rezultatelor

6. MATRICE RARE
6.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 de elemente nenule , adic n 2 . Cantitativ, matricele rare
sunt caracterizate de ponderea 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%.

6.2 Memorarea matricelor rare


Se consider matricea:

0
1 0

0 0 2
A
0 0
0

0 1 0

0
0
0
0

4
0

(6.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:

p
100 (%)
nm

(6.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 ntro zon secundar ZS.
Matricea A prezentat anterior se memoreaz astfel:
Zona primar:
Locaie 1 2 3 4
Valoare 1 -2 4 -1
Figura 6.1 Structura ZP pentru matricea A
Zona secundar:
Locaie
Valoare
Locaie
Valoare

1
1
11
0

5
0
15
0

6
0
16
0

10
1
20
0

Figura 6.2 Structura ZS pentru matricea A


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 printr-un 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) (6.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
1
G
8b
DM

(6.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
0,23 (6.5)
32

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

(6.6)

Pentru matricea A:

Glim 1

1
0,96 96%
32

(6.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 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:
Tabelul nr. 6.1 Structura ZS pe cuvinte
Numrul
cuvntului
1
2
3
4

k
k+1
k+2

Jumtatea stng

Jumtatea dreapt

Numrul de linii
Numrul de coloane
Numrul de elemente nenule
Numrul de elemente nenule Numrul de elemente nenule
n linia 1
n linia 2
Numrul de elemente nenule Numrul de elemente nenule
n linia 3
n linia 4

Numrul de elemente nenule Numrul de elemente nenule


n linia m-1
n linia m
Indicele de coloan al
Indicele de coloan al celui
primului element memorat
de-al doilea element
memorat
Indicele de coloan al celui
etc.
de-al treilea element

memorat

Indicele de coloan al
ultimului element memorat

Pentru matricea A, zona secundar ZS are structura din figura 6.3.


Locaie
Valoare

1
4

2
4

3
1

4
2 0

5
1

6
3

Figura 6.3 Structura ZS pentru matricea A


n reprezentarea din figura 6.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

(6.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

(6.9)

Raportul dintre cerinele de memorie ale acestei structuri de identificare


binar i a celei standard este:

c2

3G
5 m

2
2*m*n

(6.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:

Glimm

2 5 m
1
0,666 66,6%
3
2m 2

(6.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
Valoare
Indice linie
Indice coloan

1
1
1
1

2
-2
2
3

3
4
2
5

4
-1
4
2

Figura 6.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

(6.12)

Raportul dintre cerinele de memorie ale acestei structuri i a celei


standard este:

c3 3 G

(6.13)

Egalnd relaia anterioar cu unitatea se determin valoarea limit a


densitii matricei rare pentru care aceast structur este eficient,
Glim 33,3% .
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 aij 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

(6.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
Figura 6.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 (6.15)
-

linia i este determinat prin relaia:


i = ig(k) ( j 1 ) n

(6.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 (6.17)
Raportul dintre cerinele de memorie ale acestei structuri i a celei
standard este:

c 4 2G

(6.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.
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

ZP
ZS

1
1
0

2
1
1

3
2
0

4
-2
3

5
4
5

6
4
0

7
-1
2

8
0
0

Figura 6.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)

(6.19)

Raportul de memorare este:

c5 2 * G

2 * (m 1)
m*n

(6.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 memorarea indicilor de linie ale elementelor nenule;
ZSC indicii de coloan;
ZSU memorarea adresei urmtorului element al matricei.
Matricea A se memoreaz dup cum urmeaz:
Locaia

ZP
ZSL
ZSC
ZSU

1
1
1
1
&2

2
-2
2
3
&3

3
4
2
5
&4

4
-1
4
2
NULL

Figura 6.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:

c6 4 * G

(6.21)

Prin urmare aceast structur de memorare este eficient pentru


memorarea matricelor cu o densitate a elementelor nenule de maximum 25%.

6.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
7
A
0

0
0
0
8

6
0
0
2

0
0
9
0

0
0
0

(6.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:

5
0.25
54

(6.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:

Tabelul nr. 6.2 Valorile iniiale ale vectorilor LIN, COL i VAL
LIN
1
2
3
4
4

COL
3
1
4
2
3

VAL
6
7
9
8
2

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:
Tabelul nr. 6.3 Valorile matricei rare A
LIN_A
1
2
4

COL_A
1
2
4

VAL_A
-4
7
8

i
Tabelul nr. 6.4 Valorile matricei rare B
LIN_B
1
2
3
4
4

COL_B
1
2
2
1
3

VAL_B
4
-7
8
5
6

rezultatul final se stocheaz n vectorii:


Tabelul nr. 6.5 Valorile matricei rare rezultat C
LIN_C
1
2
3
4
4
4
?
?

COL_C
1
2
2
1
3
4
?
?

VAL_C
0
0
8
5
6
8
?
?

Vectorii LIN_C, COL_C i VAL_C au un numr de componente definite,


egal cu:
DIM (LIN_A) + DIM (LIN_A)

(6.24)

unde DIM() este funcia de extragere a dimensiunii unui masiv unidimensional:


Astfel, dac:
int a[n-m];

(6.25)

atunci:
DIM (a) = n - m+1

(6.26)

Fiind abordat problematica matricelor rare, n mod natural se produce


eliminarea elementelor nenule, obinndu-se n final:
Tabelul nr. 6.6 Coninutul final al matricei rare C
LIN_C
3
4
4
4
?
?
?
?

COL_C
2
1
3
4
?
?
?
?

VAL_C
8
5
6
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 6.6 descrie o matrice rar cu un grad de umplere
egal cu
G=

12
*100 = 37.5 %
32

(6.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)
{
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
0
A
0

3
2
0
8

5
4
9
9

0
8
0
8

0
0
5

i corespunde reprezentarea din figura 6.8.

(6.28)

a[0]
1

a[1]
2

a[2]

a[3]

-1

Figura 6.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:

2 3 4 5
1

A 6
7 8 9 10
11 12 13 14 15

(6.29)

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
3
b2

a03
4
b3

a04
5
b4

a10
6
b5

a11
7
b6

a20
11
b10

a21
12
b11

a22
13
b12

a23
14
b13

a24
15
b14

Figura 6.9 Punerea n coresponden a matricei A cu vectorul b


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)

(6.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])

(6.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])

(6.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.
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
3
A
0

0
3
0
6

0
0
0
9

0
0
1
0

4
0
7
0

0
0
0

(6.33)

se asociaz matricea boolean:

1
1
B
0

1
care prin compactare, ocup
componentele vectorului:

0
1
0
1

0
0
0
1

0
0
1
0

1
0
1
0

0
0
0

(6.34)

primii 3 octei ai unei

C = ( 8, 4, 3, 3, 1, 7, 5, 6, 9)

descrieri, urmai de

(6.35)

Compactarea este procedeul care asociaz un bit fiecrei cifre din forma
liniarizat a matricei B.

6.4 Software orientat spre lucrul cu matrice rare


Metodele de calcul cu matrice rar pentru a fi eficiente trebuie s
beneficieze de proporia mare de elemente nule din aceste matrice, ceea ce
creeaz necesitatea considerrii unor tehnici speciale de memorare,
programare i analiz numeric.
O cerin esenial n programarea matricelor rare const n memorarea
i executarea operaiilor numerice numai cu elementele nenule ale matricei, de
a salva memorie i timp de calcul. n acest caz memorarea standard, devenind
ineficient, este abandonat i nlocuit cu metode de memorare adecvate,
cteva dintre acestea fiind prezentate n paragraful anterior.
Un program de calcul cu matrice rare este cu att mai eficient cu ct
timpul de calcul i cerinele de memorie necesare sunt mai reduse fa de
acelea ale unui program tradiional. De aceea, tehnica de programare trebuie
s realizeze o proporie convenabil ntre timpul de calcul i memoria utilizat,
cerine care de cele mai multe ori sunt contradictorii. n general, este
recunoscut necesitatea unei anumite de structuri de memorare a datelor i o
anumit tehnic de manipulare a acestora n cadrul unui algoritm n care sunt
implicate matricele rare.
Principiul fundamental de programare cu matrice rare const n
memorarea i manipularea numai a elementelor nenule, de sortare i ordonare
n structuri speciale n vederea meninerii structurii de matrice rar i a
stabilitii numerice, de evitare a buclelor complete.
n scopul ilustrrii principalelor operaii efectuate asupra matricelor rare
s-a fcut implementarea acestora n C++, utiliznd mediul de programare
Visual C++. Pentru reprezentarea matricelor s-a ales memorarea compact
aleatoare, datorit flexibilitii n manevrarea datelor. Este prezentat n
continuare o parte a clasei MatriceRara, coninnd constructorii, destructorul,
cteva dintre funciile i operatorii implementai i seciunea privat.

class MatriceRara
{
/*******************************/
/*
Atribute
*/
/*******************************/
private:
long dim;
//numarul de elemente nenule
int m,n;
//dimensiunea matricei
int * coloane;
//vectorul pentru index coloane
int * linii;
//vectorul pentru index linii
double * valori;
//vectorul pentru valori
/*******************************/
/* Constructor & Destructor
*/
/*******************************/
public:
MatriceRara(void);
MatriceRara(const MatriceRara & MR);
MatriceRara(int M, int N, int D, double *val, int *lin, int *col);
MatriceRara(double **matrice, int M, int N);
virtual ~ MatriceRara( );
/*******************************/
/*
Metode auxiliare
*/
/*******************************/
public:
bool EsteRara();
static MatriceRara Unitate(int);
double Urma();
double ** GetMatrice();
/*******************************/
/*
Metode de acces
*/
/*******************************/
public:
inline int getDim();
inline int getLinii();
inline int getColoane();
inline double getValoareElement(int i);
inline int getColoanaElement(int i);
inline int getLinieElement(int i);
double getValoareElement(int i,int j);
bool setValoareElement(int i,int j, int valoare);
double operator ()(int i, int j);
friend ostream& operator <<(ostream&, MatriceRara &);
friend istream& operator >>(istream&, MatriceRara &);
/*******************************/
/*
Metode de prelucrare
*/
/*******************************/
void Sortare();
MatriceRara operator =(MatriceRara &);
MatriceRara operator +(MatriceRara &);
MatriceRara operator -(MatriceRara &);

MatriceRara
MatriceRara
MatriceRara
MatriceRara
};

operator *(MatriceRara &);


operator *(double);
operator !();
Inversa();

n cadrul seciunii private sunt definite urmtoarele atribute:


m,n
dimensiunea matricei iniiale;
dim
numrul de elemente nenule;
coloane pointer la masive de ntregi reprezentnd coloana elementelor
nenule;
linii
pointer la masive de ntregi reprezentnd linia elementelor nenule;
valori
pointer la un masiv avnd tipul de baz al elementelor matricei.
Aplicaia informatic realizat vizeaz principalele operaii necesare
manipulrii matricelor rare:
- construirea acestora prin introducerea datelor de la tastatur; acest
obiectiv este atins prin suprancrcarea operatorului >> prin rutina
istream& operator >>(istream& intrare, MatriceRara &MR)
{
if(MR.dim)
{
delete[] MR.coloane;
delete[] MR.linii;
delete[] MR.valori;
}
cout<<"\n Numarul de linii ale matricei:";intrare>>MR.m;
cout<<"\n Numarul de coloane ale matricei:";intrare>>MR.n;
cout<<"\n Numarul de elemente nenule:";intrare>>MR.dim;
MR.coloane = new int[MR.dim];
MR.linii = new int[MR.dim];
MR.valori = new double[MR.dim];
for(int i=0;i<MR.dim;i++)
{
cout<<"\n Valoare a "<<i+1<<"-a este:";
cout<<"\n\t Linia:";intrare>>MR.linii[i];
cout<<"\n\t Coloana:";intrare>>MR.coloane[i];
cout<<"\n\t Valoare:";intrare>>MR.valori[i];
}
return intrare;
}

vizualizarea matricelor rare prin intermediul operatorului >>;

ostream& operator <<(ostream& iesire, MatriceRara & MR)


{
iesire<<"\n Matricea rara de dimensiune ("<<
MR.m<<","<<MR.n<<") este:";
for(int k=0;k<MR.dim;k++)
iesire<<"\n element["<<
MR.linii[k]<<"]["<<MR.coloane[k]<<"] - "<<MR.valori[k];

iesire<<"\n Vizualizare normala :\n";


for(int i=0;i<MR.m;i++)
{
for(int j = 0;j<MR.n;j++)
iesire<<"\t"<<MR.getValoareElement(i,j);
iesire<<"\n";
}
return iesire;
}

prin intermediul constructorilor clasei este posibil crearea unei


matrice rare iniial fr valori, sau a unei matrice ce preia valorile
dintr-o colecie de trei vectori sau dintr-o matrice normal ce este
validat

/*****************************/
/* Constructori
*/
/*****************************/
MatriceRara::MatriceRara(void):m(0),n(0),dim(0)
{
coloane=NULL;
linii=NULL;
valori=NULL;
}
MatriceRara::MatriceRara(int M, int N, int D, double *val, int *lin, int
*col)
{
m=M;
n=N;
dim = D;
if(dim)
{
coloane = new int[dim];
linii = new int[dim];
valori = new double[dim];
for(int i=0;i<dim;i++)
{
coloane[i] = col[i];
linii[i] = lin[i];
valori[i] = val[i];
}
}
else
{
coloane = linii = NULL;
valori = NULL;
}
}
MatriceRara::MatriceRara(double **matrice, int M, int N)
{
/*
validare matrice rara
*/
int nenule = 0;

for(int i=0;i<M;i++)
for(int j=0;j<N;j++)
if(matrice[i][j]) nenule++;
if(((nenule*100)/(M*N))>100)
{
/*
matricea nu este rara
*/
coloane = linii = NULL;
valori = NULL;
m = n = dim = 0;
}
else
{
/*
matricea este rara
*/
coloane = new int[nenule];
linii = new int[nenule];
valori = new double[nenule];
m = M;
n = N;
dim = nenule;
int k=0;
for(i=0;i<M;i++)
for(int j=0;j<N;j++)
if(matrice[i][j])
{
coloane[k]= j;
linii[k] = i;
valori[k] = matrice[i][j];
k++;
}
}
}

clasa permite copierea valorilor ntre diferite obiecte de tip


MatriceRara prin intermediul constructorului de copiere i a
operatorului =;

/*****************************/
/* Copy constructor
*/
/*****************************/
MatriceRara::MatriceRara(const MatriceRara &MR)
{
dim = MR.dim;
m = MR.m;
n = MR.n;
if(dim)
{
coloane = new int[dim];
linii = new int[dim];
valori = new double[dim];
for(int i=0;i<dim;i++)
{
coloane[i] = MR.coloane[i];
linii[i] = MR.linii[i];

valori[i] = MR.valori[i];
}
}
else
{
coloane = linii = NULL;
valori = NULL;
}
}
MatriceRara MatriceRara::operator =(MatriceRara & MR)
{
if(dim)
{
delete[] coloane;
delete[] linii;
delete[] valori;
}
dim = MR.dim;
m = MR.m;
n = MR.n;
if(dim)
{
coloane = new int[dim];
linii = new int[dim];
valori = new double[dim];
for(int i=0;i<dim;i++)
{
coloane[i] = MR.coloane[i];
linii[i] = MR.linii[i];
valori[i] = MR.valori[i];
}
}
else
{
coloane = linii = NULL;
valori = NULL;
}
return *this;
}

pentru a asigura gestiunea eficient a memoriei aplicaiei se


implementeaz destructorul clasei care asigur eliberarea memoriei
rezervate de cele trei masiv de date;

/*****************************/
/* Desstructor
*/
/*****************************/
MatriceRara::~MatriceRara()
{
delete[] coloane;
delete[] linii;
delete[] valori;
}

principalele operaii matriceale: adunarea, scderea, transpunerea,


nmulirea i inversarea.

Pe parcursul dezvoltrii clasei MatriceRara s-a dovedit necesar


implementarea unei funcii bool MatriceRara::EsteRara(), care s verifice dac
o matrice este rar.
bool MatriceRara::EsteRara()
{
if(((dim*100)/(n*m)>30)) return false;
else return true;
}

n urma prelucrrii matricelor i prin generarea unor obiecte noi ca


rezultate ale prelucrrilor aritmetice exist situaii n care matricea i pierde
caracteristica de a fi rar. Pentru a implementa soluii software eficiente, este
indicat ca modul de stocare a matricei s fie ales n funcie de nivelul de
memorie ocupat. Prin validarea matricei rare cu metoda EsteRara(), datele pot
fi stocate sub form de matrice normal prin intermediul metodei double **
GetMatrice()
double** MatriceRara::GetMatrice()
{
double **matrice=NULL;
matrice = new double*[m];
for(int k=0;k<m;k++)
matrice[k] = new double[n];
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
{
matrice[i][j]=0;
}
for(i=0;i<this->dim;i++)
matrice[linii[i]][coloane[i]]=valori[i];
return matrice;
}

Produsul implementeaz metoda void Sortare() ce permite rearanjarea


valorilor din matricea rar astfel nct acestea s corespund unui mode de
aranjare bazat pe parcurgerea pe linii a matricei. Aceast condiie reprezint o
ipotez de start n derularea operaiilor aritmetice de adunare, scdere i
nmulire deoarece contribuie la obinerea unei metode de prelucrare mai
eficiente din punctul de vedere al efortului procesor.
void MatriceRara::Sortare()
{
/*
metoda rearanjeaza elementele dupa linii
bool flag = true;
while(flag)
{
flag = false;
for(int i=0;i<dim-1;i++)

*/

if(linii[i]>linii[i+1])
{
int temp = linii[i];
linii[i] = linii[i+1];
linii[i+1] = temp;
temp = coloane[i];
coloane[i] = coloane[i+1];
coloane[i+1] = temp;
double valoaretemp = valori[i];
valori[i] = valori[i+1];
valori[i+1] = valoaretemp;
flag = true;
}
else
if(linii[i]==linii[i+1])
if(coloane[i]>coloane[i+1])
{
int temp = coloane[i];
coloane[i] = coloane[i+1];
coloane[i+1] = temp;
double valoaretemp = valori[i];
valori[i] = valori[i+1];
valori[i+1] = valoaretemp;
flag = true;
}
}
}

Pentru a permite accesul programatorilor la atributele matricei rare, sunt


implementate o serie de metode care returneaz valorile acestor caracteristici.
/*******************************/
/* Metode de acces
*/
/*******************************/
int MatriceRara::getDim(){ return this->dim;}
int MatriceRara::getLinii(){ return this->m;}
int MatriceRara::getColoane(){ return this->n;}
double MatriceRara::getValoareElement(int i){ return valori[i];}
int MatriceRara::getColoanaElement(int i){ return coloane[i];}
int MatriceRara::getLinieElement(int i){ return linii[i];}

Metodele de prelucrare a unei matrice sunt bazate pe algoritmi n care


accesul la elementele matricei se realizeaz direct prin intermediul sintaxei
matricei[i][j]. Din punct de vedere al structurii interne, modelul ales n clasa
MatriceRara pentru implementarea unei matrice rare difer de abordarea
clasic a masivului bidimensional. Pentru a permite programatorilor, ntr-un
mod transparent, accesul direct la elementele matricei se definesc metodele:
double MatriceRara::getValoareElement(int i, int j)
{
for(int k=0;k<dim;k++)
if((linii[k]==i)&&(coloane[k]==j)) return valori[k];
return 0;

}
bool MatriceRara::setValoareElement(int i, int j, int valoare)
{
/*
metoda valideaza noua valoare */
if(valoare) return false;
for(int k=0;k<dim;k++)
if((coloane[k]==i)&&(linii[k]==j))
{
valori[k]=valoare;
return true;
}
return false;
}
double MatriceRara::operator ()(int i, int j)
{
return this->getValoareElement(i,j);
}

n subcapitolele urmtoare se face o prezentare detaliat a operatorilor


care implementeaz principalele operaii matriceale: adunarea, scderea,
transpunerea, nmulirea i inversarea.

6.5 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.
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];
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];
rezMR.valori[k]
=
this>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;
}

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 -.
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;
}

6.6 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)
{
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
...

1
p1 tr(A1 )
1
1
p 2 tr(A 2 )
2

n-1: An-1 = A * Bn-2 p n 1

n: An = A* Bn-1

pn

B1 I p1 * A1
B2 = I p2 * A2

1
tr(A n 1 ) B n 1 I p n 1 * A n 1
n 1

1
tr(A)
n

Bn I p n * A n

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;
}
}
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;
}

Avantajele acestui algoritm constau n simplitatea implementrii i


precizia rezultatelor, datorat folosirii unui numr redus de operaii de
mprire.

6.7 Tipuri particulare de matrice rare


Exist tipuri de matrice rare ce prezint o serie de caracteristici prin
prisma crora se pot defini noi metode de a stoca valorile matricei.
O astfel de matrice rar este matricea band, n care valorile nenule
sunt poziionate n mijlocul liniei. n cazul matricelor rare band ce sunt
ptratice, elemente utilizabile se grupeaz n jurul diagonalei principale sau
secundare. De exemplu, matricea de dimensiune (5,8) din figura 6.10 este o
matrice rar n care elementele nenule sunt grupate n jurul diagonalei.
9
0
0
0
0

10
7
0
0
0

0
0
12
0
0

0
9
3
3
0

0
0
0
3
0

0
0
0
0
5

0
0
0
0
0

0
0
0
0
10

Figura 6.10 Matrice rar band


Pe baza ipotezei c elementele nenule sunt grupate pe linii n zone de
dimensiune redus, se definete o nou metod de memorare a matricei
band. Spre deosebire de abordarea compact bazat pe cei trei vectori, n
aceast situaie minimizarea memoriei ocupate se realizeaz prin reducerea
informaiilor necesare localizrii elementelor. Pentru fiecare linie se reine
indexul primei i ultimei valori din grupul de valori nenule. Figura 6.11 descrie
structura asociat matricei band

0 1 2 3 5

index start grup n matrice

1 3 3 4 7

index terminare grup

9 10 7 0 9 12 3 3 3 5 0 10
linia 1

linia 2 linia 3 linia 4

linia 5

Figura 6.11 Model de stocare a matricei band


Se observ c pentru fiecare grup de valori nenule se reine prin
intermediul a doi vectori coloana primei valori nenule i coloana ultimei valori
nenule. Din acest motiv, vectorul de valori stocheaz i valorile nule cuprinse
n grup, fapt care conduce la pierderea eficienei metodei pe msur ce
matricea band crete n lime.
Pentru exemplul considerat, aceast metod de stocare este mai
eficient dect modelul compact. Dac se consider valorile ca fiind ntregi,
nivelul de memorie necesar pentru datele matricei este egal cu
DMRbanda = DMindex_start+DMindex_term+DMvalori = (5 + 5 + 12)*4 = 88 octei (6.37)
unde:
DMindex_start dimensiunea zonei de memorie asociat indexului de start;
DMindex_term dimensiunea zonei de memorie asociat indexului de terminare;
dimensiunea zonei de memorie asociat valorilor;
DMvalori
Aceeai matrice stocat n forma compact, necesit DMRcompact = 3 * 12
* 4 = 144 octei.
Din punctul de vedere al programatorului, aceast abordare conduce la
definirea clasei MatriceBanda
class MatriceBanda
{
private:
int m,n;
int dim;
int *index_start;
int *index_term;
double *valori;

//dimensiunea matricei
//numarul de elemente nenule
//vector pentru index start
//vector pentru index terminare
//vector pentru valori

public:
MatriceBanda();
MatriceBanda(double **matrice, int m, int n);
MatriceBanda(const MatriceBanda&);
~MatriceBanda();
double getValoare(int i, int j);}

Se observ c pe lng informaiile descrise n figura 6.11 este necesar


s se memoreze dimensiunea matricei band i numrul de elemente nenule.
Pentru a parcurge vectorii index_start i index_term nu este nevoie de
informaii suplimentare, deoarece masivele au un numr de elemente egal cu
numrul de linii ale matricei.
Pentru a asigura programatorilor un nivel de transparen la accesarea
direct a valorilor din matricea band i pentru a trece peste bariera dat de
structura intern a obiectului MatriceBanda se definete metoda double
getValoare(int i, int j) ce permite afiarea valorii elementului de pe linia i i
coloana j.
double MatriceBanda::getValoare(int i, int j)
{
if((i<0 || i>=m) || (j<0||j>=n))
{
cerr<<"Index gresit !";
return 0;
}
if((j<this->index_start[i])||(j>this->index_term[i]))
return 0;
else
{
int index_linie=0;
for(int k =0;k<i;k++)
index_linie+=(index_term[i]-index_start[i]+1);
return this->valori[index_linie+(j-index_start[i])];
}
}

Un alt caz de matrice rar particular este matricea diagonal. Acest


masiv bidimensional este ptratic i are elemente nenule doar pe diagonala
principal. Dac se consider matricea din figura 6.12
9 0 0 0
0 7 0 0
0 0 10 0
0 0 0 3
Figura 6.12 Matrice rar triunghiular.
se definete o metod de reprezentare particular ce se bazeaz pe
memorarea dimensiunii matricei i a valorilor de pe diagonala principal, clasa
MatriceDiagonala.
class MatriceDiagonala
{
private:
int n;
int *valori;
public:

MatriceDiagonala();
MatriceDiagonala(MatriceDiagonala&);
~MatriceDiagonala();

double getValoare(int, int);


};

Pentru a accesa elementele matricei se implementeaz metoda double


getValoare(int, int).
double MatriceDiagonala::getValoare(int i, int j)
{
if((i<0 || i>=n) || (j<0||j>=n))
{
cerr<<"Index gresit !";
return 0;
}
if(i!=j)
return 0;
else
{
return this->valori[i];
}
}

Metoda returneaz valoare elementelor pentru care i este egal cu j,


celelalte elemente ale matricei avnd valoarea zero.
Matricea diagonal este o matrice rar, deoarece o matrice ptratic de
ordin n 4, conine pe diagonala principal mai puin de 30% din valori.
Prin prisma matricei diagonale se observ c matricea unitate, figura
6.13, reprezint un caz special al acestui tip de matrice deoarece toate
elementele de pe diagonal au valoarea 1. Pentru a memora o matrice unitate
este nevoie s se indice doar ordinul matricei, aceasta putnd fi generat cu
uurin.
Matricea triunghiulara reprezint o matrice ptratic n care toate
valorile aflate sub diagonala principal au valoarea 0. Pentru matricea
triunghiulara numrul de elemente nenule este

n 1 * n ,
2

unde n reprezint

dimensiunea matricei ptratice, iar raportul acestora n mulimea de elemente


ale matricei,

n 1
n 1

ia valori n intervalul [0,33 ; 1) pentru n 2. Cu toate c

ponderea elementelor nenule nu este suficient de mic pentru a fi considerat


o matrice rar, acest tip de matrice are n funcie de rangul su un numr
destul de mare de elemente nenule pentru a fi acordat o atenie special
modului de stocare. De asemenea, proprietile algebrice ale matricei
triunghiulare contribuie la alegerea acestui tip de matrice n rezolvarea
sistemelor de ecuaii compatibile cu acest tip de matrice:

suma i diferena dintre dou matrice triunghiulare reprezint tot o


matrice triunghiular;
- rezultatul nmulirii a dou matrice triunghiulare de dimensiune egal
reprezint tot o matrice triunghiular;
- valoarea determinantului unei matrice triunghiular este dat de
produsul elementelor de pe diagonala principal.
De exemplu, matricea triunghiular din figura 6.13
2 9 3 3
0 7 1 5
0 0 10 2
0 0 0 3
Figura 6.13 Matrice triunghiular
este stocat, prin memorarea ntr-un masiv unidimensional a valorilor nenule
i prin indicarea indexului de nceput a valorilor de pe fiecare linie, figura 6.14.
index start linie n mulime a
de valori

0 4 7 9

2 9 3 3 7 1 5 10 2 3
linia 1

linia 2

linia 3

linia 4

Figura 6.14 Model de stocare a matricei triunghiulare


Clasa MatriceTriunghiulara implementeaz aceast soluie i definete
metode de acces direct la elementele matricei.
class MatriceTriunghiulara
{
private:
int * linii;
//indexul fiecarei linii in lista de valori
int n;
//dimensiunea matricei patratice
double * valori; //valorile nenule din matrice
public:
MatriceTriunghiulara();
MatriceTriunghiulara(int);
virtual ~MatriceTriunghiulara();
MatriceTriunghiulara(const MatriceTriunghiulara&);
bool setValoare(int i, int j, double valoare);
double getValoare(int i, int j);
double getDeterminant();
int getDimensiune(){return n;};

MatriceTriunghiulara operator+(MatriceTriunghiulara& );
MatriceTriunghiulara operator=(MatriceTriunghiulara& );
friend ostream& operator <<(ostream&, MatriceTriunghiulara&);
};

Constructorul implicit iniializeaz o matrice triunghiular vid, iar


constructorul cu parametrii primete dimensiunea matricei ptratice. Pentru
aceast abordare, valorile nenule sunt introduse de la tastatur de ctre
utilizator parcurgnd matricea pe linii.
MatriceTriunghiulara::MatriceTriunghiulara()
{
linii = NULL;
n = 0;
valori = NULL;
}
MatriceTriunghiulara::MatriceTriunghiulara(int dim)
{
if(dim)
{
this->n=dim;
linii = new int[n];
valori = new double[n*(n+1)/2];
int indexLinie=0;
for(int i=0;i<n;i++)
{
linii[i]=indexLinie;
for(int j=i;j<n;j++)
{
cout<<"\n Element ["<<i<<"]["<<j<<"]:";
cin>>valori[indexLinie+j-i];
}
indexLinie+=n-i;
}
}
else
{
n = 0;
linii = NULL;
valori = NULL;
}
}

Constructorul de copiere al clasei i metoda ce suprancarc operatorul


= permit crearea de noi obiecte prin copierea valorilor unor matrice existent.
Diferena dintre cele dou metode este dat de situaia n care se apeleaz
fiecare metod. Apelul operatorului = presupune existena ambelor obiecte i
programatorul trebuie s dezaloce zona de memorie a obiectului curent nainte
de a face iniializarea.
MatriceTriunghiulara::MatriceTriunghiulara
MT)

(const

MatriceTriunghiulara&

{
if(MT.n)
{
this->n=MT.n;
linii = new int[n];
valori = new double[n*(n+1)/2];
for(int i=0;i<n;i++)
linii[i] = MT.linii[i];
for(int j=0;j<n*(n+1)/2;j++)
valori[j]=MT.valori[j];
}
else
{
n=0;
linii = NULL;
valori=NULL;
}
}
MatriceTriunghiulara
MatriceTriunghiulara::operator
(MatriceTriunghiulara &MT)
{
if(n)
{
delete[] linii;
delete[] valori;
}
if(MT.n)
{
this->n=MT.n;
linii = new int[n];
valori = new double[n*(n+1)/2];
for(int i=0;i<n;i++)
linii[i] = MT.linii[i];
for(int j=0;j<n*(n+1)/2;j++)
valori[j]=MT.valori[j];
}
else
{
n=0;
linii = NULL;
valori=NULL;
}
return *this;
}

Destructorul clasei gestioneaz dezalocarea zonei de memorie rezervat


de un obiect de tip MatriceTriunghiulara prin alocarea dinamic a spaiului
aferent celor dou masive linii i valori.
MatriceTriunghiulara::~MatriceTriunghiulara()
{
delete[] linii;
delete[] valori; }

Pentru a asigura accesul la elementele matricei, ntr-un mod transparent


i apropiat cu abordarea direct dat de sintaxa matrice[i][j], clasa
implementeaz metodele getValoare i setValoare pentru a returna valoarea
elementului de pe linia i i coloana j, respectiv, pentru a iniializa elementul.
double MatriceTriunghiulara::getValoare(int i, int j)
{
if((i<0 || i>=n) || (j<0||j>=n))
{
cerr<<"Index gresit !";
return 0;
}
if(j<i)
return 0;
else
return this->valori[linii[i]+j-i];
}
bool MatriceTriunghiulara::setValoare(int i, int j,double valoare)
{
if((i<0 || i>=n) || (j<0||j>=n))
return false;
if(j>=i)
{
valori[linii[i]+j-i]=valoare;
return true;
}
else
return false;
}

n cazul metodei getValoare se returneaz valoarea zero pentru orice


element pentru care valoarea j este mai mare dect i deoarece aceste
elemente se gsesc sub diagonala principal.
Metoda setValoare valideaz coordonatele elementului de iniializat
deoarece nu este permis setarea unui element al matricei ce se gsete sub
diagonala principal, caz n care matricea i pierde caracteristica de a fi
triunghiular.
Pornind de la ipoteza c suma a dou matrice triunghiulare genereaz o
matrice de acelai tip, metoda care implementeaz aceast operaie aritmetic
adun elementele de pe poziii comune fr a lua n considerare rezultatul
acestora i fr a lua n considerare valorile de sub diagonal.
MatriceTriunghiulara
MatriceTriunghiulara::operator
MatriceTriunghiulara& MT)
{
MatriceTriunghiulara rezMT;
if(this->n==MT.n)
{
rezMT.linii = new int[n];
rezMT.valori = new double[n];
rezMT.n=this->n;
for(int i=0;i<n;i++)

{
rezMT.linii[i] = this->linii[i];
for(int j=0;j<n-i;j++)
{
rezMT.valori[rezMT.linii[i]+j]=valori[linii[i]+j]+MT.valori[MT.lini
i[i]+j];
}
}
}
return rezMT;
}

Pentru a calcula determinantul matricei triunghiulare, ipoteza de lucru


este dat de faptul c elementele de pe diagonala principal reprezint prima
valoare de pe fiecare linie a matricei.
double MatriceTriunghiulara::getDeterminant(){
double determinant = 1;
for(int i=0;i<n;i++)
determinant*=valori[linii[i]];
return determinant;
}

Matricea de permutare este un masiv bidimensional n care fiecare linie


sau coloan conine o singur valoare unu, n rest elementele fiind nule.
Matricea are aceast denumire deoarece este utilizat n operaii algebrice
pentru a permuta elementele unui vector conform unui model stabilit anterior.
Dac se consider matricea MP din figura 6.15 i vectorul X = 1,2,3,4,5
0
0
1
0
0

0
0
0
1
0

0
1
0
0
0

1
0
0
0
0

0
0
0
0
1

Figura 6.15 Matrice de permutare de ordin egal cu cinci


prin nmulirea X * MP se obine vectorul XP = 3,4,2,1,5, fapt ce evideniaz c
elementele vectorului au fost rearanjate conform poziionrii valorilor egale cu
unu n matrice de permutare.
Pentru a stoca o astfel de matrice, n care n elemente sunt egale cu unu,
restul fiind zero, se definete clasa MatricePermutare.
class MatricePermutare
{
private:
int n;
int* index_coloane;
public:
MatricePermutare();
~MatricePermutare();

//ordinul matricei
//indexul coloanei

MatricePermutare(const MatricePermutare&);

};

Valorile memorate pentru a reprezenta corect matricea de permutare


sunt reprezentate de:
- rangul matricei ptratice;
- indexul coloanei pe care se gsete valoarea unu de pe fiecare linie;
deoarece exist o singur valoare nenul pe fiecare linie i coloan,
poziia valorii n cadrul vectorului index_coloane indic linia
corespondent.
Figura 6.16 descrie vectorul index_coloane asociat matricei de permutare din
figura 6.15.
n=5

ordinul matricei ptratice

3 2 0 1 5
pe linia i = 2, valoarea 1 se gsete pe
coloana j = 0

Figura 6.16 Model de stocare a matricei de permutare.


ntr-o matrice de permutare de ordin n, numrul de valori nenule este
egal cu n, iar n cazul n care aceast valoare depete nivelul trei, matricea
este validat ca fiind o matrice rar datorit ponderii mici a valorilor nenule.

6.8 Estimarea parametrilor unei regresii statistice folosind


clasa MR
Se consider datele din tabelul 6.7 ce caracterizeaz cinci ntreprinderi
din punctul de vedere al productivitii y, al numrului de utilaje de mare
performan deinute x1 i al veniturilor suplimentare acordate salariailor x2.
Se dorete determinarea unei funcii care s caracterizeze dependena dintre
productivitate i celelalte dou variabile considerate independente.
Tabelul nr. 6.7 Evoluia indicatorilor y, x1 i x2 ntr-o ntreprindere
y productivitatea muncii (creteri procentuale) 1 2 5 6 7
x1 utilaje de mare randament (buci)
2 4 4 4 5
x2 venituri suplimentare (mil. lei)
1 1 3 5 5
Pentru a specifica forma funciei, se analizeaz pe cale grafic
dependena variabilei efect(y) n raport cu fiecare dintre variabilele cauzale.

x1

x2

Figura 6.17 Dependena y= f(x1), y= f(x2)


ntruct norul de puncte din fiecare reprezentare grafic sugereaz o
linie dreapt, specificm modelul astfel:
yi = a0 + a1x1i + a2x2i + ui

(6.38)

unde ui reprezint o variabil rezidual ce caracterizeaz influena altor


factori asupra variabilei efect y, factori care din diverse motive nu pot fi luai n
considerare.
Dac simbolizm y valorile ajustate, rezultate n urma aplicrii
modelului liniar, specificm modelul astfel:

y i a 0 a 1x 1i a 2 x 2i

(6.39)

Relaia anterioar se scrie pentru fiecare set de valori prezentate n


tabelul 6.7, rezultnd:

y1 1

y2 1


yn 1

x11
x12

x1n

x 21 a 0 u1

x 2 2 a1 u2



x 2 n a n un

(6.40)

Aadar:
Y = XA + U

(6.41)

Y XA

(6.42)

iar

Determinarea dependenei dintre variabila efect i variabilele cauz


nsemn determinarea valorilor numerice ale parametrilor

a 0 , a1 i a 2 . n acest

scop se utilizeaz metoda celor mai mici ptrate. Aceast metod presupune
minimizarea expresiei:

2
t

(6.43)

adic, matriceal, U*U. Dar:

U U Y XA Y XA

(6.44)

unde prin U' s-a notat transpusa matricei U.


n [Peci94] se demonstreaz c prin minimizarea relaiei de mai sus se
ajunge la expresia:

A X X X Y
1

(6.45)

O determinare ct mai precis a matricei parametrilor unei regresii


presupune existena unui numr foarte mare de date, adic un numr mare de
linii n matricea X. n multe cazuri practice valorile acestor date sunt nule, fapt
ce justific implementarea relaiei anterioare pe o structur de matrice rare.
Avnd deja definii operatorii de transpunere, nmulire i inversare,
implementarea relaiei de mai sus presupune scrierea unei singure linii de cod:

A !X * X .Inversa() * !X * Y (6.46)
Aadar, pentru aflarea matricei A sunt necesare dou operaii de
transpunere, trei nmuliri i o inversare. Matricele X i Y asupra crora se
opereaz au n multe dintre cazurile practice o densitate foarte mic, astfel c
este pe deplin justificat folosirea unor structuri de memorare specifice
matricelor rare.
Asistm n prezent la un fenomen care tinde s ating tot mai multe
domenii de activitate. Necesitatea de a cunoate ct mai precis anumii
factori sau parametri ce caracterizeaz domeniul respectiv, care pn nu de
mult erau fie considerai aleatori, fie nu se punea problema determinrii lor,
considerat a fi imposibil. Cunoaterea acestor factori ofer o descriere mai
detaliat a sistemului n care se lucreaz, permind n acest fel o mai bun
activitate de control i comand a acestuia.
n cele mai multe dintre cazuri baza de calcul a acestor factori o
constituie statistica matematic i teoria probabilitilor, ceea ce conduce la
necesitatea rezolvrii unor probleme liniare de foarte mari dimensiuni.
Caracterul de raritate al structurilor implicate n rezolvarea problemelor,
datorat caracteristicilor reale ale sistemelor, la care se adaug necesitatea unei
rezolvri rapide, n concordan cu dinamica crescnd a sistemelor actuale,
justific pe deplin introducerea n aplicaiile informatice asociate a unor
structuri de date adaptate acestor particulariti.

Softul orientat spre lucrul cu matrice rare exploateaz caracterul de


raritate al structurilor manipulate, oferind un dublu avantaj: memorie i timp.
n ultimii ani memoria nu mai constituie o problem, ns timpul necesar
calculelor, odat cu apariia sistemelor n timp real, se dovedete a fi tot mai
mult o resurs critic.
Referitor la lucrul cu matrice n general, n cadrul unui sistem n care
timpul reprezint o resurs critic, exist posibilitatea de a realiza un soft care
s fac o evaluare anterioar calculelor asupra densitii matricei, i, n funcie
de aceasta, s decid asupra structurilor de date ce vor fi folosite pentru
memorare i efectuarea calculelor, astfel nct timpul afectat calculelor s fie
minim.

7. ARTICOLUL STRUCTUR DE DATE NEOMOGEN


I CONTIGU
7.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:
C 8 C1 C2 C4 C3 C5 C6 C 7

(7.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 dintrun 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 7.1, unde lungimea este calculat ca
numr de caractere.

C8
C1
C2
C3
C4
C5
C6
C7
C9

Tabelul nr. 7.1 ablon de descriere a cmpurilor


Caracteristic
Natur dat
Lungimea
cod material
numr
5
nume material
ir caractere
20
unitate msur
ir caractere
3
cantitate existent n stoc
numr
6
pre unitar
numr real
4+2
data ultimei aprovizionri
ir caractere
9
intrri
numr
6
ieiri
numr
6
stoc final
numr
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 7.1.

nume articol

primul element.
din ablon

al 2-lea elem.
din ablon

...

ultimul elem.
din ablon

Figura 7.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) +
(7.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 ) lg(cmp j)

(7.3)

j 1

unde:

1 reprez int baitul cerut de alinierea cmpului j 1


0 n cazul n care cmpj 1 nu cere alinierea

(7.4)

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
{
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)

(7.5)

iar
adr(v[4]) = adr(v[1]) + (4-1) * lg(int)

(7.6)

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.

7.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
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 ) ( y , j , i 1)

(7.7)

j 0

unde prin xj nelegem elementul yj de pe nivelul i+1, astfel nct:


cont(x) adr(y1)

(7.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:
k 1

j 1

k 1

h 1

adr y j , 0 adr y1 , i lg y k , i k lg y h , i k

(7.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 7.2

student

data_nasterii

nume

zi

luna

data_admiterii

an

zi

an

luna

Figura 7.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

(7.10)

pentru c 2 = 3 = 4 = 0.
Dac se consider punctul operator ca orice alt operator, atunci
construcia:
a.b.c.d

(7.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 7.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)]
(7.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 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.

7.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 7.3.

x[0]

nume

varsta

facult

x[1]

x[2]

an_studiu

x[14]

nume

varsta

x[19]

facult

an_studiu

Figura 7.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:
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
vectori de structuri de vectori:

care

conin

vectori, respectiv

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;

(7.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];

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]

(7.14)
(7.15)

Atributele sunt comutative n raport cu operatorul de referire. Un


element din vectorul de structur se refer prin:
nume_vector[i].nume_membru

(7.16)

iar un element din structura de vectori se refer prin:


nume_structur.nume_membru[i]

(7.17)

Se consider spre exemplificare urmtorul program care gestioneaz


acionarii unei societi comerciale.
#include
#include
#include
#include
#include
#include

<windows.h>
<process.h>
<ctype.h>
<stdio.h>
<conio.h>
<string.h>

struct data
{
int da_year,da_day,da_mon;
};
struct adresa
{

};

char str[25];
int nr;
char bloc[3];
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!"
};
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))
{
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;
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
{
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)
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
{

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);
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);
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);
}
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);

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
[subunitara]:");
scanf("%f",&cota);
if (cota>1||cota <0)
eroare(6);
}
while(cota>1||cota <0);
fwrite(&cota,sizeof(float),1,pd);
fclose(pd);
}

repartizata

la

dividende

void afis_date(const actionar &act)


{
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_ye
ar);
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);

}
}
fclose(pf);

afis_date(act);
getch();

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;
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/k
soc*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);
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.adr.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.ad
r.ap,act.adr.sect,act.adr.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))
{
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);
}
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);
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:");
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)
{
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;
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.

8. VARIABILE POINTER
8.1 Tipul variabilei pointer
Pentru programatorii care cunosc un limbaj de asamblare, definirea i
utilizarea variabilelor pointer n limbaje evoluate de programare reprezint
operanzii necesari implementrii adresrii indirecte.
La adresarea indirect se utilizeaz doi operanzi i anume: operandul
care refer i operandul referit, notai n continuare R0, respectiv Or.
Operandul R0 conine adresa de nceput a zonei de memorie asociat
operandului Or. Evaluarea expresiilor:

R0 adr(Or), Or 15

(8.1)

conduce la modificarea coninutului operandului R0 i a operandului Or,


figura 8.1, ceea ce se exprim prin:

cont(R0) adr(Or)

(8.2)

R0

Or
07441A

15
07441A

Figura 8.1 Modificarea coninuturilor operanzilor R0 i Or


Aceast expresie arat c operandul R0 este de un nou tip, Tp, ale
crui valori aparin intervalului [Ai, Af] N, unde Ai i Af sunt adrese de
nceput, respectiv, de sfrit ale unui segment de memorie.
Dac:

f(cont(R0), Tp) TRUE

(8.3)

adic

cont (R0) [Ai, Af] N

(8.4)

se spune ca operandul R0 este de tipul Tp.


n continuare acest nou tip de dat este numit tipul pointer.
Funcia lg(R0,TP) definete o astfel de mrime care s permit
stocarea complet a informaiei necesare localizrii operanzilor, ale cror
adrese se ncarc n variabila R0.
Expresia:
cont(Ro) = adr(Ro)

(8.5)

are semnificaia ncrcrii n operandul R0 a propriei adrese. Dup evaluarea


expresiei, coninutul lui R0 este dat n figura 8.2.

R0
07441A
07441A
Figura 8.2 Noul coninut al operandului R0
n sine, expresia nu are o semnificaie deosebit, mai mult ofer
riscul nchiderii unui lan de referire, transformndu-l n ciclu infinit. Dac
operandul R0 devine reper i toate deplasrile sunt evaluate avndu-l ca
baz, expresia de mai sus se justific.
Dac se definesc variabilele a, b, c avnd tipul Ti, i variabilele pa,
pb, pc avnd tipul Tp, evaluarea expresiilor:
pa = adr(a)
pb = adr(b)
pc = adr(c)
realizeaz iniializarea operanzilor de tip Tp.
Dac sunt luate n considerare aceste expresii, n loc de:

c = a+b;

(8.6)

se va utiliza expresia:

ref(pc) = ref(pa)+ref(pb)

(8.7)

unde funcia de referire ref() este definit:


ref : [Ai, Af] -> I

(8.8)

unde mulimea
este interpretat drept coninutul zonei de memorie
asociate identificatorilor.
Se observ c prin definirea funciei adr():

adr : I -> [Ai, Af]

(8.9)

funciile adr() i ref() nu sunt una invers celeilalte. Este adevrat numai
egalitatea:

x = ref(adr(x))

(8.10)

Nu n toate cazurile, limbajele de programare implementeaz aceast


proprietate.

ref(x)
adr(x)

Figura 8.3 Relaia ntre funciile adr(x) i ref(x)


Proprietile funciilor sunt discutate n contextul alocrii statice a
memoriei, fr posibilitatea redefinirii sau suprapunerii de operanzi,
obinndu-se zone de memorie comune pentru seturi de operanzi.
Deci, n contextul prefixat, pentru doi operanzi diferii, xi i xj de
acelai tip sau de tipuri diferite.
adr(xi) adr(xj)

(8.11)

ref(adr(xi)) ref(adr(xj))

(8.12)

pentru c xi xj, din definiie.


Variabilele de tip Tp apar n expresii aritmetice, relaionale sau de
atribuire, nu ca elemente n sine ci ca parametri ai funciei ref().
Revenind la limbajul de asamblare, operaiile de calcul sunt
specializate pe tipuri de operanzi. Exist operator de adunare pentru
operanzi de tip ntreg, dar exist i operator de adunare pentru operanzi de
tip virgul mobil simpl precizie. Pentru operanzii de tip dubl precizie,
exist operator distinct de cei menionai.
Operatorii de comparare privesc zone de memorie de lungimi diferite,
care s acopere totalitatea lungimilor asociate tipurilor fundamentale de
date implementate n limbajele de asamblare.
Apariia operanzilor de tip Tp, determin pierderea informaiei
referitoare la tipul iniial al operandului i exist posibilitatea generrii unei
evaluri ambigue a expresiilor.
n acest sens, tipul Tp se definete ca un tip compus:
Tp = (pointer,Ti)

(8.13)

unde Ti, reprezint tipul variabilei referite i pointer este tipul specific,
fundamental al variabilei. Dac variabila care refer px, este de tip Tp i
variabila referit x, este de tip Ti, n vorbirea curent expresia se
interpreteaz, pornind de la efectul fizic al operaiilor cu pointeri, ca pointer
spre Ti.
Dac x are tipul int i px este de tip Tp, se spune c px este pointer
spre int i toi operatorii din expresiile omogene ca tip de date unde apare
px, sunt selectai pentru lucrul cu operanzi de tip int.
Tipul variabilei referite nu influeneaz lungimea variabilelor de tip Tp
i coninutul.
Dac o variabila x, este definit ca avnd tipul Ti i variabila px este
definit ca avnd tipul (pointer, Tj), expresia:
px = adr(x) (8.14)

nu este evaluat dect numai dup ce ara loc conversia de la tipul Ti la tipul
Tj, operatorul de atribuire necesitnd operanzi de acelai tip. Aceast
conversie realizeaz de fapt schimbarea setului de informaii n concordan
cu operandul din stnga.
Prin conversie, adresa este asociat unei variabile de tip Tj cu toate
implicaiile ce decurg asupra alinierilor i lungimii zonelor de memorie
asociate noului tip, asociat zonei de memoria pus n coresponden cu
variabila x.
Exist situaii n care conversia de tip este implicit (n cazul evalurii
expresiilor aritmetice), dar sunt numeroase cazurile n care aceasta trebuie
specificat explicit.
Funcia conv(x, Tj) modific tipul variabilei x de la Ti la Tj. Dac:
tip(x) = Ti

(8.15)

x = conv(x,Tj)

(8.16)

conduce la:
tip(x) = Tj

(8.17)

i n mod corespunztor:
px = convp(adr(x),Tj)

(8.18)

unde convp( ) reprezint funcia de conversie a tipului spre care pointeaz o


variabila de tip Tp.
Expresia din dreapta conduce mai nti prin evaluarea lui adr(x) la
tipul (pointer,Ti), ca mai apoi prin folosirea funciei convp(), s se obin
conversia de tip i rezultatul evalurii este (pointer, Tj), care este omogen
n raport cu tipul Tp al variabilei px.
Funcia convp() are rolul de a modifica al doilea termen al perechii
(pointer, Ti) care definete tipul Tp.
Conversiile de tip, sunt facilitai cu importante efecte asupra
performanei programelor, cu condiia ca ele s se afle strict sub controlul
programatorului.
Atunci cnd conversiile de tip depesc simplele expresii de atribuire,
prin crearea de noi vecinti pentru operanzi prin redistribuirea baiilor,
sunt interpretate componente care altfel rmneau neexplorate n program.
Toate ns, e necesar s fie sub controlul programatorului. Altfel, dintr-o
facilitate, conversia de tip se transform ntr-o problem generatoare de
greuti.

8.2 Aritmetica variabilelor pointer


ntruct pentru variabilele px i py de tip Tp:
cont(px) [Ai, Af] N

(8.19)

cont(py) [Ai, Af] N

(8.20)

pe mulimea [Ai, Af] N se definete operaia , astfel nct pentru:

,[Ai, Af] N, = , [Ai, Af] N

(8.21)

Dac, de exemplu, se consider intervalul [0; 64000] N, operaiile


de adunare, nmulire, scdere i mprire, nu sunt legi de compoziie
intern. Prin contraexemple se dovedete c exist i astfel nct:
[0;64000] N

(8.22)

Aparent, nu se vorbete de o aritmetic a variabilelor pointer. Totui


se construiesc expresiile cu variabile pointer, cu condiia ca rezultatul
evalurii s aparin intervalului [Ai, Af] N.
Dac se definete masivul unidimensional:
float x [10];

i px este un pointer spre ntreg, i:


px = adr(x[l])

(8.23)

evaluarea expresiei:
px = px + 6 = adr(x[l]) + l*lg(real) = adr(x[2])

(8.24)

conduce la posibilitatea de a referi, elementul al doilea al vectorului x.


Dac:
px = adr(x[10])

(8.25)

i se evalueaz expresia:
px = px-24

(8.26)

px = adr(x[10])-4*lg(real) = adr(x[6])

(8.27)

n expresiile:
adr(z+k) = adr(z)+k*lg(Ti)

(8.28)

unde z este o variabil de tip Ti, se observ c pentru variabila pz de tip Tp,
care a fost iniializat prin:
pz: = adr(z);

(8.29)

adr(z+k) = pz + k*lg(Ti)

(8.30)

i pentru c tipul Tp = (pointer, Ti), expresia k*lg(Ti) este rezultatul


conversiei la tipul Tp al variabilei pz, a variabilei ntregi k.

n acelai mod, se definete i operaia de scdere, n ambele cazuri


apare cerina ca rezultatul evalurii expresiilor s aparin intervalului [Ai,
Af] N.
Se construiesc teoretic, expresii deosebit de complexe, n care
operanzii s fie de tip Tp, dar restricia de apartenen a rezultatului la acel
interval le face inoperante.
n plus, lucrul cu adrese n zone de memorie contigue i mai ales
pentru structuri de date omogene, vizeaz posibilitatea de a genera
termenii unor progresii aritmetice.
Dac variabila px de tip Tp, este iniializat prin:
px = adr(x[1]);
s = 0;
for(i=0; i<10; i++)
{
s = s + ref(px);
px = px+4;
};

variabila px, permite referirea rnd pe rnd a elementelor x[0], x[1], ...,
x[9] ale vectorului x.
Mai mult, definind px variabila de tip Tp n secvena:
px = adr(x);
s = 0;
pi = adr(x[0]);
do
{
s = s+ref(pi);
pi = pi+4 ;
} while (pi <= adr(x[9]));

difer foarte puin de secvenele care implementeaz structurile repetitive


ntr-un limbaj de asamblare oarecare.
Ca i n limbajele de asamblare, restriciile de apartenen la
domeniul [Ai; Af] N, au determinat c pentru variabilele pointer,
aritmetica s se reduc la dou operaii: incrementarea i decrementarea.
Aceste operaii nseamn de fapt majorarea, respectiv diminuarea cu o
unitate, dup cum urmeaz:
adr(x+l) = adr(x) + 1*lg(Ti) = px + l*lg(Ti)
adr(x-l) = px l*lg(Ti)

(8.31)

(8.32)

Conversia de tip, determin ca unitatea s fie egal de fapt cu


lungimea asociat zonei de memorie ocupat de variabilele de tip Ti.
n cazul unor expresii complexe notate:
expr(a0 a1 ... an-1)

(8.33)

adr(x+expr(a0 a1 .....an-1)) = px+int(expr(a0 a1 .....an-1))*lg(Ti)

(8.34)

Se consider c expresia:
px+int(expr(a0 a1 .....an-1))*lg(Ti) =

(8.35)

este corect definit dac [Ai, Af] N.


n limbajul C/C++, ncrcarea adreselor variabilelor definite n
program, se efectueaz cu operatorul &.
Corespondentul C/C++ al secvenelor de mai sus este:
typedef int mat[10];
mat x = {1,2,3,4,5,6,7,8,9,10};
int * px;
unsigned int i, s;
{
s = 0;
for ( i = 0 ; i < 10 ; i + +)
{
px = &x[i]
s = s + (*px);
}
cout << s;

Absena controlului asupra limitelor de variaie pentru variabilele de


tip Tp, determin s devin operanzi zone nespecifice programului, alternd
n acest fel inclusiv componente din software de baz, cu consecine asupra
calitii prelucrrilor curente i chiar viitoare.

8.3 Niveluri de indirectare


Apare n mod firesc ntrebarea dac tipul Tp definit iniial (pointer, Ti),
unde Ti reprezint un tip fundamental de dat, include i perechea (pointer,
Tp), tiut fiind faptul c i tipul Tp este considerat tip fundamental.
Acceptnd c o astfel de construcie este corect, se creeaz
posibilitatea realizrii de pointeri spre pointeri spre Ti i a pointerilor spre
pointeri spre pointeri spre Ti.
Se definesc variabilele:
int Y, x ;
int *py, *px ;
int **ppx;

// (pointer, integer);
// (pointer,(pointer, integer));

prin expresiile:
x = 20;
px = adr(x);
ppx = adr(px));

se obin legturile reprezentate grafic n figura 8.4.

Figura 8.4 Alocarea n memorie a variabilelor x, px i ppx


Expresiile:
py = adr(y);
y = 16;

conduc la reprezentarea grafic din figura 8.5.

Figura 8.5 Alocarea n memorie a variabilelor py i y


Expresia:
ref(y) = ref(y) + ref(ref(x))

(8.36)

este echivalent cu:


y=y+x

(8.37)

Programul C/C++ care evalueaz expresia este:


typedef int * pint;
int x, y;
pint px, py;
pint *ppx;
{
x = 20 ;

y = 15;
px = &x;
py = &y;
ppx = &px;
*py = *py + **ppx;
cout << *py ;
};

Construcia
(pointer, Tp) permite compunerea unei funcii adr()
asociate nivelurilor indirectate.
n definiia:
int
int
int
int

x ;
*px ; // (pointer, integer)
**ppx ; // (pointer, (pointer, integer))
***pppx ; // (pointer, (pointer,(pointer, integer)))

i din secvena:
x = 17;
px =adr(x);
ppx = adr(px);
pppx = adr(ppx);

se obine:
pppx - adr(adr(adr(x))) = adr3 (x)

(8.38)

Numrul 3 reprezint nivelul de indirectare. Pentru referirea unei


variabile avnd nivelul 3 de indirectare, se procedeaz astfel:
ref(ref(ref(pppx))) = ref3 (pppx)

(8.39)

Pentru omogenizarea operanzilor n cadrul expresiilor, este necesar ca


variabilele n care tipul Tp are puterea n, s fie iniializate cu adr() i s fie
referite cu refn ().
Se consider:
T1p = (pointer,Ti)
T2p = (pointer, (pointer, Ti)
T3p = (pointer,(pointer (pointer, Ti)))
...
Pentru efectuarea unor generalizri privind aritmetica ordinului n a
tipului Tp este necesar definirea funciei pentru evaluarea lungimii acestui
tip. Astfel, expresii ca:
adrk (adrh(x)+i) sau adrk (adrh(x)-i)
devin interpretabile.

(8.40)

Aa cum pentru toate tipurile de date exist constante ce sunt


atribuite i pentru tipul pointer, exist constante corespunztor definite.
Constantele pentru tipul pointer, simbolizeaz adrese absolute. Se vorbete
de adres nul, se vorbete de adresa 15, sau de orice alt adres. Dac
ns un program P lansat n execuie, ocup zona de memorie delimitat
prin [Aip; Afp] N, iniializarea oricrei variabile pointer, este efectuat cu
valori cuprinse n acest interval. Folosirea valorii nule are numai scop de
jalonare la iniializare, pentru a vedea dac s-au fcut atribuiri ulterioare
pentru a lucra corect, sau atribuirile nu au fost posibil s se efectueze i
deci nu se lucreaz pentru c variabila pointer are valoare nul.
n programele C/C++, referirea unei variabile pointer px cu 5 nivele
de indirectare, de exemplu, se realizeaz prin evaluarea expresiei:
*****px;

iar pentru valoarea nul a variabilei pointer, se folosete constanta


simbolic NULL. Iniializarea unei variabile pointer cu o adres absolut de
memorie se realizeaz cu funcia Ptr(), avnd ca parametru o adres n
hexazecimal. De exemplu:
py : =

Ptr($00AA,$0020);

variabila pointer py este iniializat cu o adres absolut de memorie.

8.4 Vectori i matrice de pointeri


i cu tipul Tp se construiesc structuri de date omogene, dac
toate componentele acestora au tipul Tp. Construcia:
Tp x[n];
Tpi y[n][m];

(8.41)
(8.42)

reprezint definirea unui masiv x avnd n componente, fiecare component


fiind un pointer spre Ti i, respectiv, definirea unei matrice y avnd n linii i
m coloane, elemente ce sunt pointeri spre tipul Ti.
Se justific stocarea n masive uni- i bidimensionale a adreselor
unor operanzi, dac acetia au diferenieri ntre ei, sau dac tipologiile
determin activri de funcii dup succesiuni stabilite.
Se memoreaz, de exemplu, mesajele unui program care apar n
dialogul cu utilizatorul, sub forma unui text continuu. Acestor mesaje, n
numr de 50, li se memoreaz adresa de nceput n componentele
vectorului text[i], de pointeri spre tipul char [].
Dac se dorete aflarea tuturor mesajelor prin secvena:
for (i = 0 ; i < 50 ; i + + )
cout << ref(text[i]);

se obine acelai lucru.

Dac se dorete afiarea unui anumit mesaj, este important s se


cunoasc poziia n vectorul text[] a componentei n care este memorat
adresa respectivului mesaj.
Dac n cele cinci componente ale unui vector de pointeri numit px,
se memoreaz adresele primelor componente ale vectorilor a[], b[], c[],
d[], e[], cu numr de componente diferite i se dorete s se calculeze cu
funcia suma(), suma elementelor vectorilor, n loc s se scrie de cinci ori
apelul funciei suma(), se va construi secvena:
for(i=0; i<5; i++)
s[i] = suma(px[i], n[i]);

Dac se definete:
int x1[10], x2[10], x3[10], x4[10];
Tp y[4];
T 2p z;

prin atribuirile:
y[0]=adr(xl[0]);
y[1]=adr(x2[0]);
y[2]=adr(x3[0]);
y[3]=adr(x4[0]);
z = adr(y[0]);

s-a obinut construcia cu modelul grafic din figura 8.6.

y[0]

y[1]

X2

3]2]
y[

y[3]

X3

X4

Figura 8.6 Modelul grafic al legturilor ntre variabilele x, y i z


Modelul grafic din figura 8.6 este identic cu modelul grafic asociat structurii
de date omogene i contigue, matricea.
Tot astfel, se definete o funcie fct(), care se apeleaz prin:
fct(pl, p2, p3,, pn)

(8.43)

La apel, construiete un vector cu n componente n care sunt


memorate adresele parametrilor reali ai funciei, deci vectorul acesta este
un vector de pointeri.

Adresa primei componente a vectorului este memorat ntr-un


registru. Acest registru conine adresa listei de adrese a parametrilor.
n cazul n care la definire funcia are parametri formali: 1, 2,.....,
n i dac:
tip(pi) = tip(i) i {1, 2, ..., n}

(8.44)

nseamn c s-a obinut concordana ntre tipul parametrilor reali i tipul


parametrilor formali.
Transmiterea parametrilor formali prin valoare vizeaz efectuarea
copierii valorilor parametrilor reali pi n zonele de memorie definite n
funcie, asociate parametrilor i, operaie simbolizat prin expresia de
atribuire:

i = pi;

i = 1, 2, ...,n

(8.45)

n cazul n care are loc o inversare a parametrilor sau omiterea unuia


dintre ei, dispare concordana de tip i tipul parametrului i este Ti, iar tipul
parametrului pi este Tj, ceea ce impune efectuarea conversiei de la tipul Tj
la tipul Ti, cu toate efectele pe care conversia de tip le antreneaz.

i = conv(pi,Ti)

(8.46)

determin perturbarea radical a rezultatelor.


Dac parametrii pi sunt variabile pointer n funcie, se opereaz
asupra zonelor de memorie externe acesteia, a zonelor ale cror adrese
sunt conservate n variabilele pointer pi.
Aceasta explic necesitatea ca funcia de interschimb de valori s
conin pointeri pentru elemente i nu elementele nsi.
n continuare se ia n discuie programul executabil ca dat. Orice
program executabil este format din instruciuni executabile i zone de
memorie ce servesc ca operanzi.
Orice program are o prim instruciune executabil i o ultim
instruciune executabil. De obicei, n cazul funciilor, prima instruciunea
executabil, etichetat cu numele funciei, se numete punct de intrare n
funcie. Instruciunea de apel a funciei, efectueaz un salt necondiionat
spre punctul de intrare n funcie.
Ultima instruciune executabil dintr-o funcie, este un salt
necondiionat ctre programul apelator, pe baza informaiei care
localizeaz unde se efectueaz revenirea. Aceast instruciune se numete
punct de ieire din program. Din punct de vedere al structurilor de date,
aceste puncte sunt de fapt elemente folclorice, care prin pitorescul lor
coloreaz limbajul programatorilor.
Ca structur de date, textul executabil este un text omogen, atunci
cnd toate instruciunile au o lungime i o structur fixat. n cele mai
multe cazuri, instruciunile necesit informaii care determin extensii pe
cuvinte adiacente, reducnd gradul de omogenitate a structurii de date
numit program.
Totui, structura de date numit program executabil este delimitat
prin dou instruciuni executabile cu aceeai semnificaie, oricare ar fi
funcia scris ntr-un limbaj evoluat. Acest lucru se datoreaz standardelor
de preluare a parametrilor i de revenire n funcia apelatoare.

Dac se consider funciile f1(), f2(), ..., fm() crora le corespund m


texte program executabil, memorate n m zone de memorie, prin fl(), f2(),
..... fm(), se simbolizeaz adresele primelor instruciuni executabile ale
acestor funcii, i dac se definesc punctele de intrare ca un nou tip de dat
numit tipul de date funcie, se construiete un vector de pointeri:
(pointer, funcie) pf [m]

(8.47)

iniializat astfel:
pf[i] = f( );

i=1, 2, , m

(8.48)

n loc s se scrie o secvena cu m apeluri de funcii, se reduce totul la


secvena, unde p1, p2,.., pn sunt vectori cu parametrii asociai funciilor f1,
f2,, fn:
for ( i = 0 ; i < m ; i + + )
pf[i] (p1[i], p2[i], ..., pn[i]);

Deci vectorul de pointeri spre funcii faciliteaz crearea unor secvene


dinamice de apel n timpul execuiei, prin variabilitatea indicelui i.
Dac funciile de intrare se memoreaz ntr-o matrice de pointeri spre
funcie, atunci exist o mai mare diversitate i flexibilitate de prelucrare,
ceea ce permite realizarea de sisteme de programe vecine prin
complexitatea lor cu sistemele de program expert.

8.5 Variabile de tip pointer i structurile de date de tip


articol
Variabilele pointer, ca de altfel orice alt tip de variabil, apar ca
membri n structurile de date de tip articol. De asemenea, o variabil
pointer este definit ca pointer spre structur.
Construciile:
struct a
{
int m1;
double m2;
};
a x;
a *y;

definesc:
x ca variabil de tip articol;
y ca variabil de tip pointer spre articol.
Expresiile:
y = adr(x)
ref(y).m1

(8.49)
(8.50)

ref(y).c = adr(z)
ref(y) = ref(c)

(8.51)
(8.52)

reprezint modalitile de iniializare sau utilizare a membrilor structurii, n


condiiile n care elementul baz de referire este o variabil pointer i unul
dintre membri este, de asemenea, tot o variabil pointer.
Deosebirea este c y este un pointer spre structura x, iar c este un
pointer spre ntreg.
Lucrul cu fiiere presupune stocarea de informaii privind
caracteristicile fiierelor, precum i informaii de stare a prelucrrii
acestora. Informaiile sunt neomogene i pentru stocarea lor trebuie
definite structuri corespunztoare, care se constituie de fapt ca un vector
de structur. Numrul de componente ale vectorului, indic numrul maxim
de fiiere cu care se lucreaz ntr-un program.
Acest vector de structur se pune n coresponden cu elementele ce
se definesc n programele utilizatorilor.
Tipul de date FILE este de tip pointer spre o structur i caracterul
local sau global acordat variabilelor de acest tip permite definirea zonei
program din care programatorul are acces prin operaii de intrare/ieire la
fiier. Programatorul are acces la fiiere prin structurile de date de tip FILE.
Numeroi parametri ai funciilor de lucru cu fiiere, sunt pointeri care
au acelai tip cu membrii structurii de date FILE, parametri care permit
iniializri de membri, sau comparri care valideaz efectuarea de operaii.
Dac fiierul nsui este pus n coresponden cu un pointer spre
FILE, acesta apare ca parametru ntr-o funcie, ceea ce ofer caracter
general aplicaiilor. Cnd se spune c fiierul apare ca parametru ntr-o
funcie, realitatea este c pointerul variabil pointer spre structura de tip
FILE, care conine descrierea fiierului cu care se dorete s se lucreze n
funcie, se transmite ca parametru real.
Observm c toate informaiile despre toate entitile, date,
programe i fiiere, se structureaz adecvat i devin resurse la dispoziia
programatorului.
Limbajele evoluate, precum C i C++, sunt puternice prin
multitudinea funciilor de bibliotec pe care programatorii le apeleaz.
Numeroase funcii necesit definirea unor variabile n programe, de un tip
derivat, definit ca global n fiiere, ce trebuie incluse n program.
Programatorul trebuie s cunoasc aceste structuri, pentru a folosi
informaiile pe care la returneaz funciile apelate.
Funciile, primesc ca parametrii reali variabile elementare, sau
masive, sau structuri, sau pointeri. sau alte tipuri derivate i/sau definite
global.
Funciile returneaz valori ce se stocheaz n variabile elementare, n
structuri de tip articol, n date de tip pointer. Numrul valorilor returnate
este unu. Pentru a obine ca funcia s returneze un masiv, uni- sau
bidimensional, e suficient ca acesta s fie inclus ntr-o structur de tip
articol.
Programatorul care face distincie ntre toate tipurile de date
prezentate pn acum i care e deprins s vehiculeze uor definirile i
referirile acestora, prin funcii de bibliotec sau funcii proprii are acces la
absolut toate resursele unui sistem de calcul.
Se observ c:

referirea unei variabile elementare se face prin nume;


referirea unui masiv se face prin nume, iar al unui element al su
prin nume urmat de o expresie indicial cuprins ntre [ ];
- referirea unei structuri de tip articol se face prin nume, iar a unui
membru indicnd numele structurii separat de operatorul ., de
numele membrului respectiv;
- referirea unei date de tip pointer se face prin nume, iar a
variabilei a crui adres o conine, printr-un operator de referire;
- referirea unui fiier se efectueaz prin numele variabilei pointer
spre tipul de date FILE; iniializarea i utilizarea acestei structuri
este la dispoziia funciilor destinate lucrului cu fiiere.
Comparnd referirile, se observ c pentru a defini aceste tipuri de
date sunt necesare informaii care s indice tipul i ordinea pe care
componentele o au n cadrul structurilor ca entiti efective, desfurate
liniar i contiguu n memorie.
8.6 Definirea i utilizarea variabilelor pointer n limbajul C++
n limbajul C++, pentru tipul pointer, prin declaraia tip * nume, se
prelucreaz tipul.
De exemplu:
int *px, *py ;
int x, y ;

Pentru iniializarea variabilelor pointer, se folosete operatorul &.


De exemplu:

Funciei
Instruciunea:

de

referire

px

= & x;

py

= & y;

corespunde

operatorul

*.

*px = *px + *py;

este echivalent cu:


x = x+y;

i are corespondent n consideraiile anterioare:


ref (px) = ref (px) + ref (py)

(8.53)

n cazul definirii pointerilor spre tipuri de date derivate, se folosesc


construcii precum:
typedef struct b
{
int c;

int d;
int e;
int * g;
};
typedef

b *a;

a x;
b y;

Expresiile:
x = & y;
x->c = 3
x->d = x-> c*5;
x->e = 1
x->g = Addr(x,e);
x->e = x-> e +x-> g;

ilustreaz modaliti de referire a membrilor unei structuri.


n cazul definirii:
typedef
typedef
typedef

int c;
c * b;
b * a;

a x;
b y;
c z;

expresiile:
z =
y =
x =
cout

7;
&z;
&y;
<< **x;

ilustreaz modaliti de lucru cu pointeri spre ntreg, variabila y, i cu


pointeri spre pointeri spre ntreg, variabila x.
Funciei refk(px) i corespunde construcia ** de k ori ***px, iar
funciei adrk(px), i corespunde secvena:
p1 =
p2 =

pk =

&(x);
&(p1);

&(pk-1);

Instruciunile:
int * px;
int x;


px = &(x);
x = 7;
cout << *px

au acelai efect ca:


cout << x;

Aplicaiile complexe necesit definirea de vectori de pointeri spre


matrice, pointeri spre vectori de pointeri spre pointeri spre matrice, pointeri
spre vectori de pointeri.
Programele de mai jos, realizeaz sumele elementelor a trei matrice,
folosind diferite modaliti de referire a elementelor, specificate la fiecare
program prin comentariu.
//vectori de pointeri spre matrice
#include <iostream>
using namespace std;
typedef int mat[2][3];
typedef mat *pmat;
typedef pmat vecp[3];
vecp vp;
int i,j,k;
int s[3] = {0,0,0};
mat a = {1,1,1,2,2,2};
mat b = {3,3,3,4,4,4};
mat c = {5,5,5,6,6,6};
main()
{
vp[0] = &a;
vp[1] = &b;
vp[2] = &c;
for (k=0; k<3; k++)
{
for (i=0; i<2; i++)
for (j=0; j<3; j++)
s[k]+=(*vp[k])[i][j];
cout<<"\n Suma matricei "<<k<<" este "<<s[k];
}
}

Codul surs al programului care utilizeaz vectori de pointeri spre


pointeri la o matrice este:
//vectori de pointeri spre pointeri la o matrice
#include <iostream>
using namespace std;
typedef int mat[2][3];
typedef mat * pmat;
typedef pmat vecp[3];
typedef pmat * vecpp[3];
vecp vp;
vecpp vpp;

int i,j,k;
int s[3] = {0,0,0};
mat a = {1,1,1,2,2,2};
mat b = {3,3,3,4,4,4};
mat c = {5,5,5,6,6,6};
main()
{
vp[0] = &a;
vp[1] = &b;
vp[2] = &c;
for (i=0; i<3 ;i++)
vpp[i] = &vp[i];
for (k=0 ; k<3 ; k++)
{
for (i=0; i<2; i++)
for (j=0; j<3; j++)
s[k] + = (**vpp[k])[i][j];
cout<<"\n Suma matricei "<<k<<" este "<<s[k];
}
}

n programul:
//pointeri spre vectori de pointeri
#include <iostream>
using namespace std;
typedef vecp *pvec;
int * ppp;
vec a = {1,2,3,4,5};
vecp p;
pvec pp;
int i;
main()
{
for (i=0; i<5; i++)
p[i] = &a[i];
ppp = a;
pp = &p;
for (i=0; i<5; i++)
{
cout<<"\n"<<*(ppp+i)<<" ** "<<(*p[i]);
cout<<"\n"<<(*(*pp)[i]);
}
}

se utilizeaz pointeri spre vectori de pointeri, iar n programul urmtor sunt


folosii pointeri spre vectori de pointeri spre pointeri de matrice:
//pointeri spre vectori de pointeri spre pointeri la matrice
#include <iostream>
using namespace std;

typedef pmat vecp[3];


typedef pmat* vecpp[3];
typedef vecpp * pvecpp;
vecp vp;
vecpp vpp;
pvecpp pvpp;
int i,j,k;
int s[3] = {0,0,0};
mat a = {1,1,1,2,2,2};
mat b = {3,3,3,4,4,4};
mat c = {5,5,5,6,6,6};
main()
{
vp[0] = &a;
vp[1] = &b;
vp[2] = &c;
for (i=0; i<3; i++)
vpp[i] = &vp[i];
pvpp = &vpp;
for (k=0; k<3; k++)
{
for (i=0; i<2; i++)
for (j=0; j<3; j++)
s[k] + = (**(*pvpp)[k])[i][j];
cout<<"\n Suma matricei "<<k<<" este "<<s[k];
}
}

Un exemplu de program n care sunt puse n eviden modaliti de


referire a unor structuri complexe, este considerat urmtorul:
#include <iostream>
using namespace std;
typedef int vec[5];
typedef int * pvec[5];
typedef pvec * ppvec;
typedef struct strz
{
vec aa;
pvec paa;
ppvec pppa;
};
typedef strz * pstr;
typedef pstr ps[2];
typedef ps* pps;
int i,j;
strz st[2];
ps pst;
pps pss;
main()
{

st[0].aa[0] = 100;
st[0].aa[1] = 200;
st[0].paa[0] = &st[0].aa[0];
st[0].paa[1] = &st[0].aa[1];
st[1].aa[0] = 300;
st[1].aa[1] = 400;
st[1].paa[0] = &st[1].aa[0];
st[1].paa[1] = &st[1].aa[1];
pst[0] = &st[0];
pst[1] = &st[1];
pss = &pst;
st[0].pppa = &st[0].paa;
st[1].pppa = &st[1].paa;
for (i=0; i<2; i++)
for (j=0; j<2; j++)
{
cout<<"\n*"<<st[i].aa[j]<<"**"<<*(pst[i]->paa[j]);
cout<<"++"<<*((*pss)[i]->paa[j])<<"--"<<*((*pss)[i]->pppa[j]);
}
}

Un aspect important n utilizarea variabilelor pointer l reprezint


alocarea dinamic a memoriei.

9. REUNIUNILE DE DATE CONTIGUE


9.1 Necesitatea restructurrii datelor
n multe aplicaii codul materialului este privit ca ntreg, iar n cazul
verificrii cifrelor de control, fiecare element sau grupuri de elemente sunt
privite ca formate din cmpuri de 1 octet.
n primul i al doilea caz, zona de memorie asociat codului de
material este reprezentat prin dou modele grafice i anume:

cod_material

01A880

cod_grup

cod_subgrup

Figura 9.1 Model grafic structurii de tip reuniune material


Se observ c ambele structuri ocup aceeai zon de memorie, al
crui nceput este marcat de octetul cu adresa exprimat n hexazecimal cu
valoarea 01A880.
O alt situaie corespunde prelucrrii articolelor dintr-un fiier. Pentru
o aplicaie sunt necesare primele cinci cmpuri, pentru o alt aplicaie sunt
necesare apte cmpuri din interiorul articolului, iar pentru a treia aplicaie
sunt necesare ultimele patru cmpuri. Pentru toate aplicaiile, primul cmp
este necesar ntruct servete drept cmp de regsire a informaiilor.
Modelul grafic al zonei de memorie astfel structurat este:

0100A0

0100A0

Figura 9.2 Structura zonei de memorie

Cele trei structuri care se suprapun peste aceeai zon de memorie,


presupun lungimi identice i o aceeai adres de nceput.
n acest caz, rezult c zona de memorie este mai nti definit
pentru precizarea uneia dintre structuri, iar celelalte structuri care se
suprapun apar ca redefiniri ale zonei de memorie.
Atunci cnd se definete un masiv tridimensional a, cu 10 x 10 x 10
elemente i un alt masiv b, unidimensional cu 1000 elemente, tipul lor fiind
unic, dac se procedeaz la punerea n coresponden a elementelor
a[1][1][1] i b[1], n sensul:
adr(a[1][1][1]) = adr(b[1])

(9.1)

secvena:
for(i=0; i<10; i++)
for(j=0; j<10; j++)
for(k=0; k<10; k++)
a[i][j][k] = 0;

este nlocuit cu secvena:


for(i=0; i<1000; i++)
b[i]=0;

care din punct de vedere a volumului operaiilor de prelucrare este mai


eficient.
Exist probleme n care algoritmii de rezolvare cer definirea de
masive care difer ca nume i ca dimensiuni de la o etap la alta, dar care
sunt disjuncte din punct de vedere al utilizrii coninutului.
De exemplu, pentru un algoritm alctuit din trei etape, este necesar
utilizarea structurilor menionate n tabelul de mai jos:
Tabelul nr. 9.1 Etapele unui algoritm
Structura

Etapa
Etapa 1

Etapa 2

Etapa 3

unde structurile sunt definite astfel:


A [10][10]
B [20][20]
U [10]
V [20]
struct C {} , lg (C) = 50
struct D {} , lg (D) = 40
X [30]

*
*

*
*

Rezult c matricele A i B sunt suprapuse, vectorii U, V i X de


asemenea, iar structurile de tip articol C i D, urmeaz aceeai cale.
Cele trei suprapuneri sunt reprezentate cu modelele grafice
urmtoare:
A [1, 1]

01 AA00

B [ 1, 1]
U [1]

01 BB00

V[1]
01 BB00

X [1]
C

01 CC00

Figura 9.3 Modelul grafic al suprapunerilor pe o zon de memorie


Problema suprapunerilor structurilor de date pe zone de memorie se
rezolv folosind funcii de reunire.

9.2 Funcia de reunire


Se consider datele d1, d2, d3, ..., dn, avnd tipurile T1, T2, ..., Tn i
m1, m2,..., mn membrii de referin ai acestora.
n continuare un membru al unei structuri de date este numit de
referin, dac n raport cu el sunt puse n eviden. Se definete funcia:
union: T1*T2...*Tn (TRUE, FALSE)

(9.2)

i:
union (m1, m2,...,mn) = TRUE

(9.3)

dac i numai dac:


adr(m1) = adr(m2) = adr(mn) = ,

[Ai, Af] N

(9.4)

Se noteaz:

min {adr (d i ) | adr (m i ) }


1 i n

(9.5)

i:

max { adr (d i ) lg ( Ti ) | adr (m i ) }


1 i n

(9.6)

Cele n structuri de date redistribuite ca dispunere n raport cu adresa


, a celor n cmpuri de referin, ocup o zon de memorie de lungime
- +1 octei.
Diferitele limbaje de programare impun restricii precum:

max { A i , min { adr (d i ) | adr (m i ) } }


1 i n

(9.7)

ceea ce determin existena unui minim control asupra adresei operanzilor,


n sensul c adresele acestora s nu fie n afara domeniului prefixat n
limitele Ai i Af.
Dac se consider o dat de baz, de exemplu d1:
union (m1, m2, ..., mn) = TRUE

(9.8)

dac:

i 1, 2, ..., n
adr (mi)

adr (di) adr (d1) i 2, 3, ..., n

(9.9)

De exemplu, acest grup de restricii se regsete n limbajul


FORTRAN.
Se consider masivele definite prin:
int a[10];
int b[5];

i structura:
struct k
{
int x;
char g[10];

};
k t;

i datele elementare:
int x, y;

Funcia:
union (a[3], b[5], g, x, y )

(9.10)

conduce la modelul grafic de suprapunere urmtor:

a0

b0

b1

b2

a1

b3

a3

b4

g0

g1

g2

.......

g8

g9

x
y
Figura 9.4 Modelul grafic al suprapunerilor pe o zon de memorie
adr ( a [3] ) = adr ( b [5] ) = adr ( t*g) = adr (x) = adr (y) =

(9.11)

Adresa de nceput pentru fiecare din cele cinci cmpuri se obine:


adr
adr
adr
adr
adr
adr

(a [3] ) = adr (a [0] ) + 3*1g (int)


(a [0] ) = - 3*1g (int)
(b [0] ) = - 5*1g (int)
( g ) = - 1*1g (int)
(x)=
(y)=

n programele C/C++, exist posibilitatea realizrii reuniunii de date


folosind variabile de tip pointer care refer aceeai zon de memorie. De
exemplu, secvena:
int x[3][3],*y[3],**z;
for(int i=0;i<3;i++)
y[i]=x[i];
z=y;
for(i=0;i<3;i++)
for(int j=0;j<3;j++)

x[i][j]=i*3+j;
cout<<"Adresa de memorie alocata pentru x este:"<<&x<<endl;
cout<<"Adresa de memorie alocata pentru y este:"<<&y<<endl;
cout<<"Adresa de memorie alocata pentru z este:"<<&z<<endl;
cout<<"Adresele de memorie la care sunt stocate liniile pornind
de la variabila x:"<<endl;
for(i=0;i<3;i++)
cout<<"Linia "<<i+1<<"este incepe la adresa "<<x[i]<<endl;
cout<<"Adresele de memorie la care sunt stocate liniile pornind
de la variabila y:"<<endl;
for(i=0;i<3;i++)
cout<<"Linia "<<i+1<<"este incepe la adresa "<<y[i]<<endl;
cout<<"Adresele de memorie la care sunt stocate liniile pornind
de la variabila z:"<<endl;
for(i=0;i<3;i++)
cout<<"Linia
"<<i+1<<"este
incepe
la
adresa
"<<*(z+i)<<endl;
cout<<"Elementele matricei afisate prin variabila x:"<<endl;
for(i=0;i<3;i++){
cout<<endl;
for(int j=0;j<3;j++)
cout<<"X["<<i+1<<"]["<<j+1<<"]="<<x[i][j]<<" ";
}
cout<<"Elementele matricei afisate prin variabila y:"<<endl;
for(i=0;i<3;i++){
cout<<endl;
for(int j=0;j<3;j++)
cout<<"Y["<<i+1<<"]["<<j+1<<"]="<<*(y[i]+j)<<" ";
}
cout<<"Elementele
matricei
afisate
prin
variabila
z:"<<endl;
for(i=0;i<3;i++){
cout<<endl;
for(int j=0;j<3;j++)
cout<<"Z["<<i+1<<"]["<<j+1<<"]="<<*(*(z+i)+j)<<" ";
}
cout<<"Adresele elementelor matricei afisate prin variabila
x:"<<endl;
for(i=0;i<3;i++){
cout<<endl;
for(int j=0;j<3;j++)
cout<<"ADR(X["<<i+1<<"]["<<j+1<<"])="<<&x[i][j]<<" ";
}
cout<<"Adresele elementelor matricei afisate prin variabila
y:"<<endl;
for(i=0;i<3;i++){
cout<<endl;
for(int j=0;j<3;j++)
cout<<"ADR(Y["<<i+1<<"]["<<j+1<<"])="<<y[i]+j<<" ";
}
cout<<"Adresele

elementelor

matricei

afisate

prin

variabila

z:"<<endl;
for(i=0;i<3;i++){
cout<<endl;
for(int j=0;j<3;j++)
cout<<"ADR(Z["<<i+1<<"]["<<j+1<<"])="<<*(z+i)+j<<" ";
}

efectueaz punerea n coresponden:


adr(x[0][0])
adr(x[0][1])
adr(x[0][2])
adr(x[1][0])
adr(x[1][1])
adr(x[1][2])
adr(x[2][0])
adr(x[2][1])
adr(x[2][2])

=
=
=
=
=
=
=
=
=

y[0]+0
y[0]+1
y[0]+2
y[1]+0
y[1]+1
y[1]+2
y[2]+0
y[2]+1
y[2]+2

ntre variabilele x i y, precum i punerea n coresponden:


adr(x[0][0])
adr(x[0][1])
adr(x[0][2])
adr(x[1][0])
adr(x[1][1])
adr(x[1][2])
adr(x[2][0])
adr(x[2][1])
adr(x[2][2])

=
=
=
=
=
=
=
=
=

*(z+0)+0
*(z+0)+1
*(z+0)+2
*(z+1)+0
*(z+1)+1
*(z+1)+2
*(z+2)+0
*(z+2)+1
*(z+2)+2

ntre variabilele x i z.
Astfel, aceeai zon de memorie este accesat prin intermediul
variabilelor x, y i z.
n condiiile construirii de tipuri de date derivate, nu se efectueaz
alocare de zone de memorie. Prin definirea de variabile pointer p1, p2, ..., pn
spre tipurile de date derivate, o dat cu construirea modelului grafic al
reuniunii de date, se evalueaz adresa ca fiind adresa de nceput a unei
variabile dk, din lista d1, d2, ..., dn.

adr (d k ) min{ adr (d i ) }


1 i n

(9.12)

cu condiia ca:
adr(m1) = adr(m2) = ... = adr(mn)
Se calculeaz deplasrile:
Di = depl(di, dk)
cu Di > 0 pentru i = 1, 2, ..., n.
Prin evaluarea expresiei:

(9.14)

(9.13)

Pi convp ( , Ti ) D i

(9.15)

se obine adresa de nceput a fiecrei date din tipul Ti, astfel nct:
adr(m1) = adr(m2) = ... = adr(mn) = adr(d1) + depl(m1, d1)

(9.16)

Mecanismele de implementare prin variabile pointer a reuniunii de


structuri de date sunt utile mai ales n cazul structurilor de date contigue a
cror alocare se efectueaz dinamic.
Chiar dac, iniial, funciile de reuniune sunt definite cu unele
restricii asupra alinierii, la stnga sau la dreapta a elementelor, printr-o
aritmetic adecvat de evaluare a expresiilor n care apar variabile pointer
aceste restricii sunt eliminate.
Reuniunile de date, n multe cazuri, sunt rezultatul unor prelucrri
accidentale i n depanarea programelor e necesar identificarea modulului
n care s-au fcut suprapunerile unor date peste altele.

9.3 Zonele de memorie tampon gazde ale reuniunilor


de date contigue
Prelucrarea datelor n condiiile scderii intensitii pe care resursa
memorie o impune ca restricie revine la a defini zone de memorie n care
se citesc date din fiiere, uneori chiar fiierele integral, pentru a fi prelucrate
ca date existente numai n memoria intern a calculatorului.
n acest context, memoria tampon este pus n coresponden cu
structuri de tip articol i pri ale sale sunt interpretate sau intr ca operanzi
n expresii, sub forma membrilor de structur.
Cazul cel mai frecvent corespunde situaiei n care, pentru orice
structur de date din irul ordonat d1, d2, ... dn avem:
union( mi, mj ) = FALSE

(9.17)

adr(di+1) = adr(di) + lg(di)

(9.18)

( ) i 1, 2, ..., n 1 , adic datele sunt disjuncte.


n cazul lucrului cu buffere, se caut ca prin aritmetica de pointeri s
se obin acea suprapunere care coincide, fie cu modul n care a fost creat
fiierul, fie cu obiectivul urmrit.
Dac programul de creare a fiierului conine ca definire structura dk,
iar programul de exploatare a fiierului conin aceeai structur dk, cu
deosebirea c:
lg(dk) lg(d'k)

(9.19)

conduce la concluzia c cele dou structuri sunt asemntoare numai prin


numrul identic de cmpuri i coincidena tipurilor, dar difer prin lungimile
a dou cmpuri corespondente.
Se presupune c:

lg(dk) - lg(d'k)=1

(9.20)

Dup n citiri de nregistrri dk din fiierul creat cu nregistrri avnd


structura dk, se obine o diferen de n baii pn la a n + 1 nregistrare
corect plasat n fiier.
d2

d1

d'1

d'2

d4

d3

d'3

d'4

d5

d'5

Figura 9.5 Decalajele ntre articolele celor dou fiiere


Decalajul de 1 octet genereaz o suprapunere dinamic, distana
ntre nceputul nregistrrii dk i nregistrarea d'k se mrete pe msur ce
indicele k crete, fiind de k-1 octei.
Problema depistrii cauzelor de obinere a rezultatelor eronate crete
n complexitate dac diferena:
lg(dk) lg(d'k)>1

(9.21)

n aceste cazuri, este necesar s se defineasc funcii de verificare a


concordanei dintre parametrii ce caracterizeaz o structur.
De asemenea, din punct de vedere al tipurilor pe care le au datele
elementare, care intr n componena articolelor sau a masivelor, reunirile
apar ca suprapuneri de tipuri, omogene sau nu, cu toate consecinele ce
decurg.
Astfel, o dat elementar sau atom este definit din punct de vedere
al tipului ca:
E = (Ti)

(9.22)

unde Ti, este unul din tipurile fundamentale T1, T2,..., Tn, n fiind numrul de
tipuri fundamentale implementate n limbajul de programare considerat.
Masivul unidimensional, se definete ca tip vector Tv astfel:
Tv = (Ti, Ti, ..., Ti)

(9.23)

Numrul de componente coincide cu dimensiunea vectorului i tipul


este acelai pentru toate elementele.
O matrice este de tipul Tm, definit:
Tm = (Tv, Tv ..., Tv)

(9.24)

O structur de tip articol, are tipul:


Ta = (Ti1, Ti2, ..., Tim)
unde, Tik { T1 , T2 , ..., Tn , Tv , Tm , Ta } .

(9.25)

O structur de vectori, este definit prin:


Tsv = (Tv, Tv ..., Tv)

(9.26)

Un vector de structur, se definete prin:


Tvs = (Ta, Ta ..., Ta)

(9.27)

Deplasnd problematica reuniunii de date la nivelul reuniunii de date


ca tipuri, obinem o nou interpretare i anume:

union(Tx1 , Tx 2 , ..., Tx n ) TRUE

(9.28)

dac exist cel puin un Tki Txi, astfel nct oricare ar fi dou elemente de
tip Txi i Txj s existe:
tadr ( Tk i ) tadr ( Tk j )

(9.29)

unde, Tki reprezint tipul membrului cu poziia k din tipul derivat Txi.
n acest context, tadr(Tki) reprezint o mulime a adreselor
operanzilor de tip Tki ai structurii de tip derivat Txi.
Se consider spre exemplificare structurile:
struct a
{
char e[20];
int a;
float c;
};

i:
struct x
{
int y;
char z[4];
float u;
int w;
};

Se consider structura:
Ta = ( char[], int, float, bool)

(9.30)

Tx = ( int, char[], float, int)

(9.31)

i structura:

Structura union (Ta, Tx) se evalueaz definind mulimile:

tadr ( Ta int ) { adr (a) }

(9.32)

tadr ( Tb int ) { adr (y) , adr (w) }

(9.33)

unde tadr() este funcia de extragere a adreselor elementelor de un tip


specificat dintr-o structur.

tadr ( Ta float ) { adr (c) }

(9.34)

tadr ( Tx float ) { adr (u) }

(9.35)

tadr ( Ta char[ ] ) { adr (e) }

(9.36)

tadr ( Tx char[ ] ) { adr (z) }

(9.37)

tadr ( Ta bool ) { adr (d) }

(9.38)

tadr ( Tx bool )

(9.39)

Dac se presupune c programatorul are la dispoziie posibilitatea de


a modifica coninutul contorului de locaii, care s realizeze o definire a
structurilor a i b nct:

tadr ( Ta boolean ) tadr ( Tx real )

(9.40)

deci:

union ( Ta , Tx ) TRUE

(9.41)

Implementrile curente genereaz cazuri particulare n care:

adr ( x i' ) adr ( x 'j )

(9.42)

unde xi, xj sunt membrii cu poziia 1 din structurile i i j.


Membrii care alctuiesc o structur dintr-o reuniune de structuri, au
adresa calculat fa de primul membru al structurii. Toi primii membrii ai
structurilor, au aceeai adres.
Privind structurile de date ca structuri de tipuri:

tadr ( T1i ) tadr ( T2i ) adr ( x 'j ) adr ( x i' )

(9.43)

pentru oricare i i j, ce corespund tipurilor de date derivate ce definesc


structurile considerate.
O astfel de abordare mrete generalitatea modelului asociat
reuniunii de structuri, ntruct nu mai sunt luate n calcul direct lungimile
efective ale operanzilor de un anumit tip, lungimi ce difer de la un mod de
implementare al unui limbaj la alt limbaj.
Mai mult, aplicarea funciei union la suprapunerile accidentale a
structurilor, prin considerarea tipurilor ca entiti de baz, permite
descifrarea rezultatelor care numai aparent au caracter nedeterminat.
Evidenierea lucrului cu astfel de structuri se realizeaz prin
intermediul urmtoarelor dou exemple.

Primul exemplu iniializeaz o zon sub forma unui masiv


unidimensional pe care o utilizeaz ca masiv bidimensional la afiare.
union reuniune
{
int a[3][3];
int b[9];
}z;

int i, j;
for(i=0; i<9; i++)
z.b[i] = i * i;
for(i=0; i<3; i++)
{
for(j=0; j<3; j++)
cout<<"\n "<<z.a[i][j];
}

Al doilea exemplu definete n cadrul unor structuri de tip struct,


cmpuri pe care le reunete la aceeai adres de memorie, efectund
exploatarea diferenial a acesteia.
struct union1
{
char x[10];
};
struct union2
{
char y[5];
};
struct union3
{
char z[10];
};
union uniune2
{
union1 un1;
union2 un2;
union3 un3;
}u2;

strcpy(u2.un1.x,"1234567890");
cout<<u2.un1.x;
cout<<"\n";
for(int i=0; i<5; i++)
u2.un2.y[i]='a'+i;
for(i=0; i<5; i++)
cout<<u2.un2.y[i];
cout<<"\n"<<u2.un1.x;
cout<<"\n";

for(i=0; i<15; i++)


cout<<u2.un3.z[i];

Exemplele anterioare evideniaz modaliti de realizare i operarare


a reunirilor de date.

10. LISTELE STRUCTURI DINAMICE NECONTIGUE


10.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.

INFO
URM

NULL

Figura 10.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

Figura 10.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, pierznduse 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.
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-introdus-primulsuprimat 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.

10.2. Lista simplu nlnuit


10.2.1 Crearea listelor. Inseria nodurilor
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

prim

NULL

ultim

Figura 10.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;
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

Figura 10.4 Inseria unui nod dup un nod specificat


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

INFO

Figura 10.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);
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";
}

10.2.2 Localizarea unui nod


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)
{
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;
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 aa-numitei 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

INFO
URM

prim

NULL

fanion

Figura 10.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;
}

10.2.3 Suprimarea nodurilor


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";
}

10.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);
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)
{
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)
{
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();
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);

10.4 Structuri de date dinamice necontigue


Structurile de date statice sunt cele care se definesc n program, iar
n faza de compilare se calculeaz deplasri pentru fiecare cmp elementar
sau grup de cmpuri, astfel nct dup editarea de legturi orice evaluare
de adres s permit referirea corect a structurilor sau a componentelor
lor.
Caracterul local sau global al variabilelor conduce la o abordare
diferenial a variabilelor statice. Chiar variabilele care se definesc n cadrul
unui bloc i care au caracter local, a cror alocare de memorie i iniializare
este realizat prin mecanisme proprii mediului de programare, sunt tratate
tot ca structuri sau funcii apelate la cerere de ctre programatori.
n continuare, se grupeaz sub denumirea de structuri dinamice acele
structuri pentru care alocarea i dezalocarea memoriei este gestionat de
programator.
Funciile care aloc memorie au ca parametri acele informaii care
conduc la rspunsuri neambigue la ntrebrile:
care este lungimea zonei de memorie care se aloc?
unde este stocat adresa zonei de memorie alocat, pentru a fi la
dispoziia programatorului?
se iniializeaz zona de memorie alocat nainte de a fi utilizat
de programator?
Mecanismul de alocare dinamic a memoriei, are la baz faptul c n
memoria intern a unui sistem de calcul, apare o zon disponibil delimitat

prin [ D i , D f ] N , unde:
Di
adresa de nceput a zonei;
adresa de sfrit a zonei.
Df
La intrarea n execuie a programului, un registru RO, sau o zon de
memorie fixat, se iniializeaz cu valoarea Di.
Dac ntr-un program este activat funcia de alocare definit prin:
y = alocare (lg (Ti) )

(10.1)

i
(10.2)

alocare : N [ D i , D f ] N

y cont ( RO ), unde tip ( y ) ( pointer, Ti )


RO = RO

Dac:
cont ( RO ) > Df

lg ( Ti )

(10.3)

(10.4)
0

(10.5)

Dac ntr-un program este activat funcia de dezalocare a memoriei:


z = dezalocare ( y )

(10.6)

RO = RO lg ( Ti )

(10.7)

i n cazul alocrii, dac:


tip (y) = (pointer, Tj)

(10.8)

i variabila x din
alocare (lg (x) )

(10.9)

are tipul Ti, se impune efectuarea unei conversii de tip.


Astfel,

y convtp ( tip( y ) , tip ( alocare ( x ) ) )

(10.10)

unde, convtp( ) este o funcie de conversie a tipului de dat Tp.


Algoritmii de structurare a memoriei n pagini, modul n care se face
ncrcarea programelor, precum i momentele uneori aleatoare din program
la care se apeleaz funciile de alocare/dezalocare, conduc n cele mai multe
cazuri ca, pentru dou apelri consecutive ale funciei de alocare, zonele
alocate s fie necontigue.
Necesitatea definirii n programe a datelor de tip dinamic, este dat
de utilizarea mai bun a memoriei, lungimea unui program variind n funcie
de volumul datelor cu care se lucreaz.
n programe C/C++, pentru alocarea dinamic de memorie, se
apeleaz proceduri din biblioteca malloc.h sau operatorul new, iar pentru
dezalocare, procedura free( ) sau operatorul delete. Ca argumente,
operatorul new are tipul de dat i numrul de elemente pentru care se face
alocarea zonei de memorie, iar free( ) i delete au o variabil pointer, care
indic adresa zonei ce se elibereaz.
Se consider un program P pentru calculul inversei unei matrice.
Pentru a-i oferi o mai larg aplicabilitate, se consider c acest program
este definit aa fel nct s inverseze o matrice cu cel mult 50 linii i 50
coloane.
Definirea static a matricei:

int a[50][50];

determin ocuparea unei zone de 2500 * lg (int) baii.


Deci:
lg ( P ) 2500 * lg ( baiti ) lg ( text executabil )

(10.11)

unde reprezint lungimea total a zonei de memorie rezervat altor


variabile de control sau de lucru.
Indiferent de problema de rezolvat, lg( P ) = constant.
Se tie c n lucru multiuser, un factor care influeneaz ateptarea
ntr-un fir, nainte de intrarea n prelucrare, este i lungimea programului.
n contextul definirii matricei ca operand alocat dinamic, la execuia
de fiecare dat a programului, se introduce dimensiunea matricei de
inversat, folosind variabila n de tip ntreg.
Lungimea programului pentru rezolvarea problemei Pi, se determin
dinamic:

lg ( Pi ) n * lg (integer) lg ( text executabil )

(10.12)

i se are n vedere c:
lg ( Pi )

lg ( P )

(10.13)

tiut fiind faptul c zona la dispoziia alocrii dinamice este [ D i , D f ] N ,


finit.
Restriciile impuse asupra domeniul pe care este definit funcia
alocare( ), determin filozofia ntregului proces de construire a variabilelor
dinamice.
n cazul n care:
alocare: Z [ D i , D f ] N

(10.14)

este creat posibilitatea efecturii de reacoperiri de zone, deci se genereaz


mecanisme de realizare a uniunii de structuri dinamice de date.
De exemplu, se consider structurile:
int a[10];
int b[10];

i variabilele pa i pb de tip Tp = (pointer, int), prin:


pa = alocare (10 * lg (int))
pb = alocare (7 * lg (int))
pb = alocare (-3 * lg (int))

se obinute reuniunea cu modelul grafic:

a0

a1

a2

a3

a4

a5

a6

a7

a8

a9

...
b0

b1

b2

b8

b9

Figura 10.7 Modelul grafic al reuniunii


n cele mai multe cazuri, modul de definire al funciilor de alocare
este limitativ, dar reuniunea este totui posibil prin aritmetica variabilelor
pointer, care se iniializeaz cu aceste funcii.
Structurile de date formate din elementele omogene E1, E2, ..., Em, de
un tip derivat sau fundamental TE, se numesc necontigue, dac exist cel
puin o pereche (Ei, Ei+1) astfel nct:
adr( Ei+1 ) > adr( Ei ) + lg(TE)

(10.15)

i dac:
adr( Ei+1 ) = adr( Ei ) + lg(TE) + i

(10.16)

unde e o variabil aleatoare, de regul uniform distribuit, pentru a avea


acces la elementele E1, E2, ..., Em, sub o form care s permit adresarea
corect a elementelor necontigue.
Exist posibilitatea prin definirea unor pointeri spre pointeri i
iniializarea corespunztoare a acestora, s se procedeze la reaezarea
operanzilor alocai dinamic, n aa fel nct s dispar golurile dintre
operanzi, rezultate n procesul de alocare/dezalocare.
Dac se consider constantele C1, C2, ..., Cn avnd tipul Ti, care
ocup zonele de memorie Z1, Z2, ..., Zn, i numerele aleatoare 1, 2, , n a
cror lege de distribuie este, de regul, uniform distribuit, se spune c
mulimile de perechi (Zj, j) determin o structur de date necontigu de tip
list, dac:
succ( Zj) = Zj + 1

(10.17)

pred( Zj) = Zj 1

(10.18)

i
adr( Zj+1 ) = adr( Zj ) + lg(Ti) + j = j

(10.19)

Dac:

1 = 2 = = n

(10.20)

atunci se obine cazul particular de dat unidimensional cu necontiguitate


nul, ce corespunde masivului unidimensional contiguu, respectiv vectorul,

i zona de memorie pentru conservarea variabilei


ntruct ea este calculabil ca:

j = adr( Zj ) + lg(Ti) = adr( Z1) + (j-1)*lg( Ti)

nu se mai justific,
(10.21)

Dac se ia n considerare mecanismul alocrii dinamice a memoriei,


atunci:

j = cont ( RO )

(10.22)

la momentul tj, ce corespunde alocrii zonei de memorie pentru perechea


(Zj+1, j+1).
Privit din punct de vedere al tipului de dat, Zj reprezint informaia
util, iar j reprezint informaia de identificare a succesorului.
adr(succ( Zi )) =

(10.23)

Perechea (Zj, j) definete o structur de date de tip list, numit TL,


unde j este dat de tip Tp = (pointer, TL).

10.5 Modelul grafic al listei ca structur necontigu


Se consider necesar ca o matrice rar s fie memorat fr a se
cunoate n prealabil numrul elementelor nenule ale sale.
Rnd pe rnd, se introduce de la terminal linia, coloana i valoarea
elementului nenul. Se aloc dinamic o zon de memorie pentru stocarea
acestor elemente, precum i un cmp pentru stocarea adresei zonei n care
se stocheaz elementul urmtor.
class zona
{
int i;
int j;
float val;
zona *poz;
};
zona * pa;

Folosind succesiv operatorul new:


pg = new

zona;

unde pg este o variabil de tip pa, iar pa la rndul ei este un pointer spre
structura zona, se aloc o zon de memorie de adresa cont(RO) i lungime
lg(Tzona), ce este referit folosind variabila pointer pg, care este de tipul Tp =
(pointer, Tzona).
n program, membrii structurii se refer prin:
pg->i
pg->j

pg->val

Dac pn este variabil de tip pa i se efectueaz atribuirea pn = new


zona, variabila pn conine adresa zonei n care este stocat urmtorul
element al matricei rare.
Atribuirea:
pg->poz = pn

este echivalent cu:

j = adr(succ( Zj ))

(10.24)

pg
01788 A

018800
pg->poz = pn;

new(pg)
01788 A
i
Zj

new (pn)

018800
i

val
poz

val

Zj+1

Figura 10.8 Alocarea zonelor de memorie


Dac:
succ( Zn) =

(10.25)

atunci:

grafic:

adr( succ( Zn)) = NULL

(10.26)

pg->poz->poz = NULL;

(10.27)

Acest algoritm de construire a irului 1, 2,

...

n conduce la modelul

NULL

Figura 10.9 Modelul grafic al listei simplu nlnuit


O astfel de list se numete list liniar simplu nlnuit.
ncrcarea matricei rare se face exact cu attea elemente cte valori
nenule se afl pe linii i coloane. Spre deosebire de cazul n care se foloseau
3 vectori pentru stocarea informaiilor, acum nu mai exist restricii legate
de rezervrile predefinite ale celor 3 vectori.

Totul depinde de modul n care programul solicit spaii din zon [Di,
Df] N i mai ales de dimensiunile proiectate ale acestei zone la generarea
sistemului de operare.
Dac n loc de:
adr( succ( Zn)) = NULL

(10.28)

se construiete:
adr( succ( Zn)) = adr( Z1)

(10.29)

lista se numete circular i n <> NULL.


Pentru transformarea listei care stocheaz matricea rar n list
circular, la terminarea prelucrrii:
pn->poz = pg;

unde pg stocheaz adresa zonei de memorie alocat pentru primul element


nenul al matricei rare.
Modelul grafic al listei circulare este:

...
pg->poz = pn;

pg->poz = pg;
Figura 10.10 Modelul grafic al listei circulare
Lungimea listei liniare:

L lg [ ( Z i , i ) ]

(10.30)

i <> NULL

(10.31)

i 1

unde lg[Zi, NULL] = 0.

10.6 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.
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, ) de tip TL,


w = adr( Z1 )

(10.32)

conine adresele de regsire a elementelor listei.


parcurgere (adr (w))
{
while (w != NULL)
{
tipareste (w, z);
parcurgere (ref (w), );
}
}

tergerea unui element ( Zk, k) al listei


nainte de tergere lista este:

Zk-1

Zk

k+1

Zk+1

k+2

....

Figura 10.11 Lista nainte de tergere


Dup efectuarea tergerii:

Zk-1

k+1

Zk

k+1

Zk+1

k+2

Figura 10.12 Lista dup de tergerea elementului


stergere ( w, w1, Zk )
{
while (ref (w). != NULL || ref (w).Z != Zk)
{
w1 = ref (w, );
stergere (w1, w, Zk)
}

....

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)]

(10.33)

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)

...

...

NULL

( U1, 1)

( U2, 2)

( U3, 3)

( Zn, n)

( Um, m)

Figura 10.13 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

(10.34)

dup care se realizeaz atribuirea:


Zk = b;

(10.35)

n toate cazurile, parcurgere, tergere, concatenare, modificare,


disciplina de parcurgere este de la primul element ctre ultimul, First In
First Out.
modificare (w)
{
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, j), j = 1, 2, ... , n

(10.36)

cont(Zj) = cont(Zj)

(10.37)

astfel nct:

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


Se spune c o list este ordonat cresctor dac:

cont ( Z j ) cont ( Z j 1 )

(10.38)

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} astfel nct:

cont ( Z k ) a cont ( Z k 1 )

(10.39)

cont ( Z k ) a cont ( Z k 1 )

(10.40)

sau

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
(, )
Figura 10.14 Configuraia nainte de inserare nodului n interiorul listei
la configuraia:

(Zk+1, k+1)

(Zk, adr())
...

...

a
(, adr (Zk+1))
Figura 10.15 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

...

(, )

(, )

...

Figura 10.16 Configuraia nainte de inserarea nodului la un capt al listei


la configuraia:

(Z1, 1)

(Zn, adr( ))
...

...

NULL

(, )

(,adr ( Z1)

Figura 10.17 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:

(Zk-1, k-1)

(Zk, k)

(Zk+1, k+1)

...

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

(Zj, j)

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

Figura 10.18 Modelul grafic al listei nainte de interschimbul nodurilor


Dup efectuarea interschimbului, legturile dintre componente sunt:

(Zk, k)

(Zk+1, k+1)

...

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

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

(Zj+1, j+1)

Figura 10.19 Modelul grafic al listei dup interschimbul nodurilor


Funcia pentru efectuarea interschimbului, realizeaz atribuirile:

k-1 = adr( Zj )

(10.41)

j-1 = adr( Zk )

(10.42)

j = adr( Zk+1 ) (10.43)


k = 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
N i corespunde
(Zk, k), k = 1, 2, ..., n astfel nct oricrui k [1, n]
un j [1, n]
N i numai unul aa nct:
cont( Zk ) = cont( Zj )

(10.45)

cont ( Z k ) cont ( Z k 1 )

(10.46)

pentru orice k = 1, 2, , n 1.
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:
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.);
}
}
}
}

10.7 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, j )

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

...

...

Figura 10.20 Model grafic al listei dublu nlnuite


sau:

...

...

Figura 10.21 Model grafic al listei dublu nlnuite


Lista dublu nlnuit este de fapt format din dou liste (Zj, j) i (Uj,

j) cu proprietile:
adr ( Z j )

adr ( U j ) cont ( Z j ) cont ( U j )

adr ( j )

adr ( j ) lg ( j )

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

adr ( Z j 1 )

cont (

cont (

j 1

j 1

(10.47)

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;
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)
{
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";
}
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;

}
else
}

}
delete(ptr);
cout<<"Suprimare cu succes!\n";
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";
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;
}
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);

11. STIVE I COZI


11.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

Figura 11.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 face tot
timpul n capul listei iar tergerea de asemenea se va face n capul listei.
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

Figura 11.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
nlnuite 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.

11.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;
- 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)

adr (C3)

(Z'4, '4)

(Z'3, '3)

adr (C2)

(Z'2, '2)

adr (C1)

(Z'1, '1)

NULL

baza activ

Figura 11.3 Structura unei stive


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

b
a
NULL

baza activ

Figura 11.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

(11.1)

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.

11.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.
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!";
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";
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.

11.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.
Tabelul nr. 11.1 Forme ale scrierii unei expresii matematice
Expresia n forma
polonez (scriere
prefixat)

Expresia n forma
polonez invers
(scriere
postfixat)

4 + 5

+ 4 5

4 5 +

4 + 5 * 5

+ 4 * 5 5

4 5 5 * +

4 * 2 + 3

+ * 4 2 3

4 2 * 3 +

4 + 2 + 3

+ + 4 2 3

4 2 + 3 +

4 * (2 + 3)

* 4 + 2 3

4 2 3 + *

Expresia
matematic
(scriere infixat)

Dup cum se vede din tabelul 11.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 11.2.

Tabelul nr. 11.2 Ierarhia operatorilor


Operator

Ierarhie

( [ {

) ] }

+ -

* /

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;
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 11.5:

Elem entul citit

Stiv a

8
4

2
8
4

3
2
8
4

6
8
4

2
4

2
2
4

1
4

Figura 11.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()).
Mai exist o clas fisier care are rolul de a parcurge fiierul n care se
afl expresie aritmetic ce trebuie evaluat.
Codul surs al programului este:

#pragma warning(disable:4786)
#include <deque>
#include <iostream>
#include <string>
using namespace std;
#include
#include
#include
#include

"ierarhie.h"
"stiva.h"
"fisier.h"
"coada.h"

/////////////////////Variabile globale
fisier f;
stiva <char> OPER;
stiva <double> EVAL;
coada POSTFIX;
void eroare(const char * text,int nr){
cout<<"\n"<<text<<"\n";
exit(nr);
}
void scrierePOSTFIX(string &op)
{
char opc;
if(op=="")
return;
int ierOp=ierarhie(op);
//daca e valoare, se trece direct in scrierea postfixata
if(!ierOp)
POSTFIX.adauga(op);
else
{
opc=op[0];
switch(ierOp)
{
//daca e paranteza deschisa, se introduce pe stiva
case PD: //([{
OPER.push(opc);
break;
//daca e paranteza inchisa, se extrag toate elementele
//de pe stiva pana la intalnirea unei paranteze deschise
case PI: //)]}
while(ierarhie(OPER.top())!=PD)
POSTFIX.adauga(OPER.pop());
OPER.pop();
break;
//daca e alt operator, se extrag elemente de pe stiva atat
//timp cat stiva nu este goala si ierarhia operatorului
//din varful stivei este mai mare sau egala decat ierahia
//operatorului curent
//la sfarsit operatorul curent se depune pe stiva
default:
while((!OPER.eGoala())&&ierarhie(OPER.top())>=ierOp)

POSTFIX.adauga(OPER.pop());
OPER.push(opc);
break;
}
}
}
void evalPOSTFIX()
{
string op;
char opc;
double t1, t2, rez;
//atat timp cat nu s-a ajuns la sfarsitul expresiei postfixate
while(!POSTFIX.eGoala())
{
//daca elementul curent este numar acesta se depune pe stiva
while(POSTFIX.eNumar())
EVAL.push(POSTFIX.getNumar());
//se extrag 2 valori de pe stiva
t2=EVAL.pop();
t1=EVAL.pop();
op=POSTFIX.extrage();
opc=op[0];
//se efectueaza operatia dintre cele 2 valori
switch(opc)
{
case '+':
rez=t1+t2;
break;
case '-':
rez=t1-t2;
break;
case '*':
rez=t1*t2;
break;
case '/':
rez=t1/t2;
break;
default:
eroare("Operator necunoscut!", 1);
}
//rezultatul operatiei se depune pe stiva
EVAL.push(rez);
}
}
void main(int argc, char* argv[])
{
//se primeste ca parametru numele fisierului in care se
//gaseste expresia matematica ce se doreste a fi evaluata
string numefis;
if(argc!=2)

{
cout<<"Specificati
scrierea postfixata!";
exit(1);
}

numele

unui

fisier

pentru

care

doriti

numefis=argv[1];
f.open(numefis.c_str());

if(f.bad())
{
cout<<"Fisierul "<<numefis<<" nu exista.\n";
exit(2);
}
cout<<"Lucrez cu fisierul "<<numefis<<".\n";

//parcurge fisierul primit ca parametru


string op;
while(!f.eof())
{
op=f.citeste();
scrierePOSTFIX(op);
}
f.close();
//se extrag toti operatorii ramasi pe stiva
while(!OPER.eGoala())
POSTFIX.adauga(OPER.pop());
//afiseaza expresia din fisier in forma postfixata
cout<<"\n\nExpresia in scriere postfixata\n";
for(int i=0;i<POSTFIX.size();i++)
cout<<POSTFIX[i]<<" ";
//efectueaza calculul expresiei
evalPOSTFIX();
//afiseaza valoarea expresiei (ultima valoare ramasa pe stiva)
cout<<"\nValoarea expresiei este: "<<EVAL.pop()<<"\n";
}
//ierarhie.h - informatii despre ierarhia operatorilor
#define PD 1
#define PI 2
#define PLUSMINUS 3
#define INMIMP 4
int ierarhie(char c)
{
switch (c)
{
case '+':
case '-':
return PLUSMINUS;

break;
case '*':
case '/':
return
break;
case '(':
case '[':
case '{':
return
break;
case ')':
case ']':
case '}':
return
break;
default:
return
break;

INMIMP;

PD;

PI;

0;

}
};
int ierarhie(string & s)
{
char c;
c=s[0];
return ierarhie(c);
};
/////coada.h - Clasa coada
void eroare(const char * text,int nr);
class coada{
private:
//implementarea cozii cu o clasa template double ended que din
//C++ Standard Template Library
deque <string> s;
public:
//verifica daca coada e goala
bool eGoala()
{
return s.empty();
};

//adauga un element in coada


void adauga(const string & str)
{
s.push_back(str);
};

//adauga un element in coada


void adauga(char c)
{
string str;

str=c;
s.push_back(str);
};
//intoarce si extrage primul element din coada
string extrage()
{
string t;
if(!s.empty())
{
t=s.front();
s.pop_front();
}
else eroare("Eroare de sintaxa.",2);
return t;
};
//verifica daca primul element din coada reprezinta un numar
bool eNumar()
{
char *stop;
string st=s.front();
strtod(st.c_str(), &stop);
return (*stop) == 0;
};
//extrage elementul din coada ca numar
double getNumar()
{
char *stop;
double v;
string st;
st=s.front();
s.pop_front();
v=strtod(st.c_str(), &stop);
return v;
};
//intoarce a i-lea element din coada (0 - primul element)
string operator [] (unsigned int i)
{
if((i>=0) && (i<s.size()))
return s[i];
eroare("Eroare de sintaxa.",4);
return "";
};
//intoarce numarul de elemente din coada
int size()
{
return s.size();
};
};
/////stiva.h - Clasa template stiva
void eroare(const char * text,int nr);

template <class T>


class stiva{
private:
//implementarea stivei cu o clasa template double ended que din
//C++ Standard Template Library
deque <T> s;
public:
//verifica daca stiva e goala
bool eGoala()
{
return s.empty();
};
//introduce un element in stiva
void push(T str)
{
s.push_back(str);
};
//intoarce si extrage elementul din stiva
T pop()
{
T t;
if(!s.empty())
{
t=s.back();
s.pop_back();
}
else eroare("Eroare de sintaxa.",5);
return t;
};
//intoarce elementul din stiva
T top()
{
if (!s.empty())
return s.back();
eroare("Eroare de sintaxa.",6);
return NULL;
};
};
/////fisier.h - clasa fisier
#include <string>
#include <fstream>
using namespace std;
class fisier
{
public:
//citeste o noua secventa de caractere din fisierul de date
string citeste();
//deschide fisierul

void open(const char * numefis);


//verifica daca fisierul a fost deschis cu succes
bool bad();
//verifica daca s-a ajuns la sfarsitul fisierului
bool eof();
//inchide fisierul deschis
void close();
private:
// fisierul din care se citesc datele
ifstream fis;
// verifica daca parametrul primit este separator sau operator
bool isSeparator(string s);
// verifica daca parametrul primit este separator sau operator
bool isSeparator(char c);
//scoate spatiile albe din datele de intrare (tab, spatiu, sfarsit
de linie)
void eatWhite();
};

/////fisier.cpp - clasa fisier


#include "fisier.h"
// verifica daca parametrul primit este separator sau operator
bool fisier::isSeparator(string s)
{
char c=s[0];
return (c=='+') || (c=='-') || (c=='*') || (c=='/') || (c=='(') ||
(c=='[') || (c=='{') || (c==')') || (c==']') || (c=='}')
||
(c=='\n') || (c=='\t') || (c==' ');
};
// verifica daca parametrul primit este separator sau operator
bool fisier::isSeparator(char c)
{
return (c=='+') || (c=='-') || (c=='*') || (c=='/') || (c=='(') ||
(c=='[') || (c=='{') || (c==')') || (c==']') || (c=='}')
||
(c=='\n') || (c=='\t') || (c==' ');
}
//scoate spatiile albe din datele de intrare (tab, spatiu, sfarsit de
linie)
void fisier::eatWhite()
{
while((fis.peek()=='
')
||
(fis.peek()=='\n')
||
(fis.peek()=='\t'))
fis.get();
}
//citeste o noua secventa de caractere din fisierul de date
string fisier::citeste()
{
string rez="";

rez=fis.get();
if(!isSeparator(rez))
while(!isSeparator(fis.peek()) && !fis.eof())
rez+=fis.get();
eatWhite();
return rez;
}
//deschide fisierul
void fisier::open(const char * numefis)
{
fis.open(numefis);
eatWhite();
}
//verifica daca fisierul a fost deschis cu succes
bool fisier::bad()
{
return fis.bad();
}
//verifica daca s-a ajuns la sfarsitul fisierului
bool fisier::eof()
{
return fis.eof();
}
//inchide fisierul deschis
void fisier::close()
{
fis.close();
}

12. ARBORI BINARI I ARBORI DE CUTARE


12.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

(12.1)

f ( a )*f ( c ) 0

(12.2)

Dac:

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

a+b

[a ,

3a + b
4

,b]

3a + b
4

a+b

a+b

a + 3b

a + 3b

,b]

Figura 12.1 Reprezentarea grafic asociat divizrii intervalelor


Pentru evaluarea expresiei aritmetice:

(a b c d ) ( x y )
cd

(12.3)

prin modul de efectuare a calculelor conduce la reprezentarea din figura


12.2.

*
e

/
*
c

+
x

+
a

Figura 12.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 12.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

Figura 12.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:
Z
informaia util care face obiectul prelucrrii, ea descriind
elementul sau 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 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

Zj

Zj + 1

j+1

j-1

adr (Z j - 1 )

adr (Z j 1 )

j-1

Figura 12.4 Modelul grafic asociat tripletului (Zj,

j,

j)

Dac tripletul (Zj, j, j) ce corespunde modelului grafic din figura


12.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

pentru localizarea nodului

din stnga de pe acelai nivel.


Un astfel de arbore se descrie prin:

( Z j , j, j, j , j )

(12.4)

Descrierea complet a unui arbore, presupune crearea mulimii


tripletelor sau cvintuplelor asociate nodurilor.
De exemplu, arborescenei asociate evalurii expresiei:
e = a + b + c;

(12.5)

prezentate n figura 12.5 i corespunde alocarea i iniializarea zonelor de


memorie din figura 12.6.

=
+
e
c

+
a

Figura 12.5 Structura arborescent asociat evalurii expresiei e

null

null

null

null

null

Figura 12.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:
pp
pointerul spre zona alocat nodului precedent (printe);
pc
pointerul spre zona asociat nodului curent;
pd
pointerul spre zona asociat primului descendent;
Construirea nodului ( Zj, j, j ) revine la efectuarea iniializrilor:

ref (pc) . Z j valoare_citita;


ref (pc) .

pp;

ref (pc) .

pd;

pp pc;
pc pd;

(12.6)

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 12.5 se
reprezint prin:
N={e,
i

, a, +, b, + , c }

(12.7)

A = { (a+) ,

(b+) ,

(c+) ,

(+, =) ,

(=, e) }

(12.8)

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 figura 12.7 construirea se
efectueaz prin alocarea a 7 zone de memorie cu structura ( Z 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]
pp [5]
pp [6]
pp [7]

adr (ZD)
adr (ZE)
adr (ZF)
adr (ZG)

(12.9)

Baleierea mulimii:

A { (AB), (AC), (BD), (BE), (CF), (CG) }

(12.10)

revine la efectuarea atribuirilor:

ref
ref
ref
ref
ref
ref
ref
ref
ref
ref

(pp[1] ).
(pp[2] ).
(pp[3] ).
(pp[4] ).
(pp[2] ).
(pp[4] ).
(pp[3] ).
(pp[6] ).
(pp[7] ).
(pp[1] ).

pp[2]
pp[4]
pp[6]
ref (pp[5] ). ref (pp[6] ). ref (pp[7] ). NULL (12.11)
pp[1]
pp[2]
pp[1]
pp[3]
pp[3]
ref (pp[1] ). NULL

A
B
D

Figura 12.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;
};
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;
};

12.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 de
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 12.8,
transformarea ei n structur arborescent binar, revine la introducerea
nodurilor fictive, x, y, u, v, w, rezultnd arborele din figura 12.9.

Figura 12.8 Structur arborescent oarecare

k
Figura 12.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).
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.

12.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

Figura 12.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;
};

Rdcina arborelui binar de cutare va fi definit n felul urmtor:


arb *Radacina = NULL;

(12.12)

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 12.11.
20

10

25

12

22

26

Figura 12.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 ntruna din variantele urmtoare:
a) nodul nu are subarbori (fii);
b) nodul are doar subarbore stng;
c) nodul are doar subarbore drept;
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.

Figura 12.12 Arbore binar de cutare nainte de tergerea unui nod

n figura 12.12 este prezentat un fragment al unui arbore binar de


cutare din care dorim s tergem nodul din dreapta, iar n figura 12.13 se
prezint acelai fragment de arbore dar dup tergerea nodului dorit.

Figura 12.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 12.14 i 12.16 se prezint
cte un fragment dintr-un arbore binar de cutare din care dorim s
tergem nodul evideniat. n figura 12.14, nodul pe care dorim s-l tergem
are numai subarbore stng iar cel din figura 12.16 are doar subarbore
drept. n figurile 12.15 i 12.17 se pot observa efectele operaiei de
tergere.

Figura 12.14 Subarbore numai cu descendent stng

Figura 12.15 Subarbore dup efectuarea tergerii

Figura 12.16 Subarbore numai cu descendent drept

Figura 12.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.
n figura 12.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 12.19 este
reprezentat fragmentul de arbore dup efectuarea operaiei de tergere a
nodului dorit.

Figura 12.18 Subarbore cu doi descendeni

Figura 12.19 Subarbore dup efectuarea tergerii

12.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
#include
#include
#include
#include

<iostream.h>
<conio.h>
<stdio.h>
<malloc.h>
<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!");
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);
}
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);
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
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
cout<<"\n
cout<<"\n
cout<<"\n
cout<<"\n
cout<<"\n
cout<<"\n
cout<<"\n

1.ADAUGARE NOD DE LA TASTATURA";


2.STERGERE NOD DE LA TASTATURA";
3.CAUTARE NOD IN ARBORE";
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();
optiune='1';
while (optiune!='0')
{
cout<<"\nOptiunea dorita este:";
cin>>optiune;
if(((optiune>='0')&&(optiune<='9'))||(optiune=='n'))
{
switch(optiune)
{
case '1':
char op;
cout<<"\nNoul nod de introdus:";
cin>>informatie;
arb+informatie;
cout<<"\nMai doriti adaugarea unui alt nod(d/n)?";
cin>>op;
while (op=='d')
{
cout<<"\nNoul nod de introdus:";
cin>>informatie;
arb+informatie;
cout<<"\nSe va mai adauga alt nod(d/n)?";
cin>>op;
}
getch();
meniu();
break;
case '2':
cout<<"\nNodul care se va sterge:";
cin>>informatie;
arb-informatie;
getch();
meniu();
break;
case '3':
int elem;
cout<<"\nNodul care va fi cautat:";
cin>>elem;
arb[elem];
getch();
meniu();
break;
case '4':
printf("\nArborele traversat in SRD:");

arb.srd();
printf("\nArborele traversat in RSD:");
arb.rsd();
printf("\nArborele traversat in SDR:");
arb.sdr();
getch();
meniu();
break;
case '5':
printf("\nSuma inf frunze:%d", arb.sFrunza());
getch();
meniu();
break;
case '6':
cout<<"\nNumarul de noduri:"<<arb.numara_nod();
getch();
meniu();
break;
case '7':
printf("\nArborele tiparit in RSD:");
arb.tiparire();
getch();
meniu();
break;
case '0':
break;
}
}
else cout<<"\nOptiunea nu exista in meniu!";
}
}

13. ARBORI B
13.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 13.1.

15

18

20

26

30

40

42

36

50

100

43

58

70

60

62

65

120

110

130

140

136

69

150

145

Figura 13.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 13.2;

P0

K0

P1

K1

P2

Pn

Kn - 1

Pn

Figura 13.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

(13.1)

toate valorile de chei din nodurile subarborelui indicat de Pi sunt


mai mici dect valoarea cheii Ki, i 0, n 1 .
toate valorile de chei din nodurile subarborelui indicat de Pn sunt
mai mari dect valoarea de cheie Kn 1.
subarborii indicai de Pi, 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. toate nodurile frunz sunt pe acelai nivel;
ii. rdcina are cel puin doi descendeni, dac nu este frunz;

iii. fiecare pagin conine cel puin chei (excepie face rdcina
2
care poate avea mai puine chei, dac este frunz);
iv. nodul este fie frunz, fie are n + 1 descendeni (unde n este

numrul de chei din nodul respectiv, cu n m-1 );


2
v. 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

(13.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.

13.2 Operaii de baz ntr-un arbore B


Procesul de cutare ntr-un arbore B este o extindere a cutrii ntrun 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:
ci < x < ci +1, i 1, n se continu cutarea n nodul indicat de Pi;
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 ntrun 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 cele

dou noduri. Cele chei din stnga rmn n nodul care


2

fisioneaz. Cele din dreapta vor forma cel de-al doilea nod.
2
Cheia median va urca n nodul printe, care la rndul lui poate s

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
13.3.

Figura 13.3 Modificrile dimensionale ale unui arbore B


dup efectuarea inserrii
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 13.4.
22

41

57

59

Figura 13.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 13.5.
cheia mediana
54 va promova

22

41

54

57

22

59

41

57

Figura 13.5 Fisionarea nodului rdcin


n urma promovrii cheii mediene 54 se va forma o nou rdcin,
arborele crescnd n nlime cu 1, figura 13.6.

54

c
22

41

57

59

Figura 13.6 Formarea noii rdcini a arborelui


Inserarea cheilor 33, 75, 124 nu ridic probleme, figura 13.7.

59

54

22

33

41

57

59

75

124

Figura 13.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

57

124

59

75

124

Figura 13.8 Fisionarea nodului c


Cheia 62 va promova n nodul rdcin, figura 13.9.

a
54

62

c
22

41

57

59

75

124

Figura 13.9 Promovarea cheii 62 n rdcin


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

b
33

41

62

122

22

54

45

55

d
57

59

60

66

75

123

124

Figura 13.10 Structura arborelui dup inserri succesive


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

c
55

Cheia mediana 57
va promova

56

57

59

60

55

56

Figura 13.11 Fisionarea nodului c

59

60

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 13.12.

a
35

Cheia mediana 57
va promova

54

57

62

35

122

54

62

122

Figura 13.12 Fisionarea nodului a


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

a
35

b
22

f
33

41

54

62

c
45

55

g
56

59

122

d
60

66

e
75

123

124

Figura 13.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 13.14.

Adaug noua valoare


de cheie n nodul
frunz corespunzator
OVERFLOW?

Divide nodul n dou noduri


aflate pe acelai nivel i
promoveaz cheia median
Figura 13.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

singur nod nou. Fiecare nod are un minim de 1 chei, cu excepia


2
rdcinii, care are cel puin o cheie. Deci arborele cu p noduri conine cel

m
1 chei. Probabilitatea ca o fisionare s fie necesar
2

puin 1 p 1

dup inserarea unei valori de cheie este mai mic dect :

p2
m
1 p 1 1
2

divizari
chei

(13.3)

care este mai mic dect 1 divizare per 1 inserri de chei (deoarece
2

p 1
1
tinde ctre 0 pentru o valoare mare a lui p i
este aproximativ
p2
p2

1). De exemplu, pentru m = 10, 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 nlocuit cu o alta, vecin n inordine, care
va fi tears efectiv. n urma tergerii se disting urmtoarele cazuri:

dac nodul conine mai mult de chei, tergerea nu ridic


2
probleme;

dac nodul are numrul minim de chei , dup tergere


2
numrul de chei din nod va fi insuficient. De aceea se mprumut
o cheie din nodul vecin (aflat pe acelai nivel n arbore) dac

acesta are cel puin chei, caz n care avem de-a face cu o
2
partajare. Dac nu se poate face o partajare cu nici unul din

S considerm urmtoarea configuraie de arbore B de ordin 5 din


figura 13.15.
a
57

35

54

d
22

62

f
33

22

55

56

122

59

60

66

75

123

124

e
40

41

45

50

Figura 13.15 Arbore B de ordin 5


tergerea valorilor de cheie 40 i 45 din nodul e nu ridic probleme,
figura 13.16
a
57

b
35

d
22

e
33

34

41

c
54

62

f
50

55

g
56

59

122

h
60

66

i
75

123

124

Figura 13.16 Structura arborelui dup tergerea cheilor 40 i 45


tergerea valorii de cheie 50 necesit ns partajarea ntre nodurile d
i e, figura 13.17.

a
57

valoarea de cheie 35
coboara; valoarea de
cheie 34 urca

35

54

22

33

34

62

41

50

55

56

59

122

h
60

66

75

123

124

Figura 13.17 Partajarea ntre nodurile d i e

a
57

34

22

33

54

62

35

41

122

55

56

59

60

66

75

123

124

Figura 13.18 Structura arborelui dup partajare


tergerea valorii de cheie 22 va necesita fuzionarea nodurilor d i e,
figura 13.19.
a
57

fuzionare

34

d
22

e
33

35

c
54

62

f
41

55

122

g
56

59

h
60

66

i
75

123

124

Figura 13.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 13.20.

a
fuzionare

57

c
54

d
33

62

f
34

35

41

55

122

g
56

59

h
60

66

i
75

123

124

123

124

Figura 13.20 Fuzionarea nodurilor a, b i c


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

54

33

34

35

41

55

57

56

62

122

59

60

66

75

Figura 13.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.

13.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 ;
}
} ; /**
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
typedef
typedef
typedef

KEY_NODE_TYPE key_node_type ;
key_node_type* key_node_ptr ;
btree_node<KEY_NODE_TYPE> btree_node_type ;
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
} ;
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>
equal_to<key_node_type>

_less_than ;
_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:
EMPTY
nodul nu conine nici o cheie ;
UNDERFLOW
nodul conine prea puine chei ;
MINIMAL
nodul conine numrul minim de chei ;
OPERATIONAL numrul de chei coninut de nod respecta cerinele
de arbore B;
FULL
nodul conine numrul maxim de chei ;
OVERFLOW
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
memoria alocat:

btree_node<KEY_TYPE>::~btree_node()

elibereaz

template <typename KEY_NODE_TYPE>


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-un nod va fi m 1, iar numrul

minim de chei va fi 1 . Parametrul primit de constructor va fi m 1,


2
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 ;
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 ;
}
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()).
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 ;
}

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.
template <typename KEY_NODE_TYPE>
int
btree_node<KEY_NODE_TYPE>::split(
btree_node<KEY_NODE_TYPE>::btree_node_tuple*
) {
if( 0 == tuple ) return 0 ;
if( OVERFLOW != this->status() ) return 0;

tuple

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
typedef
typedef
typedef
typedef
typedef

KEY_TYPE
key_type ;
btree_node<KEY_TYPE>
btree_node_type ;
btree_node_type*
btree_node_ptr ;
btree_node_type::btree_node_tuple btree_node_tuple ;
btree<KEY_TYPE>
btree_type ;
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 ) ;
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 root ;
int
order ;
} ; /**
class btree

*/

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 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:
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 ;
}

13.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 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
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
>status() )
restore( current, position ) ;

current->child(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 13.22.
z

move_right()

move_left()
x

combine()

Figura 13.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.
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 ;
}

14. ARBORI ECHILIBRAI


14.1 Echilibrarea structurilor arborescente
Pentru a descrie gradul de echilibru al structurilor arborescente sunt
definite dou abordri:
- arbori perfeci echilibrai n care pentru fiecare nod, diferena dintre
numrul de noduri ale subarborelui drept i stng ia valori n
mulimea {-1 ; 0 ; +1}; ntr-un arbore perfect echilibrat de nlime
h, toate nodurile frunz sunt pe acelai nivel i orice nod de pe
nivelurile intermediare 1..h-2 are numrul maxim de fii; de exemplu,
figura 14.1 descrie un arbore binar de cutare perfect echilibrat;
23

10

27

18

Figura 14.1 Arbore binar perfect echilibrat


cea mai simpl metoda de a obine un astfel de arbore se bazeaz pe
parcurgerea prin metoda divide et impera a irului de chei ordonate
cresctor i inserarea valorii din mijloc n arbore; aceasta metoda
este ineficienta n practic deoarece presupune realizarea unui volum
mare de calcule dup fiecare operaie de inserare sau tergere ;
efortul ridicat de prelucrare este dat de parcurgerea n inordine a
arborelui pentru a obine irul sortat cresctor al cheilor si de
reconstrucia structurii ; considernd un arbore binar de cutare,
metoda utilizata n acest sens, echilibrareArb, are ca parametrii irul
sortat cresctor al valorilor, vectorul chei, dimensiunea acestuia, dim,
limitele intervalului curent, stanga, respectiv, dreapta, i rdcina
arborelui ce va fi creat; meninerea unei astfel de structuri reprezint
o operaie cu grad de complexitate foarte ridicat, fapt care conduce la
recrearea arborelui perfect echilibrat dup fiecare operaie de
inserare sau tergere cu metoda echilibrareArb;
void echlibrareArbore(int *chei, int dim, int stanga, int dreapta,
NodArbore *&radacina){
if (dreapta>=stanga){
int mijloc=(dreapta+stanga)/2;
if (dreapta-stanga==1){
radacina =inserareArbore(radacina, chei[stanga]);
radacina = inserareArbore (radacina, chei[dreapta]);
}

else{
if (dreapta==stanga)
radacina = inserareArbore (radacina, chei[stanga]);
else{
radacina = inserareArbore (radacina, chei[mijloc]);
echlibrareArbore (chei,dim,stanga, mijloc -1, radacina);
echlibrareArbore (chei,dim, mijloc +1,dreapta, radacina);
}
}
}
}

imperfect echilibrat n care pentru fiecare nod, diferena dintre


nlimea subarborelui drept i nlimea subarborelui stng ia valori
n mulimea {-1 ; 0 ; +1}; crearea unei astfel de structuri se bazeaz
pe utilizarea metodei prezentate anterior pornind de la un set de
valori sortate cresctor sau descresctor; meninerea gradului de
echilibru al structurii dup operaiile de inserare sau tergere este
posibil prin metode cu un grad de complexitate acceptabil i care
sunt specifice unor structuri arborescente echilibrate particulare, AVL,
arbori B, arbori Rosu & Negru; aceste metode implic un efort de
prelucrare mai mic dect volumul operaiilor asociat reconstruciei
arborelui prin metoda echilibrareArb.

Structurile arborescente sunt structuri de date dinamice n care


elementele sunt poziionate ierarhic n funcie de legtura printe copil ce
exist ntre dou elemente. Din punct de vedere al minimizrii efortului de
regsire, aceast organizare este mai eficient n raport cu structurile
dinamice liniare, deoarece reduce numrul de comparri necesar identificrii
unui element. n cazul unei structuri de date liniare, cel mai nefavorabil caz
descrie o complexitate egal cu O(m), unde m reprezint numrul de
elemente, i este generat de cutarea ultimului element. Aceast situaie
este ntlnit i n cazul structurilor arborescente ineficient construite, n
care fiecare nod are maxim un fiu i care au asociat imaginea unei liste.
Pentru a evita acest lucru i pentru de a beneficia de efectele pozitive ale
utilizrii structurilor arborescente n operaiile de cutare se definesc reguli
stricte de realizare ale unei astfel de structuri. Prin prisma acestor reguli,
structurile arborescente se difereniaz pe mai multe tipuri. Dintre acestea,
structurile arborescente echilibrate ocup o pondere ridicat n dezvoltarea
de soluii eficiente deoarece descriu un nivel constant de efort apropiat de
cel optim.
Se consider irul valorilor 23, 10, 27, 18, 3, 2 pe baza crora se
definete un arbore binar de cutare. Structura arborescenta obinut este
descris n figura 14.2.

23

10

27

18

Figura 14.2 Arbore binar de cutare


Din analiza modului in care sunt poziionate valorile, situaia cea mai
puin favorabil ce caracterizeaz acest arbore binar de cutare este data
de cazul n care nodul ce conine cheia cu valoarea 2 are o frecven ridicat
de utilizare. Situaia este generat de faptul ca cel mai lung drum de la
nodul rdcin la un nod frunza este dat de drumul 23 10 3 2, de
lungime 4. Dimensiunea este determinat de numrul de comparri
necesare identificrii nodului cutat.
Pe baza unei aranjri echilibrate a valorilor, acelai set de valori este
reprezentat de structura arborescenta urmtoare
10

23

18

27

Figura 14.3 Arbore binar de cutare echilibrat


Prin prisma cazului cel mai puin favorabil, arborele din figura 14.3
este mai eficient dect cel anterior deoarece numrul maxim de comparri
necesare identificrii oricrui nod din structura este egal cu 3.
n funcie de numrul de elemente dintr-un arbore binar de cutare,
n, o structur echilibrat descrie o complexitate egal cu O(log2n) pentru
cel mai nefavorabil caz. n schimb, o structur arborescent de acelai tip,
dar care nu este echilibrat, este caracterizat pentru cel mai nefavorabil
caz de o complexitate egal cu O(n).
Minimizarea efortului de regsire a informaiilor se obine printr-o
aranjare echilibrata a valorilor pe ambii subarbori ai fiecrui nod.

14.2 Caracteristici ale arborilor AVL


Un arbore AVL, definit prima dat de G.M. Adelson-Velskii i E.M.
Landis n [Ande62], este un arbore binar de cutare echilibrat pe nlime.
Un arbore binar de cutare este AVL dac gradul de echilibru al fiecrui nod
ia valori n mulimea {-1,0,1}.
Pentru a msura gradul de echilibru al unui nod se definete
indicatorul GE ce descrie relaia:
GE = H(SD) H(SS)

(14.1)

unde H() reprezint funcia de calcul a nlimii unei structuri


arborescente. Pentru a determina nlimea unui arbore, al crui nod
rdcin este rad, se utilizeaz formula:
H(rad) = 1 + max (H(subarbore drept), H(subarbore stng))

(14.2)

n care funcia max() este utilizat pentru a determina maximul dintre dou
valori.
int max(int valoare_1, valoare_2)
{
return valoare_1 < valoare_2 ? valoare_2 : valoare_1;
}

Pentru valoarea indicatorului GE = 0, nodul este echilibrat, iar pentru


valorile 1 si -1, nodul descrie un dezechilibru la dreapta, respectiv la stnga.
Figura 14.4, descrie arborele binar de cutare pentru care s-a determinat
gradul de echilibru.
Situaiile n care GE are valoarea -1 sau 1 sunt acceptate deoarece,
pentru un numr par de valori este imposibil sa se defineasc un arbore
binar de cutare n care toate nodurile sunt perfect echilibrate.

10

GE = 0

GE = -1

GE = 0
23

3
GE = 0

GE = 0
2

18

GE = 0
27

Figura 14.4 Arbore binar de cutare echilibrat


Arborele AVL, reprezint un arbore binar de cutare echilibrat.
Pornind de la aceast ipoteza, acest tip de arbore motenete toate
operaiile implementate de arborii binari de cutare. Caracteristica de
echilibru se gestioneaz prin verificarea atent a gradului de echilibru,
pentru fiecare nod n parte, n urma operaiilor de inserare i tergere.

Aceste tipuri de prelucrri afecteaz structura arborelui i conduc la situaii


de dezechilibru.
Pentru a menine arborele AVL, dup fiecare operaie de inserare,
respectiv tergere, sunt cutate situaiile de dezechilibru puternic,
identificate prin intermediul nodurilor pentru care indicatorul GE ia valori n
mulimea {-2,2}.
Reechilibrarea arborelui binar de cutare i pstrarea caracteristicilor
aferente arborilor AVL se realizeaz prin operaii de rotire:
rotire simpla la stnga;
rotire simpla la dreapta;
dubla rotire la stnga ;
dubla rotire la dreapta.
Este important de reinut c printr-o singur rotaie, selectat n
funcie de situaie, un arbore AVL dezechilibrat n urma operaiei de inserare
va fi reechilibrat. n schimb, operaie de reechilibrare n urma tergerii unui
nod este mult mai complex, necesitnd minim o rotaie.

14.3 Operaii pe arbori AVL


Determinarea metodei adecvate de reechilibrare se realizeaz prin
analiza gradului de echilibru a nodurilor aflate pe drumul de la rdcina
arborelui la locaia n care a fost inserat, respectiv ters, nodul.
Se considera situaia din figura 14.4 n care se insereaz nodul cu
cheia 1. Arborele binar obinut este:

10

GE = -1

GE = -2

GE = 0
23

3
GE = -1

GE = 0
2

18

GE = 0
27

GE =0
1
Figura 14.5 Arbore binar AVL dezechilibrat
n urma procesului de inserarea se recalculeaza gradul de echilibru al
nodurilor ce sunt afectate. Aecstea noduri se gasesc in multimea nodurilor
{10, 3, 2, 1}. Se observa ca arborele i pierde caracteristica de a fi AVL
deoarece nodul cu cheia 3 are un grad de echilibru egal cu -2, ceea ce
evideniaz un dezechilibru puternic la stnga.
Pentru a aplica procesul de echilibrare, bazat pe operaie de rotire, se
identifica un nod, numit pivot, n care se realizeaz rotirea subarborelui.

Selectarea nodului pivot se face printr-o abordare jos-sus pornind de


locaia nodului inserat, respectiv, ters ctre rdcina arborelui.
Reechilibrarea arborelui se face cat mai aproape de locaia care a
generat dezechilibrul. Astfel, printr-un numr minim de rotaii se
reconstruiete caracteristica arborelui AVL.
n figura, 14.5, nodul pivot este nodul a crui cheie are valoarea 3.
Pentru a reechilibra arborele este nevoie s transferm o parte din
greutatea subarborelui stng ctre subarborele drept al nodului pivot.
Pentru a identifica operaia de rotaie corespunztoare se analizeaz
gradul de echilibru al nodului pivot si cel al nodului fiu de pe direcia
dezechilibrului. Analiznd arborele din figura 14.4, se observa ca nodul
pivot, cu cheia 3, are gradul de echilibru GE = -2, ceea ce implic un
dezechilibru la stnga. Deoarece nodul fiu stnga, cu cheia 2, are
dezechilibru simplu tot la stnga, reechilibrarea se realizeaz prin operaia
de rotire simpl la dreapta, figura 14.6.
la

GE = -2
NOD PIVOT

GE = 0

GE = 0

GE = -1
NOD FIU PE
DIRECTIA
DEZECHILIBRULUI

X - Subarbore drept
pivot

Y - Subarbore stang
fiu dreapta pivot

Figura 14.6 Procesul de rotire simpl la dreapta


Procesul de rotire simpl la dreapta implic existena a dou
elemente principale, nodul pivot, ce este dezechilibrat puternic la stnga i
fiul acestuia de pe direcia dezechilibrului, care este la rndul su
dezechilibrat slab tot la stnga. Pentru a reechilibra arborele n aceast
situaie este necesar i suficient s scdem cu o unitate nlimea
subarborelui stng al pivotului i s cretem cu o unitate nlimea
subarborelui drept. Pentru a atinge acest obiectiv, se modific cele dou
legturi evideniate n figura 14.5. Se observ c subarborele Y devine
subarbore drept pentru nodul pivot, fapt ce nu contrazice existena unui
arbore binar de cutare deoarece toate valorile din acest subarbore sunt
mai mici dect valoarea nodului pivot.
Prin aplicarea procesului de rotire, arborele se reechilibreaz i i
pstreaz caracteristicile specifice unui arbore AVL i unui arbore binar de
cutare. Se observ c prin rotaia simpl la dreapta sunt afectate doar
gradele de echilibru ale nodului pivot i nodului fiu de pe direcia
dezechilibrului, nodul fiu stnga. Prin modificarea structurii arborescente,
cele dou noduri devin perfect echilibrate, GE = 0. Explicaia este dat de
faptul c nodul fiu stnga urc pe nivelul superior n locul nodului printe,
iar acesta, fiind nod pivot, coboar n subarborele drept. Figura 14.7

utilizeaz ca reper nlimea subarborelui Y pentru a descrie modul n care


se ajunge la acest rezultat. Este evideniat modul de calcul al indicatorului
GE pentru a sublinia modul n care se ajunge la rezultat.
?

NOD PIVOT

GE = 0

GE = H1 (H1 + 2) = -2

GE = 0
2

H1

GE = H1 (H1 + 1) = -1
H1+2
H1+1

H1

H1+1

H1+1

H1

X
H1

Figura 14.7 Procesul de rotire simpl la dreapta


Metoda clasei AVLArbore ce implementeaz aceast rotaie simpl
primete ca parametru referina la nodul pivot.
void AVLArbore::RotatieSimplaDreapta(AVLNod * &pivot)
{
AVLNod *FiuStanga = pivot->st;
pivot->st = FiuStanga->dr;
FiuStanga->dr = pivot;
pivot->Echilibru = 0;
FiuStanga->Echilibru = 0;
pivot = FiuStanga;
}

Aplicnd aceast metod


arborescent din figura 14.8.

arborelui

10

analizat,

GE = 0

23
GE = 0

GE =0

structura

GE = 0

GE = 0

rezult

18

GE = 0
27

GE = 0

Figura 14.8 Arbore AVL reechilibrat


Dac prin inserarea sau tergerea unui nod se ajunge n situaia din
figura 14.9, reechilibrarea arborelui se realizeaz printr-o rotire simpl la
stnga.

10

GE = 1

GE = -1

GE = 2

23

GE =0

GE = 1

27
GE = 0
30

Figura 14.9 Arbore AVL dezechilibrat


n aceast situaie, pivotul este dat de nodul cu valoarea 23, acesta
fiind puternic dezechilibrat la dreapta, GE = 2. Deoarece nodul fiu dreapta,
este dezechilibrat slab pe aceeai direcie, reechilibrarea se realizeaz prin
operaia de rotire simpl la stnga.
?

?
NOD PIVOT

X - Subarbore stng
pivot

GE = 2

GE = 0

23
GE = 1
30

GE = 0

NOD FIU PE
DIRECTIA
DEZECHILIBRULUI

30

23

Y - Subarbore stang
fiu stnga pivot

Figura 14.10 Procesul de rotire simpl la stnga


Asemntor operaiei de rotaie simpl la dreapt, cele dou noduri
afectate direct de reorganizarea legturilor, nodul pivot i fiul acestuia din
dreapta, au n final grade de echilibru egale cu valoarea zero. Reorganizarea
celor dou legturi evideniate n figura 14.10 are ca efect reducerea cu o
unitate a nlimii subarborelui drept al nodului pivot i creterea cu o
unitate a subarborelui stng.
Metoda ce implementeaz aceast operaie, RotatieSimplaStanga, are
ca parametru de intrare referina nodului pivot.
void AVLArbore::RotatieSimplaStanga(AVLNod * &pivot)
{
AVLNod *FiuDreapta = pivot->dr;
pivot->dr = FiuDreapta->st;
FiuDreapta->st = pivot;

pivot->Echilibru = 0;
FiuDreapta->Echilibru = 0;
pivot = FiuDreapta;
}

Aplicnd aceast operaie arborelui din figura 14.10 se obine


arborele AVL:

10

GE = 1

GE = -1

GE = 0

2
GE =0

27
GE = 0

GE = 0

23

30

Figura 14.11 Arbore AVL reechilibrat


n cazul operaiile de rotire duble, situaia iniial este caracterizat
de sensuri opuse de dezechilibru pentru nodul pivot si pentru nodul su fiu
de pe direcia dezechilibrului. Pentru a exemplifica o astfel de situaie se
insereaz n arborele AVL din figura 14.11, elementele cu valorile 16, 24,
26. Structura arborescent obinut este:

10

GE = 2

GE = -1
2

27

GE =0

GE = -2

GE = 1

GE = 0

23
GE = 0
16

30
GE = 1
24
GE = 0
26

Figura 14.12 Arbore AVL dezechilibrat


Structura arborescent din figura 14.12 nu este arbore AVL deoarece
exist noduri pentru care gradul de echilibru, GE, are valori n mulimea
{-2, +2}. Situaia este generat de inserarea nodului cu valoarea 26, iar
analiza drumului de la acest nod napoi ctre nodul rdcin conduce la

identificarea pivotului, nodul cu valoarea 27. Se observ c, acest nod este


puternic dezechilibrat la stnga, iar nodul fiu de pe aceast direcie, nodul
23, este dezechilibrat slab pe direcia opus. Soluia pentru aceast
problem necesit o abordare diferit de cele dou tipuri de rotiri simple
descrise, deoarece acestea nu conduc la reechilibrarea arborelui. Pentru a
exemplifica aceast abordare greit se simuleaz o rotire simpl la dreapta
aplicat pivotului. Rezultatul obinut este:
10

GE = 2

GE = -1

GE = 2

2
GE =0

23
GE = 0

GE = -1

16

27
GE = 1
24

30
GE = 0

GE = 0

26

Figura 14.13 Arbore AVL dezechilibrat


Se observ c arborele, figura 14.13, este n continuare dezechilibrat,
numai c de data aceasta, dezechilibrul este n sens opus. ncercarea
reechilibrrii, tot cu o rotire simpl, dar n sens opus, va conduce la
obinerea ipotezei iniiale, descris n figura 14.12.
Soluia eficient a acestui tip de dezechilibru este dat de aplicarea
unei rotiri duble, ce const n aplicarea a dou rotiri simple. Scopul primei
rotiri este de a rearanja structura arborescent astfel nct direciile
dezechilibrului nodului pivot i a fiului acestuia s aib acelai sens. Cea dea doua rotire are ca obiectiv reechilibrarea arborelui. Pe baza acestor
motive, cele dou rotaii sunt aplicate unor noduri diferite. Prima rotaie se
aplic nodului fiu al nodului pivot, nod ce se gsete pe direcia
dezechilibrului. Sensul acestei prime rotiri este identic cu direcia
dezechilibrului. A dou rotire simpl se aplic nodului pivot i are sens opus
dezechilibrului.
Pentru structura arborescent din figura 14.12, pivotul este dat de
nodul cu valoarea 27 i acesta este puternic dezechilibrat la stnga. Pentru
a reechilibra arborele se parcurg urmtoarele etape:
- se analizeaz nodul fiu al nodului pivot pe direcia dezechilibrului;
acest nod are valoarea 23 i este slab dezechilibrat la dreapta;
- deoarece pivotul i nodul fiu sunt dezechilibrate pe direcii
diferite, reechilibrarea se realizeaz printr-o dubl rotaie;
- prima rotaie se aplic nodului fiu i are sens identic cu
dezechilibrul nodului pivot; se observ c aceast operaie
intermediar, figura 14.12.a, redefinete situaia aducnd-o ntr-o
form specific cazurilor n care se aplic rotaii simple;

a doua rotaie se
dezechilibrului.

10

GE = 2

GE = -1
27

GE = -1

GE = 0
16

27

GE =0

23

30
GE = 1

GE = -2

NOD PIVOT

GE = 1
NOD FIU

GE = 2

10
GE = -2

2
GE =0

aplic nodului pivot i are sens opus

NOD PIVOT

GE = -1

GE = 0

24

30

GE = -1

GE = 0

24

GE = 0

23

26

GE = 0
26

A) ROTATIE SIMPLA LA STANGA

16

GE = 0

B) ROTATIE SIMPLA LA DREAPTA

10

GE = 1

GE = -1

GE = 0

2
GE =0

24
GE = -1

GE = 0

23
GE = 0

27
GE = 0

16

26

GE = 0
30

C) ARBORE REECHILIBRAT

Figura 14.14 Rotaia dubl la dreapta


Din analiza dublei rotaii la dreapta sunt evideniate 3 elemente
importante, n funcie de care sunt reiniializate o serie de legturi:
- nodul pivot, puternic dezechilibrat la stnga, GE = -2;
- fiul stnga la pivotului, FiuStanga ,ce este slab dezechilibrat la
dreapta, GE = 1;
- fiul dreapta al nodului FiuStanga, notat cu FiuStanga_FiuDreapta;
n funcie de situaie, acest nod prezint un grad de echilibru ce ia
valori n mulimea {-1, 0 , 1}.
Pentru a determina gradul de echilibru final al nodurilor afectate de
dubla rotaie, se analizeaz modul n care se distribuie nlimea
subarborilor n urma rotaiilor. Tabelul 14.1 descrie situaiile iniiale i
rezultatele la care se ajunge n urma reechilibrrii.
Tabelul nr. 14.1 Rezultatul operaiei de dubl rotaie la dreapta
Situaie iniial

Situaie final

Pivot

FiuStanga

FiuStanga_FiuDreapta

Pivot

FiuStanga

FiuStanga_FiuDreapta

-2
-2
-2

+1
+1
+1

-1
0
+1

1
0
0

0
0
-1

0
0
0

Situaia descris n tabelul anterior este utilizat pentru a defini mai eficient
metoda care implementeaz acest tip de rotaie. Astfel este evitat efortul
suplimentar de a recalcula gradul de echilibru pentru cele trei noduri
afectate.

Clasa AVLArbore implementeaz aceast operaie prin intermediul


metodei RotatieDublaDreapta ce primete ca parametrul referina nodului
pivot.
void AVLArbore::RotatieDublaDreapta(AVLNod * &pivot)
{
AVLNod *FiuStanga, *FiuStanga_FiuDreapta;
FiuStanga = pivot->st;
FiuStanga_FiuDreapta = FiuStanga->dr;
//realizare rotatie 1 - simpla stanga
FiuStanga->dr = FiuStanga_FiuDreapta->st;
FiuStanga_FiuDreapta->st = FiuStanga;
//realizare rotatie 2 - simpla dreapta
pivot->st = FiuStanga_FiuDreapta->dr;
FiuStanga_FiuDreapta->dr = pivot;
//modificare grade de echilibru
if(FiuStanga_FiuDreapta->Echilibru == 1)
{
pivot->Echilibru = 0;
FiuStanga->Echilibru = -1;
}
else
if(FiuStanga_FiuDreapta->Echilibru == 0)
{
pivot->Echilibru = 0;
FiuStanga->Echilibru = 0;
}
else
{
pivot->Echilibru = 1;
FiuStanga->Echilibru = 0;
}
FiuStanga_FiuDreapta->Echilibru=0;
pivot = FiuStanga_FiuDreapta;
}

Structura arborescent este modificat prin tergerea nodului cu


valoarea 16 i prin adugarea unei noi valori, 25. Arborele obinut, descris
n figura 14.15, nceteaz s mai fie AVL n urma aplicrii ultimei modificri.
10

GE = 2

GE = -1

GE = 2

2
GE =0

24
GE = -1

GE = -1

23

27
GE = -1
26

GE = 0
30

GE = 0
25

Figura 14.15 Arbore AVL dezechilibrat

Se observ c exist dou noduri, cu valoarea 24 i 10, ce descriu


dezechilibre puternice, GE = 2, la dreapta. Analiznd, de jos n sus, drumul
de la noul nod inserat la rdcin arborelui, se stabilete ca fiind pivot nodul
cu valoarea 24. n mod asemntor cu situaia descris anterior, fiul
pivotului de pe direcia dezechilibrului este dezechilibrat uor n sens opus.
Tentativa de a rezolva situaia prin intermediul unei rotaii simple nu
conduce la soluionarea problemei reechilibrrii deoarece are ca rezultat
mutarea dezechilibrului pe partea stng.
Avnd n vedere condiiile de lucru, reechilibrarea arborelui din figura
14.15 presupune:
- aplicarea unei rotaii simple la dreapta n nodul fiu al pivotului;
dac pivotul are ambii fii atunci rotaia se face n toate situaiile
asupra nodului fiu de pe direcia dezechilibrului; deoarece pivotul
este dezechilibrat puternic la dreapta, nodul fiu selecta este 27;
- aplicarea unei rotaii simple la stnga, n sens opus
dezechilibrului, n nodul pivot.
10
GE = -1

GE = -1

GE = -1

GE =0

GE = -1

23

GE = 2

NOD PIVOT

24

GE = 2

10
GE = 2

NOD PIVOT

2
GE =0

GE = 2

27
GE = -1
26

NOD FIU

24
GE = -1

GE = 1

23

26

NOD FIU

GE = 0

GE = 0

GE = 1

25

30

27

GE = 0
B) ROTATIE SIMPLA LA STANGA

25

GE = 0

30

A) ROTATIE SIMPLA LA DREAPTA

10

GE = 1

GE = -1

GE = 0

2
GE =0

26
GE = 0

GE = 1

24

27
GE = 0

GE = 0

23

25

GE = 0
30

C) ARBORE REECHILIBRAT

Figura 14.16 Rotaia dubl la stnga


Pentru a implementa o soluie software care s gestioneze datele prin
intermediul unui arbore binar de cutare echilibrat de tip AVL, trebuie s fie
dezvoltate rutine complementare operaiilor de inserare i tergere n arbori
binari de cutare care s reechilibreze structura arborescent aflat n una
din cele patru situaii descrise.
Tabelul 14.2 sintetizeaz situaiile de dezechilibru i modul n care
arborele AVL este meninut n urma operaiilor de inserare.

Tabelul nr. 14.2 Situaii dezechilibru arbori AVL


Grad
echilibru nod
pivot

Nod fiu
analizat

Grad
echilibru
nod fiu

+2
+2

dreapta
dreapta

+1
-1

-2
-2

stnga
stnga

-1
+1

Rotire

Simpl la stnga
Dubl la stnga: rotire simpl la
dreapta n fiul din dreapta al pivotului;
rotire simpl la stnga n pivot.
Simpl la dreapta
Dubl la dreapta: rotire simpl la
stnga n fiul din stnga al pivotului;
rotire simpl la dreapta n pivot.

Dezvoltarea de aplicaii care implementeaz lucrul cu arbori de tip


AVL se bazeaz pe dezvoltarea unei biblioteci de cod n care sunt definite
clasele AVLNod i AVLArbore. Clasa AVLNod descrie atributele i metodele
unui obiect ce reprezint nodul unui arbore binar de cutare echilibrat.
class AVLNod
{
private:
int Echilibru;
int Info;
AVLNod *st;
AVLNod *dr;
public:
//constructorii clasei
AVLNod(void);
AVLNod(int echilibru, int info, AVLNod * stanga, AVLNod *
dreapta);
//destructorul clasei
virtual ~AVLNod(void);
//interfata pentru atributul Echilibru
int GetEchilibru(void){return this->Echilibru;};
// acces la atributele private din clasa AVLArbore
friend class AVLArbore;
//acces la atributele private din clasa AVLNodeStack
friend class AVLNodeStack;
};

n comparaie cu nodul unui arbore binar de cutare, aceast clas


definete o proprietate nou, Echilibru, utilizat pentru a gestiona gradul de
echilibru asociat fiecrui nod. Atributele Info, st i dr sunt utilizate pentru a
memora valoarea nodului curent i pentru a face legtur ntre nodul
printe i nodul fiu stnga, respectiv, dreapta.
Cele dou metode constructor
AVLNod::AVLNod(void)
{
Echilibru = 0;
Info = 0;
st = NULL;
dr = NULL;

}
AVLNod::AVLNod(int echilibru, int info, AVLNod * stanga, AVLNod *
dreapta)
{
Echilibru = echilibru;
Info = info;
st = stanga;
dr = dreapta;
}

permit programatorilor crearea i iniializarea unui nod al arborelui cu valori


implicite sau pe baza unor parametrii de intrare.
Clasa AVLArbore definete atributele i metodele unui obiect de tip
arbore AVL. Acesta gestioneaz structura dinamic de elemente prin
intermediul referinei ctre nodul rdcin, radacina.
class AVLArbore
{
public:
AVLNod *radacina;
public:
//constructorul clasei
AVLArbore(void);
//constructorul de copiere al clasei
AVLArbore(const AVLArbore & arbore);
//destructorul clasei
virtual ~AVLArbore(void);
//operatorul =
AVLArbore operator = (AVLArbore & arbore);
//metodele clasei pentru inserare/stergere nod
void Insert(const int info);
void Delete(const int info);
//metoda pentru afisarea arborelui
static void AfisareArbore(AVLNod * rad);
//metoda pentru stergerea arborelui
void StergereArbore(AVLNod * &rad);
private:
void AVLInsert(AVLNod* &arbore,AVLNod * nodNou, int &
echilibruNou);
void AVLDelete(AVLNod* &arbore,const int Info,AVLNodeStack
&stiva);
//rotatii simple utilizate la inserare
void RotatieSimplaDreapta(AVLNod * &pivot);
void RotatieSimplaStanga(AVLNod * &pivot);
//rotatii simple utilizate la stergere
void RotatieSimplaDreaptaStergere(AVLNod * &pivot);
void RotatieSimplaStangaStergere(AVLNod * &pivot);
void RotatieDublaDreapta(AVLNod * &pivot);
void RotatieDublaStanga(AVLNod * &pivot);
//metodele clasei pentru reechilibrarea arborelui

void ReechilibrareSubarboreStang(AVLNod * &pivot, int


&echilibruNou);
void ReechilibrareSubarboreDrept(AVLNod * &pivot, int
&echilibruNou);
//metoda utilizata pentru copierea arborelui
void CopiereArbore(AVLArbore &arboreNou, AVLNod * rad);
//metoda inserare a unui arbore binar de cautare
AVLNod * Inserare(AVLNod *rad, const int Valoare, int echilibru
= 0);
static int Stergere(AVLNod*& Subarbore, AVLNodeStack &stiva);
//metoda pentru determinarea inaltimii unui arbore
int inaltime(AVLNod * radacina);
//metoda ce determina maximul dintre doua valori
int max(int a, int b){return a < b? b : a;}
//metoda ce determina gradul de echilibru al nodului
int CalculeazaEchilibru(AVLNod *& radacina);
//metoda recalculeaza gradul de echilibru pentru toate nodurile
void RecalculeazaEchilibrul(AVLNod *&rad);
};

O atenie deosebit se acord formei dat de programator a


constructorului de copiere i a operatorului =. Necesitatea este dat de
existena atributului dinamic AVLNod *radacina i de efectele negative pe
care le au formele implicite ale acestor dou metode asupra programului.
Programatorul trebuie s se asigure c n situaiile n care aceste dou
metode sunt apelate se vor crea structuri noi cu valori egale i nu se vor
face doar simple iniializri de referine ctre aceeai zon de memorie.
Copierea arborelui presupune parcurgerea structurii existente, cu
pstrarea caracteristicilor acesteia. Din acest motiv, cele dou metode se
bazeaz pe o parcurgere n preordine a arborelui existent, completat de
inserarea nodului curent n structura nou creat. Spre deosebire de
parcurgere n inordine i postordine, parcurgere n preordine asigur
crearea unui nou arbore binar de cutare identic cu structura surs i cu
minim de efort.
Se consider structura arborescent din figura 14.17 pentru care se
obin irurile valorilor elementelor, parcurgnd arborele prin cele trei
metode cunoscute.
10

23

18

Preordine: 10, 3, 2, 23, 18, 27


Inordine: 2, 3, 10, 18, 23, 27
Postordine: 2, 3, 18, 27, 23, 10

27

Figura 14.17 Structur arborescenta de tip AVL

Prin inserarea valorilor ntr-o nou structur arborescent, pe msur


ce acestea sunt accesate i analizate, se obin cei trei arbori binari de
cutare din figura 14.18.
10

2
3

23

10
2

18

27

18
18

3
23

A) Arbore obinut n urma


parcurgerii n preordine

B) Arbore obinut n urma


parcurgerii n inordine

27
23

27

C) Arbore obinut n urma


parcurgerii n postordine

Figura 14.18 Structuri arborescente binare


Se observ c, dintre cele trei metode de parcurgere a unui arbore
binar, cea mai potrivit pentru operaia de copiere este abordarea n
preordine. Celelalte dou metode necesit un efort suplimentar de
rearanjare a nodurilor i nu asigur obinerea unei arbore identic cu sursa.
Din punct de vedere al reechilibrrii, efortul este mult mai mare datorit
prelucrrilor suplimentare.
Metoda CopiereArbore construiete copia arborelui radArboreVechi
parcurgnd-ul n preordine.
void AVLArbore::CopiereArbore(AVLArbore &arboreNou, AVLNod
*radArboreVechi)
{
if(radArboreVechi!=NULL)
{
arboreNou.radacina =
arboreNou.Inserare(arboreNou.radacina,radArboreVechi->Info,
radArboreVechi->Echilibru);
CopiereArbore( arboreNou, radArboreVechi->st);
CopiereArbore( arboreNou, radArboreVechi->dr);
}
}

Metoda anterior, se bazeaz pe parcurgerea recursiv a arborelui


curent i apeleaz rutina Inserare specific arborilor binari de vutare
pentru a insera o valoare ntr-o nou structur arborescent gestionat prin
pointerul arboreNou.
AVLNod * AVLArbore::Inserare(AVLNod *rad, const int Valoare, int
echilibru)
{
if(rad == NULL)
{
rad = new AVLNod(echilibru,Valoare, NULL, NULL);
}
else
if(rad->Info<Valoare)
rad->dr = Inserare(rad->dr,Valoare,echilibru);

else
if(rad->Info>Valoare)
rad->st = Inserare(rad->st,Valoare,echilibru);
return rad;
}

Formele explicite ale constructorului de copiere i a operatorului de


egal implementeaz rutina de copiere a unui arbore pentru a genera noi
structuri arborescente cu valori identice.
AVLArbore::AVLArbore(const AVLArbore &arbore)
{
this->radacina = NULL;
CopiereArbore((*this),arbore.radacina);
}

Spre deosebire de constructorul de copiere, operatorul = presupune


tergerea arborelui existent i recrearea acestuia prin copierea valorilor
structurii arbore.
AVLArbore AVLArbore::operator = (AVLArbore & arbore)
{
StergereArbore(this->radacina);
CopiereArbore((*this),arbore.radacina);
return *this;
}

Metoda utilizat pentru tergerea arborelui AVL este dat de operaia


specific structurilor arborescente binare, ce realizeaz eliberarea memoriei
de jos n sus, pornind cu nodurile frunz.
void AVLArbore::StergereArbore(AVLNod * &rad){
if(rad!=NULL){
StergereArbore(rad->st);
StergereArbore(rad->dr);
delete rad;
rad = NULL;
}
}

Operaia de inserare n arborii AVL este derivat din metoda specific


arborilor binari de cutare. Operaiile suplimentare sunt necesare procesului
de reechilibrare i de conservare a caracteristicii acestui tip de structur,
meninerea gradului de echilibru n mulimea {-1; 0; 1} pentru toate
nodurile arborelui.
Metoda AVLInsert parcurge o serie de etape necesare inserrii unui
nou nod, nodNou, ntr-un arbore de tip AVL, gestionat prin intermediul
pointerului arbore:
- dac arborele este vid, noul nod devine rdcina arborelui AVL;
- dac arborele exist, se caut poziia noului nod prin parcurgerea
acestuia asemenea unui arbore binar de cutare; parcurgerea
este recursiv, accesndu-se nodul fiu stnga sau dreapta funcie
de rezultatul comparrii valorii nodului nou cu valoarea nodului
curent;

se recalculeaz gradul de echilibru pentru toate nodurile parcurse;


fiind un proces recursiv, revenirea din apelul rutinei asigur
poziionarea pe nodul anterior; variabilele echilibruNou i
Reechilibrare indic faptul c a avut loc o modificare de structur
n apelul anterior, lucru care poate conduce la dezechilibre; n
cazul n care aceste variabile sunt iniializate cu valoare 1, este
testat gradul de echilibru al nodului curent;

void AVLArbore::AVLInsert(AVLNod* &arbore,AVLNod * nodNou, int &


echilibruNou){
int Reechilibrare;
if(arbore == NULL){
arbore = nodNou;
arbore->Echilibru = 0;
echilibruNou = 1;
}
else
if(nodNou->Info<arbore->Info){
AVLInsert(arbore->st,nodNou,Reechilibrare);
if(Reechilibrare){
if(arbore->Echilibru == -1)
ReechilibrareSubarboreStang(arbore,echilibruNou);
else
if(arbore->Echilibru == 0){
arbore->Echilibru = -1;
echilibruNou = 1;
}
else{
arbore->Echilibru = 0;
echilibruNou = 0;
}
}
else
echilibruNou = 0;
}
else{
if(nodNou->Info>arbore->Info){
AVLInsert(arbore->dr, nodNou,
if(Reechilibrare){
if(arbore->Echilibru ==
arbore->Echilibru = 0;
echilibruNou = 0;
}
else
if(arbore->Echilibru ==
arbore->Echilibru
echilibruNou = 1;
}
else

Reechilibrare);
-1){

0){
= 1;

ReechilibrareSubarboreDrept(arbore,echilibruNou);
}
else
echilibruNou = 0;
}
else
echilibruNou = 0;

}}

identificarea nodului dezechilibrat, pivotul operaiilor de rotire,


este realizat doar dac variabila Reechilibrare este setat, prin
verificarea elementelor vizitate;
dac nodul curent are gradul de echilibru egal cu -1 iar nodul nou
a fost inserat n subarborele stng, are loc reechilibrarea acestuia
prin apelul metodei ReechilibrareSubarboreStang;
dac nodul curent are gradul de echilibru egal cu 0 sau +1 iar
nodul nou a fost inserat n subarborele stng, atunci noul grad de
echilibru al elementului curent este -1, respectiv 0; prin
iniializarea variabilei echilibruNou cu valoare 1 se continu
verificarea dezechilibrului la nodurile superioare; dac nodul
curent devine perfect echilibrat, se oprete verificarea n acest
punct, iar echilibruNou ia valoarea 0;
dac nodul curent are gradul de echilibru egal cu +1 iar nodul nou
a fost inserat n subarborele drept, are loc reechilibrarea acestuia
prin apelul metodei ReechilibrareSubarboreDrept;
dac nodul curent are gradul de echilibru egal cu 0 sau -1 iar
nodul nou a fost inserat n subarborele drept, atunci noul grad de
echilibru al elementului curent este +1, respectiv 0; asemenea
situaiei anterioare, variabila echilibruNou condiioneaz prin
valorile ei continuarea sau ncetarea procesului de cutare;
metoda ReechilibrareSubarboreStang ia n considerare toate
situaiile posibile de dezechilibru ctre stnga i n funcie de tipul
acesteia reechilibreaz subarborele cu rdcina n nodul pivot prin
rotaie simpl la dreapta, metoda RotatieSimplaDreapta, sau prin
rotaie dubl la dreapta, metoda RotatieDublaDreapta; se observ
caracterul general al acestei metode de reechilibrare ce este
utilizat i la tergerea unui nod, procesul fiind descris n
continuare;

void AVLArbore::ReechilibrareSubarboreStang(AVLNod * &pivot, int


&echilibruNou){
AVLNod * FiuStanga = pivot->st;
if(FiuStanga->Echilibru == -1){
RotatieSimplaDreapta(pivot);
echilibruNou = 0;
}
else
if(FiuStanga->Echilibru == 1){
RotatieDublaDreapta(pivot);
echilibruNou = 0;
}
else
//situatie specifica operatiei de stergere
if(FiuStanga->Echilibru == 0){
RotatieSimplaDreaptaStergere(pivot);
echilibruNou = 0;
}
}

metoda ReechilibrareSubarboreDrept analizeaz cazurile de


dezechilibru la dreapta, reechilibrnd pivotul prin una din cele
dou tehnici de rotaie la stnga;

void AVLArbore::ReechilibrareSubarboreDrept(AVLNod * &pivot, int


&echilibruNou){
AVLNod * FiuDreapta = pivot->dr;
if(FiuDreapta->Echilibru == 1){
RotatieSimplaStanga(pivot);
echilibruNou = 0;
}
else
if(FiuDreapta->Echilibru == -1){
RotatieDublaStanga(pivot);
echilibruNou = 0;
}
else
//situatie specifica operatiei de stergere
if(FiuDreapta->Echilibru == 0){
RotatieSimplaStangaStergere(pivot);
echilibruNou = 0;
}
}

Metoda AVLInsert este o metod intern clasei. Aceasta este epelat


din programul principal de ctre metoda public Insert ce primete ca
parametru valoarea de inserat n arborele AVL.
void AVLArbore::Insert(const int info)
{
AVLNod* RadacinaArbore = this->radacina;
AVLNod* NodNou = new AVLNod(0,info,NULL,NULL);
int EchilibruNou = 0;
AVLInsert(RadacinaArbore,NodNou,EchilibruNou);
this->radacina = RadacinaArbore;
}

Spre deosebire de operaie de inserare, care necesit maxim o


singur rotaie pentru remedierea dezechilibrului, n cazul procedurii de
tergere a unui nod sunt necesare mai multe operaii de rotaie pentru a
reechilibra arborele AVL i pentru a conserva caracteristicile acestuia.
Etapele parcurse se concentreaz pe analiza tuturor nodurilor direct
influenate
- se identific nodul de ters pe baza caracteristicilor arborilor
binari de cutare;
- pe msur ce se parcurge arborele, nodurile vizitate sunt salvate
ntr-o structur de tip stiv; aceast operaie suplimentar este
necesar pentru a permite reconstruirea n sens invers a drumului
parcurs de la rdcina arborelui;

struct NodeStack
{
AVLNod* Nod;
NodeStack *next;
};
class AVLNodeStack
{
private:
NodeStack * VarfStiva;
public:
AVLNodeStack()
{
VarfStiva=NULL;
}
void PUSH(AVLNod* &NodNou){
NodeStack *elementNou= new NodeStack;
elementNou->Nod = NodNou;
if(this->VarfStiva==NULL){
this->VarfStiva = elementNou;
elementNou->next=NULL;
}
else
{
elementNou->next = this->VarfStiva;
this->VarfStiva = elementNou;
}
}
AVLNod* POP(){
if(this->VarfStiva==NULL)
return NULL;
else
{
NodeStack *elementSters = this->VarfStiva;
AVLNod* NodAuxiliar = this->VarfStiva->Nod;
this->VarfStiva = this->VarfStiva->next;
delete elementSters;
return NodAuxiliar;
}
}
void AfiseazaStiva()
{
NodeStack *temp = this->VarfStiva;
while(temp!=NULL)
{
printf("\n Nod in stiva este %d",temp->Nod->Info);
temp=temp->next;
}
}
};

nodul se terge n mod asemntor cu operaia asociat arborilor


binari de cutare; dac nodul este frunz se terge efectiv; dac
nodul are un singur fiu, acesta l nlocuiete n structur; dac
nodul are cei doi fii, este nlocuit de nodul cu valoarea cea mai
mare din subarborele drept, metoda Stergere;

int AVLArbore::Stergere(AVLNod*& SubarboreDrept, AVLNodeStack &stiva )


{
if(SubarboreDrept->st)
{
stiva.PUSH(SubarboreDrept);
return AVLArbore::Stergere(SubarboreDrept->st,stiva);
}
else
{
AVLNod * NodSters= SubarboreDrept;
int valoare = SubarboreDrept->Info;
SubarboreDrept = SubarboreDrept->dr;
delete NodSters;
return valoare;
}
}

sunt analizate toate nodurile parcurse i sunt reechilibrate


situaiile de dezechilibru lund n calcul ipotezele de aplicare a
celor patru tipuri de rotaii; operaia de tergere se ncheie n
momentul n care sunt verificate toate locaiile de dezechilibru
posibil; pentru abordarea aleas ca soluie n acest capitol,
operaia se consider ncheiat n momentul n care stiva este
golit;
din analiza metodei Stergere, se observ c n etapa de
identificare a nodului cu valoarea ce mai mare din subarborele
drept, ce va lua locul nodului de ters, este completat de
salvarea n stiva utilizat a nodurilor vizitate; necesitatea acestei
operaii suplimentare este dat de faptul c tergerea unui nod
poate conduce la dezechilibrarea nodurilor superioare aflate pe
drumul de la rdcin la poziia lui; de asemenea, reechilibrarea
unui nod printe poate conduce la generarea unei alte situaii de
dezechilibru; pentru a exemplifica aceast situaie, se ia n
considerare arborele AVL din figura 14.19 n care se terge nodul
cu valoarea 50;
35

47

GE = -1

stiva

35

22

47

GE = -1

GE = 1

16

31

GE = -1

37
GE = -1

GE = 1

50
GE =0

27

32

GE =0

42
GE =0

GE = -1

GE =0

29
GE =0

Figura 14.19 Structur arborescenta de tip AVL


Prin tergerea nodului cu valoarea 50, se obine stiva cu valorile 47 i
35. Din analiza acestor noduri, se observ c arborele AVL, descris n figura
14.20, devine dezechilibrat n nodul cu valoarea 47.

35
35

GE = -1

stiva

35
GE = -2

22

47

GE = -2

GE = 1

22

37

GE =0

GE = 1

16

31

GE = -1

37
GE = -1

GE = 1

16

31

GE = -1

27

32

GE =0

GE =0
GE = -1

42

GE = -1

42
9

GE =0

27

47

GE = 0

32

GE =0

GE =0

29

GE = -1

GE =0

GE =0

29
GE =0

Figura 14.20 Structur arborescenta de tip AVL dezechilibrat


Prin reechilibrare, aplicnd o rotaie simpl la dreapta n pivot, se
obine o nou situaie de dezechilibru n urmtoare valoare din stiv, 35,
figura 14.20. Printr-o rotaie simpl la dreapta n nodul cu valoarea 35
considerat pivot, arborele AVL este reechilibrat. Deoarece stiva a fost golit,
operaie de tergere se consider ncheiat, figura 14.21.
35

GE =0

31

GE = -2

22

GE =0

(2)

22

37

35 GE = -1

GE =0

GE =0

GE = 1

16
16
GE = -1

31

(1)

GE = -1

27

32

42
GE = 0

47

27

GE = -1

32

37 GE =0

GE = 1

GE =0

29

42

47

GE =0
GE =0

GE = -1

GE =0

29

GE =0

GE =0

GE = 0

GE =0

Figura 14.21 Structur arborescenta de tip AVL


Exist cazuri n care prin tergerea unui nod, se ajunge la situaii de
dezechilibru diferite de ipotezele analizate la operaia de inserare. Lund n
considerare arborele AVL din figura 14.22, se propune tergerea nodului cu
valoarea 16.
17

17

20

GE = 2

GE = 1

16

GE = -1

20

20
GE = 0

17
GE = 0

23
GE = -1

GE = 0

GE =0

19

23

19

23

19

GE =0

GE =0

GE =0

GE =0

GE =0

Figura 14.22 tergere din structur arborescenta de tip AVL


Situaia difer de cele ntlnite la inserare prin faptul c n acest
dezechilibru pivotul are un grad de echilibru egal cu +2, iar nodul fiu de pe
direcia dezechilibrului are un echilibru egal cu 0. Soluia acestui
dezechilibru este dat de o rotaie simpl n pivot la stnga.

Din acest motiv, metodele clasei AVLArbore, destinate analizei i


implementrii tipului de rotaie potrivit, sunt modificate n cazul operaiei de
tergere. Cele dou metode descrise anterior , ReechilibrareSubarboreDrept
i ReechilibrareSubarboreStang analizeaz i situaiile particulare n care
nodul de pe direcia dezechilibrului are gradul de echilibru egal cu zero, caz
n care
sunt
apelate
metodele
RotatieSimplaDreaptaStergere i
RotatieSimplaStangaStergere.
void AVLArbore::RotatieSimplaDreaptaStergere(AVLNod * &pivot)
{
AVLNod *FiuStanga = pivot->st;
pivot->st = FiuStanga->dr;
FiuStanga->dr = pivot;
pivot->Echilibru += 1;
FiuStanga->Echilibru += 1;
pivot = FiuStanga;
}
void AVLArbore::RotatieSimplaStangaStergere(AVLNod * &pivot)
{
AVLNod *FiuDreapta = pivot->dr;
pivot->dr = FiuDreapta->st;
FiuDreapta->st = pivot;
pivot->Echilibru -=1;
FiuDreapta->Echilibru -= 1;
pivot = FiuDreapta;
}

Pentru a implementa operaia de tergere, se definete n clasa


AVLArbore metoda Delete.
void AVLArbore::Delete(const int Info)
{
int valTemp;
//definesc stiva nodurilor parcurse
AVLNodeStack stiva;
//se sterge nodul
AVLDelete(this->radacina,Info,stiva);
//se analizeaza nodurile parcurse
AVLNod *temp = stiva.POP();
while(temp!=NULL){
temp->Echilibru = this->CalculeazaEchilibru(temp);
if(temp->Echilibru==2){
AVLNod *parinte = stiva.POP();
if(parinte!=NULL){
if(parinte->dr==temp)
this->ReechilibrareSubarboreDrept(parinte->dr,valTemp);
else
this->ReechilibrareSubarboreDrept(parinte->st,valTemp);
parinte->Echilibru=this->CalculeazaEchilibru(parinte);
}
}

else
if(temp->Echilibru==-2){
AVLNod *parinte = stiva.POP();
if(parinte!=NULL){
if(parinte->dr==temp)
this->ReechilibrareSubarboreStang(parinte->dr,valTemp);
else
this->ReechilibrareSubarboreStang(parinte->st,valTemp);
parinte->Echilibru=this->CalculeazaEchilibru(parinte);
}
}
temp=stiva.POP();
}
}

Aceast metod se bazeaz pe apelul metodei AVLDelete pentru a


realiza tergerea efectiv a nodului dorit, secvena de cod asociat fiind
concentrat pe analiza nodurilor din stiva. Pentru fiecare din acestea, se
recalculeaz
gradul
de
echilibru
prin
intermediul
metodei
CalculeazaEchilibru.
int AVLArbore::CalculeazaEchilibru(AVLNod *& radacina)
{
return inaltime(radacina->dr) - inaltime(radacina->st);
}

Metoda AVLDelete completeaz metoda ntlnit la tergerea


nodurilor din arbori binari de cutare prin gestiunea unei stive n care sunt
inserate toate valorile ntlnite.
void AVLArbore::AVLDelete(AVLNod* &arbore,const int Info,AVLNodeStack
&stiva){
AVLNod *NodAuxiliar;
if(arbore){
if(Info == arbore->Info){
NodAuxiliar = arbore;
if(!NodAuxiliar->dr){
arbore = NodAuxiliar->st;
delete NodAuxiliar;
}
else
if(!NodAuxiliar->st){
arbore = NodAuxiliar->dr;
delete NodAuxiliar;
}
else{
stiva.PUSH(arbore);
arbore->Info = AVLArbore::Stergere(arbore->dr,stiva);
}
}
else
if(Info < arbore->Info){
stiva.PUSH(arbore);
AVLDelete(arbore->st,Info,stiva);
}
else{
stiva.PUSH(arbore);
AVLDelete(arbore->dr,Info,stiva);
}

} }

n ciuda efortului asociat implementrii i executrii secvenelor de rotire


ale structurii, arborii AVL ofer un ridicat nivel de eficien n ceea ce
privete procesul de cutare n arbori binari de cutare. Structura
arborescent echilibrat

14.4 Caracteristici ale arborilor Rou & Negru


Arborii Rou & Negru reprezint o alt tipologie de arbori binari de
cutare echilibrai, fiind prima dat definii de Rudolf Bayer n 1972 sub
forma de arbori simetrici. Asemenea arborilor AVL, aceast structur este
caracterizat de o complexitate a operaiei de cutare egal cu O(log n), n
fiind numrul de noduri din arbore, datorit modului n care nodurile sunt
plasate n mod simetric n subarborii stngi sau drepi.
Spre deosebire de arborele AVL, n care principala caracteristic se
determin pe baza gradului de echilibru al fiecrui nod, n structurile
arborescente de tip Rosu & Negru, factorul cel mai important este dat de
culoarea fiecrui nod:
- fiecare nod are una dintre cele dou culori, rou sau negru;
- nodul rdcin este ntotdeauna negru;
- ambele noduri fiu ale unui nod printe rou sunt negre; un nod
rou nu poate avea ca printe dect un nod negru;
- toate drumurile de la rdcin la oricare din nodurile frunz conin
acelai numr de noduri negre.
Analiznd aceste caracteristici sunt derivate proprieti noi care s fie
utilizate n implementarea algoritmilor sau care s evidenieze eficiena
acestui tip de structur fa de un arbore binar de cutare:
- n arborele Rou & Negru nu exist pe un drum dou noduri
adiacente de culoare roie deoarece orice nod rou are ambii fii
de culoare neagr;
- dac se consider c cel mai scurt drum din arbore are numai
noduri negre n numr de k, atunci cel mai lung drum din arbore
are maxim dublu noduri, 2 * k; ipoteza este demonstrat pe baza
faptului c toate drumurile din acest tip de structur au acelai
numr de noduri negre, fapt care conduce la concluzia c drumul
cel mai lung poate fi format doar din perechi de noduri adiacente
de culori opuse, figura 14.23.
15

negru

rou

rou
23

3
negru

negru
2

18

negru
27

Figura 14.23 Structur arborescenta de tip Rou & Negru

Pentru a facilita implementarea operaiilor cu structuri arborescente


de tip Rosu & Negru se propune o structur asociat nodului, clasa RNNode
class RNNod
{
int Info;
bool Culoare;
RNNod *st;
RNNod *dr;
RNNod *parinte;
};

Elementele de tip RNNode includ pe lng atributele ntlnite la toate


structurile arborescente binare:
- informaia util;
- cele dou legturi ctre nodurile fiu din stnga, respectiv,
dreapta;
i informaia ce descrie culoare nodului, precum i o legtur suplimentar
ctre nodul printe. Aceast abordare contribuie la implementarea mult mai
facil a operaiilor de inserare sau tergere, minimiznd n timp real efortul
de a identifica nodul printe al nodului curent.

14.5 Operaii pe arbori Rou & Negru


Operaiile pe arborii Rou i Negru descrise, inserare i tergere, sunt
realizate asemenea arborilor binari de cutare deoarece acest tip de arbore
este o structur binar particular. Asigurarea caracteristicilor specifice
acestui tip de structur arborescent este realizat printr-o serie de operaii
auxiliare i complementare procesului de inserare sau tergere ce constau n
rotiri sau modificri de culoare.
Pentru a descrie metodele specifice operaiilor se definete ca nod
bunic al nodului nou creat, nodul ce se gsete pe al doilea nivel superior
fa de nodul analizat, figura 14.24. Se definete ca nod unchi al nodului
analizat, al doilea nod fiu al nodului bunic.

15

nod bunic

nod printe

nod unchi
23

3
nod analizat
2

18

27

Figura 14.24 Relaii ntre noduri Rou & Negru

Pentru a determina poziia acestor noduri particulare este utilizat


atributul RNNod *parinte al fiecrui obiect de tip RNNod. De exemplu nodul
bunic al nodului curent este determinat prin expresia NodCurent->parinte>parinte, iar nodul unchi este dat de NodCurent->parinte->parinte->st sau
NodCurent->parinte->parinte->st n funcie de poziia acestuia relativ la
nodul printe al nodului curent.
Operaia de inserare este analizat prin prisma cazurilor particulare.
Acestea sunt definite de contextul n care se gsete nodul nou creat i de
situaiile de dezechilibru aprute.
Fiecare nod nou creat i inserat n structura arborescent de tip Rou
i Negru are culoarea iniial roie. Astfel se ncearc evitarea situaie n
care este nclcat proprietatea c toate drumurile din arbore au acelai
numr de noduri negre.
Se consider arborele Rou & Negru vid n care se insereaz valoarea
43. prin inserare se obine structura arborescent din figura 14.25 ce
trebuie reechilibrat prin modificarea culorii nodului rdcin n negru.
Astfel nodul rdcin este negru.

reechilibrare prin modificare


culoare
43

43

nod rou

nod negru

Figura 14.25 Arbore Rou & Negru cu un singur nod


n arborele analizat se insereaz valorile 25 i 78. Nodurile nou create
au culoare roie, figura 14.26 i nu este nclcat nici o proprietate a
arborilor.

43

25

78

Figura 14.26 Arbore Rou & Negru echilibrat


Se ia n considerare situaia n care se insereaz nodul cu valoarea
14. Nodul nou creat este rou, fapt care ncalc proprietile arborilor Rou
& Negru, deoarece un nod rou are ntotdeauna un nod negru ca printe.
Dac nodul este recolorat n negru atunci toate drumurile din arbore nu vor
avea acelai numr de noduri negre. Situaia este analizat prin prisma
nodului printe i a nodului unchi. Dac aceste dou noduri sunt roii atunci
ele i schimb culoarea n negru, iar nodul bunic, printele celor dou
noduri, devine negru, figura 14.27. Dac prin modificarea culorii nodului
bunic, arborele este dezechilibrat atunci situaia este remediat n manier
recursiv pn se ajunge la rdcina arborelui.

43

25

reechilibrare prin modificare


culoare
78

43
dezechilibru

78

25

14

14

dezechilibru

nod bunic
43
nod printe

nod unchi
25

78

nod nou
14

Figura 14.27 Arbore Rou & Negru reechilibrat


n cazul arborelui din figura 14.27, nodul rdcin devine negru la
pasul urmtor, structura fiind reechilibrat.
Se consider exemplul dat de inserarea valorii 17. Nodul nou are
culoare roie, fapt ce ncalc proprietatea acestui tip de arbore, toate
nodurile fiu ale unui nod rou sunt negre, figura 14.28.
43
nod bunic
25

78

nod printe
14

nod NULL

17

dezechilibru

nod nou

Figura 14.28 Arbore Rou & Negru dezechilibrat


Reechilibrarea arborelui n aceast situaie este realizat printr-o
dubl rotaie. ntr-o prim faz, se realizeaz o simpl rotaie la stnga n
nodul printe. Ipoteza de lucru este definit de faptul c:
- nodul printe are culoare roie, dar nodul unchi este fie negru, fie
nod NULL;
- nodul nou creat este fiu dreapta pentru nodul printe, care la
rndul su este nod fiu stnga pentru nodul bunic.
Rotaia este realizat asemenea arborilor AVL considernd pivot,
nodul printe. Dup aceast prima rotaie se obine arborele din figura
14.29.

43

43

nod bunic

nod bunic
78

25

78

25

nod printe
14

nod NULL

17

nod NULL

nod printe nou


dezechilibru

17
nod nou

14
dezechilibru

Figura 14.29 Arbore Rou & Negru dezechilibrat


Structura arborescent este dezechilibrat prin prisma aceleiai
proprieti nclcate. Din acest motiv este necesar o a doua operaie de
rotaie ce are ca pivot, nodul bunic. De data aceasta, rotaie se realizeaz la
dreapta, avnd sens opus cu direcia nodului fiu fa de nodul printe.
Ipoteza de lucru este definit de condiiile:
- nodul printe are culoare roie, dar nodul unchi este fie negru, fie
nod NULL;
- noul nod printe este fiu stnga pentru nodul bunic i nodul nou
inserat este fiu stnga pentru acesta.
Rotaia descris n figura 14.30 este nsoit i de o recolorare a
nodurilor, astfel nct nodul bunic devine rou i noul nod printe devine
negru.
43

43

nod bunic
25

17

78

nod NULL

nod printe nou

78

17

14

25
nod bunic vechi

nod NULL

14
dezechilibru

Figura 14.30 Arbore Rou & Negru reechilibrat


n cazul n care, se insera valoarea 10 atunci erau atinse condiiile
implementrii celei de a doua operaie de rotaie fiind evitat prima rotaie
la stnga.
Dac nodul nou are ca printe un nod de culoare roie i acesta este
fiul din dreapta al nodului bunic, atunci situaia reprezint imaginea n
oglind a cazului anterior.
De exemplu, se insereaz valorile 89 i 95 n aceast ordine. Figura
14.31 descrie pii parcuri pentru reechilibrarea arborelui.

43

43
nod bunic

89

17

78

17

nod parinte
14

89

25

nod nou
dezechilibru

14

78

25

95

95

Figura 14.31 Reechilibrare arbore Rou & Negru


Situaia descris anterior este condiionat de atingerea urmtoarelor
condiii de lucru:
- nodul printe are culoare roie, dar nodul unchi este fie negru, fie
nod NULL;
- nodul printe este fiu dreapta pentru nodul bunic i nodul nou
inserat este fiu dreapta pentru acesta.
n cazul n care ultima condiie nu este ndeplinit, noul nod fiind fiu
stnga, situaia este ajustat prin operaia de rotire la dreapta n nodul
printe.
Operaia de tergere n arbori Rou i Negru completeaz procesul
ntlnit la arborii binari de cutare prin operaii specifice de recolorare sau
rotire a nodurilor astfel nct s fie pstrate caracteristicile acestei structuri
arborescente.
n cazul n care nodul de ters are dou noduri fiu atunci acesta este
nlocuit de nodul cu valoarea cea mai mare din subarborele stng sau de
nodul cu valoarea cea mai mic din subarborele drept. Copierea de valoarea
este nsoit de pstrarea culorii nodului ters astfel nct s nu fie afectat
arborele. Oricare variant se alege, nodul care va nlocui nodul de ters este
la rndul su eliminat din structura arborescent. Acesta este fie nod
frunz, fie are maxim un fiu. De exemplu, se terge nodul cu valoarea 43
din arborele descris n figura 14.32.
nod de ters

78

43

nlocuire cu cel mai mic nod


din subarborele drept

14

25

78

89

17

89

17

95

14

Pstrare
culoare

25

95

Figura 14.32 tergere nod din arbore Rou & Negru


Prin prisma exemplului anterior, problemele aprute la tergerea
unui nod dintr-un arbore Rou i Negru sunt concentrate n cazurile
de tergerea unui nod care are maxim un fiu.
Dac nodul de ters este de culoare roie, figura 14.30, atunci nodul
su fiu este de culoare neagr, aceasta fiind o caracteristic a arborilor Rou
i Negru. tergerea nodului implic n aceast situaie nlocuirea sa cu nodul

fiu. Arborele este n continuare Rou i Negru deoarece tergerea unui nod
rou nu are implicaii asupra numrului de noduri negre de pe fiecare drum.
43

43

nlocuire cu unicul fiu al


nodului de ters

nod de ters

89

17

14

89

78

14

78

15

15

Figura 14.33 Cazul 1 de tergere nod din arbore Rou & Negru
Dac nodul ters este de culoare neagr, iar fiul su este de culoare
roie, figura 14.34, atunci arborele devine dezechilibrat pe drumul care
trece prin aceast zon deoarece numrul de noduri negre este mai mic cu
unul. Reechilibrarea structurii arborescente se face n aceast situaie prin
recolorarea n negru a nodului fiu. Astfel este refcut numrul de noduri
negre.
43

43

nlocuire cu unicul fiu al


nodului de ters

nod de ters

79

17

79

14
recolorat in
negru

54

14

15

48

83

59

80

54

15

48

83

59

80

Figura 14.34 Cazul 2 de tergere nod din arbore Rou & Negru
Situaiile complexe aprute la tergerea unui nod dintr-un arbore de
tip Rou i Negru sunt apar n cazul n care nodul de ters i fiul su sunt de
culoare neagr. Prin eliminarea nodului, arborele devine dezechilibrat
deoarece o parte din drumuri conin cu un nod negru mai puin. Spre
deosebire de cazurile prezentate anterior, nu mai este posibil refacerea
numrului de noduri negre prin recolorarea fiului deoarece acesta are deja
culoarea neagr. Reechilibrarea arborelui este realizat printr-un numr fix
de operaii de rotire sau recolorare.
Pentru a descrie aceste cazuri particulare de dezechilibru i soluiile
asociate, se fac o serie de notaii care s ajute nelegerea operaiilor, figura
14.35. Se noteaz cu:
- P, nodul printe al nodului de ters;
- F, nodul fiu al nodului de ters;
- B, nodul bunic al nodului de ters; acest nod este nodul printe al
nodului P;
- U, nodul unchi al nodului de ters; acest nod este reprezentat de
al doilea fiu al nodului B;

N1 nodul nepot al nodului de ters; este reprezentat de fiul din


stnga al nodului unchi;
N2 nodul nepot al nodului de ters; este reprezentat de fiul din
dreapta al nodului unchi;
B

nod bunic

32

nod printe
P

nod unchi
U

79

25
nod nepot

nod de ters

N1

14

nod fiu

17

45

N2

54

58

89

81

Figura 14.35 Arbore Rou & Negru


Urmtorul caz analizat este dat de figura 14.36 n care nodul cu valoarea
25 este ters. Situaia este descris de ipotezele:
- nodul de ters este negru;
- unicul fiu al nodului de ters este negru;
- nodul unchi al nodului de ters este negru;
- nodul printe este negru;
- nodurile nepoi sunt negre.
B

B
P

32

nod de ters

10

N1

14

17

79

25

45

dezechilibru

N2

54

58

89

81

10

32
U

79

14

17

N1

45

N2

54

58

89

81

Figura 14.36 Cazul 3 de tergere nod din arbore Rou & Negru
Prin tergerea nodului cu valoarea 25, arborele sau subarborele
analizat ce are rdcin pe nodul cu valoarea 32 este dezechilibrat la
dreapta deoarece drumurile care pornesc din rdcin i continu pe partea
stng au cu un nod negru mai puin. Reechilibrarea arborelui se realizeaz
prin modificarea culorii nodului unchi, valoarea 79, n rou, figura 14.37.
Astfel, este redus cu unu numrul de noduri negre din drumurile ce pornesc
din rdcina 32.

B
P

dezechilibru

79

14

10

N1

17

modificare culoare nod unchi

32

N2

54

45

58

89

32
U

79

14

10

N1

17

81

N2

54

45

58

89

81

Figura 14.37 Soluie de reechilibrare caz 3 pentru arbore Rou & Negru
n cazul n care, nodul cu valoarea 32 reprezint rdcina unui
subarbore, analiza se continu n sus pn cnd se atinge rdcina arborelui
sau pn cnd arborele este reechilibrat pe baza unei soluii din cele
descrise.
Al patrulea caz de tergere a unui nod dintr-un arbore Rou i Negru
ia n considerare situaia descris n figura 14.38:
- nodul de ters este negru;
- unicul fiu al nodului de ters este negru;
- nodul unchi al nodului de ters este negru;
- nodul printe este rou;
- nodurile nepoi sunt negre.
B

B
P

22

nod de ters

N1

69

15

45

dezechilibru

N2

54

58

89

81

22
U

69

N1

45

N2

54

58

89

81

Figura 14.38 Cazul 4 de tergere nod din arbore Rou & Negru
Asemenea cazului anterior, arborele i pierde calitatea de a fi Rou i
Negru n urma tergerii deoarece nu toate drumurile de la rdcin la
nodurile frunz au acelai numr de noduri negre. Reechilibrarea este
realizat prin interschimbarea culorilor nodului printe i nodului unchi,
figura 14.39.

B
P

dezechilibru
F

B
P

22
U

N1

N2

54

45

58

69

89

22

69

N1

81

N2

54

45

58

89

81

Figura 14.39 Soluie de reechilibrare caz 4 pentru arbore Rou & Negru
n situaia n care arborele din figura 14.34 reprezint un subarbore
atunci soluia de reechilibrare prezentat are doar efecte locale, deoarece
lungimea msurat n numr de noduri negre a tuturor drumurilor din acest
subarbore este mai mic cu unu fa de situaia iniial. Din acest motiv,
reechilibrarea se continu recursiv ctre rdcina arborelui.
Cazul al cincilea de tergere a unui nod ia n considerare ipotezele
descrise n figura 14.40:
- nodul de ters este negru;
- unicul fiu al nodului de ters este negru;
- nodul unchi al nodului de ters este rou;
- nodul printe este negru;
- nodurile nepoi sunt negre.
B

B
P

32

nod de ters

10

N1

14

17

79

25

45

dezechilibru

N2

54

58

89

81

10

32
U

79

14

17

N1

45

N2

54

58

89

81

Figura 14.40 Cazul 5 de tergere nod din arbore Rou & Negru
Reechilibrarea arborelui pentru cazul 5 de dezechilibru se realizeaz
prin interschimbarea culorilor nodului unchi i nodului printe, urmat de o
rotaie la stnga n nodul printe, figura 14.41.

B
P

32

14

10

N1

17

dezechilibru

79

(2)

N2

54

45

interschimb culori (1) + rotire (2)

(1)

58

89

81

N2

32

N1

14

10

79

17

81

54

45

89

58

Figura 14.41 Soluie de reechilibrare caz 5 pentru arbore Rou & Negru
Analiznd figura 14.41 se observ c soluia cazului 5 nu conduce la
reechilibrarea total a arborelui. Zona de dezechilibru este modificat astfel
nct s poat fi reechilibrat ntr-un numr finit de pai. Aceast este
analizat prin prisma cazului patru care a fost descris sau prin intermediul
cazurilor ase i apte. De exemplu, arborele obinut n figura 14.41 este
reechilibrat, n figura 14.42 prin intermediul soluie oferite n cazul patru,
interschimbnd culorile nodului cu valoare 32 i nodului cu valoarea 54.
B
U
dezechilibru

10

17

N2

N1

45

Interschimbare culori

79

32

14

89
F

81

54

58

10

79

N2

32

N1

14

17

45

89

81

54

58

Figura 14.42 Reechilibrare arbore Rou & Negru din figura 14.41
Urmtoarele dou cazuri analizeaz culoarea nodurilor nepoi lund n
calcul situaii derivate din cazul patru.
Cazul ase, descris n figura 14.43, este definit de urmtoarele
ipoteze:
- nodul de ters este negru;
- unicul fiu al nodului de ters este negru;
- nodul unchi al nodului de ters este negru;
- nodul printe este rou sau negru;
- nodul nepot N1 este rou;
- nodul nepot N2 este negru.

B
P

22

nod de ters

N1

N2

54

45

58

89

22
U

69

15

dezechilibru

69

N1

45

81

N2

54

58

89

81

Figura 14.43 Cazul 6 de tergere nod din arbore Rou & Negru
Reechilibrarea arborelui din figura 14.43 este realizat prin:
- interschimbarea culorilor nodului printe i a nodului unchi;
- rotirea subarborelui cu rdcin n nodul unchi la dreapta.
B
P

dezechilibru
F

22

B
interschimb culori (1) + rotire (2)
U

69

(1)

dezechilibru

22

N1

54

N2

N1

45

54

89

(2)

58

81

69

45

58

N2

89

81

Figura 14.44 Soluie de reechilibrare caz 6 pentru arbore Rou & Negru
Rezultatul obinut n urma operaiei de schimbare a culorii i de rotire
nu conduce la reechilibrarea arborelui. Cu toate acestea, noua form a
subarborelui permite reechilibrarea la pasul urmtor, deoarece situaia
curent descrie cazul apte .
Cazul ase, descris n figura 14.45, este definit de urmtoarele
ipoteze:
- nodul de ters este negru;
- unicul fiu al nodului de ters este negru;
- nodul unchi al nodului de ters este negru;
- nodul printe este rou sau negru;
- nodul nepot N1 este rou sau negru;
- nodul nepot N2 este rou.

B
P

22

nod de ters

N1

N2

54

45

58

89

22
U

69

15

dezechilibru

69

N1

45

81

N2

54

58

89

81

Figura 14.45 Cazul 7 de tergere nod din arbore Rou & Negru
Reechilibrarea situaie descrise n figura 14.46 se realizeaz prin:
- interschimbare culoare nod printe cu nodul unchi;
- rotire la stnga a arborelui n nodul printe;
- modificare culoare nepot N2 n negru.
B
P

dezechilibru
F

22

(1)

interschimb culori (1) + rotire (2)+


schimbare culoare(3)
U

N1

45

22
(3)

N2

54

58

69

(2)

69

89

81

N2
N1

45

54

89

81

58

Figura 14.46 Soluie de reechilibrare caz 7 pentru arbore Rou & Negru
Figura 14.46 prezint rezultatul obinut n urma reechilibrrii. Se
observ eliminarea dezechilibrului din acest arbore sau subarbore.
Pentru exemplele analizate n acest capitol s-a considerat c nodul de
ters se gsete n partea stng a nodului printe. Pentru situaia opus,
soluiile descrise au aceleai efect dac sufer mici modificri prin prisma
noului reper de vizualizare a arborelui.
De asemenea, n exemplele prezentate reechilibrarea arborelui are un
caracter local pentru a descrie tehnicile de reechilibrare, ns realizare unei
aplicaii trebuie s implementeze secvene care s parcurg arborele de jos
n sus, de la poziia nodului de ters ctre rdcina arborelui i care s
reechilibreze toat structura.

15. HEAP

15.1 Structura de tip Heap


Heap-ul, figura 15.1, este un arbore binar care respect proprietile
de structur i de ordonare. Fiecare nod al arborelui trebuie s conin o
valoare asociat numit cheie i poate conine alte informaii suplimentare.
Cheia trebuie s permit definirea unei relaii de ordine total pe mulimea
nodurilor. n funcie de obiectivul urmrit, heap-ul poate fi organizat sub
form de max-heap sau min-heap. Cele dou tipuri de heap sunt
echivalente. Transformarea unui max-heap n min-heap, sau invers, se
poate realiza prin simpla inversare a relaiei de ordine.
32

24

12

23

17

Figura 15.1 Reprezentarea grafic a structurii heap


Proprietatea de structur specific faptul c elementele sunt
organizate sub forma unui arbore binar complet. Prin arbore binar complet
nelegem un arbore binar n care toate nodurile, cu excepia celor de pe
ultimul nivel, au exact doi fii, iar nodurile de pe ultimul nivel sunt
completate de la stnga la dreapta.
Proprietatea de ordonare impune ca valoarea asociat fiecrui nod, cu
excepia nodului rdcin, s fie mai mic sau egal dect valoarea asociat
nodului printe. Se observ c, spre deosebire de arborii binari de cutare,
nu se impune nici o regul referitoare la poziia sau relaia dintre nodurile
fiu.
Structura heap este preferat pentru multe tipuri de aplicaii. Cele
mai importante utilizri sunt:
implementarea cozilor de prioritate utilizate pentru simularea pe
baz de evenimente sau algoritmi de alocare a resurselor;
implementarea seleciei n algoritmi de tip greedy cum ar fi
algoritmul lui Prim pentru determinarea arborelui de acoperire
minim sau algoritmul lui Dijkstra pentru determinarea drumului
minim;
sortarea masivelor utiliznd algoritmul HeapSort.
Operaiile principale care se execut pe o structur heap sunt cele de
construire a heap-ului pornind de la un masiv unidimensional oarecare,
inserarea unui element n structur i extragerea elementului maxim sau
minim pentru un min-heap.

Construirea structurii se face utiliznd o procedur ajuttoare numit


procedur de filtrare. Rolul acesteia este de a transforma un arbore n care
doar subarborii rdcinii sunt heap-uri ale cror nlime difer cu cel mult o
unitate ntr-un heap prin coborrea valorii din rdcin pe poziia corect.
Structura rezultat n urma aplicrii procedurii de filtrare este un heap
(respect proprietile de structur i ordonare).
23

32

32

12

24

17

23

12

a) Situaia iniial

24

17

b) Arborele dup aplicarea primului pas


32

24

Subarbori organizai sub


form de heap (respect
proprietile de structur i
ordonare)

12

23

17

c) Arborele la sfritul procedurii de filtrare

Figura 15.2 Aplicarea algoritmului de filtrare


Algoritmul de filtrare, figura 15.2, presupune parcurgerea
urmtoarelor etape ncepnd cu nodul rdcin:
se determin maximul dintre nodul curent, fiul stnga i fiul
dreapta (dac exist).
dac maximul se afl n nodul curent, atunci algoritmul se oprete.
dac maximul de afl ntr-unul dintre fii, atunci se interschimb
valoarea din nodul curent cu cea din fiu i se continu execuia
algoritmului cu nodul fiu.
Construirea heap-ului pornind de la un arbore binar care respect
doar proprietatea de structur se face aplicnd procedura de filtrare pe
nodurile non frunz ale arborelui ncepnd cu nodurile de la baza arborelui
i continund n sus pn cnd se ajunge la nodul rdcin. Corectitudinea
algoritmului este garantat de faptul c la fiecare pas subarborii nodului
curent sunt heap-uri (deoarece sunt noduri frunz sau sunt noduri pe care a
fost aplicat procedura de filtrare).
Inserarea elementelor ntr-un heap se poate face i dup etapa
iniial de construcie. n acest caz adugarea elementului nou trebuie
fcut astfel nct structura rezultat s pstreze proprietatea de ordonare.
Inserarea unui element, figura 15.3, n heap presupune parcurgerea
urmtoarelor etape:

se adaug elementul ca nod frunz la sfritul arborelui pentru a


pstra proprietatea de structur;
se compar nodul curent cu nodul printe;
dac nodul printe este mai mic se interschimb nodul curent cu
nodul printe;
dac nodul printe este mai mare sau egal atunci algoritmul se
oprete.

Figura 15.3 Aplicarea algoritmului de inserare


Procedura prezentat permite inserarea rapid a oricrei valori n
cadrul heap-ului cu pstrarea proprietilor de structur i de ordonare.
tergerea elementelor dintr-un heap se poate efectua doar prin
extragerea elementului maxim sau minim n cazul unui min-heap. Pentru
pstrarea structurii de heap se utilizeaz procedura de filtrare prezentat
anterior. Algoritmul de extragere al elementului maxim, figura 15.4,
presupune parcurgerea urmtoarelor etape:
se interschimb valoarea din nodul rdcin cu valoarea din
ultimul nod al arborelui;
se elimin ultimul nod din arbore;
se aplica procedura de filtrare pe nodul rdcin pentru a pstra
proprietatea de ordonare;
se returneaz valoarea din nodul eliminat.

Figura 15.4 Aplicarea algoritmului extragere element maxim


Operaiile prezentate n seciunea 15.1 permit utilizarea structurii
heap ca punct de plecare pentru implementarea eficient a cozilor de
prioritate i a algoritmului de sortare HeapSort.

15.2 Implementarea structurii Heap


Dei este posibil implementarea structurii heap folosind arbori
binari, datorit particularitilor arborelui de tip heap, stocarea eficient a
acestuia poate realizeaz fr ajutorul pointerilor, folosind un masiv
unidimensional. Elementele arborelui se stocheaz n masiv ncepnd cu
nodul rdcin i continund cu nodurile de pe nivelurile urmtoare preluate
de la stnga la dreapta. Reprezentarea sub form de masiv a heap-ului din
figura 15.1 este prezentat n figura 15.5.
32

24

12

23

17

Figura 15.5 Reprezentarea n memorie pentru structura heap


Navigarea ntre elementele arborelui se poate face n ambele direcii
folosind urmtoarele formule:

i 1
Parinte(i )
, Stnga (i ) 2 i 1, Dreapta(i ) 2 i 2
2

(15.1)

Pentru implementarea structurii heap n limbajul C++ a fost


construit o clas template denumit Heap care implementeaz algoritmii
prezentai anterior. Clasa permite construirea de heap-uri pentru orice tip
de date care implementeaz operatorul < (necesar pentru construirea
relaiei de ordine pe mulimea nodurilor) i un constructor implicit.
Interfaa clasei este urmtoarea:
template <class T>
class Heap
{
public:
// --- Constructori i destructor --- //
// Construire heap nou (cu dimensiunea specificat)
Heap(int capacitateInitiala = DimensiuneMinima);
// Construire heap pe baza unor elemente date
Heap(T elemente[], int numarElemente);
// Constructor de copiere
Heap(Heap& heap);
~Heap() { delete [] this->elemente; }
// --- Metode coada prioritate

--- //

// Insereaz un element n structur


void Insereaza(T element);
// Intoarce elementul maxim si il elimina din structura
T ExtrageMaxim();
// Intoarce elementul maxim dar nu il elimina din structura
T CitesteMaxim();
// --- Acces la elemente --- //
int GetNumarElemente() { return this->dimensiuneHeap; }
T operator[] (int index) { return this->elemente[index]; }
private:
// --- Datele structurii --- //
T* elemente;

// pointer la vectorul de elemente

// Invariant: dimensiuneHeap <= memorieAlocata


int memorieAlocata; // dimensiunea mem alocate (ca nr de elem)
int dimensiuneHeap; // nr de elemente existente n heap
static const int DimensiuneMinima = 10;
// --- Functia de reordonare a elementelor --- //
void Filtrare(int index);
// --- Calculare pozitie elemente --- //

inline int Parinte(int i)


{
// <=> i / 2 cu indici pe 1..n
return (i - 1) / 2;
}
inline int Stanga(int i)
{
// <=> i * 2 cu indici pe 1..n
return 2 * i + 1;
}
inline int Dreapta(int i)
{
// <=> i * 2 + 1 cu indici pe 1..n
return 2 * i + 2;
}
inline void Interschimb(int index1, int index2)
{
T temp = this->elemente[index1];
this->elemente[index1] = this->elemente[index2];
this->elemente[index2] = temp;
}
//--- Metode folosite pt realocarea dinamica a structurii --//
void CresteMemoriaAlocata();
void ScadeMemoriaAlocata();

};

// Nu permitem operatii de atribuire intre obiecte de tip Heap


Heap operator= (const Heap&);

Se observ c, n afara datelor efective stocate n masiv, este


necesar stocarea a dou informaii suplimentare:
dimensiunea memoriei alocate pentru structur;
numrul de elemente prezente efectiv n structur.
Memoria necesar stocrii masivului este alocat dinamic i
gestionat automat de ctre clasa Heap prin intermediul metodelor
CresteMemoriaAlocata i ScadeMemoria alocat:
template <class T>
void Heap<T>::CresteMemoriaAlocata()
{
// alocm un vector nou de dimensiune dubl i copiem elem
T* masivNou = new T[this->memorieAlocata * 2];
for (int i = 0; i < this->memorieAlocata; i++)
{
masivNou[i] = this->elemente[i];
}
// nlocuim vectorul existent cu cel nou i actualizm dim
delete [] this->elemente;
this->elemente = masivNou;
}

this->memorieAlocata = this->memorieAlocata * 2;

template <class T>


void Heap<T>::ScadeMemoriaAlocata()

// alocm un vector nou mai mic i copiem elementele


T* masivNou = new T[this->memorieAlocata / 2];
for (int i = 0; i < this->memorieAlocata / 2; i++)
{
masivNou[i] = this->elemente[i];
}
// nlocuim vectorul existent cu cel nou i actualizm dim
delete [] this->elemente;
this->elemente = masivNou;

this->memorieAlocata = this->memorieAlocata / 2;

Memoria alocat de ctre instanele clasei este dealocat de ctre


destructor. Pentru a evita cazul n care dou instane ale clasei Heap conin
referine ctre aceleai elemente a fost interzis operaia de atribuire prin
suprancrcarea privat a operatorului corespunztor.
Pentru construirea structurii heap pe baza unui masiv oarecare i
pentru asigurarea proprietii de ordonare n cazul metodei de extragere
maxim a fost implementat metoda ajuttoare Filtrare conform algoritmului
prezentat n seciunea 15.1:
template <class T>
void Heap<T>::Filtrare(int index)
{
// 1. Determinam maximul dintre elemente[index],
// elemente[Stanga(index)] si elemente[Dreapta(index)]
int indexMax = index;
int indexStanga = this->Stanga(index);
int indexDreapta = this->Dreapta(index);
if (indexStanga < this->dimensiuneHeap &&
this->elemente[indexStanga] > this->elemente[indexMax])
{
indexMax = indexStanga;
}
if (indexDreapta < this->dimensiuneHeap &&
this->elemente[indexDreapta] > this->elemente[indexMax])
{
indexMax = indexDreapta;
}

// 2. Daca varful actual nu respecta prop de ordonare atunci


// coboram nodul in arbore si reapelam recursiv procedura
if (index != indexMax)
{
this->Interschimb(index, indexMax);
this->Filtrare(indexMax);
}

Constructorii clasei Heap realizeaz iniializarea structurii n trei


cazuri distincte:
construirea unei structuri noi cu o capacitate iniial specificat
opional de ctre utilizator;

construirea structurii noi pe baza elementelor unui masiv


unidimensional primit ca parametru, folosind algoritmul de filtrare
prezentat n seciunea 15.1;
construirea unui heap prin copierea datelor dintr-o structur heap
existent.
Implementarea constructorilor n cadrul clasei Heap este urmtoarea:
template <class T>
Heap<T>::Heap(int capacitateInitiala)
{
// ne asigurm c dimensiunea iniial a structurii este
// cel puin dimensiunea minim
capacitateInitiala = max(capacitateInitiala, DimensiuneMinima);

// alocm memoria pentru elemente i iniializm cmpurile


this->elemente = new T[capacitateInitiala];
this->memorieAlocata = capacitateInitiala;
this->dimensiuneHeap = 0;

template <class T>


Heap<T>::Heap(T elemente[], int numarElemente)
{
// iniializm cmpurile
this->memorieAlocata =
max(numarElemente, DimensiuneMinima);
this->dimensiuneHeap = numarElemente;
// copiem elementele din vector n structur
this->elemente = new T[this->memorieAlocata];
for (int i = 0; i < numarElemente; i++)
{
this->elemente[i] = elemente[i];
}

// Rearanjm elem a.. s satisfac prop de ordonare folosind


// metoda Filtrare pt coborrea elem n arbore (elementele din
// a doua jumatate a masivului respecta implicit proprietatea
// de heap deoarece reprezinta subarbori cu maxim un element)
for (int i = (numarElemente - 1) / 2; i >= 0; i--)
{
this->Filtrare(i);
}

template <class T> Heap<T>::Heap(Heap& heap)


{
this->memorieAlocata = heap->memorieAlocata;
this->dimensiuneHeap = heap->dimensiuneHeap;

this->elemente = new T[this->memorieAlocata];


for (int i = 0; i < numarElemente; i++)
{
this->elemente[i] = heap->elemente[i];
}

Medodele de inserare i extragere nod au fost implementate conform


algoritmilor prezenzaii n seciunea 15.1.

template <class T>


void Heap<T>::Insereaza(T element)
{
// verificam faptul c avem memorie disponibil
if (this->dimensiuneHeap == this->memorieAlocata)
{
this->CresteMemoriaAlocata();
}
// expandam heap-ul
this->dimensiuneHeap++;
// adaugm elementul nou la sfritul heap-ului
int index = this->dimensiuneHeap - 1;
this->elemente[index] = element;

// si l urcm n arbore att ct este cazul


// pentru a pstra proprietatea de heap
while (this->Parinte(index) >= 0 &&
this->elemente[index] > this->elemente[Parinte(index)])
{
this->Interschimb(index, this->Parinte(index));
index = this->Parinte(index);
}

template <class T>


T Heap<T>::ExtrageMaxim()
{
// ne asigurm c avem cel puin un element n heap
assert(this->dimensiuneHeap > 0);
// scdem memoria alocat dac este cazul
if (this->memorieAlocata > DimensiuneMinima * 2 &&
this->dimensiuneHeap < this->memorieAlocata / 3)
{
this->ScadeMemoriaAlocata();
}
//mutm elementul n afara heap-ului i refacem struct de heap
this->dimensiuneHeap--;
Interschimb(0, this->dimensiuneHeap);
this->Filtrare(0);
}

return this->elemente[this->dimensiuneHeap];

template <class T>


T Heap<T>::CitesteMaxim()
{
// ne asigurm c avem cel puin un element n heap
assert(this->dimensiuneHeap > 0);

// i ntoarcem maximul
return this->elemente[0];

Metodele
de
inserare
i
extragere
utilizeaz
funciile
CresteMemoriaAlocata i ScadeMemoria pentru extinderea / restrngerea
memoriei utilizate de ctre heap.

15.3 Cozi de prioritate


Cozile de prioritate sunt structuri de date care suport urmtoarele
dou operaii de baz:
inserarea unui element cu o prioritate asociat;
extragerea elementului cu prioritate maxim.
Cele mai importante aplicaii ale cozilor de prioritate sunt: simularea
bazat pe evenimente, gestionarea resurselor partajate (lime de band,
timp de procesare) i cutare n spaiul soluiilor (de exemplu, algoritmul A*
utilizeaz o coad de prioritate pentru a reine rutele neexplorate).
Structura de date de tip Heap este una dintre cele mai eficiente
modaliti de implementare a cozilor de prioritate. Prioritatea elementelor
este dat de relaia de ordine existent ntre valorile asociate nodurilor.
Pentru exemplificarea modului de utilizare a clasei Heap prezentat n
seciunea 15.2 vom construi un simulator discret pentru o coad de
ateptare la un magazin.
n simularea discret, modul de operare al unui sistem este
reprezentat sub forma unei secvene de evenimente ordonate cronologic. n
cazul de fa evenimentele sunt sosirile clienilor n coada de ateptare i
servirea clienilor. Simulatorul conine o coad de evenimente.
Evenimentele sunt adugate n coad pe msur ce timpul lor de producere
poate fi determinat i sunt extrase din coad pentru procesare n ordine
cronologic.
Un simulator discret pe baz de evenimente are urmtoarele
componente:
coada de evenimente o coad de prioritate care conine lista
evenimentelor care se vor petrece n viitor;
starea simulatorului conine un contor pentru memorarea
timpului curent, informaiile referitoare la starea actual a
sistemului simulat (n cazul curent clienii aflai n coad i starea
staiei de servire) i indicatori;
logica de procesare extrage din coad evenimentele n ordine
cronologic i le proceseaz; procesarea unui eveniment
determin modificarea strii sistemului i generarea de alte
evenimente.
Pentru simularea propus au fost luate n considerare urmtoarele
ipoteze:
exist o singur staie de servire cu un timp de servire distribuit
normal, cu o medie i dispersie cunoscut;
exist o singur coad pentru clieni, iar intervalul de timp dintre
dou sosiri este distribuit uniform ntr-un interval dat;
durata simulrii este stabilit de ctre utilizator.
Simularea se realizeaz prin extragerea evenimentelor din heap i
procesarea acestora pe baz de reguli. Evenimentele de tip sosire determin
generarea evenimentului corespunztor sosirii urmtoare i a unui
eveniment de servire n cazul n care staia este liber la momentul curent.
n cazul evenimentelor de tip servire se genereaz urmtorul eveniment de

tip servire dac mai exist clieni n coad. Pe msur ce sunt procesate
evenimentele sunt reinute i informaiile necesare pentru calcularea
indicatorilor de performan afereni sistemului simulat.
Codul surs pentru implementarea simulatorului pe baza clasei Heap
prezentat n seciunea 15.2 este urmtorul:
#include <cmath>
#include <ctime>
#include <iostream>
#include "Heap.h"
// --- Definirea clasei Eveniment --- //
enum TipEveniment { Sosire, Servire };
class Eveniment
{
public:
// Constructor
Eveniment(int timp = 0, TipEveniment tip = Sosire) :
timp(timp), tip(tip) {}
// Implementarea relatiei de ordine pentru min-heap
bool operator > (Eveniment& eveniment)
{
return this->timp < eveniment.timp;
}
// Acces la elemente
int GetTimp() { return this->timp; }
TipEveniment GetTip() { return this->tip; }
private:
int timp;
TipEveniment tip;
};
// --- Generarea timpilor de sosire / servire --- //
int GenerareTimpSosire(int timpCurent,
int minIntervalSosire, int maxIntervalSosire)
{
return timpCurent + minIntervalSosire + rand() %
maxIntervalSosire;
}
int GenerareTimpServire(int timpCurent,
int medieServire, int dispersieServire)
{
// generare variabila aleatoare distribuita
// normal folosind metoda Box - Muller
double a = (double)rand() / RAND_MAX;
double b = (double)rand() / RAND_MAX;
double PI = 3.14159265358979323846;
double c = sqrt(-2 * log(a)) * cos(2 * PI * b);

return timpCurent + abs((int)


(medieServire + c * dispersieServire));
}
// --- Simularea magazinului --- //
void Simulare(
// Parametri de intrare:
int durata, int medieServire, int dispersieServire,
int minIntervalSosire, int maxIntervalSosire,

// Parametri de iesire:
double& timpMediuAsteptare,
double& timpMediuServire,
double& lungimeMedieCoada,
int& numarClientiServiti)
// iniializare indicatori
int timp = 0;
int numarMasuratoriCoada = 0;
timpMediuAsteptare = 0;
timpMediuServire = 0;
lungimeMedieCoada = 0;
numarClientiServiti = 0;
// lista de clienti care asteapta sa fie serviti (inclusiv
// clientul care este servit la momentul curent)
Heap<Eveniment> clientiInAsteptare;
// lista de evenimente programate in viitor
Heap<Eveniment> coadaEvenimente;
// simularea incepe cu o sosire
coadaEvenimente.Insereaza(Eveniment(0, Sosire));
while(timp < durata)
{
// extragem ev urmtor din coad (n funcie de timp)
Eveniment e = coadaEvenimente.ExtrageMaxim();
timp = e.GetTimp();
lungimeMedieCoada += clientiInAsteptare.GetNumarElemente();
numarMasuratoriCoada++;
if (e.GetTip() == Sosire)
{
// A. Eveniment de tip sosire
// adugm clientul n coada de ateptare
clientiInAsteptare.Insereaza(e);
// programm sosirea urmtoare
Eveniment sosire = Eveniment(GenerareTimpSosire(
timp, minIntervalSosire, maxIntervalSosire), Sosire);
coadaEvenimente.Insereaza(sosire);
// dac este unicul client din coad
if (clientiInAsteptare.GetNumarElemente() == 1)
{

// atunci programm i servirea


Eveniment servire(GenerareTimpServire(
timp, medieServire, dispersieServire), Servire);
coadaEvenimente.Insereaza(servire);

}
else
{

timpMediuServire += servire.GetTimp() - timp;

// B. Eveniment de tip servire


// eliminm primul client din coad (cel servit)
Eveniment clientServit =
clientiInAsteptare.ExtrageMaxim();
timpMediuAsteptare += timp
clientServit.GetTimp();
numarClientiServiti++;
// dac mai avem clieni n ateptare
if (clientiInAsteptare.GetNumarElemente() > 0)
{
// programm servirea pt clientul urmtor
Eveniment servire(GenerareTimpServire(
timp, medieServire, dispersieServire), Servire);
coadaEvenimente.Insereaza(servire);

timpMediuServire += servire.GetTimp() - timp;

// calcul indicatori
lungimeMedieCoada = lungimeMedieCoada / numarMasuratoriCoada;
timpMediuAsteptare = timpMediuAsteptare / numarClientiServiti;
timpMediuServire = timpMediuServire /
(numarClientiServiti + clientiInAsteptare.GetNumarElemente());
}
void main()
{
// --- Citirea datelor de intrare --- //
// Parametri de intrare:
int durata, medieServire, dispersieServire;
int minIntervalSosire, maxIntervalSosire;
// Parametri de iesire:
double timpMediuAsteptare;
double timpMediuServire;
double lungimeMedieCoada;
int numarClientiServiti;
// Citire parametri:
cout << "Durata simularii:";
cin >> durata;

cout << "Media timpului de servire: ";


cin >> medieServire;
cout << "Dispersia timpului de servire: ";
cin >> dispersieServire;
cout << "Intervalul minim dintre sosiri: ";
cin >> minIntervalSosire;
cout << "Intervalul maxim dintre sosiri: ";
cin >> maxIntervalSosire;
// --- Simularea procesului --- //
cout << endl;
cout << "Simulare proces cu durata " << durata << "." << endl;
cout << "Interval intre sosiri: [" << minIntervalSosire <<
", " << maxIntervalSosire << "]" << endl;
cout << "Timp servire ~N(" << medieServire << "," <<
dispersieServire << ")" << endl;
cout << endl;
Simulare(
// Parametri de intrare:
durata,
medieServire, dispersieServire,
minIntervalSosire, maxIntervalSosire,
// Parametri de iesire:
timpMediuAsteptare,
timpMediuServire,
lungimeMedieCoada,numarClientiServiti);
// --- Afisarea rezultatelor --- //
cout
cout
cout
cout

<< endl << "Rezultate simulare:" << endl;


<< "Nr clienti serviti:" << numarClientiServiti << endl;
<< "Lungime medie coada:" << lungimeMedieCoada << endl;
<< "Timpul mediu de asteptare pentru clienti: " <<
fixed << timpMediuAsteptare << endl;
cout << "Timpul mediu de servire: " <<
fixed << timpMediuServire << endl;

15.4 Sortarea datelor prin HeapSort


O alt aplicaie a structurii heap este implementarea algoritmului de
sortare Heapsort. Sortarea presupune extragerea elementelor din heap i
stocarea acestora, n ordine invers, la sfritul masivului utilizat pentru
memorarea structurii.
Codul surs pentru implementarea algoritmului Heapsort n cadrul
clasei Heap este urmtorul:
template <class T>
void Heap<T>::SorteazaHeap(T* destinatie)
{
// salvm numrul de elemente din heap

int dimensiuneaInitiala = this->dimensiuneHeap;


// sortm vectorul n cadrul structurii
for (int i = this->dimensiuneHeap - 1; i > 0; i--)
{
this->Interschimb(0, i);

this->dimensiuneHeap--;
this->Filtrare(0);

// copiem elementele sortate n vectorul destinaie


for (int i = 0; i < dimensiuneaInitiala; i++)
{
destinatie[i] = this->elemente[i];
}

Algoritmul poate fi utilizat pentru sortarea oricrui masiv


unidimensional pentru care a fost definit o relaie de ordine.
Codul surs pentru sortarea unui vector de numere ntregi este
urmtorul:
void main()
{
// 1. Construirea vectorului de sortat si a heap-ului asociat
const int NumarElemente = 8;
int elemente[] = {23, 32, 2, 6, 23, 8, 3, 6};
Heap<int> heap(elemente, NumarElemente);
// 2. Obtinerea si afisarea vectorului sortat
int* elementeSortate = new int[NumarElemente];
heap.SorteazaHeap(elementeSortate);

for (int i = 0; i < NumarElemente; i++)


{
cout << elementeSortate[i] << " ";
}
cout << endl;

16. GRAFURI
16.1 Structura de date de tip graf
Proiectarea unui sistem informatic care s gestioneze reeaua
naional de ci ferate presupune crearea unui mediu de lucru capabil s
utilizeze baza de date a staiilor i pe cea a liniilor de cale ferate pentru a
oferi soluii optime i reale care s minimizeze costurile i timpul de folosire
a reelei. Lund de exemplu situaia real din figura 16.1, se pune problema
parcurgerii traseului Arad Bucureti cu cost redus, acest lucru implicnd
parcurgerea distanei cea mai scurt.

Arad

274
59

Cluj

Timioara

331

324

497
225

Craiova
209

Constana

Bucureti

Figura 16.1 O parte a reelei de cale ferat


Sistemul prelucreaz informaiile iniiale, aflate n dou mulimi N i
A, unde N = {Arad, Bucureti, Cluj, Craiova, Constana, Timioara} este
mulimea oraelor iar A = {Arad Timioara = 59 km, Timioara Craiova
= 324 km, Craiova Bucureti = 209 km, Bucureti Constana = 225 km,
Arad Cluj = 274 km, Cluj Craiova = 331 km, Cluj Bucureti = 497
km} este mulimea distanelor dintre dou orae i ofer soluia cutat.
Reprezentarea n memorie a acestor informaii care au multiple
legturi ntre ele, astfel nct s permit parcurgerea complet a zonelor
sau localizarea unui element din structur, se face n situaia de fa
utiliznd graful.
Graful, asemenea arborelui, este o structur n care relaia dintre
nodul printe i nodul fiu este una ierarhic, dar care este mai puin
restrictiv n sensul c un nod are mai muli succesori dar i mai muli
predecesori. El este definit ca o colecie de date reunite n dou mulimi:
mulimea N = {N1, N2, , Nn | n numrul de noduri al grafului} ce conine
toate nodurile grafului i mulimea A = {( Ni, Nj ) = Aij | Ni, Nj N i i,j =
1, ..., n cu i j} care conine arcele dintre dou noduri vecine.
Graful este larg utilizat n domeniile: ciberneticii, matematicii,
cercetrilor operaionale n vederea optimizrii diferitelor activiti
economice, chimiei pentru descrierea structurii cristalelor, reelelor de
transport de toate tipurile pentru optimizarea traseelor, circuitelor electrice
pentru simularea funcionri corecte, inteligenei artificiale i nu n ultimul
rnd n domeniul analizei aplicaiilor software.
Graful este de mai multe tipuri, fiind clasificat n funcie de :
- direcia arcelor n cazul n care arcele dintre nodurile grafului
sunt nedirecionate atunci graful este unul neorientat; cnd exist

sens ntre dou noduri Ni, Nj i arcul este direcionat (Ni Nj sau
Ni Nj sau Ni Nj) atunci graful este unul orientat;
B

Graf neorientat

Graf orientat

Figura 16.2 Graf orientat i neorientat


-

greutatea arcelor dac oricare arc dintre dou noduri al grafului


are asociat o valoare numeric (care reprezint de cele mai
multe ori distana, durata de timp sau costul) atunci graful este
cu greutate; n cazul n care arcele nu au asociate valori
numerice, graful este unul fr greutate;
4

2
C

Figura 16.3 Graf orientat cu greutate


-

existena arcelor; dac ntr-un graf nu exist nici un nod izolat,


altfel spus pentru oricare nod Ni cu i = 1..n exist cel puin un
nod Nj cu 1 j n i i j pentru care exist arcul Aij asociat,
atunci graful este conectat; un graf este neconectat dac exist
cel puin un nod izolat.
B

Nod liber
D

Figura 16.4 Graf orientat neconectat


La rndul lui graful orientat este puternic conectat dac ntre oricare
dou noduri Ni i Nj cu i, j = 1..n exist drum ( un drum este format din
unul sau mai multe arce ) orientat de la i la j, Ni Nj. Graful orientat este
slab conectat dac ntre oricare dou noduri Ni i Nj cu i, j = 1..n exist
drum orientat de la i la j, Ni Nj sau de la j la i, Ni Nj ( doar unul dintre
ele).

Graf orientat puternic conectat

Graf orientat slab conectat

Figura 16.5 Tipuri de graf orientat


De exemplu n figura 16.5 se observ c al doilea graf orientat este
slab conectat pentru c ntre nodul A i D exist drum orientat, ns de la D
la A nu exist drum.

16.2 Implementarea grafului


Exist numeroase metode de reprezentare n memoria calculatorului
a grafului, fiecare cu avantajele i dezavantajele ei:
- matricea de adiacen;
- liste nlnuite;
- un vector de pointeri la liste simple sau dublu nlnuite de noduri
adiacente;
- o list simplu sau dublu nlnuit de pointeri la liste simple sau
dublu nlnuite de noduri adiacente;
- un vector de pointeri la liste simple sau dublu nlnuite de arce.
Dintre toate, cele mai utilizate metode sunt primele dou.
Reprezentarea prin matrice de adiacen a grafului este eficient
cnd se cunoate numrul nodurilor i numrul mediu al arcelor; acesta din
urm trebuie s fie mare pentru ca gradul de umplere al matricei de
adiacen s fie sczut. Cum crearea de software optimizat nseamn i
generalizarea problemei, lucru care d puine anse s se cunoasc numrul
nodurilor i cel al arcelor, singurul argument pro pentru aceast metod
este dat doar de uurin implementrii i utilizrii matricelor. n cele mai
multe situaii reale, cea mai mare parte a memoriei necesar stocrii
matricei de adiacen este nefolosit.
Pentru un graf cu n noduri este necesar o matrice ptratic M de
dimensiuni nxn, care pentru un n foarte mare ocup mult spaiu.
Iniial matricea de adiacen are toate elementele egale cu valoarea
0. Pentru a reprezenta arcul Aij dintre nodurile Ni i Nj (orientat de la Ni la
Nj) la intersecia liniei i cu coloana j se trece valoarea 1, n cazul grafului cu
fr greutate, sau greutatea arcului, pentru graful cu greutate.
Aadar: M[i][j] =

1, dac exist arc ntre nodul Ni i Nj;

0, dac nu exist arc ntre nodul Ni i Nj;


n cazul grafului fr greutate i:

M[i][j] =

aij, dac exist arc ntre nodul Ni i Nj, iar aij


reprezint greutatea arcului;
0, dac nu exist arc ntre nodul Ni i Nj;

n cazul unui graf neorientat, matricea de adiacen asociat este


simetric. Pentru graful cu 5 noduri din figura 16.6, Matricele de adiacen
corespunztoare grafurilor sunt prezentate n figura 16.7.
B

A
5

A
C

Figura 16.6 Graf orientat cu greutate i fr greutate

0 7 5 0

1 1 0

0 0 8 1
0 0 0 3

0
0
0

0 1 1
0 0 1
0 0 0

0 0 0 0

Figura 16.7 Matrice de adiacen


Lungimea drumului dintre dou noduri ale unui graf orientat fr
greutate este dat de numrul de arce. Astfel n cazul grafului din figura
16.6, ntre nodul A i D exist trei drumuri posibile, unul de lungime 3 (A-BC-D) i dou de lungime 2 (A-C-D i A-B-D).
Uurina lucrului cu matricea de adiacen const i n faptul c prin
simple ridicri la putere se verific dac exist drum ntre dou noduri i
care este lungimea acestuia (doar n cazul grafului orientat fr greutate).
Fie M[4][4] matricea de adiacen din figura 16.7 asociat grafului
orientat fr greutate, atunci obinem:

0
0
2
M
0

0
0
0
0

1
0
0
0

2
1
0

0
0
3
M
0

0
0
0
0

0
0
0
0

1
0

0
0
4
M
0
0

0
0

0
0
0
0

0
0
0
0

0
0
0

(16.1)

Dup cum se observ, dac Mk[i][j] = val 0 atunci ntre nodurile Ni


i Nj exist val drumuri pe direcia Ni Nj, drumuri de lungime k.
n cazul lui M2 exist 2 drumuri de lungime 2 de la nodul A la nodul D
(A-B-D i A-C-D), un drum de lungime 2 de la nodul A la nodul C (A-B-C) i
un drum de lungime 2 de la nodul B la nodul D (B-C-D).

Pentru k = 3 exist drumul de lungime 3 de la nodul a la nodul D (AB-C-D).


Procesul de ridicare la putere se oprete cnd se ajunge la k = n,
unde n este dimensiunea matricei.
Clasa graf care implementeaz diferite operaii cu grafuri
reprezentate prin intermediul matricei de adiacen este:
#ifndef GRAF_MATRICE_H
#define GRAF_MATRICE_H
#include <iostream>
using namespace std;
class graf_matrice
{
public:
static const int MaxN = 50;
// numarul maxim de noduri
static const int Infinit = INT_MAX; // nu exista drum intre noduri
enum TipMatrice
{
MatriceAdiacenta = 1,
MatriceCosturi = 2
};
private:
//matricea de adiacenta (1=exista, 0=nu exista)
int matm[MaxN][MaxN];
//matricea de costuri (Infinit=nu e drum)
long matc[MaxN][MaxN];
int nr_noduri;
int nr_arce;
protected:
int vect_rez[MaxN];
//vectorul n care se in minte drumuri
int nr_rez;
//nr. nodurilor din rezultat
int matr[MaxN][MaxN];
//matricea rezultatelor obinute din
prelucrri
int valid(int);
//testez daca int se afla deja in vect soluie
int suma(int);
//calculeaz lungimea drumului
public:
graf_matrice();
void init(TipMatrice tip); //iniializare graf
void
void
void
void
noduri */
void
void
void
bool
void
bool
void
void

vect_r();
//afieaz vectorul rezultat
matr_r();
//afieaz matricea rezultat
matr_r1();
drum_minim();/* se calculeaz drumul minim intre oricare 2
este_drum(); //verif. existenta drumului intre 2 noduri
drum_minim(int, int);
//drumul minim ntre 2 noduri date
toate_drm(int,int);
//toate drumurile dintre 2 noduri
este_arbore();
//verific daca este sau nu arbore
componente_conexe();
//determin componentele conexe
graf_matrice::este_eulerian();
parcurgere_bf(int);
//parcurgerea grafului n lime
parcurgere_df(int);
//parcurgerea grafului n adncime

};

void hamiltonian();
void prim();

graf_matrice::graf_matrice()
{
nr_noduri = 0;
nr_arce = 0;
nr_rez = 0;
for(int i = 0; i < MaxN;
{
for(int j = 0; j <
{
matm[i][j] =
matc[i][j] =
}
}
}

//verif. exist. cicluri hamiltoniene


//det. arbore parial de cost minim

i++)
MaxN; j++)
0;
Infinit;

void graf_matrice::init(TipMatrice tip)


{
cout << "Introduceti numarul de noduri: ";
Do
{
cin >> nr_noduri;
if (nr_noduri <= 0)
{
cout << "EROARE: Numar invalid de noduri" << endl;
}
} while(nr_noduri <= 0);
if (tip ==
{
cout
cout
}
else
{
cout
}

MatriceAdiacenta)
<< "Se va introduce matricea de adiacenta" << endl;
<< "Se introduc valorile (1-e drum; 0- nu e drum\n";

<< "Se va introduce matricea de costuri" << endl;

for (int j = 0; j < nr_noduri; j++)


{
for (int i = 0; i < j; i++)
{
cout << "m[" << i + 1 << "][" << j + 1 << "]=";
if (tip == MatriceAdiacenta)
{
do
{
cin >> matm[i][j];
matm[j][i] = matm[i][j];
} while (matm[i][j] != 0 && matm[i][j] != 1);
if (matm[i][j] != 0)
{
nr_arce++;
matc[i][j] = matc[j][i] = 1;
}
else

}
else
{

matc[i][j]=Infinit;
matc[j][i]=Infinit;

do
{

cin >> matc[i][j];


matc[j][i] = matc[i][j];
} while (matc[i][j] < 0);

}
} // for j
} // for i

if (matc[i][j] != 0)
{
nr_arce++;
matm[j][i] = matm[i][j] = 1;
}
else
{
matc[i][j] = matc[j][i] = Infinit;
matm[i][j] = matm[j][i] = 0;
}

void graf_matrice::vect_r()
{
cout << "Vectorul rezultat este:" << endl;
for (int i = 0; i < nr_rez; i++)
{
cout << vect_rez[i] << " " << endl;
}
}
void graf_matrice::matr_r()
{
for (int i = 0; i < nr_noduri; i++)
{
for (int j = 0; j < i; j++)
{
cout << "Intre " << i + 1 << " si " << j + 1 <<
(matr[i][j] != 0 ? "" : " nu") << "exista drum.\n";
}
}
}
void graf_matrice::matr_r1()
{
for (int i = 0; i < nr_noduri; i++)
{
for (int j = 0; j < i; j++)
{
if (matr[i][j] != Infinit)
{
cout << "Intre " << i + 1 << " si " << j + 1 <<
"exista drum de cost minim " << matr[i][j] <<
endl;
}

else
{

Cout << "Intre " << i + 1 << " si " << j + 1 <<
"nu exista drum." << endl;

void graf_matrice::drum_minim()
{
for (int i = 0; i < nr_noduri; i++)
{
for (int j = 0; j < nr_noduri; j++)
{
matr[i][j] = matc[i][j];
}
}

for (int k = 0; k < nr_noduri; k++)


{
for (int i = 0; i < nr_noduri; i++)
{
for (int j=0; j < nr_noduri; j++)
{
matr[i][j] = min(matr[i][j],matr[i][k]+matr[k][j]);
}
}
}

void graf_matrice::este_drum()
{
for (int i = 0; i < nr_noduri; i++)
{
for (int j = 0; j < nr_noduri; j++)
{
matr[i][j] = matm[i][j];
}
}

for (int k = 0; k < nr_noduri; k++)


{
for (int i = 0; i < nr_noduri; i++)
{
for (int j = 0; j < nr_noduri; j++)
{
matr[i][j]=matr[i][j]||(matr[i][k]&&matr[k][j]);
}
}
}

int graf_matrice::valid(int k)
{
if (matc[vect_rez[k-1]][vect_rez[k]] == Infinit)
{
return 0;
}

for (int i = 0; i < k; i++)


{
if (vect_rez[i] == vect_rez[k])
{
return 0;
}
}
}

return 1;

int graf_matrice::suma(int k)
{
int suma = 0;
for (int i=0; i<k; i++)
{
suma += matc[vect_rez[i]][vect_rez[i+1]];
}
}

return suma;

void graf_matrice::drum_minim(int a, int b)


{
int h, vect[MaxN], lgdrum, min = Infinit;
for (int f = 0; f < MaxN; f++)
{
vect_rez[f] = -1;
}
vect_rez[0] = a;
h = 1;
while(h >= 1)
{
while(vect_rez[h] < nr_noduri)
{
vect_rez[h]++;
if(valid(h))
{
if(vect_rez[h] == b)
{
lgdrum = suma(h);
if(lgdrum < min)
{
for (int e = 0; e <= h; e++)
{
vect[e] = vect_rez[e];
}

}
else
{

min = lgdrum;
nr_rez = h + 1;

h++;
vect_rez[h] = -1;

}
} // if (valid(h))

}
}

h--;

for (int e = 0; e < nr_rez; e++)


{
vect_rez[e] = vect[e];
}

bool graf_matrice::este_arbore()
{
int b[MaxN], i, j, v, s;
v = 0;
for (i = 0; i < nr_noduri; i++)
{
for(j = i + 1; j < nr_noduri; j++)
{
v += (matm[i][j] == 1);
}
}
for (int i = 0; i < nr_noduri; b[i] = 0, i++);
b[0]=1;
for(i=0;i<nr_noduri;i++)
{
for(j=0;j<nr_noduri;j++)
{
if((matm[i][j] == 1) && (b[i] == 1))
{
b[j] = 1;
}
}
}
s=0;
for (i = 0; i < nr_noduri; i++)
{
s += (b[i] == 0);
}
}

return (s == 0) && (v == nr_noduri - 1);

bool graf_matrice::este_eulerian()
{
int b[MaxN],i,j,k,v,s;
for (int i = 0; i < nr_noduri; b[i] = 0, i++);
b[0] = 1;
for(i=0;i<nr_noduri;i++)
{
for(j=0;j<nr_noduri;j++)
{
if((matm[i][j] == 1) && (b[i] == 1))
{

b[j] = 1;

s=0;
for (i = 0; i < nr_noduri; i++)
{
s += (b[i] == 0);
}
for(i = 0; i < nr_noduri; i++)
{
k = 0;
for(j = 0; j < nr_noduri; j++)
{
k += (matm[i][j]==1);
v = k / 2;

}
}

if ((k - 2 * v) > 0)
s = 1;

return s <= 0;

void graf_matrice::parcurgere_bf(int i)
{
int j,u,p,v,c[MaxN],vizitat[MaxN];
for (int i = 0; i < nr_noduri; vizitat[i] = 0, i++);
c[1] = i; u = 1; vizitat[i] = 1;
for(p = 1; p <= u; p++)
{
v = c[p];
for(j = 0; j < nr_noduri; j++)
{
if ((matm[v][j] == 1) && (vizitat[j] == 0))
{
u++;
c[u] = j;
vizitat[j] = 1;
}
}
}
cout << "lista vf. parcurse cu metoda BF pornind de la varful "
<< i + 1 << ": " << endl;

for(j = 2; j <= u; j++)


cout << c[j] + 1 << endl;

void graf_matrice::parcurgere_df(int i)
{
int vizitat[MaxN], urmator[MaxN], s[MaxN], k, v, j, ps;
for (int i=0; i < nr_noduri; urmator[i] = 0, vizitat[i] = 0, i++);

s[1] = i; ps = 1; vizitat[i] = 1;
cout << "ordinea in DF este " << i+1 << " ";
while(ps >= 1)
{
j = s[ps];
k = urmator[j] + 1;
while ((k <= nr_noduri) &&
((matm[j][k]==0) || (matm[j][k]==1)&&(vizitat[k]==1)))
{
k++;
}
urmator[j] = k;

if(k == nr_noduri + 1)
{
ps--;
}
else
{
cout << k + 1 << " ";
vizitat[k] = 1;
ps++;
s[ps] = k;
}

void graf_matrice::prim()
{
int s[MaxN],t[MaxN],p[MaxN];
int min,k,l,c,i,j,v;
cout << "Nodul de pornire: ";
cin >> v;
for (int i = 0; i < nr_noduri; s[i] = t[i] = p[i] = 0, i++);
s[v - 1] = 1;
for(k = 1; k <= nr_noduri - 1; k++)
{
min = Infinit;
for(i = 0; i < nr_noduri; i++)
{
for(j = 0; j < nr_noduri; j++)
{
if ((s[i]==1)&&(s[j]==0) && (min > matc[i][j]))
{
min = matc[i][j];
l = i; c = j;
}
}
}

s[c] = 1;
t[c] = l + 1;
p[c] = matc[l][c];

for(i = 0; i < nr_noduri; i++)


{
cout << t[i] << " ";
}
cout << endl;

for(i = 0; i < nr_noduri; i++)


{
cout << p[i] << " ";
}

void graf_matrice::hamiltonian()
{
int x[MaxN], v, k, j, sol, i;
sol = 0;
x[1] = 1; x[2] = 1; k = 2;
while(k > 1)
{
v = 0;
while((x[k] + 1 <= nr_noduri) && (v == 0))
{
x[k]++;
v = 1;
for(i = 1;i <= k - 1; i++)
{
if(x[k] == x[i])
{
v = 0;
break;
}
}

if(matm[x[k - 1] - 1][x[k] - 1] == 0)
{
v = 0;
}

if(v == 0)
{
k--;
}
else
{
if ((k==nr_noduri)&&(matm[x[nr_noduri]-1][x[1]-1]==1))
{
sol++;
if(sol==1)
{
cout << "Cicluri hamiltoniene:" << endl;
}

}
else

for (int j = 1; j <= nr_noduri; j++)


cout << x[j] << " ";
cout << endl;

if(k < nr_noduri)


{
k++;
x[k] = 1;
}

if (sol == 0)
cout << "Graful nu este hamiltonian.";

void graf_matrice::componente_conexe()
{
int b[MaxN],k,j,i;
for(i = 0; i < nr_noduri; i++)
{
for(j = 0; j < nr_noduri; j++)
{
matr[i][j] = matm[i][j];
}
}
cout << "Componentele conexe sunt: ";
for(k = 0; k < nr_noduri; k++)
{
for(j = 0; j < nr_noduri; j++)
{
b[j] = -2;
}
b[k] = k;
if(matr[k][k] > -1)
cout << endl << "

";

for(i = 0; i < nr_noduri; i++)


{
for(j = 0;j < nr_noduri; j++)
{
if((matr[i][j] == 1) && (b[i] == k))
{
b[j] = k;
matr[i][j] = 0;
matr[j][i] = 0;
}
}

for(j = 0; j < nr_noduri; j++)


{
if((b[j] == k) && (matr[j][j] > -1))
{
matr[j][j] = -1;
cout << " " << j + 1;
}
}

}
void graf_matrice::toate_drm(int a, int b)
{
int h;
for (int f = 0; f < MaxN; f++)
vect_rez[f] = -1;

vect_rez[0] = a;
h = 1;
while(h >= 1)
{
while(vect_rez[h] < nr_noduri)
{
vect_rez[h]++;
if(valid(h))
{
if(vect_rez[h] == b)
{
cout << endl;
for (int e = 0; e <= h; e++)
cout << vect_rez[e] + 1 << " ";
}
else
{
h++;
vect_rez[h] = -1;
}
}
}
h--;
}

#endif //GRAF_MATRICE_H

De cele mai multe ori nu se cunoate numrul de noduri ale grafului,


apelndu-se la construirea dinamic a grafului pe parcursul rezolvrii
problemei, deci nu se cunoate dimensiunea matricei de adiacen. n
aceste situaii graful este reprezentat printr-o reea de liste nlnuite, liste
de adiacen. Asemenea matricei de adiacen descrierea grafului cuprinde
mulimea de noduri i pe cea de arce, preciznd orientarea arcului i dup
caz greutatea lui.
Se definete structura arc care este asociat elementelor din
mulimea arcelor:
struct arc
{
struct nodgraf * destinaie; /*adresa nodului ctre care
exist arc;*/
struct arc* next_arc; /*referin ctre elementul urmtor
din lista de arce;*/
int greutate; };
//greutatea arcului;

Este vorba de o list a arcelor ce este construit dinamic. Structura


este cea a unei liste oarecare, cuprinznd informaia propriu-zis, greutatea
arcului, precum i cea necesar nlnuirii n list, adresa elementului

urmtor. Cu toate c nodgraf * destinaie este un pointer, el face parte din


informaia de baz i nu din cea destinat regsirii n list. n list exist
mai multe liste, organizate pe principiul descendenei dintr-un nod. Cum
fiecare nod din graf este unic, se elimin astfel posibilitatea ca un arc s fie
n mai multe liste.
Tipul de structura nodgraf este tot o structur de tip list. Pe lng
informaia nodului i adresa urmtorului nod, ea conine i adresa de start a
listei ce cuprinde arcele descendente din nodul respectiv.
struct nodgraf {
int info;
//informaia nodului;
struct nodgraf* next; //referin ctre urmtorul nod;
struct arc *capat;
//captul listei de arce;
}

La crearea grafului se introduc iniial informaiile nodurilor, crenduse astfel lista lor. Dup aceasta se vor crea listele de arce introducndu-se
informaia nodului surs, a nodului destinaie i greutatea arcului.
Pentru graful
cu greutate din figura 16.6 reprezentarea sa n
memorie prin intermediul listelor de liste este dat n figura 16.8.

&B

&C

&D

NULL
NULL

&C

NULL

&D

NULL

NULL

&nod reprezint adresa lui nod

Figura 16.8 Reprezentarea n memorie a unui graf cu ajutorul listelor


Clasa graf_liste care implementeaz graful definit cu ajutorul listelor
nlnuite este:
#ifndef GRAF_LISTE_H
#define GRAF_LISTE_H
#include <iostream>
using namespace std;
typedef struct arc
{
struct nodgraf *destinatie;
int greutate;

// adresa nodului catre care e arc;


// greutatea arcului;

// referinta catre elementul urmator din lista de arce


struct arc *next_arc;
} arc;

typedef struct nodgraf


{
int info;
struct nodgraf *next;
struct arc *capat;
} nodgraf;
typedef struct stiva
{
nodgraf *n;
struct stiva *next;
} stiva;
struct lista
{
nodgraf *n;
struct lista *next;
};

//informatia nodului;
//referinta catre urmatorul nod;
//capatul listei de arce;

//elementul stivei
//referinta catre urmatorul element;

//elementul listei
//referinta catre urmatorul element;

typedef struct coada


{
struct lista *pred;
struct lista *succ;
} coada;
class graf_liste
{
int nr;
public:
static const int MaxN = 50;
// numarul maxim de noduri
static const int Infinit = INT_MAX; /* nu exista drum intre
noduri */
graf_liste(){}
graf_liste(int nrnoduri){ nr=nrnoduri;}
nodgraf *inserare_nod(nodgraf *,int); //insereaza un nod in graf
nodgraf *gaseste_nod(nodgraf *, int); //gaseste un nod in graf
void inserare_arc(nodgraf *,int,int,int); //insereaza arc
int adiacent(nodgraf *,nodgraf *); /* vf dc 2 noduri sunt
adiacente */
int sterge_arc(nodgraf *,nodgraf *); //sterge arcul
void vizitare(nodgraf *nd,int vizitat[]) /* march. nodul ca
vizitat */
{
vizitat[nd->info]=1;
cout<<nd->info<<" - ";
}
stiva *push(stiva *stk,nodgraf *nd) //pune un element in stiva
{
stiva *t = new stiva();
t->n = nd;
t->next = stk;
return t;
}
stiva *pop(stiva *stk, nodgraf **nd) /* scoate un element din
stiva */

if(!stk)
{
return NULL;
}
else
{
stiva *t = stk->next;
(*nd) = stk->n;
delete stk;

return t;

void depth(nodgraf *); //parcurgere in adancime


void put(struct coada *q, nodgraf *nd)
{
lista *t = new lista();
t->n=nd;
t->next=NULL;
lista *keep = q->succ;
if (keep != NULL)
{
keep->next=t;
}
else
{
q->pred=t;
}
}

q->succ = t;

nodgraf *get(struct coada *q) //scoate un element din coada


{
if(q->pred == NULL)
{
return NULL;
}
else
{
lista* t = q->pred;
nodgraf* n=t->n;
if(q->pred == q->succ)
{
q->succ = NULL;
}
q->pred = q->pred->next;

delete t;
return n;

void breadth(nodgraf *); //parcurgere in latime


int drum_minim(nodgraf *,int,int,int[]); //gaseste drumul minim

};

void stergere_nod(nodgraf *&,int);// sterge un nod din graf

nodgraf* graf_liste::inserare_nod(nodgraf *cap,int info)


{
nodgraf *nou= new nodgraf();
nou->info=info;
nou->next=NULL;
nou->capat=NULL;
if(cap==NULL)
{
return nou;
}
else
{
nodgraf *temp = cap;
while(temp->next != NULL)
{
temp=temp->next;
}

temp->next = nou;
return nou;

nodgraf* graf_liste::gaseste_nod(nodgraf *cap, int info)


{
while(cap != NULL && cap->info != info)
{
cap = cap->next;
}
}

return cap;

void graf_liste::inserare_arc(nodgraf *cap,int sursa,int dest,int


greutate)
{
nodgraf* s = gaseste_nod(cap,sursa);
if(s == NULL)
{
s = inserare_nod(cap,sursa);
}
nodgraf* d=gaseste_nod(cap,dest);
if(d == NULL)
{
d = inserare_nod(cap,dest);
}
arc *temp = s->capat,*keep=NULL;
int gasit = 0;
while(temp != NULL && !gasit)
{
if(temp->destinatie == d)
{
temp->greutate = greutate;

}
else
{

gasit = 1;

keep = temp;
temp = temp->next_arc;

if(!gasit)
{
temp= new arc();
temp->destinatie=d;
temp->greutate=greutate;
temp->next_arc=NULL;

if(keep == NULL)
{
s->capat=temp;
}
else
{
keep->next_arc=temp;
}

int graf_liste::adiacent(nodgraf *s,nodgraf *d)


{
arc *temp = s->capat;
while(temp != NULL && temp->destinatie != d)
{
temp=temp->next_arc;
}
}

return temp != NULL ? temp->greutate : 0;

int graf_liste::sterge_arc(nodgraf *s,nodgraf *d)


{
arc *keep = NULL;
arc* temp=s->capat;
while(temp != NULL)
{
if(temp->destinatie == d)
{
if(keep == NULL)
{
s->capat=temp->next_arc;
}
else
{
keep->next_arc=temp->next_arc;
}

}
else

delete temp;
return 1; // nodul a fost ters

keep = temp;
temp = temp->next_arc;

return 0; // nodul nu a fost ters

void graf_liste::depth(nodgraf *g)


{
int vizitat[MaxN];
for(int i=0; i < MaxN; i++)
{
vizitat[i] = 0;
}
nodgraf *curent,**nd;
arc *temp;
stiva *stk=NULL;
{

while(g != NULL)
if(!vizitat[g->info])
{
cout << endl << "Componenta : " <<endl;
stk = push(stk,g);
do
{

stk = pop(stk,nd);
curent = *nd;
vizitare(curent,vizitat);
temp=curent->capat;
while(temp)
{
if(!vizitat[temp->destinatie->info])
stk=push(stk,temp->destinatie);

temp=temp->next_arc;
}
} while(stk != NULL);

}
g=g->next;

void graf_liste::breadth(nodgraf *g)


{
nodgraf *curent;
arc *temp;
coada *q,coada={NULL,NULL};
q = &coada;
int vizitat[MaxN];
for(int i = 0; i < MaxN; i++)
{
vizitat[i] = 0;
}

while(g != NULL)
{
if(!vizitat[g->info])
{
cout << endl << "Componenta : " << endl;
put(q,g);
do
{

curent = get(q);
if(!vizitat[curent->info])
{
vizitare(curent,vizitat);
temp = curent->capat;
while(temp)
{
if(!vizitat[temp->destinatie-

>info])

put(q,temp->destinatie);
}

temp=temp->next_arc;

}
} while(q->pred != NULL);

g = g->next;

int graf_liste::drum_minim(nodgraf *g,int sursa,int dest,int


precede[MaxN])
{
nodgraf *tmp;
arc *temp;
int distanta[MaxN],i,k,min,curent, perm[MaxN], dc, distanta_noua;
for(i = 0; i < MaxN; i++)
{
distanta[i]=precede[i]=graf_liste::Infinit;
perm[i]=-1;
}
distanta[sursa] = 0;
curent = sursa;
perm[sursa] = 1;
while(curent != dest)
{
dc = distanta[curent];
tmp = gaseste_nod(g,curent);
temp = tmp->capat;
while(temp)
{
distanta_noua = dc + temp->greutate;
tmp = temp->destinatie;

if(distanta_noua < distanta[tmp->info])

}
}

distanta[tmp->info] = distanta_noua;
precede[tmp->info] = curent;

temp = temp->next_arc;

for(i = 0; i < MaxN && perm[i] > 0; i++);

}
}

k = i;
min = distanta[k];
for(i = k; i < MaxN; i++)
{
if(perm[i] < 0 && distanta[i] < min)
{
min = distanta[i];
k = i;
}
}
curent = k;
perm[k] = 1;

return distanta[dest];

void graf_liste::stergere_nod(nodgraf *&temp,int sursa)


{
if(temp->info == sursa)
{
nodgraf *a = temp;
temp = a->next;
delete a;
}
else
{
stergere_nod(temp->next, sursa);
}
}
#endif //GRAF_LISTE_H

16.3 Traversarea unui graf


Un graf este n esen o reea, care de cele mai multe ori are
coresponden n lume real. Cum principala caracteristic a unei reele
este mobilitatea continu din interiorul su, se pune problema parcurgerii
grafului. n cazul altor structuri de date, vectori, liste i chiar arbori lucrurile
sunt clare: se pornea de la un capt i trecndu-se de la un element la
urmtorul se parcurgea integral structura fr ca un element s fie vizitat
de mai multe ori.
Graful fiind o structur de date mai general n care nodurile au mai
mult de un predecesor se pune deci problema trecerii o singur dat prin
fiecare nod. Pentru a complica mai mult problema se ia n considerare i
faptul c oricare nod al grafului este un posibil punct de start al traversrii,

lucru care demonstreaz c aceasta nu este unic, rezultatele variind de la


caza la caz.
Evitarea revenirii ntr-un nod vizitat se face asociind acestuia o
etichet care s indice acest lucru. Metodele de traversare a grafului sunt
bazate pe acest principiu, deosebindu-le doar modul n care stocheaz i
revin asupra unor direcii necercetate.
Cele dou metode sunt :
- traversarea n adncime (depth-first traversal);
- traversarea n lime (breadth-first traversal).
Pe scurt, cele dou traversri sunt asemenea drumului parcurs de
exploratori ntr-o peter, numai c n cazul traversrii n adncime avem
un singur explorator care se ntoarce de fiecare dat cnd ajunge la un
capt de drum la ultima intersecie, iar n cazul traversrii n lime sunt o
echip, fiecare lund-o pe un drum.
Odat traversat graful cu una dintre aceste metode, se creeaz o
pdure acoperitoare pentru el, lucru util pentru punerea n eviden a
componentelor sale conexe, dar i un arbore acoperitor. Arborele acoperitor
este un subgraf ce conine nodurile grafului iniial i doar attea arce nct
s fie construit un arbore.
Pentru un graf implementat prin intermediul unei matrice de
adiacen, ordinul de complexitate al operaiei de traversare este O (n2).
Graful cu n noduri, are matricea asociat de dimensiune nxn. Rezult c
timpul alocat prelucrrii unui nod n vederea gsirii tuturor nodurilor
adiacente este O (n); se parcurge linia de n elemente a nodului. Deci pentru
n noduri timpul necesar traversrii este de n*O (n) = O (n2). Dup cum se
observ, indicatorul depinde doar de numrul nodurilor, numrul arcelor
dintre noduri neavnd nici o influen.
n cealalt situaie, pentru un graf reprezentat cu ajutorul listelor
nlnuite, ordinul de complexitate al operaiei de traversare este cuprins
ntre O (n) i O (n2). Indicatorul depinde aici de numrul de arce al fiecrui
nod. Cel mai fericit caz este acela cnd nu exist nici un arc ntre noduri i
atunci traversarea are ordinul de complexitate minim O(n). Dac fiecare nod
al grafului are arce ctre toate celelalte n-1 noduri ale grafului, se obine
complexitatea maxim O (n2).
Procesul de traversare n adncime a unui graf, este unul de tip
backtracking, analogic cu traversarea n preordine a unui arbore.
Algoritmul folosete n acest scop un vector sau o list n care pune
nodurile vizitate i o stiv n care sunt puse nodurile adiacente nodului
curent. Odat vizitat un nod, traversarea se ndeprteaz n adncime pn
cnd ajunge la un capt de drum.
Fie nodul de start al parcurgerii, nodul notat cu X. Acesta este
etichetat ca vizitat i este trecut n list. Toate n nodurile adiacente lui X, Xi
cu i = 1..n, sunt puse n stiva de ateptare. Primul nod din stiv, X1, este
verificat dac nu este n list, caz n care este vizitat, fiind scos i pus n
list. n cealalt situaie, nodul se afla deja n list, el este scos din stiv i
este verificat nodul de sub el. Aceti pai sunt repetai pentru fiecare nod
adiacent al lui X1.
Altfel spus, se pleac pe drumul dat de primul nod adiacent al nodului
de start, primul nod adiacent al nodului deja vizitat i tot aa pn cnd se
ajunge la un nod al crui prim nod adiacent a fost vizitat sau nu exist fiind
un capt de drum. Atunci se trece la urmtorul nod adiacent pe care se
continu, dac este posibil, sau n acest moment, algoritmul se ntoarce la

penultimul nod vizitat i pleac pe al doilea nod adiacent al acestuia, n


condiiile n care el nu a fost vizitat i exist. Algoritmul se ntoarce atta
timp ct exist noduri n stiv.
B

F
D

Figura 16.9 Graf orientat


Parcurgerea n adncime a grafului orientat din figura 16.9 presupune
parcurgerea etapelor :
1. se alege nodul A ca punct de start al traversrii. Nodul A este
vizitat i este trecut n lista nodurilor pe la care s-a trecut. n stiv
sunt trecute nodurile ctre care are arce direcionate: B i C;

Noduri
adiacente

C
B

Lista nodurilor vizitate

Stiva nodurilor de vizitat


Figura 16.10 Paii primei etape

2. se scoate primul nod din stiv, C, i cum acesta nu a fost vizitat


(nu se afla n lista nodurilor vizitate) este trecut acum n list.
Nodurile sale adiacente, doar D, sunt trecute n stiv;

F
A C

D
B

Noduri
adiacente

Lista nodurilor vizitate


Stiva nodurilor de vizitat
Figura 16.11 Paii etapei 2
3. se scoate nodul D din stiv i este pus n list, deoarece nu a fost
vizitat. Nodurile sale adiacente, F i E, se pun n stiv;

A C D

Nu are noduri
adiacente

F
E
B

Lista nodurilor vizitate

Stiva nodurilor de vizitat


Figura 16.12 Paii etapei 3
4. se scoate nodul din stiv nodul F i este etichetat ca vizitat. n
acest punct se ajunge la un sfrit de drum i astfel trece la
urmtorul element din stiv;

F
A C D F E

Noduri adiacente

Lista nodurilor vizitate


Stiva nodurilor de vizitat
Figura 16.13 Paii etapei 4
5. se viziteaz nodul E. Iniial se trece n stiv nodul adiacent lui E,
anume nodul F, dar el este scos apoi din stiv existnd, deja n
lista nodurilor vizitate. n stiv rmne doar nodul B;

A C D F E B

Figura 16.14 Lista nodurilor vizitate


6. traversarea este completat prin vizitarea nodului B. Cele dou
noduri adiacente ale sale, F i C, sunt trecute n stiv, ns sunt
scoase apoi unul cte unul, ele fiind deja nsemnate ca vizitate.
Nive l 1
Nive l 2
Nive l 3
Nive l 4
Nive l 5
Nive l 6

A
C
D
F
E
B

Figura 16.15 Arbore acoperitor n adncime


pentru graful din figura 16.9
La scrierea codului surs, algoritmul se mbuntete fcndu-se o
cutare n list a nodurilor care se introduc n stiv pentru a se vedea dac
sunt sau nu deja vizitate. Traversarea grafului neorientat nu implic

modificarea n vreun fel a algoritmului, acesta fiind aplicat fr nici o


restricie.
Arborele acoperitor este construit odat cu lista nodurilor vizitate,
adugnd un nod la list se completeaz i arborele.
Traversarea grafului, folosind acest procedeu, d un rezultat diferit
pentru un nod de start altul dect A, acest lucru fiind valabil i pentru
arborele acoperitor din figura 16.9.
Procesul de traversare n lime a unui graf orientat sau neorientat,
este analog procesului de traversare n inordine a unui arbore, i const n
parcurgerea o singur dat a tuturor nodurilor din graf.
Deosebirile de cealalt metod constau n folosirea unei cozi de data
aceasta pentru a pstra nodurile de verificat, i n faptul c algoritmul se
ndeprteaz de nodul vizitat doar dup ce a examinat i vizitat, dac este
posibil, toi succesorii si. n schimb i aceast metod este aplicabil att
grafului orientat ct i neorienatat dnd rezultate ce variaz n funcie de
alegerea nodului de pornire.
Parcurgerea n lime a grafului din figura 16.9, presupune paii:
1. se alege nodul A ca punct de start al traversrii. Nodul A este
vizitat i este trecut n lista nodurilor pe la care s-a trecut. n
coad sunt trecute nodurile ctre care are arce (direcionate sau
nedirecionate, n funcie de tipul grafului): B i C;

C B

Lista nodurilor vizitate

Coada nodurilor de vizitat

Noduri
adiacente

Figura 16.16 Paii primei etape


2. sunt verificate nodurile adiacente i sunt vizitate dac nu se afl
deja n list. n momentul trecerii n lista nodurilor deja parcurse,
nodurile lor adiacente sunt adugate la coad, nodul F i C
adiacente lui B i nodul D adiacent lui C;

A B C
Lista nodurilor vizitate

D C F

Noduri
adiacente

Coada nodurilor de vizitat

Figura 16.17 Paii etapei 2


3. din coad trec n lista nodurilor traversate, nodurile F i D. Nodul
C exist deja n list i doar este scos din coad. Nodul F nu are
adiaceni i reprezint un capt de drum, nici un nod nefiind
adugat n coad. n schimb, coada este completat cu nodurile E
i F, care sunt adiacente lui D;

A B C F D
Lista nodurilor vizitate

F E

Noduri
adiacente

Coada nodurilor de vizitat


Figura 16.18 Paii etapei 3

4. traversarea este ncheiat prin adugarea nodului E la list.


Nodurile F i C au fost vizitate i nu se mai pun iar n list.

A B C F D E

Figura 16.19 Lista nodurilor vizitate


Ca i n cazul metodei precedente, algoritmul se optimizeaz
verificndu-se nainte de a fi pus n coada de ateptare dac nodul respectiv
se afl n list sau nu.

Nivel 1
Nivel 2
Nivel 3

A
B

Nivel 4

Figura 16.20 Arbore acoperitor n lime


pentru graful din figura 16.9
Arborele acoperitor este construit odat cu lista nodurilor vizitate.
Adugnd un nod la list se completeaz i arborele, iar toate nodurile
adiacente cu acesta i care nu au fost vizitate sunt adugate la arbore ca
noduri copii (ulterior devin noduri printe pentru nodurile adiacente din
graf).
n concordan cu rezultatul dat de traversarea n lime a arborelui,
i forma arborelui acoperitor variaz n funcie de nodul de start.

16.4 nchiderea tranzitiv a grafului


Graful este de cele mai multe ori reprezentarea matematic a
problemelor economice i nu numai, legate de transport, de costul
deplasrii dintr-un punct n altul, de durata realizrii acestuia. Cea mai
sugestiv aplicaie legat de implicarea grafului n rezolvarea acestor
probleme este cea a drumului minim, ns de multe ori este necesar s se
tie doar dac este drum ntre dou noduri. Soluia acestei probleme este
dat de realizarea unei matrice, numit nchiderea tranzitiv a matricei de

adiacen, care s arate pentru fiecare nod n parte unde se poate ajunge
plecnd din el.
O modalitate de creare a acesteia este dat de traversarea n
adncime a grafului. Traversnd graful din fiecare nod al su, se obin
attea liste cte noduri sunt, liste care arat n ce noduri se ajunge din
nodul de start. Acestea din urm se transpun ntr-o matrice M[n][n], care
are elementul mij =1 dac exist drum de la nodul Ni la nodul Nj i 0 n rest.
B

F
D

Figura 16.21 Exemplu de structur de graf


Pentru graful din figura 16.21 traversarea prin metoda DFS pornind
din fiecare nod are ca rezultat urmtoarele liste :
pornind din A avem: A, C, D, F, E, B;
pornind din B avem: B, C, D, F, E;
pornind din C avem: C, D, F, E;
pornind din D avem: D, F, E, C;
pornind din E avem: E, F, C, D;
pornind din F avem: F.
Matricea nchiderii tranzitive obinute prin intermediul listelor este:

1
0

0
MIT
0
0

1
1
0
0
0
0

1
1
1
1
1
0

1
1
1
1
1
0

1
1
1
1
1
0

1
1
1
.
1
1

(16.2)

Folosind matricea, crem graful extins numit nchidere tranzitiv.


Lum fiecare nod n parte, iar pe reprezentarea grafului iniial se deseneaz
sgei punctate ctre nodurile la care se ajunge i care nu sunt adiacente
lui.
nchiderea tranzitiv este prezentat n figura 16.22.

B
F
A

E
C

Figura 16.22 nchiderea tranzitiv a grafului din figura 16.9


Ordinul de complexitate al acestei operaii este foarte mare, i anume
O (n3), pentru c pentru fiecare dintre cele n noduri se aplic traversarea n
adncime, care are complexitatea maxim O (n2). Dei este uor de
implementat, pentru un graf foarte mare metoda consum multe resurse.
O alt soluie, mai elegant dar cu acelai grad de complexitate, a
fost dat de Stephan Warshall. n construirea algoritmului su, el a plecat
de la ideea urmtoare: lund nodurile Ni, Nj, Nk, (cu i, j, k = 1..n
i i j k ) i dac exist arcele Aik i Akj atunci exist drum de la nodul Ni
la nodul Nj, acest lucru nsemnndu-se n nchiderea tranzitiv a matricei de
adiacen.
Algoritmul iniializeaz matricea nchiderii tranzitive cu valoarea
matricei de adiacen a grafului i pune valoarea 1 pe diagonala principal
(evident exist arc de la un nod la el). n urma a trei cicluri dup variabilele
i, j, k (cu i, j, k = 0 ... n-1), elementele matricei nchiderii tranzitive
A[n][n] iau valoarea 1 dac a[i][k] = a[k][j], adic:
a[i][j] = a[i][k] & a[k][j]

(16.3)

n clasa graf_matrice, metoda asociat algoritmului este:


void graf_matrice::inchidere_tranzitiva()
{
int i,j,k;
int a_tranz[MaxLungGraf][MaxLungGraf];
//se creeza matricea nchiderii tranzitive iniial plecnd de la
//matricea de adiacen a grafului
for(i = 0; i<nr_noduri; i++)
for(j = 0; j<nr_noduri; j++)
if(i==j)
a_tranz[i][j] = 1;
else
a_tranz[i][j] = matm[i][j];
//se cerceteaza perechi de cte 3 noduri si se formeaza matricea
for(i = 0; i<nr_noduri; i++)
for(j = 0; j<nr_noduri; j++)
for(k = 0; k<nr_noduri; k++)
a_tranz[i][j] = a_tranz[i][k] && a_tranz[k][j];
//se afiseaza matricea de adiacen a grafului
for(i = 0; i<nr_noduri; i++)
{

for(j = 0; j<nr_noduri; j++)


cout << a_tranz[i][j] << " ";
cout << endl;

16.5 Problema drumului de lungime minim n graf


Revenim la problema iniial, cea a parcurgerii traseului Arad
Bucureti avnd cel mai mic cost. Soluia const n gsirea acelor orae prin
care trece traseul astfel nct suma distanelor parcurse s fie cea mai mic,
n raport cu lungimea altor posibile drumuri de parcurs. La nivelul grafului n
loc de orae, distane avem noduri i arce cu greutate.
Parial, problema este rezolvat deoarece folosind cele dou metode
de traversare a unui graf avem capacitatea de a afla ce noduri se afl pe
trase i astfel putem forma o serie de drumuri de urmat. Nu mai rmne
dect s vedem n cazul grafului cu greutate care drum are suma valorilor
arcelor minim sau n cazul grafului fr greutate care drum are mai puine
arce. Dei aceast soluia este simplu de implementat, ea este mare
consumatoare de resurse n cazul unui graf mare, aa c ne trebuie un
program care s combine cele dou etape, reducnd traversrile repetate
ale grafului la una.
Acest lucru este fcut de algoritmul Dijkstra, care examineaz toate
drumurile ce pornesc din nodul curent, actualiznd distanele dintre el i
celelalte noduri. Pentru a pstra nodurile prin care trece drumul cel mai
scurt, programul le reine ntr-o list pe care o notm cu L. n final lista
conine mulimea minim de noduri care s le conin pe toate cele care vor
forma efectiv drumul optim.
Nodurile care se adaug n aceast list sunt acele noduri ale grafului
la care se ajunge prin arce directe doar de la nodurile din lista L (ele
reprezint nodurile adiacente celor din L) i care au lungimea cumulat
pn n acel moment minim.
Pentru a nu calcula de fiecare dat distana minim pn n acel nod,
ea este atribuit ca informaie nodului, asemenea unei etichete. Ele sunt
implementate utiliznd un vector de lungime n, unde n este numrul de
noduri al grafului (vector[i] reine eticheta nodului i), sau crend o list cu n
elemente de tip etichet.
Drumul minim este gsit n momentul n care n lista L se afl nodul
destinaie.
Algoritmul const n paii:
Pasul 1. Se construiete lista L i elementele vectorului/listei
distanelor sunt iniializate cu o valoare foarte mare;
Pasul 2. Se alege nodul surs, el devenind i primul nod pus n lista
L. Valoarea etichetei corespunztoare lui ia valoarea 0;
Pasul 3. Se repet pn cnd nodul destinaie se afl n L. Sunt
analizate nodurile grafului care nu sunt n lista L.
Dac exist noduri Ni n care se poate ajunge prin arce directe de la
noduri din L, se calculeaz distana de la nodul de start pn la ele:
distana(Ni)=min(val_etichet(Ni),val_etichet(Nk)+greutate_arc(Nk ,Ni))

(16.4)

unde:
reprezint valoarea etichetei asociat nodului Ni;
val_etichet(Ni)
greutate_arc(Nk,Ni)) reprezint valoarea arcului dintre nodurile Nk i
Ni ;
este un nod din lista L de la care se ajunge prin
Nk
arc direct la nodul Ni care nu se afl n list.
Se adaug la lista L acel nod Ni care are distana(Ni) obinut minim.
Pentru el ca i pentru celelalte noduri pentru care s-au calculat distanele se
reactualizeaz etichetele:
val_etichet(Ni) = min(val_etichet(Ni), distana(Ni))

(16.5)

Dac nu exist nici un nod de acest tip atunci nu exist nici un drum
pn la destinaie.
Pasul 4. Dac nodul destinaie se afl n lista L, atunci valoarea
etichetei sale reprezint drumul de lungime minim. Pentru gsirea acestui
drum, se pornete napoi de la nodul final i folosind nodurile din L.
Pentru a nu pierde timp la Pasul 4 reconstituind drumul, aa cum s-a
ataat fiecrui nod o etichet, i se asociaz o list n care sunt memorate
nodurile precedente care au dat valoarea etichetei n acel moment. Nodul
nou introdus n lista L, iniializeaz lista drumului deja parcurs cu valorile
din lista predecesorului su direct i apoi l adaug i pe acesta.
4
B

7
A

3
2

20

2
C

Figura 16.23 Graf orientat cu greutate


Pentru a exemplifica metoda, se aplic algoritmul lui Dijkstra pentru a
calcula drumul minim de la nodul A la nodul F, noduri ce aparin grafului din
figura 16.23.
Se fac notaiile ajuttoare:
E(Ni) valoarea etichetei nodului Ni;
L(Ni) lista nodurilor prin care s-a ajuns la nodul Ni;
L
lista nodurilor care au fost luate n considerare.
Etapele parcurse sunt :
- se iniializeaz eticheta nodului A cu valoarea 0, E(A) = 0, iar
pentru celelalte noduri cu o valoare foarte mare, E(B) = E(C) =
E(D) = E(E) = E(F) = . Se pune nodul A n lista L;
- se calculeaz valoarea etichetei vecinilor nodului A, E(B) = 7 i
E(C) = 2. Cum nodul C are valoarea etichetei minim i nu se afl
n lista L, el este adugat la aceasta. n lista nodurilor precedente
lui C, L(C), se pune nodul A, iar L = { A, C };

se calculeaz valoarea etichetelor vecinilor nodului C, E(B) = 5 i


E(E) = 4. Vechea valoare al lui E(B), care este 7, este nlocuit de
noua valoare calculat, aceasta din urm fiind mai mic. Cum
nodul E are eticheta minim i nu se afl n lista L, este adugat la
aceasta i L(E) = { A, C }, iar L = { A, C, E };
se calculeaz etichetele pentru nodul F care este vecinul direct al
nodului E, E(F)=13. Dintre toate etichetele, cea a nodului are
valoarea minim, 5, i cum el nu este n L, este pus n aceast
list, deci L = {A, C, E, B}. n lista predecesorilor si sunt pui
predecesorii nodului de la care s-a ajuns la B, adic ai nodului C,
i acesta din urm, L(B) = {A, C};
E(B)=5
L(B)={ A, C }

E(D)=infinit

4
B

7
E(A)=0

3
2

20

2
C

E(C)=2
L(C)={ A }

E(F)=13

L = { A, C, E, B }

E(E)=4
L(E)={ A, C }

Figura 16.24 Pas intermediar


-

se calculeaz valoarea etichetelor vecinilor nodului B, E(D) = 9,


E(E) = 13 i E(F) = = 25. n cazul nodurilor E i F etichetele i
pstreaz vechile valori, care sunt mai mici. Cu toate c n acest
moment nodul E are eticheta cu valoare minim, el nu este ales ca
fiind urmtorul nod al drumului pentru c se afl deja n lista L.
Deci nodul care se traverseaz este D, iar L(D) = {A, C, B} i L =
{A, C, E, B, D};
se calculeaz valoarea etichetei pentru nodurile E i F (sunt noduri
adiacente directe pentru nodul D), E(E) = 11 i E(F) = 10. Pentru
nodul E valoarea etichetei nu este nlocuit cu cea nou. Nodul
care nu se afl n lista L i care are valoarea etichetei minim este
F. Este adugat la list i n acest moment cutarea ia sfrit.
Drumul minim este A C B D F.
E(B)=5
L(B)={ A, C }

E(D)=9
L(D)={ A, C, B }

4
B

7
E(A)=0

3
2

E
2

E(F)=10
L(F)={ A, C, B, D}
F

2
C

E(C)=2
L(C)={ A }

20

L = { A, C, E, B, D, F}

E(E)=4
L(E)={ A, C }

Figura 16.25 Drumul minim

Afiarea drumului minim i a lungimii sale folosind funcia din clasa


graf_liste are codul surs:
int lungime = x.drum_minim(g,start,stop,precede);
if(test != MAXINT)
{
cout<<endl<<"Drum minim "<<stop<<"<-"<<start<<" are lungimea :
"<<lungime<<" si este : ";
for(int i=stop;i>0&&(i !=MAXINT);i = precede[i])
cout<<i<<"<-";
}
else
cout<<"Nu exista drum de la nodul "<<start<<" la nodul
"<<stop;

16.6 Operaii de concatenare i deconcatenare cu grafuri


Se definete concatenarea a doua grafuri ca fiind operaia de
adugare a altui graf la unul din nodurile
grafului iniial respectnd
urmtoarele reguli:
- nodul capt al grafului al doilea se va lega printr-un arc de un nod
al primului graf; arcul este orientat pe direcia nod graf 1 ->capt
graf 2;
- se introduce de la tastatur informaia nodului unde se va aduga
graful al doilea;
- dac nodul capt al grafului 2 este nod n graful 1 i concatenarea
se face n acel nod nu se va mai face nici un arc, completndu-se
lista de arce a nodului din graful 1 cu arcele nodului din graful 2;
- dac nodul final al grafurilor difer, atunci ntre nodul final al
grafului 2 i cel al primului graf se va forma un arc a crui
informaie se va citi de la tastatur;
- dac al doilea graf se leag la nodul final al grafului 1 atunci nodul
final al grafului 2 devine nod final al noului graf obinut prin
concatenarea celor dou grafuri;
- dac n graful al doilea se afl minim dou noduri care se afl i
n primul graf, o condiie esenial de a face concatenarea celor
dou grafuri este c, dac exist n ambele grafuri arc n acelai
sens ntre cele dou noduri, acesta s aib aceeai greutate.
Funcia care verific aceast ultima condiie este urmtoarea:
int graf_liste::verificare(nodgraf *cap,nodgraf *cap2)
{
nodgraf *p,*q,*aux1,*aux2;
int k=1;
for(p=cap;p!=NULL;p=p->next)
for(q=cap2;q!=NULL;q=q->next)
{
if (q->info==p->info)
for(aux1=cap;aux1!=NULL;aux1=aux1->next)
/*nodurile comune le compar 2 cte 2 s vd care este greutatea
arcului dac exist vreunul*/
for(aux2=q->next;aux2!=NULL;aux2=aux2->next)
{

}
return k;
}

if(aux2->info==aux1->info)
if (verif_arc(p,aux1)!=verif_arc(q,aux2))
{
//dac au greutate diferit atunci
printf("\n Nu se poate face concatenarea !");
k=0;
return k;
}
}

Funcia care realizeaz concatenarea celor dou grafuri primete ca


date de intrare doi pointeri la capetele celor dou grafuri i returneaz 0
dac nu se poate face concatenarea i l dac a reuit. Funcia respectnd
condiiile de mai sus, adaug la lista nodurilor grafului 1 i nodurile care nu
sunt comune ale grafului 2, iar listele de arce ale acestor noduri sunt i ele
copiate. n cazul nodurilor comune, listele arcelor sunt doar completate cu
arce noi. Exist i cazuri cnd este nevoie s se creeze arce noi (cnd se
leag de exemplu nodurile finale ale grafurilor) iar atunci greutatea lor este
citit de la tastatur.
Funcia este:
int graf_liste::concatenaregraf(nodgraf *cap,nodgraf *cap2)
{
int k;
nodgraf *p,*q,*aux,*ultim1,*ultim2;
arc *r;
//verifica dac se poate face concatenarea
if(verificare(cap,cap2)==0) return 0;
printf("\n La ce nod are loc concatenarea ?");
/*se introduce nodul unde se face concatenarea i se verific dac el
exist n primul graf*/
k=citire();
ultim1=cauta_nodgraf_final(cap);
// memorez ultimul nod al grafului 1
ultim2=cauta_nodgraf_final(cap2);
//memorez ultimul nod al grafului 2
/*se insereaz n lista nodurilor primului graf nodurile necomune din
al doilea graf*/
for(q=cap2;q!=NULL;q=q->next)
{
p=cauta_nodgraf(cap,q->info);
if(p==NULL) cap=ins_nodgraf(cap,q->info);
}
/* se copiaz pentru noduri i lista arcelor, iar pentru acele noduri
care existau se completeaz aceast list*/
for(q=cap2;q!=NULL;q=q->next)
{
p=cauta_nodgraf(cap,q->info);
if(p!=NULL) for(r=q->capat;r!=NULL;r=r->next_arc)
/*functia ins_arc(nod cap,int surs,int destinaie,int greutate)
insereaz un nou arc catre nodul cu informaia destinaie de greutate
greutate n lista arcelor nodului cu informaia surs din graful cu
capt cap */
ins_arc(cap,p->info,r->destinatie->info,r->weight);
}

/* dac nodul unde se face concatenarea nu este capt al grafului 2


atunci se face arc ntre cele dou cu citirea informaiei de la
tastatur*/
if(cap2->info!=aux->info)
{
printf("\n Distanta dintre nodul ales si capatul grafului 2 este ?");
k=citire();
ins_arc(cap,aux->info,cap2->info,k);
}
/* dac al doilea graf nu se leag la nodul final al primului graf i
dac nodurile finale nu sunt aceleai, atunci ele se leag printr-un
arc */
if(aux->info!=ultim1->info)
if(ultim1->info!=ultim2->info)
if(cauta_nodgraf(cap,ultim2->info)==NULL)
/* funcia cauta_nodgraf(nod * cap,int k) cauta un nod cu informaia k
n graful cu capt cap, returnnd adresa nodului sau NULL */
{
printf("\n Distanta dintre nodul final al grafului 2 si
cel al lui 1 este ?");
k=citire();
//se creaz arc intre nod final al grafului 1 si cel al grafului 2
ins_arc(cap,ultim2->info,ultim1->info,k);
}
return 1;
}

Pentru a exemplifica procesul de concatenare a dou grafuri lum


grafurile din figura 16.26.

1
9
8

0
5

3
2

Graf 1

Graf 2

Figura 16.26 Exemple de grafuri


Dac se dorete concatenarea grafului 2 la graful 1 n nodul cu
valoare 2 atunci graful care va rezulta va fi:

1
7

4
10

3
2

6
4
6

Figura 16.27 Concatenarea a dou grafuri


Informaia arcului dintre nodul cu informaia 6 i cel cu informaia 4 a
fost introdus de la tastatur fiind ceruta de funcia de concatenare.
Deconcatenarea unui graf este operaia de rupere a acestui graf n
dou grafuri diferite din punct de vedere al nodurilor care le formeaz i al
arcelor ce le leag.
Funcia care realizeaz deconcatenarea grafului primete ca date de
intrare pointer la captul grafului de deconcatenat i ea va returna n
pointer la captul celui de-al doilea graf.
Pentru a exemplifica deconcatenarea se consider graful din figura
16.28.

2
3

11

3
4
4

Figura 16.28. Deconcatenarea unui graf

Graful rmas

Noul graf

Figura 16.29. Deconcatenarea unui graf


n momentul lansrii n execuie se cere s se introduc informaia
nodurilor care formeaz al doilea graf. Astfel se va crea lista nodurilor
noului graf care se va obine. Primul lucru care se face dup aceasta este
crearea listelor de arce pentru aceste noduri. Pentru fiecare nod al noului
graf se verific dac el are arce cu nodurile acestui nou graf cu ajutorul
funciilor:

/*funcia verific dac nodul referit prin *s are arc ctre nodul
referit prin *d, i ntoarce ca rezultat greutatea arcului sau 0 dac
nu este arc*/
int graf_liste::verif_arc(nodgraf *s,nodgraf *d)
{
arc * p,*aux;
int gasit=0;
for(p=s->capat;p!=NULL;p=p->next_arc)
if(p->destinatie==d) {
gasit=1;
aux=p;
return aux->weight;
}
if (gasit==0) {
return 0;
}
else return aux->weight;
}
/* funcia primete ca date de intrare pointer la captul grafului i
informaia nodului pe care l caut i va returna adresa nodului
cutat sau NULL*/
nodgraf *cauta_nodgraf(nodgraf * cap,int info)
{
nodgraf *p,*q;
int gasit=0;
for(p=cap;p!=NULL;p=p->next)
if(p->info==info) {
gasit=1;
q=p;
}
if(gasit==0){
return NULL;
}
else return q;
}

Odat creat lista nodurilor noului graf i a listelor de arce asociate


acestora, se va reactualiza lista nodurilor i a arcelor grafului iniial. Pentru
a realiza acest lucru trebuie respectate urmtoarele:
- nodurile celui de-al doilea graf care se formeaz i care sunt surs
sau destinaie n arce numai cu noduri care formeaz i ele al
doilea graf, sunt terse dintre nodurile grafului iniial;
- nodurile
grafului care se formeaz i care sunt i surs i
destinaie n arce cu noduri care nu intr n al doilea graf rmn n
primul graf, dar se terg arcele cu nodurile care nu rmn; funcia
care verific dac un nod respect aceast condiie sau nu este:
/* funcia primete ca date de intrare referine la captul grafului
iniial, la al celui nou i la nodul care se verific ; ea va returna
0 dac nu are legtur cu noduri care rmn n graful iniial i o
valoare diferit de 0 n caz contrar*/
int graf_liste::verif_stergere(nodgraf *cap,nodgraf *cap2,nodgraf *q)
{

nodgraf *p,*z;
int k=0;
int vb=0;
for(p=cap;p!=NULL;p=p->next)
{
if(cauta_nodgraf(cap2,p->info)==NULL)
{
z=cauta_nodgraf(cap,q->info);
if(z!=NULL) k=verif_arc(p,z);
if(k!=0) vb=k;
}
}
return vb;
}

Funciile care realizeaz deconcatenarea unui graf sunt:


/* functia se apeleaz dupa funcia deconcatengraf i are ca scop
reactualizarea listei de noduri a grafului iniial primeste ca date de
intrare referinta la capetele celor doua grafuri*/
void graf_liste::stergerenodgrafuri(nodgraf *&cap,nodgraf *cap2)
{
int t;
nodgraf *p,*q,*z,w;
for(p=cap;p!=NULL;p=p->next)
for(q=cap2;q!=NULL;q=q->next)
{
z=cauta_nodgraf(cap,q->info);
if(z!=NULL)
if(z->capat==NULL) sterg_arc(p,z);
else
{
t=verif_stergere(cap,cap2,q);
if(t==0){
z->capat=NULL;
}
}
}
for(q=cap2;q!=NULL;q=q->next)
{
p=cauta_nodgraf(cap,q->info);
if(p!=NULL)
if(p->capat==NULL) cap=sterg_nodgraf(cap,p->info);
}
}
/* funcia realizeaz deconcatenarea grafului iniial crend un nou
graf
primete ca date de intrare referinta la captul primului graf i
ntoarce adresa captului noului graf*/
nodgraf * graf_liste::deconcatengraf(nodgraf * &cap)
{
nodgraf *cap2,*p,*q,*w,*z;
arc *r;
int k,nd,arc;
cap2=NULL;
/*citete de la tastatur informaia nodului capt al noului graf*/

printf("\n Nodul capat al grafului al 2-lea este :");


k=citire2(&nd,cap,MAX);
/*o dat citit informaia este creat i inserat un nod cu aceast
informaie n lista de noduri */
cap2=ins_nodgraf(cap2,nd);
if(k==0)
{
printf("\n Urmatoarele nodgrafuri sunt:");
k=citire2(&nd,cap,MAX);
cap2=ins_nodgraf(cap2,nd);
//se creeaz i celelalte noduri
while(k==0)
{
k=citire2(&nd,cap,MAX);
cap2=ins_nodgraf(cap2,nd);
}
}
/* n secvena urmtoare se creeaz listele de arce ale nodurilor
noului graf ; se parcurge lista nodurilor iniiale verificndu-se care
se afl n noul graf ; dac se gsete un astfel de nod se verific
dac are arce cu alte noduri ale noului graf ; cnd se gsesc aceste
arce, ele se scriu n noul graf i se terg din graful iniial din
listele acelor noduri*/
for(p=cap2;p!=NULL;p=p->next)
{
q=cauta_nodgraf(cap,p->info);
//caut echivalentul lui n graful iniial*/
for(z=cap2;z!=NULL;z=z->next)
{
w=cauta_nodgraf(cap,z->info);
arc=verif_arc(q,w);
/* funcia verific dac exist arc
intre nodurile cu adresele q i w*/
if(arc!=0) {
/*dac se gsete arc se scrie n graful
nou i se terge de aici*/
ins_arc(cap2,q->info,w->info,arc);
sterg_arc(q,w);
}
}
}
return cap2;//returneaz adresa nodului capt a noului graf }

17. TABELE DE DISPERSIE


17.1 Structura de date de tip tabela de dispersie
Cel mai eficient algoritm de cutare este acela n care fiecrei valori
din colecia de elemente i se asociaz o poziie unic n cadrul mulimii.
Astfel, pe baza valorii cutate se determin poziia acesteia n cadrul
coleciei.
n cadrul vectorilor, aceast abordare se implementeaz cu uurin
deoarece asocierea dintre valoarea unei chei numerice ntreag i poziia
acesteia n vector se realizeaz prin:
- definirea unui vector cu numr de elemente egal cu valoarea
maxim posibil a cheii de cutare;
- se stabilete o valoare neutilizat n cadrul problemei de rezolvat
pentru a indica dac elementul cu cheia cutat exist deoarece
vectorul reprezint o zon de memorie continu, se implementeaz
principiul conform cruia elementele exist sau sunt terse logic;
pentru o cheie de cutare ce ia valori n intervalul [0255],
valoarea -1 este fi utilizat pentru a indic inexistena elementului
cutat n colecie.
Principalul avantaj al cutrii bazate pe acces direct este dat de
rapiditate cu care se gsete elementul cutat sau este indicat inexistena
acestuia.
Pentru structura element funcia de regsire este descris n secvena
de cod:
struct element
{
int cheie
int valoare
}
int ValoareVector(element* vector, int n, int cheie)
{
if(cheie >= n) return -1;
else if(vector [cheie].cheie == -1) return -1;
else return vector [cheie].valoare;
}

Cu toate acestea, n practic este evitat aceast soluie direct care,


dei minimizeaz timpul de regsirea prezint numeroase dezavantaje
generate de:
- dimensiunea memoriei ocupate spaiul de memorie, MEMORIE,
necesar implementrii acestei structuri este dat de relaia
MEMORIE = maxim(valoare cheie cutare) * dimensiune(element)

(17.1)

iar n cazul n care valoarea maxim este foarte mare atunci


trebuie s fie rezervat un spaiu considerabil;
gradului de utilizare a spaiului deoarece dimensiunea structurii
este dependent de valoarea maxim a cheii de cutare, nu se
ine cont de numrul real de elemente utilizate; cea mai
nefavorabil situaie este dat de un raport foarte mic dintre

numrul de elemente i valoarea maxim a cheii; de exemplu, se


consider o aplicaie informatic ce gestioneaz studenii din
cadrul unei faculti reprezentai de structura:
struct Student
{
char nume[20];
int varsta;
char facultate[20];
int nrMatricol;
}

pentru a minimiza timpul de regsire, se implementeaz o


structur cu acces direct n care numrul matricol al studentului
este egal cu poziia n cadrul coleciei a respectivului element; n
cazul n care valoarea maxim a cmpului nrMatricol este egal
55630, iar numrul real de studeni este egal cu 1450 rezult un
spaiu ocupat egal cu
MEMORIE=max(nrMatricol)*dimensiune(Student)=55630*48=2,54 MB
(17.2)
iar gradul de utilizare a acestui spaiu este egal cu:
GUM =

MEM elemente
1450 * 48
*100 =
*100 = 2,6%
MEM
55630 * 48

(17.3)

tipului cheii de cutare acesta trebuie s aib un tip numeric


ntreg deoarece trebuie s corespund indexului cu care se
acceseaz elementele unui vector.
Pentru a remedia dezavantajele utilizrii structurilor cu acces direct
sunt definite tabelele de dispersie, hash tables, ce reprezint colecii de date
n care pe baza unei funcii hash cheia de cutare este pus n
coresponden cu poziia elementului n cadrul coleciei. Avantajul acestei
tip de structur de stocare i cutare este dat de:
- utilizarea mai eficient a spaiului fr a se stoca memorie pentru
elemente care nu sunt utilizate n cazul aplicaiei de gestiune a
studenilor se observ c din totalul de 55630 de elemente, doar
1450 sunt utilizate; faptul se datoreaz concentrrii valorilor cheii
de cutare n intervalul [54130, 55630]; reducerea spaiului
ocupat de tabela de dispersie se realizeaz n aceast situaie prin
implementarea unei funcii hash: [54130, 55630] [0, 1500]
care s reduc valoarea maxim a cheii de cutarea prin
conversia valorii din intervalul [54130, 55630] ntr-o valoare din
intervalul [0, 1500]; o astfel de funcie hash cu un nivel de
complexitate sczut este
-

hash(X) = X modulo 1500, cu X 54130,55630


-

(17.4)

implementarea de chei alfanumerice prin utilizarea unei funcii


care s prelucreze cheia de cutare este depit bariera care
limita pentru structurile cu acces direct tipul cheii de cutare la o

valoarea numeric ntreag; n aceast situaie rolul funciei hash


este de a translata valoarea alfanumeric ntr-o valoare ntreag
pozitiv; de exemplu, n limbajul Java este implementat funcia
hash pentru un string
hash(S) = s[0]*31n-1+s[1]*31n-2++s[n-2]*31+s[n-1]

(17.5)

unde:
s[i] codul ASCII al caracterului i din ir;
n dimensiunea irului de caractere.
Pentru cheia alfanumeric cu valoarea salut aplicarea funciei hash
conduce la obinerea valorii
hash(salut)=115*314+97*313+108*312+117*31+116=109202173 (17.6)
valoarea obinut este introdus din nou ntr-o funcie hash care s
identifice poziia corespondent din mulime pentru aceast valoare
numeric foarte mare.
Dezavantajul tabelelor de dispersie n cadrul proceselor de cutare
este descris de:
- efortul suplimentar de prelucrare dat de funcia hash care poate
avea n unele situaii un nivel de complexitate ridicat;
- apariia n cadrul tabelei a coliziunilor acestea sunt generate de
situaia n care dou valori Xh i YH ce aparin domeniului de
definiie a funcie hash conduc la obinerea de valori identice,
hash(Xh) = hash(Yh); evitarea coliziunilor implic realizarea de
operaii suplimentare prin care s se identifice poziia elementului
n cadrul mulimii; printre cele mai utilizate tehnici de evitare a
coliziunilor se numr chaining, re-hashing, linear probing,
quadratic probing i overflow area, [Will96], [Seng94].
Din aceste puncte de vedere, tabela de dispersie utilizat n procesul
de regsire a informaiei se descrie ca o structur n care datele sunt
stocate n tabele cu adresare direct, iar poziia acestora este determinat
prin prelucrarea cheii de cutare cu o funcie hash, figura 17.1.
Cheie de cutare
Funcie hash
Valoarealfanumeric

Valoarenumeric
Valoare[Elem1, Elemn]

EVITARE COLIZIUNI

Elem1

Elemn/2-1 Elemn/2 Elemn/2+1

Tabel cu acces direct

Figura 17.1 Tabela de dispersie

Elemn

17.2 Chei si funcii hash


Pentru a identifica n mod unic fiecare nregistrare sunt utilizate valori
dintr-un domeniu bine definit. Asocierea dintre aceste valori unice i
nregistrrile pe care le reprezint este de unu la unu. n funcie de tipul
valorii cu rol de cheie se identific:
- chei
numerice
reprezentate
prin
intermediul
tipurilor
fundamentale definite de limbajul de programare utilizat;
- chei alfanumerice reprezentate prin iruri de caractere;
- chei compuse definite pe baza a mai multor atribute;
Rolul funciei hash este de a prelucra cheia asociat fiecrei
nregistrri i de a determina poziia n cadrul tabelei de dispersie a
elementului respectiv. Deoarece nu exist o funcie hash general, iar
alegerea acesteia se face n funcie de caracteristicile mulimii de valori
chei, sunt utilizate modele matematice bazate pe:
- mprire n modul; aceast metod are un grad ridicat de
utilizare n diferitele modele de implementare a tabelelor de
dispersie prin prisma complexitii sczute a modelului i a
uurinei n implementare; indiferent de forma cheii de cutarea,
aceasta este transformat ntr-o valoare numeric i apoi
transpus n mulimea [0nht-1], unde nht reprezint
dimensiunea tabelei de dispersie; acest tip de funcie are asociat
modelul:
pozitie_tabela = val_cheie % val_baza

(17.7)

unde:
pozitie_tabela valoarea hash obinut;
val_cheie
valoarea numeric ce identific unic fiecare
nregistrare;
val_baza
valoarea numeric stabilit la definirea modelului;
Valoarea utilizat ca baz n modelul funciei hash este dat de
dimensiunea tabelei de dispersie; obiectivul definirii funciei hash
este de a obine un model care s minimizeze mulimea de chei
diferite ce conduc la aceeai valoare pozitie_tabela; n acest sens
se utilizeaz numere prime apropiate de numrul total de
nregistrri; astfel, dac cheile nregistrrilor definesc o mulime
continu de valori unice, valorile hash determinate vor acoperi
toat mulimea [0..val_baza-1]; pentru a stabili dimensiunea
iniial a tabelei de dispersii sunt alese numere prime particulare;
de exemplu limbajul Java definete o clas HashTable ce descrie o
tabel de dispersie cu dimensiunea iniial egal cu 101; valoarea
nu este aleas la ntmplare deoarece permite redimensionarea
tabelei la o nou valoare prim; pentru cazuri generale, este
indicat s se foloseasc un numr prim determinat prin relaia:

dimensiune_hash = (4*i+3) cu i=0, 1, 2, 3,


-

(17.8)

nmulirea cu un numr real aleatoriu i prelucrarea ulterioar a


prii zecimale valoarea cheii de cutare este nmulit cu un
numr din intervalul [0;1) ce este ales aleatoriu; n urma

nmulirii se obine o valoarea temporara, temp, a crei parte


zecimal are valori cuprinse n intervalul [0 ;1); nmulind temp
cu dimensiunea tabelei de dispersie, dimensiune_hash, se obine
o valoare n intervalul [0, dimensiune_hash-1] ce reprezint
poziia elementului cutat n tabel; relaia 17.9 descrie procesul
de determinare a valorii hash:
val_hash=((val_cheie*random[0;1))[(val_cheie*random[0;1))])*nth
unde:
val_hash
val_cheie
random[0;1)
nth
-

valoarea hash calculat;


valoarea cheii de cutare;
numr aleatoriu din mulimea [0;1);
dimensiunea tabelei de dispersie.

utilizarea unui bloc de bii obinut prin prelucrarea cheii de


cutare de exemplu se definete funcia care multiplic valoarea
codului i care determin valoarea hash pe baza celor 10 bii din
interiorul acesteia; dac se consider cheile reprezentate pe 32
de bii atunci relaia este:

val_hash = ((val_chei * val_cheie)>>11) % nhash

(17.9)

(17.10)

prelucrarea codurilor ASCII ale caracterelor alfanumerice n


cazul n care cheia de cutare este dat de o secven de
caractere atunci este necesar ca aceasta s fie prelucrat pentru
a se genera un numr ntreg care s indice poziia elementului n
tabel; prelucrarea se face pe baza codului ASCII asociat
caracterului; pe baza primului caracter din cheie se definete
relaia:
val_hashs1 = string_cheie[0] % 255

(17.11)

unde:
valoarea hash calculat;
val_hashs1
string_cheie valoarea cheii de cutare;
Relaia anterioar definete un model cu un nivel de complexitate
sczut ce este destinat gestiunii unei colectiviti mici de
elemente; n cazul n care valoarea cheii reprezint numele unei
persoane, modelul este ineficient deoarece genereaz multe
coliziuni pentru nume diferite dar care ncep cu acelai caracter;
rafinarea acestui model se poate face prin preluarea mai multor
caractere din irul respectiv; de exemplu se definete o nou
relaie
val_hashs2=(string_cheie[0]+string_cheie[lungimestring_cheie] )%dim_has

unde:
val_hashs2
string_cheie
lungimestring_cheie

valoarea hash calculat;


valoarea cheii de cutare;
dimensiunea irului de caractere;

(17.12)

dim_hash
dimensiunea tabelei de dispersie;
ce ia n calcul primul i ultimul caracter; de asemenea, pentru a
nu reduce dimensiunea tabelei la maxim 255 elemente, se
utilizeaz un numr prim, dim_hash suficient de mare; alte funcii
hash de prelucrare a cheilor alfanumerice analizeaz toate
caracterele din ir; de exemplu relaia
lungime _ cheie

ASCII (string _ cheie[i])

val_hashs3 =

% dim_hash

(17.13)

i 1

unde:
valoarea hash calculat;
val_hashs3
string_cheie
valoarea cheii de cutare;
lungime_cheie
dimensiunea irului de caractere;
dim_hash
dimensiunea tabelei de dispersie;
determin valoarea hash pe baza sumei codurilor ASCII asociate
tuturor caracterelor din cheie;
-

nmulirea cu un numr real definit; aceast valoare este


determinat prin evaluarea expresiei m

m 1

5 1
sau a expresiei
2

5 1
; funcia hash definit pe baza acestui model este
2

f(val_cheie) = [val_cheie * m]

(17.14)

acest model este implementat n cadrul funciile de rehashing ce


sunt utilizate n cadrul procedurilor de rezolvare a coliziunilor.
Dificultatea n definirea funciilor hash const n identificare acelui
model care s minimizeze numrul coliziunilor i care s genereze o
distribuie uniform a valorilor pe ntreg intervalul.

17.3 Evitarea coliziunilor


Funciile de evitare a coliziunilor definesc metode de regsire a
elementelor ce sunt descrise de chei cu valori diferite dar care conduc la
valori hash identice ceea ce implic poziii identice n cadrul structurii:
- chaining implementreaz lucrul cu liste fiecare poziiei din
cadrul tabelei de dispersie conine captul unei liste de elemente
cu valori hash egale; regsirea unui element presupune
determinarea poziiei n cadrul tabelei prin calcularea valorii hash
i parcurgerea secvenial a listei ataate poziiei respective dup
valoarea cheii de cutare, figura 17.2;

Tabel de dispersie

valoare cheie
hash(x) = k

legtura nod list

hash(y) = k
hash(z) = k

NULL

k+1

Figura 17.2 Metoda chaining de evitare a coliziunilor n tabele de dispersie.


-

re-hashing presupune aplicarea n cascad a aceleiai funcii hash


sau a altui model dintr-o mulime de funcii pn cnd valoarea
obinut reprezint o poziie liber din cadrul tabelei de dispersie;
la fiecare pas al procesului de regsire, valoarea cheii de cutare
este introdus ntr-o list de funcii hash pn cnd se identific
elementul cu valoarea cutat sau nu mai exist alte posibiliti
de a recalcula valoarea hash; tipul i numrul de funcii hash
aplicate valorilor intermediare descriu o procedur bine definit
de cutare, respectiv iniializare element nou, pentru a conduce
de fiecare dat la aceleai rezultate, figura 17.3;
Tabel de dispersie

hash1(x) = k

hash1(y) = k
k+n
hash2(y) = k+n
hash3(y) = k+m

hash1(z) = k+n

k+m

Figura 17.3 Metoda rehashing de evitare a coliziunilor n


tabele de dispersie
-

linear probing se bazeaz pe cutarea secvenial a primei poziii


libere n care s fie inserat elementul nou, poziie aflat la stnga
sau la dreapta coliziunii; n cazul cutrii, procesul presupune
verificarea elementelor adiacente poziiei indicate de valoarea
hash; cu toate c are un grad sczut de complexitate, aceast
metod de evitare a coliziunilor are ca efecte secundare gruparea
coliziunilor de acelai tip n aceeai zon, cluster, fapt care
conduce la creterea probabilitii de apariie a coliziunilor pentru
valorile hash adiacente, figura 17.4;

Tabel de dispersie

hash(y) = k
verificare poziii
adiacente

hash(x) = k

k+1

hash(z) = k+1

k+2

Figura 17.4 Metoda linear probing de evitare a coliziunilor n


tabele de dispersie
-

quadratic probing este o metod de tipul linear probing care evit


crearea grupurilor de coliziuni prin utilizarea unui pas de regsire
a urmtoarei poziii libere diferit de 1; astfel, n caz de coliziuni au
loc salturi n tabela de dispersie din dou n dou poziii sau din
patru n patru; n general, pentru a determina urmtoare poziie
n care s se insereze un element nou sau n care s se caute un
element existent este dat de funcia:
poziie = hash(X)+c*i2

(17.15)

unde:
poziie

noua poziie din tabela de dispersie n care se insereaz sau


se caut un element;
X
valoarea cheii asociate elementului;
hash(X) poziia indicat de valoarea hash a elementului care se
adaug sau se cut;
c
valoare constant definit n mulimea {1, 2, 4};
i
numrul operaiei de rehash sau numrul de poziii verificate.
Prin utilizarea de repoziionri pe poziii neadiacente se evit
gruparea coliziunilor n aceeai zon din tabela de dispersie,
figura 17.5;
Tabel de dispersie

hash(y) = k

hash(x) = k

hash(y)+2*12
k+2
hash(y)+2*2

hash(z) = k+2

k+8

Figura 17.5 Metoda quadratic probing de evitare a coliziunilor n


tabele de dispersie

overflow area presupune o abordare ce mparte tabela de


dispersie n dou zone, primar pentru reinerea elementelor
iniiale i secundar alocat elementelor ce genereaz coliziuni; la
apariia unei coliziuni, fie la operaia de regsire sau la adugarea
unui element nou, se utilizeaz un element al zonei secundare
pentru a reine noua valoare sau pentru a continua cutarea;
accesul la zona secundar se realizeaz doar prin intermediul unui
pointer din zona primar, funcia hash negenernd poziii n acest
interval de valori; aceast metod, descris n figura 17.6,
permite o regsire mai rapid a informaiilor dect metoda
chaining care presupune parcurgerea de liste;
Tabel de dispersie

hash(x) = k

zon principal

hash(y) = k

zon overflow

Figura 17.6 Metoda overflow de evitare a coliziunilor n tabele de dispersie


Se observ n cazul tabelelor de dispersie c probabilitatea de apariie
a coliziunilor la inserare sau la cutare crete proporional cu gradul de
utilizare a tabelei. n cele mai multe cazuri, funciile hash cu un grad redus
de complexitate nu conduc la rezultate unice pentru valori de intrare
distincte. Din acest motiv, cu ct tabela pune la dispoziie un numr din ce
n ce mai mic de poziii disponibile, crete riscul de a avea numeroase
elemente cu chei de cutare diferite care ar trebui s se regseasc pe
poziii identice. Metodele de evitare a coliziunilor rezolv parial problema
deoarece ele plaseaz elementele noi pe alte poziii definind o nou cauz
pentru coliziuni viitoare.
Pentru a rezolva aceast situaie i pentru a menine eficiena
operaiei de cutare la un nivel acceptabil, astfel nct s nu se parcurg
toat mulimea de elemente la fiecare operaie de regsire de informaie, se
analizeaz gradul de utilizare a tabelei hash, GUhash.

GU hash

NPO
*100
NTP

unde:
NPO numrul de poziii ocupate;
NTP numrul total de poziii.

(17.16)

care este meninut la o valoarea mai mic de 50%. n ciuda faptului c are
loc o gestiune ineficient a spaiului, eficiena abordrii se bazeaz pe o
vitez de regsire mare. Pentru a menine valoarea indicatorului GUhash sub
limit impus, tabela se redimensioneaz prin dublarea numrului de poziii.
n cazul n care modelul funciei hash este bazat pe mprirea n modul la
dimensiunea acesteia, redimensionarea trebuie s conduc la o nou
dimensiune care s reprezinte tot un numr prim. Avnd n vedere
importana acestor tipuri de numere, lucru evideniat la descrierea tipurilor
de funcii hash, redimensionarea se face prin intermediul relaiei
dimensiune_noua = dimensiune_curenta*2 + 1

(17.17)

n cazul limbajului Java, unde dimensiunea iniial a tabelei de


dispersie este 101, redimensionarea ulterioar a acesteia utiliznd relaia
descris conduce la o serie de valori care reprezint tot valori prime. De
exemplu 203 i 407. n cazul n care relaia utilizat pentru a determina
numrul prim este
dimensiune_hash = (4*i+3) cu i=0, 1, 2, 3,

(17.18)

redimensionarea se realizeaz prin nlocuirea lui i cu 2*i. Astfel, noua


dimensiune va fi reprezentat tot de un numr prim.

17.4 Implementare
n continuare se exemplific printr-un program scris n limbajul de
programare C++ implementarea unei tabele de dispersie pentru nregistrri
de tip Student, cutrile realizndu-se dup numrul matricolei. Ca funcie
de dispersie a fost ales un exemplu simplu cu ajutorul operatorului modulo
(%). Dimensiunea implicit a tabelei a fost aleas 101, o valoare
cuprinztoare pentru exemplul ales.
Tabela de dispersie este implementat sub forma unui masiv
unidimensional alocat dinamic, ale crui elemente sunt liste care conin
datele propriu zise. Acest mod de implementare permite tratarea
coliziunilor, n cazul n care pentru indexul generat exist o nregistrarea n
tabel, noua nregistrare va fi adugat ca element la lista simplu nlnuit
de la nodul respectiv.
#include "stdafx.h"
#include <iostream>
using namespace std;
//structura de date stocata in tabela de dispersie
//pentru simplificarea codului s-a utilizat o structura
struct Student
{
int matricola;
char nume[20];
char adresa[30];
char localitate[20];
char e_mail[20];
char telefon[15];

void Afisare()
{
cout<<"Matricola:
cout<<"Matricola:
cout<<"Matricola:
cout<<"Matricola:
cout<<"Matricola:
cout<<"Matricola:
}

"<<
"<<
"<<
"<<
"<<
"<<

matricola <<endl;
nume <<endl;
adresa<<endl;
localitate<<endl;
e_mail <<endl;
telefon <<endl;

};
//elementele tabelei de dispersie
//pentru simplificarea codului s-a utilizat o structura
struct NodDate
{
NodDate *urm;
Student data;
};
//Cheia este matricola studentului
//
class HashStudent
{
int size;
NodDate ** elem;
//functie simpla de dispersie care utilizeaza operatorul modulo
//pentru a genera pozitia in tabela
int HashFunction(int cheie)
{
return cheie%size;
};
//aloca memorie pentru structura si initializeaza cu null
valorile
void AlocaMemorie()
{
elem = new NodDate*[size];
for (int i=0;i<size;i++)
elem[i] = NULL;
}
void ElibereazaMemorieLista(NodDate *&);
public:
HashStudent():size(101)
{
AlocaMemorie();
}
~HashStudent();
HashStudent(int iSize): size (iSize)
{
AlocaMemorie();
}
int Insereaza(Student data);
Student Cauta(int matricola);
int Sterge(int matricola);
void HashStudent::Afiseaza();
};

//insereaza un element pe o pozitie generata de functia de dispersi

//pe baza valorii matricolei din structura


int HashStudent::Insereaza(Student s)
{
int index = -1;
if (s.matricola<0)
return index;
if (elem!=NULL)
{
index = HashFunction(s.matricola);
//prima inregistrare
NodDate *nod_nou = new NodDate;;
nod_nou->urm = NULL;
nod_nou->data = s;
if (elem[index]== NULL)
{
elem[index] = nod_nou;
}
else //coliziune
{
NodDate *t = elem[index];
while(t->urm!=NULL)
t = t->urm;
t->urm = nod_nou;
}
}
return index;
}
//cauta un element dupa chiea matricola
//in cazul in care nu este gasita nici o inregistrare este
//returnata o structura Student cu matricola -1
Student HashStudent::Cauta(int matricola)
{
//valoare de retur pentru cazurile in care nu este gasit
elementul
Student s_negasit;
s_negasit.matricola = -1;
if (matricola<0)
return s_negasit;
if (elem!=NULL)
{
int index = HashFunction(matricola);
if (elem[index]==NULL)
{
return s_negasit;
}
else
{
if (elem[index]->data.matricola ==matricola)
return elem[index]->data;
else//coliziune
{
NodDate *t = elem[index];
while (t!= NULL && t->data.matricola!=matricola)
t = t->urm;

if (t==NULL)
return s_negasit;
else
return t->data;
}
}
}
//neinitializat sau nu exista inregistrarea
return s_negasit;
}
//functie recursiva care eliberaza memoria ocupata de nodurile listei
void HashStudent::ElibereazaMemorieLista(NodDate *&inceput)
{
if (inceput == NULL)
return;
else
{
ElibereazaMemorieLista(inceput->urm);
cout<<"Eliminat nodul cu codul: " <<inceput>data.matricola<<endl;
delete inceput;
}
}
//elibereaza memoria ocupa de tabela de dispesie
HashStudent::~HashStudent()
{
if (elem != NULL)
{
for (int i=0;i<size;i++)
ElibereazaMemorieLista(elem[i]);
delete [] elem;
}
}
//Sterge elementul a carui matricola coincide cu matricola trimisa ca
parametru
//returneaza pozitia din tabela de a fost eliminat elementul
//sau -1 in cazul in care nu exista elementul cu cheia data
int HashStudent::Sterge(int matricola)
{
if (matricola<0)
return matricola;
if (elem!=NULL)
{
int index = HashFunction(matricola);
if (elem[index]==NULL)
{
return -1;
}
else
{
//este primul
if (elem[index]->data.matricola ==matricola)
{
if(elem[index]->urm == NULL)
{
delete elem[index];
elem[index]=NULL;

}
else//mai sint si alte elemente in lista
{
NodDate *t = elem[index];
elem[index] = t->urm;
delete t;
}
}
else//coliziune, nu este primul, il cautam
{
NodDate *t = elem[index];
while (t->urm!= NULL && t->urm->data.matricola
!=matricola)
t = t->urm;
if (t->urm==NULL)
return -1;
else//t->urm.data.matricola == matricola
{
NodDate *p = t->urm;
if (p->urm ==NULL)
{
t->urm = NULL;
delete p;
}
else
{
t->urm = p->urm;
delete p;
}
}
}
return index;
}
return -1;
}
}
//Afiseaza continutul tabelei de dispersie
void HashStudent::Afiseaza()
{
if (elem!=NULL)
{
for (int i=0;i<size;i++)
{
if (elem[i]!= NULL)
{
NodDate *t = elem[i];
while (t!=NULL)
{
cout<<"S-a inserat in pozitia: " << i <<
" cheia: "<<t->data.matricola<<endl;
t = t->urm;
}
}
}
}

}
int _tmain(int argc, _TCHAR* argv[])
{
Student grupa[] ={
{1000,"Ion Vlad", "Str. Norilor
20","Cluj","ion@server.ro", "0101010101"},
{204,"Mihai Vlad", "Str. Norilor
20","Cluj","mihai@server.ro", "0101010101"},
{406,"Dan Vlad", "Str. Norilor 20","Cluj","dan@server.ro",
"0101010101"},
{305,"Lili Vlad", "Str. Norilor
20","Cluj","lili@server.ro", "0101010101"},
{1022,"Silviu Vlad", "Str. Norilor
20","Cluj","silviu@server.ro", "0101010101"},
{1021,"Alina Vlad", "Str. Norilor
20","Cluj","alina@server.ro", "0101010101"},
{1030,"Sorin Vlad", "Str. Norilor
20","Cluj","sorin@server.ro", "0101010101"},
{1032,"Titi Vlad", "Str. Norilor
20","Cluj","titi@server.ro", "0101010101"},
{1200,"Gigel Vlad", "Str. Norilor
20","Cluj","gigel@server.ro", "0101010101"},
{2021,"Anca Vlad", "Str. Norilor
20","Cluj","ioanca@server.ro", "0101010101"},
{1230,"Maria Vlad", "Str. Norilor
20","Cluj","maria@server.ro", "0101010101"},
{1008,"Elena Vlad", "Str. Norilor
20","Cluj","elena@server.ro", "0101010101"}
};
HashStudent hs;
for (int i=0;i<sizeof(grupa)/sizeof(Student);i++)
{
hs.Insereaza(grupa[i]);
}
hs.Afiseaza();
int m;
cout<<"Matricola: ";
cin>>m;
while (m!=-1)
{
Student rez = hs.Cauta(m);
if (rez.matricola != -1)
rez.Afisare();
cout<<"Matricola: ";
cin>>m;
}
hs.Sterge(1008);
hs.Sterge(406);
hs.Sterge(305);
hs.Afiseaza();
cin.get();
return 0;

Dup rulare, programul produce urmtoarele rezultate la dispozitivul


standard de ieire:

Figura 17.7 Rezultatele programului HashStudent

18. FIIERE
18.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 asemenea, 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

Memoria
extern

6
1

adresa de nceput
buffer

Operaii de
citire / scriere
4

zona de memorie
definit de
utilizator

Figura 18.1 Operaii de intrare/ieire

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.

5 CR

algoritm
conversie
alfanumeri
c- ntreg

variabila A

0 CR

algoritm
conversie
alfanumericntreg

variabila B

CR

algoritm
conversie
alfanumeric
-ntreg

CR

algoritm
conversie
alfanumeric
-real

variabila C variabila D

Figura 18.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
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 leau 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 sunt 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 informaiile
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) L (18.1)
i dac exist:

L
k
L'

(18.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:

256
k
3,2 3
80

(18.3)

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
10
100 100 30%
n 256
32

(18.4)

n cazul n care k = 2:
g2

n / 2 m160 100
n 256

(18.5)

unde:

0, daca n e numar par


m
1, daca n e numar impar
Pentru k = 3:
g3

n 3 m 240 100
n 256

(18.6)

Se observ c:

g 3 g 2 g1

(18.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.
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.

18.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 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 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:

Lf
cf
L
citiri

(18.8)

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 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.

18.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 printrun 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:

lgSDi lg

(18.9)

fiierul avnd n final lungimea:


n

L f n lg lgSDi
i 1

(18.10)

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

cont lgSDi

(18.11)

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

adr E1 A lg

(18.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 A cont j lg i
j 1

(18.13)

sau:
i 1

adr Ei A lgSDi i lg
j 1

(18.14)

unde, k reprezint grupul de baii de lungime lg() ataat elementului Ek


n fiier. Dac:

lgSD1 lgSD2 ... lgSDn

(18.15)

atunci:

cont 1 cont 2 ... cont n

(18.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 lgSD j
j 1

(18.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 E m adr E n lgS n lg

(18.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)

(18.19)

dac sortarea s-a fcut descresctor, sau:


cont (E1.x) cont (E2.x) cont (En.x)

(18.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 ()

(18.21)

adr (pred (Ei)) = adr (Ei) lg (SDi-1) lg ()

(18.22)

Se spune c fiierul este nchis dac:

lim suc m Ei E n

(18.23)

Se spune c fiierul este deschis dac:

lim pred m Ei E1

(18.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.

18.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)

(18.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:

lgSD lg

(18.26)

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:

L f n lg lgSD

(18.27)

Se construiete mulimea perechilor:


(cont(Ei.x).ai)

(18.28)

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


calculeaz:

cont E x x
n

i.

i 1

(18.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

(18.30)

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

Q1 cont E1 .x , a1

(18.31)

- pentru al doilea subir:

Q2 cont E1 m .x , a m1

(18.32)

- pentru al treilea subir:

Q3 cont E1 2 m .x , a2 m1

(18.33)

- pentru al patrulea subir:

Q4 cont E13m .x , a3m1

(18.34)

Perechile pentru delimitarea sfritului pentru fiecare din cele 4


subiruri sunt:

P1 cont E m .x , a m

P2 cont E 2 m .x , a 2 m
P3 cont E3m .x , a3m
P4 cont E n .x , a n

(18.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)

(Q1, P1)

(Q3, P4)

(Q2, P2)

(Q3, P3)

(Q4, P4)

Figura 18.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)

(18.36)

ntruct:

7233

cont (E n .x) cont (E i .x)


2

(18.37)

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


ntruct:

7233

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


2

(18.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. 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)

(18.39)

n acest caz, articolul ca atare este memorat ntr-o zon rezervat a


fiierului, legtura cu celelalte articole efectundu-se prin:

cont k adr E j

cont j adr E k 1

(18.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.

18.5 Fiiere aleatoare


Sunt acele fiiere care ocup o anumit zon din memoria extern i
ale cror elemente nu sunt reperate dect prin adresa fizic pe care o au pe
suportul extern.
ntruct abordarea fiierelor random, depete prin amploarea
fundamentrii acest cadru, se consider oportun prezentarea unui caz
particular de fiiere ale cror adrese ale elementelor se calculeaz n funcie
de poziia pe care acestea o au, tot astfel cum se procedeaz n cazul
masivelor unidimensionale.
Se consider funcia poz(Em) care indic poziia elementului Em n
irul de articole.
Astfel, dac mulimea de elemente E1, E2, , En are exact n
elemente:
1 poz (Ej) n

(18.41)

poz (succ (Ej)) = poz (Ej) + 1

(18.42)

poz (pred (Ej)) = poz (Ej) 1

(18.43)

Din cele dou relaii rezult c:


poz (succ (Ej)) - poz (pred (Ej)) = 2

(18.44)

sau:

poz E j

poz succE j poz pred E j

(18.45)

Cu aceste definiii:
adr(Ej) = A + (poz(Ej) 1) * lg(Ej)

(18.46)

se construiete irul de perechi:


(cont(Ej.x), poz(Ej))

(18.47)

care permite, n cazul n care se stabilete o relaie de forma:


poz(Ej) = (cont (Ej.x))

(18.48)

regsirea elementului dup poziie pe care o are n fiier.


n cazul n care nu se identific funcia ( ), elementele cont(Ej.x), j =
1, 2, ..., n, vor fi memorate ntr-o structur omogen unidimensional i
poziia elementului n care este memorat aceast informaie, corespunde
poziiei articolului n fiier.

18.6 Fiiere interdependente


Interdependena fiierelor, este privit sub dou aspecte i anume:
interdependena
la
nivelul
articolelor
unui
fiier
i,
respectiv,
interdependena la nivelul a dou sau mai multe fiiere.
Se consider dou fiiere:
- fiierul PRODUSE, ale crui elemente sunt generate dup structura
PROD, cu cmpurile cod produs, denumire produs, stoc, unitate de
msur, numr materii prime care intr n componena sa i
materiile prime, gama de operaii;
- fiierul MATERIALE, ale crui elemente sunt generate dup
structura MAT, cu cmpurile cod material, denumire material,
unitate de msur, cantitate existent n stoc, pre unitar.
Dac dorim s aflm costul materiilor prime necesare realizrii unui
produs, citim din fiierul PRODUSE articolul corespunztor respectivului
produs i rnd pe rnd prelum codurile materialelor ce intr n componena
sa. Pentru fiecare material ce intr n componena produsului, citim cte un
articol din fiierul MATERIALE.
S ne imaginm c fiierele sunt secveniale. Rezolvarea este extrem
de greoaie, pentru c accesul la materiale este obinut numai prin baleierea
de la nceput a fiierului MATERIALE, dac materialele nu au fost memorate

n ordine cresctoare dup codurile acestora n articolul din fiierul


PRODUSE i dac fiierul MATERIALE nu este sortat.
Problema devine eficient dac, n locul codurilor materialelor,
dispunem de adresele articolelor n fiierul MATERIALE, dar reorganizarea
fiierului de materiale, ar atrage dup sine actualizarea adreselor pentru
materiale din componena fiierului PRODUSE.
Dac se lucreaz cu fiiere indexat secveniale, rezultatul este bun,
iar dac se folosesc poziiile materialelor dintr-o list, performana devine i
mai bun.
Acest tip de dependen este unidirecional ntre dou fiiere.
Numrul de legturi dintre un articol din fiierul PRODUSE i fiierul
MATERIALE este variabil, strict dependent de numrul de materiale ce intr
n componena produsului cu care articolul din fiierul PRODUSE este pus n
coresponden.

PRODUSE

MATERIALE
Cod mat.1 ..
Cod mat. 2 ..
Cod mat. 3. ..
Cod mat. 4 ..

Produsul x

Cod
Denumire UM
produs produs

Pre

Nr.
mat.
4

Cod
mat.
1

Cod
mat.
2

Cod
mat.
3

Cod
mat.
4

Figura 18.4 Legtura ntre fiierele PRODUSE i MATERIALE


Este de dorit ca fiierele PRODUSE i MATERIALE s se realizeze ntro prim etap cu codurile materialelor i lng ele s fie rezervate cmpuri
pentru memorarea adreselor fizice ale articolelor ce corespund n fiierul
MATERIALE respectivelor coduri.
n a doua faz, un sistem de programe identific poziia fiecrui
articol i ncarc n fiierul PRODUSE n zonele disponibile de adres chiar
adresa articolului al crui cod se afl n imediata sa vecintate.
Reorganizarea fiierelor pe fondul definirii codurilor nu implic dect
rencrcarea de adrese, lucru ce se efectueaz automat.
Fiierele interdependente privesc legturi ntr-un singur sens, de la
fiierul de produse ctre fiierul de materiale. Se construiesc i legturi n
sensul opus, pentru fiecare articol al fiierului de materiale, indicndu-se n
care dintre produse, este folosit respectivul material. Este o informaie
necesar, dar care lungete mult articolul MAT i de aceea, n acest caz este
puin utilizat o astfel de abordare.
Dac totui se dorete i o legtur dinspre fiierul MATERIALE ctre
fiierul PRODUSE, realizarea se efectueaz n acelai fel, parcurgndu-se tot
doi pai.
n final se vor obine legturile dintre articole, ilustrate n figura 18.5.

MATERIALE
x1

y1

PRODUSE
y1

x2

y2

x3

y3

x4

y4

Figura 18.5 Legturile dintre articolele fiierelor MATERIALE i PRODUSE


Rezult c produsul y3, utilizeaz materialele x1, x2 i x4, iar
materialul x3 este prezent n compoziia produselor y1 i y2.
Articolele fiecrui fiier rmn n continuare independente unele de
celelalte. Ele sunt prelucrate separat, singurele combinaii fiind cele legate
de posibilitatea de a permite calculul necesarului de materiale, pentru
realizarea de k produse de tipul y3 i de a stabili dac stocurile materialelor
x1, x2, x4 sunt suficiente.
La serviciul aprovizionare, prin parcurgerea fiierului MATERIALE, se
observ care sunt viitoarele cereri din materialul x3.
Se observ c o astfel de construcie este limitativ, dar cu o serie de
artificii de consultare a celor dou fiiere se obin i alte regrupri de date.
O privire de ansamblu pune n eviden c pstrarea articolelor din
fiecare fiier ca entiti independente reduce diversitatea combinaiilor de
date, care pentru un manager competent, reprezint o surs sigur de
fundamentare a deciziilor.
18.7 Fiiere nlnuite
Apar ntrebrile: structurile dinamice cu multe elemente sunt
memorate pe suport extern? Se implementeaz mecanisme de nlnuire n
memoria extern? Cum se genereaz legturile exprimate prin adrese dintre
elementele deja alocate dac se specific numai cheile de identificare a
acestora?
Rspunsurile nu sunt simple, dar au fost date cu muli ani n urm,
obinndu-se funcii de creare i prelucrare a fiierelor nlnuite.
Se consider c pentru realizarea unui produs, este necesar
parcurgerea unor etape, efectuarea unor operaii de prelucrare ntr-o
anumit succesiune, numit gama de operaii.
Descrierea unei operaii, conine informaii precum:
- codul operaiei;
- denumirea operaiei;
- durata de execuie;
- calificarea cerut;
- tipul de meseria care o execut;
- utilajul necesar;
- scule necesare;

- plata pentru efectuarea operaiei;


- operaia precedent;
- operaia urmtoare;
La execuia programului de ncrcare a fiierului gamei de operaii, se
introduc rnd pe rnd, datele ce urmresc ablonul descris mai sus. Pentru
primul articol al unei game, operaia precedent este NULL, iar pentru
ultimul articol din gam, operaia urmtoare este de asemenea NULL.
Sistemul de creare a fiierului cu acest tip de nlnuire adaug dou
cmpuri ce vor fi iniializate cu adresele ce corespund poziiei articolelor
pentru operaia precedent i pentru operaia urmtoare din gama de
operaii.
Cte game de operaii sunt definite ntr-un proces de producie,
attea liste dublu nlnuite vor fi realizate.
Prin parcurgerea unui fiier cu articole nlnuite, se determin care
este necesarul de specializri i care sunt salariile ce trebuie pltite, dac
sunt realizate k produse de tipul x, pentru care este necesar efectuarea
operaiilor din gama de operaii cu un cod specificat.
Fiierul gamei de operaii, va avea articole ce corespund gamelor G1,
G2, ..., Gn, care la rndul lor conin operaiile O11, O12, ..., Ok1, ..., O21, O22,
..., Ok2, ..., On1, On2, ..., Okn.

NULL | G1

Gama de
operaii
G1

Gama de
operaii
Gn

O11

G1

O12

G1

O13

G1

O1k

NULL | Gn

On1

Gn

Okn

Figura 18.6 Structura fiierului gamei de operaii


O alt situaie, este aceea corespunztoare memorrii pe suport
extern a structurilor arborescente.
Se consider c la o ntreprindere se realizeaz N produse P1, P2, ...,
PN. Fiecrui produs i se asociaz o structur arborescent, deci vor exista N
noduri rdcin i N noduri terminale.
N

M Ki
i 1

(18.49)

unde Ki reprezint numrul de materii prime, repere sau subansamble


nerealizate n ntreprindere i care intr n componena produsului Pi.
Se pune problema memorrii celor N arborescente, cu condiia
realizrii cel puin a unui nivel de redundan controlat, dac minimizarea
este dificil de realizat sau chiar nu este dorit.
Nodurile reprezint produse, subansamble, repere sau materii prime,
despre care trebuie cunoscut:
- cod de recunoatere, respectiv cheia;
- denumire;
- unitate de msur;
- cantitate necesar, respectiv greutate specific;
- pre unitar;
- gama de operaii;
- cod reet de fabricaie;
- caracteristici de performan standard;
De asemenea, se impune stabilirea poziiei n arborescen, indicnd
nodul printe, nodul descendent i nodul vecin din dreapta.
Aceast multitudine de date, se grupeaz n dou categorii:
- date pentru descrierea componentei ce corespunde unui nod;
- date pentru descrierea poziiei nodului n arborescen.
Celor dou categorii de date le vor corespunde fiiere descriptive i
fiiere de legturi.
Fiierele descriptive, au articole formate din dou tipuri de date:
- date ce se completeaz de ctre utilizatori cu acele elemente ce
caracterizeaz un produs, un ansamblu, subansamblu, reper sau
materie prim:
- date ce se completeaz de un sistem de gestiune a fiierelor
nlnuite i care conin adrese de articole sau identificatori de
marcare a finalului nlnuirii.
De exemplu, pentru produsul A care este format prin asamblarea
reperelor B, C, D i E n ordinea menionat n figura 18.7 fiierul descriptiv,
conine 3 articole ce corespund elementelor A, B, C, D i E, iar fiierul de
structur descrie legturile dintre elementele A, B, C, D i E, conform
arcelor ce definesc gradul de tip arbore. Deci, acest fiier are articolele: (A;
B), (A; C), (C; D), (C; E).
A

Figura 18.7 Structura componentelor produsului A

Indicarea acestor articole nu necesit o anumit ordine. Analiza


perechilor este aceea care permite identificarea tuturor elementelor
necesare completrii cmpurilor de adres din fiierul descriptiv.
Astfel, ntr-o pereche (x; y) rezult c pentru nodul x, nodul y este
descendent, iar pentru nodul y nodul x este printe.
Nodul A este printe pentru nodurile B i C, dar el nu apare n nici
una dintre perechi ca descendent, deci A este nod rdcin. n articolul din
fiierul descriptiv ce corespunde produsului A, un cmp este completat cu
NULL.
Nodul B nu apare ca printe n nici o alt pereche, deci nu are
descendeni. Se completeaz i un cmp din articolul fiierului descriptiv ce
corespunde reperului B.
Pentru regsirea rapid a informaiilor, nainte de a completa
cmpurile din fiierul descriptiv, vor fi completate n fiierul de legtur,
cmpurile ce corespund adreselor fizice pe care le ocup articolele fiierului
descriptiv.
Astfel, legturii corespunztoare perechii (A; B) i se asociaz n
fiierul de legtur, articolul:
cod A

cod B

adr (articol A)

adr (articol B)

Figura 18.8 Structura articolului n fiierul de legtur


Dac este analizat un produs, problema nu difer prea mult cu modul
de reprezentare a structurilor dinamice n memoria intern. Dac ns
fiierul descriptiv conine totalitatea nodurilor ce descriu cele N produse care
se realizeaz, iar fiierul de legturi are attea elemente cte arce au cele N
arborescene dup care se descompun produsele P1, P2, ..., Pn, avem o
imagine mai exact a ceea ce nseamn reprezentarea n memoria extern
a datelor ce formeaz articole interdependente i care n final, dau fiierele
nlnuite.
Obinerea controlului asupra redundanei revine la a deplasa
informaii privind adresele din articolele fiierului descriptiv n fiierul de
legtur. Dac se minimizeaz redundana, n sensul c fiierul de produse
conine repere i materii prime descrise o singur dat, reconstituirea
arborescenei se obine prin construirea tuturor adreselor de regsire nu n
articolele din fiierul descriptiv, ci n articolele de legtur.
Problema traversrii unui arbore se menine din punct de vedere al
semnificaiei, dar n cazul fiierelor nlnuite revine la a utiliza, alternativ,
informaii din fiierul de legturi i din fiierul descriptiv.
Operaiile care sunt specifice structurilor arborescente construite n
memoria intern sunt definite i n cazul arborescenelor de pe mediile
externe. Modificrile vizeaz pointerii, al cror coninut reflect adrese ale
suportului extern.
Funciile specifice pentru lucru cu fiiere nlnuite au ca parametri
toate elementele necesare tergerii unui nod, modificrii unor legturi sau a
unor cmpuri. Adresele care apar la descrierea legturilor dintre noduri
permit parcurgerea arborelui de la rdcin spre baz sau de la orice nod
ctre baz sau de la orice nod ctre rdcin.
Cnd se proiecteaz o anumit structur, nu numai list sau arbore,
trebuie ca programatorul s se familiarizeze cu ideea c oricrui arc i
corespund dou adrese. El trebuie s gseasc algoritmi pentru ncrcarea

n cmpuri, pe care are obligaia s le defineasc, a acestor dou adrese.


Programatorul este obligat s cunoasc funciile care ntorc adresa fizic a
nceputului zonal de memorie pe care o ocup un anumit articol.
Dac sistemele de gestiune a fiierelor nlnuite realizeaz aceste
operaii pentru structuri arborescente sau pentru liste, cazuri particulare de
arborescene, n cazul structurilor oarecare programatorul trebuie s-i
construiasc funcii proprii. El are grij s rezerve zone pentru adrese, i
definete structura de date adecvat descrierii structurii produsului. De
fiecare dat, va avea grij ca ceea ce realizeaz s nu genereze ambiguiti
sau s conduc la reprezentri ce nu concord cu structura pe care dorete
s o implementeze n fiiere.

18.8 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.
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
x
x
x
x

este
este
este
este
este

vecin cu y
fiul lui y
tatl lui y
soia lui y
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.
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

vecini

familie

soie

copilul Y1

copii

vecinul
din fa

vecinul
din spate

vecinul
din stnga

vecinul
din dreapta

copilul Y10

Figura 18.9 Arborescena asociat elementelor mulimii M1


18.10.

Pentru elementele mulimii M2 se asociaz arborescena din figura

Productor

Model

Marca 1

Marca 2

Culoare

An fabric.

Marca n

Figura 18.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)

(18.50)

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)

(18.51)

al adreselor articolelor ce corespund autoturismelor de culoare z:


P S = (sk1, sk2, skm)

(18.52)

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

(18.53)

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.
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
-

(18.54)

se construiete irul capacitilor cilindrice:


(C1, C2, Ch)

(18.55)

al autoturismelor, nsoite de adresele articolelor din fiierul asociat mulimii


M2, ce corespund autoturismelor pentru capacitile identificate:
m1 m2 me

(18.56)

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, 1]

(18.57)

[2, 2]

(18.58)

Aceste limite conduc la filtrarea elementelor mulimii C1, C2, Ch,


astfel nct rezult submulimea adreselor:
m = {m1, m2, mp}

(18.59)

de regul p < e.
Se construiete irul de adrese:

u = (i0 1 k; cont (.i.adresa_autoturism) m)

(18.60)

Scrierea elementelor irului:


(cont (u->nume_persoana)),

u = 1, 2, , w

(18.61)

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

(18.62)

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.
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

(18.63)

cont ( i) = Bi lg (SDi)

(18.64)

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, Ch, i j nrr (M2)

(18.65)

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

(18.66)

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

(18.67)

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.
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.

19. EXPRESII DE DEFINIRE I REFERIRE ALE


STRUCTURILOR DE DATE
19.1 Construirea expresiilor
Structurile statice de date uzuale sunt: vector, matrice, articol, fiier.
Structurile dinamice uzuale sunt: list, stiv, coad, arbore i graf.
Limbajele de programare conin tipuri fundamentale de date i tipuri
derivate care permit implementarea tuturor structurilor de date.
Este important ca reprezentarea structurilor de date s se efectueze
cu ajutorul modelului analitic pentru o descriere riguroas.
Se definesc funcii de descriere a structurilor de date:
cont()
extragere coninut structur de date;
succ() stabilire succesor;
pred() stabilire predecesor;
adr()
specificare adres zon de memorie unde se afl o anumit
structur;
lg()
specificare lungime zon de memorie ocupat de structura
de date aleas;
tip()
specificare elemente structur de date sub forma naturii
acestora;
ref()
referire variabil de tip pointer.
Obiectivul acestui capitol este de a evidenia modul n care se
construiesc i se utilizeaz expresiile de definire i referire a tuturor
structurilor de date.
Se consider mulimea operanzilor format din identificator i
constante.
Se consider mulimea operatorilor utilizai n limbajul C++ pentru
descrierea i referirea structurilor de date:
- parantezele: [ ], ( )
- operatorul punct: .
- operatorul de indirectare: *
- operatorul de referire a articolelor declarate ca pointeri: ->
Expresiile sunt definite cu ajutorul unui metalimbaj, astfel [Rosc98]:
<expresie>
<expresie>
<expresie>
<expresie>

::=
::=
::=
::=

<termen> |
<termen><operator binar><termen> |
<operator unar><termen> |
<termen><operator unar>

Definirea expresiilor, cu menionarea explicit a operatorilor, se


realizeaz, conform [Rosc98], prin intermediul diagramelor de sintax:
a) definirea masivelor:
expresie definire
masive

termen

expresie

Figura 19.1 Diagrama de sintax pentru definirea masivelor

b) definirea datelor de tip articol:

expresie definire
articole

termen

expresie definire
cmpuri

Figura 19.2 Diagrama de sintax pentru definirea articolului


Metalimbajele au rol universal n construirea expresiilor de definire i
referire a structurilor de date. Diagramele ofer o imagine grafic cu privire
la procesul de construirea a acestor expresii.

19.2 Expresii de definire


Pentru masive unidimensionale, expresia de definire n limbajul C++
este:
tip nume_v[expresie];
unde:
tip

natura elementelor reinute n vector. Este un tip fundamental


al limbajului, char, int, float, double etc., sau un tip construit de
utilizator, de exemplu articol, obiect etc;
nume_v
identificatorului
masivului
unidimensional
n
cadrul
programului;
expresie specific dimensiunea maxim a vectorului, numrul de
elemente al acestuia.
Pentru masivele bidimensionale, expresia de definire este:
tip nume_mat[expresie1][expresie2];

unde:
tip

nume_mat
expresie1,
expresie2

natura elementelor reinute n matrice. Este un tip


fundamental al limbajului, char, int, float, double etc.,
sau un tip construit de utilizator, de exemplu articol,
obiect etc;
identificatorului masivului bidimensional n cadrul
programului;
specific dimensiunea maxim a matricei, adic
numrul de linii, respectiv numrul de coloane al
acesteia.

Pentru masive n-dimensionale, expresia de definire este:


tip nume_m[expresie1][expresie2] [expresien];

unde:
tip
nume_m

natura elementelor reinute n masiv. Este un tip


fundamental al limbajului, char, int, float, double etc., sau
un unul construit de utilizator, de tip articol, obiect etc;
identificatorului masivului n-dimensional n cadrul
programului;
specific dimensiunea maxim a masivului, adic
numrul componente al acestuia pe fiecare dimensiune.

expresie1,
expresie2,,
expresien
La definirea masivelor, se remarc faptul c expresiile care specific
numrul maxim de elemente ale masivului pe fiecare dimensiune trebuie s
fie constant. Acesta este un neajuns atunci cnd se urmrete realizarea
unei aplicaii flexibile i eficiente din punctul de vedere al gestionrii
memoriei.
Soluia la aceast problem o constituie alocarea dinamic a
masivelor, folosindu-se fie funciile malloc, respectiv free, fie operatorii new,
respectiv delete.
Pentru definirea tipului articol se construiete urmtoarea expresie:
struct nume_art {
tip1 cmp1;
tip2 cmp2;

tipn cmpn;
};
unde:
struct

cuvnt cheie care permite definirea articolelor n


limbajul C++;
nume_art
identificatorul articolului n cadrul programului;
tip1, tip2, , tipn natura cmpurilor din cadrul articolului. Tipurile
utilizate sunt cele specifice limbajului, char, int, float,
double etc., sau definite de utilizator. De menionat c
nu se permite definirea n cadrul articolului a unui cmp
de tipul articolului, dar se pot defini pointeri ctre
acesta;
cmp1, cmp2, identificatorii cmpurilor articolului prin intermediul
crora se refer componentele tipului articol.
, cmpn
Definirea structurilor autoreferite se realizeaz dup urmtorul
ablon:
struct nume_auto {
tip1 cmp1;
tip2 cmp2;

tipn cmpn;
nume_auto *pcmp;
};
Definirea structurii de tip fiier se realizeaz cu ajutorul structurii
standard FILE astfel:

FILE *f;

unde:
FILE structura standard care permite definirea identificatorilor de fiier
n limbajul C++;
f
identificatorul de fiier folosit n cadrul programului, care se
declar ca pointer ctre structura standard FILE.
Lista simplu nlnuit este o structur de date secvenial, fiecare
element al listei, denumit nod, coninnd informaia propriu-zis i adresa
de legtur cu un alt nod. Definirea unui nod al listei se realizeaz astfel:
struct nods {
tip1 cmp1;
tip2 cmp2;

tipn cmpn;
nods *urm;
};
unde:
nods
tip1, tip2, , tipn

identificatorul structurii unui element n cadrul listei;


natura elementelor care formeaz informaia util a
nodului;
cmp2, identificatorii cmpurilor informaiei utile;

cmp1,
, cmpn
urm
reine adresa urmtorului nod n list.
Lista dublu nlnuit este o structur de date secvenial, fiecare
element al listei, denumit nod, coninnd informaia propriu-zis i adresele
de legtur cu nodul anterior, respectiv nodul urmtor n list. Definirea
unui nod al listei se realizeaz astfel:
struct nodd {
tip1 cmp1;
tip2 cmp2;

tipn cmpn;
nodd *pred, *urm;
};
unde semnificaia cmpurilor este aceeai ca la lista simplu nlnuit, cu
meniunea c pred reine adresa nodului predecesor n list.
Stiva este, de asemenea, o structur liniar, de tip list simplu
nlnuit, cu deosebirea c inserrile i extragerile de elemente ale liste se
face ntotdeauna la un acelai capt al listei, respectnd disciplina LIFO
Last Input First Output.
Definirea structurii de tip stiv se realizeaz la fel cu cea a unei liste
simplu nlnuite.
Pointerul la stiv prin care se manipuleaz structura n ansamblu se
numete vrful stivei. Definirea acestuia are loc astfel:
nods *stiva;

Conceptul de stiv este implementat folosind i masivele


unidimensionale. Dezavantajul acestei abordri este dat de numrul maxim
de elemente care sunt stocate, numrul de elemente din list fiind acelai
pe durata de execuie a programului.
n funcie de dimensiunea tipurilor elementelor care sunt stocate n
structur, exist dou abordri:
1. dac dimensiunea este mare, se folosete vectorul pentru
stocarea pointerilor spre elementele alocate dinamic:
struct stiva {
tip *vec[dim_max];
int sp;
};
unde:
stiva
vec
sp

structur de tip articol;


vectorul de pointeri spre elementele stivei;
vrful stivei.
Aceast implementare este preferabil s fie evitat, ea combinnd
dezavantajul inflexibilitii vectorilor cu inconvenientul ocuprii unui spaiu
apreciabil de memorie cu informaia de legtur. Acesta este motivul pentru
care se stiva se implementeaz cu ajutorul listelor simplu nlnuite.
2. dac tipul elementelor este unul standard, vectorul este utilizat
pentru stocarea elementelor propriu-zise:
struct stiva {
tip vec[dim_max];
int sp;
};
Aceast implementare se justific pentru anumite probleme, ea
eliminnd necesitatea rezervrii unui spaiu suplimentar de memorie pentru
informaia de legtur.
Coada este implementat avnd la baz tot structura de list. n
cadrul acestei structuri disciplina de lucru este FIFO First Input First
Output. Inserarea nodurilor are loc la sfritul listei, iar extragerea acestora
la nceputul listei.
Definirea acestei structuri are loc astfel:
struct coada{
nods *prim, *ultim;
};

unde:
coada
nods
prim, ultim

structura de tip coad;


identificator structur element list simplu nlnuit;
primul, respectiv ultimul nod care permit implementarea
operaiilor specifice structurii de coad.
Arborele reprezint o mulime nevid i finit de elemente, numite
noduri, cu proprietile:
- exist doar un singur nod, numit rdcina arborelui;

celelalte noduri formeaz submulimi disjuncte ale arborelui, care


la rndul lor respect cele dou proprieti. Aceste submulimi
poart numele de subarbori ai rdcinii.
Arborii binari sunt formai dintr-o mulime finit de elemente care
este fie vid, fie format dintr-un singur element numit rdcin, iar
celelalte elemente sunt distribuite n maxim dou submulimi disjuncte, care
reprezint tot arbori binari.
Descrierea structurii de tip arbore binar se realizeaz n limbajul C++
astfel:
-

struct arb_bin{
tip inf;
arb_bin *st, *dr;
};

unde:
arb_bin

identificatorul unui element de tip nod al arborelui n cadrul


programului;
inf
informaia util prezent n nod;
st, dr
elemente ce pstreaz adresele de legtur cu subarborele
stng, respectiv drept al arborelui curent.
Matricea rar este un tip particular de masiv bidimensional cu
urmtoarele caracteristici:
- numr foarte mare de linii i de coloane;
- numr de elemente nenule foarte mic.
n aplicaiile curente, matricele rare au grade de umplere cuprinse
ntre 0,15% i 3%.
Memorarea i lucrul cu matrice rare se efectueaz prin intermediul a
trei vectori n care se memoreaz linia, coloana i valoarea nenul a tuturor
elementelor care nu sunt nule.
Din categoria structurilor de date agregate fac parte i vectorii de
pointeri spre funcii. Definirea unui vector de pointeri spre funcii se face
conform urmtorului ablon:
tip (*pf[10])();
unde:
tip tipul returnat de funcie;
pf vectorul de pointeri spre funcii, dimensiunea acestuia fiind de 10
elemente.
Introducerea n vector a pointerilor spre diverse funcii care
returneaz tip se realizeaz astfel:
pf[i]=functie;

unde:
pf

vectorul de pointeri spre funcii care returneaz tip, iar lista


parametrilor este vid;
functie numele dat unei funcii care ntoarce tip i are list vid de
parameteri; identificatorul este pointer spre funcia respectiv.

Trecerea la limbajul de programare C++ a permis implementarea de


noi structuri de date utilizator pe baza conceptelor definite i caracterizate
n teoria programrii orientate obiect.
Astfel, utilizatorul definete o clas de obiecte dup modelul:
class ClasaUser {
private:
//definire atribute i metode private (ncapsulare)
public:
//definire metode de tip constructor i destructor
//definire atribute i metode publice
protected:
//definire atribute i metode de tip protected (motenire)
};
De exemplu, definirea clasei de obiecte pentru implementarea
structurii de date de tip arbore binar este:
class ArbBinClass{
tip inf;
arb_bin *st, *dr;
public:
ArbBinClass();
~ArbBinClass();
//alte metode
};

Spre deosebire de structura de date de tip articol, o clas de obiecte


include att date, denumite atribute, ct i operaii care se efectueaz
asupra acestora.
Deci, alturi de stare, o clas de obiecte include i comportamentul
variabilelor de tip obiect. Comportamentul este evideniat prin metodele
construite n clasa de obiecte. Acestea opereaz asupra atributelor,
determinnd schimbarea strii variabilei obiect prin modificarea valorilor
atributelor.

19.3 Expresii de referire


Referirea structurilor de date are loc:
- la nivel de ansamblu (ntreaga structur);
- la nivel de element al structurii.
Referirea elementelor masivelor unidimensionale se realizeaz astfel:
- cu ajutorul operatorului de indexare: nume[exp];
- cu ajutorul operatorului de indirectare: *(nume+exp).
Identificatorul de masiv unidimensional nume conine adresa primului
element din vector.
Reprezentarea sub form de zone de memorie a unui vector este
ilustrat n figura urmtoare:

nume
...
nume[0]

nume[1]

nume[2]

zona de memorie
nume[n-1]

Figura 19.3 Reprezentarea n memorie a unui vector


La referirea elementelor unui vector, depirea dimensiunii acestuia
conduce la accesarea zonelor de memorie nvecinate, rezultatul fiind
imprevizibil.
Referirea elementelor masivelor bidimensionale:
- cu ajutorul operatorului de indexare: nume[exp1][exp2];
- cu ajutorul operatorului de indirectare: *(*(nume+exp1)+exp2).
Identificatorul unui masiv bidimensional conine adresa vectorului de
pointeri ctre linii. Adresa fiecrei linii a masivului se obine pornind de la
identificatorul de masiv astfel: nume[exp1] sau *(nume+exp1).
Reprezentarea unui masiv bidimensional sub form de zone de
memorie este:
nume
...

nume[0][0]

...

vector de pointeri

nume[0][n-1]

nume[m-1][0]

...

nume[m-1][n-1]

Figura 19.4 Reprezentarea n memorie a unui masiv bidimensional


Referirea elementelor masivelor tridimensionale:
- cu ajutorul operatorului de indexare: nume[exp1][exp2][exp3];
- cu
ajutorul
operatorului
de
indirectare:
*(*(*(nume+exp1)+exp2)+exp3).
Identificatorul masivului tridimensional conine adresa unui vector de
pointeri ctre matrice, vector de dimensiune exp1. Vectorul de pointeri ctre
masive bidimensionale este referit astfel:
nume[exp1] sau *(nume+exp1)
Referirea structurilor de tip articol se realizeaz prin identificatorii de
variabile declarate de acest tip. Astfel, articolele sunt utilizate, la nivel de
ansamblu, n urmtoarele operaii:
- extragere de adres a unei variabile de tip structur;
- atribuire;
- transmiterea ca argument n funcie sau returnarea unei structuri
din funcie.
Nu sunt acceptate comparri de forma: nume1!=nume2.
Referirea cmpurilor din structurile de tip articol se realizeaz prin
intermediul operatorului punct, astfel:

nume.cmp;
Reprezentarea i referirea cmpurilor unei structuri de tip articol:

nume
cmp 1

cmp 2

cmp i

cmp n

nume.cmp i

Figura 19.5 Reprezentarea n memorie i referirea unui articol


Dac articolul conine cmpuri de tip articol, atunci cmpurile
acestora se refer n mod similar, astfel:
nume.cmp1.cmp2. .cmpn;
Referirea structurilor de date autoreferite se realizeaz prin
intermediul operatorului ->. Astfel, informaia util din nod, precum i
adresa de legtur cu nodul urmtor, respectiv succesor sunt referite astfel:
nodd *listad;

listad->inf;
listad->urm;
listad->pred;

De asemenea, referirea adresei de legtur a unui nod oarecare a


unui nod din lista dublu nlnuit are loc astfel:
nodd *listad

listad->urm->urm->->urm;
listad->pred->pred->->pred;

Pentru structurile de tip stiv i coad, referirea elementelor are loc


n mod asemntor celui de la listele simplu nlnuite.
Referirea elementelor structurii de tip stiv:
nods *stiva;

stiva->inf;
stiva->urm;
..
stiva ->urm->urm->->urm;

Referirea nodurilor structurii de tip coad:


coada q;

q.prim->inf;
q.ultim->inf;
q.prim->urm;
q.ultim->ultim;

Adresele i informaia util din nodurile unui arbore binar se refer n


mod analog cu cele ale unei liste dublu nlnuite:
arb_bin *arbore;

arbore->inf;
arbore->st;
arbore->dr;

Referirea atributelor i metodelor definite i construite ntr-o clas de


obiecte se efectueaz n mod similar cu referirea cmpurilor din structurile
de date de tip articol.

19.4 Expresii ale structurilor agregate


Structurile prezentate n paragrafele anterioare sunt agregate sub
urmtoarele forme:
- vector de structuri;
- structur de vectori;
- vectori de pointeri spre structuri;
- vectori de pointeri spre funcii;
- masive bi- i n-dimensionale de structuri;
- structur de masive bi- i n-dimensionale;
- masive bi- i n-dimensionale de pointeri spre structuri.
Definirea unui vector de structuri se realizeaz astfel:
nume_art vector[expresie];
unde:
nume_art tipul elementelor stocate n vector (articol);
vector
identificatorul vectorului de structuri n cadrul programului;
expresie
dimensiunea maxim a vectorului.
Referirea elementelor acestui vector are loc astfel:
vector[i].cmp;
unde:
vector identificator vector de structuri;

i
elementul de rang i care se refer;
cmp un cmp din cadrul elementului de rang i.
Definirea unei structuri de vectori:
struct struct_vect {
tip1 v1[exp1];

tipn vn[expn];
};
unde:
struct_vect
tip1, , tipn
v1, , vn
exp1, , expn
Exemplu:

identificatorul structurii de vectori;


natura elementelor vectorilor;
identificatorii vectorilor;
dimensiuni maxime ale vectorilor.
struct str{
int v[100];
};

Referirea elementelor unei structuri de vectori:


struct_vect s, *ps;

s.v1[i];
ps->vn[j];

unde:
s
variabile de tip articol;
ps variabil de tip pointer spre articol;
i,j rangul elementelor care au fost referite.
Definirea vectorilor de pointeri spre structuri se realizeaz conform
urmtorului ablon:
nume_art *v[exp];
unde:
nume_art denumire structur de tip articol;
v
identificator variabil de tip masiv unidimensional cu
elemente pointeri spre structuri de tip nume_art;
exp
dimensiune maxim a vectorului.
Referirea unui cmp a vectorului de pointeri:
v[i]->cmp;
unde:
v
i

identificatorul vectorului de pointeri n cadrul programului;


rangul termenului a crui coninut este referit;

cmp un cmp al articolului desemnat de elementul i.


ntr-o expresie de referire a unei structuri agregate, precizarea
cmpului are loc de la dreapta la stnga, astfel:
a.b.c refer cmpul c din structura b, structura b fiind un cmp
al structurii a;
a.b.c[i] refer elementul i al cmpului c de tip vector, inclus n
structura b, unde b este un cmp al structurii a;
a.b[i].c refer cmpul c al elementului i din vectorul b, b fiind un
cmp structura a;
a[i].b.c refer cmpul c al structurii b, care este la rndul ei
cmp n elementul de rang i al structurii a;
a->b->c refer cmpul c din structura b, aceasta fiind un cmp
de tip pointer al structurii a, care este, de asemenea, o dat de tip
pointer;
a.(*b) refer coninutul zonei de memorie a crei adres se afl
n cmpul b, dat de tip pointer, al structurii statice a;
(*a).b refer cmpul b al structurii aflate n zona de memorie a
crei adres este reinut n a, care este o structur alocat
dinamic;
*(a+i).b refer cmpul b al unei structuri de tip articol care se
afl stocat la adresa coninut n vectorul a, i indicnd rangul
structurii n vector;
a.*(b+i).c refer cmpul c al articolului aflat pe poziia i n
vectorul b, b fiind cmp al structurii de tip articol a;
a->b.c refer cmpul c al structurii statice de tip articol b, care
este un cmp al structurii dinamice de tip articol a;
a.b->c refer cmpul c al structurii dinamice b, b fiind cmp al
structurii statice a, de tip articol.
Se consider urmtoarea secven scris n limbajul de programare
C++:
#include <stdio.h>
#include <iostream.h>
class matrix{
public:
int n,m,**a;
matrix();
matrix(int k, int l);
int** aloc_mat(int, int);
};
matrix::matrix()
{
cout<<"The line number is:";
cin>>n;
cout<<"The column number is:";
cin>>m;
a=aloc_mat(n,m);
cout<<"The matrix is:";
for(int i=0; i<n; i++)
for(int j=0; j<m; j++){
cout<<"\n a["<<i<<"]["<<j<<"]=";
cin>>a[i][j];
}
}

matrix::matrix(int k, int l)
{
n=k;
m=l;
a=aloc_mat(n,m);
for(int i=0; i<n; i++)
for (int j=0; j<m; j++) a[i][j]=0;
}
int** matrix::aloc_mat(int, int){
int **pmat;
pmat=new int*[n];
for (int i=0; i<n; i++)
pmat[i]=new int[m];
return pmat;
}
void main(){
matrix A(2,3);
matrix *B=new matrix(3,2);
matrix *C=new matrix[3];

n tabelul 19.1 sunt exemplificate expresii de referire a obiectelor i


atributelor, coninutul accesat i descrierea acestuia.
Tabelul nr. 19.1 Exemplificarea expresiilor de referire a obiectelor i
atributelor clasei matrix
Expresie de
referire
A.n
A.m
A.a
A.a[0]
A.a[0][0]
&A
&A.n
&A.m
&A.a
B->n
(*B).n
B->m
(*B).m

Coninut

Descriere

2 Numrul de linii al matricei A


3 Numrul de coloane al matricei A
0x00441ac0 Adresa nceput a zonei de memorie alocat
dinamic
pentru
stocarea
elementelor
matricei A
0x00441a80 Adresa de nceput a zonei de memorie
alocat dinamic pentru linia 1 a matricei A
0 Valoarea elementului de pe linia 1, coloana
1 din matricea A
0x0013ff68 Adresa de memorie static la care este
stocat obiectul A
0x0013ff68 Adresa de memorie static la care este
stocat atributul n
0x0013ff6c Adresa de memorie static la care este
stocat atributul m
0x0013ff70 Adresa de memorie static la care este
stocat atributul a
3 Numrul de linii al matricei A
2 Numrul de coloane al matricei A

B->a
(*B).a
B->a[0]
(*B).a[0]
B->a[0][0]
(*B).a[0][0]
&B
&B->n
&(*B).n
&B->m
&(*B).m
&B->a
&(*B).a
C[0].n
C[1].m
*(C+i)
C[2].a

C[1].a[0]

C[0].a[0][0]

0x004419c0 Adresa nceput a zonei de memorie alocat


dinamic
pentru
stocarea
elementelor
matricei B
0x00441980 Adresa de nceput a zonei de memorie
alocat dinamic pentru linia 1 a matricei B
0 Valoarea elementului de pe linia 1, coloana
1 din matricea B
0x0013ff64 Adresa de memorie static la care este
stocat obiectul A
0x00441a00 Adresa de memorie dinamic la care este
stocat atributul n
0x00441a04 Adresa de memorie dinamic la care este
stocat atributul m
0x00441a08 Adresa de memorie dinamic la care este
stocat atributul a
2 Numrul de linii al primei matrice din
vectorul alocat dinamic C
2 Numrul de coloane al celei de-a doua
matrice din vectorul alocat dinamic C
- Referirea obiectului matrice cu poziia i+1 n
vectorul alocat dinamic C
0x00441210 Adresa nceput a zonei de memorie alocat
dinamic
pentru
stocarea
elementelor
matricei cu poziia 3 n vectorul de obiecte
alocat dinamic C
0x00441310 Adresa de nceput a zonei de memorie
alocat dinamic pentru linia 1 a matricei cu
poziia 2 n vectorul de obiecte alocat
dinamic C
13 Valoarea elementului de pe linia 1, coloana
1 din matricea cu poziia 1 n vectorul de
obiecte alocat dinamic C

Apelul unei funcii i obinerea rezultatului returnat de aceasta prin


intermediul vectorului de pointeri spre funcii se realizeaz dup cum
urmeaz:
(*pf[i])();

unde:
pf vectorul de pointeri spre funcii;
i
rangul n vectorul de pointeri a funciei care se apeleaz.
Construirea expresiilor n limbajele de programare, conform
specificaiilor prevzute de fiecare limbaj, prezint o importan deosebit
n dezvoltarea corect din punct de vedere sintactic i mai ales semantic a
programelor surs. Expresiile trebuie s conduc la atingerea obiectivelor
stabilite n etapa de proiectare a aplicaiei.
Construirea expresiilor este o condiie necesar, dar nu i suficient
pentru a obine rezultate corecte din punct de vedere semantic. Se impune,
deci, i o referire corect a variabilelor i expresiilor definite, respectiv
construite ntr-un program surs pentru ca rezultatele obinute s fie n
concordan cu cerinele utilizatorilor.

20. STANDARD TEMPLATE LIBRARY STL


20.1 Structura STL
Structura STL cuprinde trei elemente principale: containerele,
iteratorii i algoritmii.
Containerele sunt diferite tipuri de obiecte care conin alte obiecte, de
tipuri predefinite sau definite de programator.
Iteratorii sunt obiecte care se comport asemntor pointerilor i
care sunt utilizai pentru a accesa elementele unui container.
Algoritmii furnizeaz funcionaliti de acces i prelucrare asupra
elementelor containerelor.
n tabelul 20.1 este prezentat structura STL.

Containere

Secveniale

Tabelul nr. 20.1 Structura STL


vector
list
deque

set

Adaptive

Asociative

multiset

Iteratori

map

multimap

stack
queue
priority_queue

Acces aleatoriu
Bidirecionali
nainte
De intrare
De ieire

Algoritmi

Implementeaz
vectori
alocai dinamic.
Lista liniar dublu nlnuit
Asemntor
containerului
vector, operaiile putnduse realiza la ambele capete.
Mulime
sortat
de
elemente unice.
Mulime
sortat
de
elemente.
Stocheaz perechi sortate
de tip <cheie, valoare> n
care o cheie identific n
mod unic o valoare.
Stocheaz perechi sortate
de tip <cheie, valoare> n
care o cheie identific una
sau mai multe valori.
Structur de tip stiv.
Structur de tip coad
Structur de tip coad n
care elementelor le sunt
asociate prioriti.

#include <vector>
#include < list >
#include < deque >

#include < set >


#include < set >
#include < map >

#include < map >

#include < stack >


#include < queue >
#include < queue >

Stocheaz i regsete valori. Elemente pot fi accesate


aleatoriu.
Stocheaz i regsete valori. Iteratorul poate nainta i
reveni.
Stocheaz i regsete valori. Iteratorul poate doar
nainta.
Regsete dar nu stocheaz valori. Iteratorul poate
doar nainta.
Stocheaz dar nu regsete valori. Iteratorul poate
doar nainta.

Funcii globale care ofer servicii generale cum ar fi


sortri, reordonri, modificri, copieri, cutri etc.

#include <algorithm>

n paragrafele urmtoare sunt exemplificate modaliti de lucru


utiliznd diferitele tipuri de containere, iteratori i algoritmi.

20.2 Containere STL


Containerele sunt ncadrate n trei mari categorii: secveniale,
asociative i adaptive.
Containerele secveniale sunt colecii liniare i ordonate de date n
care accesul se face pe baza poziiei elementului n cadrul containerului.
Cele trei clase predefinite de tip container secvenial sunt: vector, list i
deque. Ordinea n care se gsesc elementele n cadrul acestor containere
este ordinea n care ele sunt adugate. De reinut c, in cazul abloanelor
list i deque elementele pot fi adugate pe la ambele capete.
Containerele asociative se difereniaz de celelalte prin faptul c
stocarea elementelor se face pe baza unor chei. Accesul n acest caz se
poate realiza dup cheie, deci n mod direct. Elementele sunt reinute n
ordinea dat de modul de aranjare a cheilor. Din aceast categorie de
containere fac parte set, multiset, map i multimap.
Containerele
adaptive
adaug
funcionaliti
containerelor
secveniale. Acestea nu pot fi parcurse cu ajutorul iteratorilor ntruct nu
sunt folosite n mod independent. Pentru a folosi un astfel de container,
programatorul trebuie s aleag mai nti containerul de baz cruia s i fie
aplicat un container adaptiv. Astfel containerul stack poate adapta
containerele vector, list i deque, containerul queue poate fi implementat
pentru list i deque, iar priority_queue poate fi implementat pentru vector
i deque.
Pentru a exemplifica modul de lucru cu containerele secveniale att
pe tipuri de date predefinite, ct i pentru tipuri de date definite
programator, este propus urmtorul exemplu.
#include
#include
#include
#include

<iostream>
<vector>
<list>
<deque>

using namespace std;


class Student
{
private:
int nrMat;
char nume[20];
public:
Student(int nr = 0, char* n = "Student"):nrMat(nr)
{
strcpy(nume, n);
cout<<"Constructor clasa Student"<<endl;
}
int getNrMat()
{
return nrMat;
}
char* getNume()
{
return nume;

}
void setNume(char* n)
{
strcpy(this->nume,n);
}
Student(const Student& s)
{
this->nrMat = s.nrMat;
strcpy(this->nume, s.nume);
cout<<"Constructor de copiere clasa Student"<<endl;
}
bool operator<(Student& s)
{
return (this->nrMat < s.nrMat);
}
friend ostream& operator<<(ostream& o, Student s)
{
o<<s.getNrMat()<<" "<<s.getNume()<<endl;
return o;
}
};
void main()
{
Student s1(1, "Gigel");
Student s2(2, "Ionel");
Student s3(3, "Balanel");
//////////////////////////////////////////////////////////////////
//vector de tip de data predefinit
vector<int> vectInt;
vectInt.push_back(10);
vectInt.push_back(1);
vectInt.push_back(25);
vectInt.push_back(12);
for(int i=0; i<vectInt.size(); i++)
cout<<vectInt[i]<<" ";
cout<<endl;
//--------------------------------------------------//vector de tip de data definit de programator
vector<Student> vectStud;
vectStud.push_back(s3);
vectStud.push_back(s2);
vectStud.push_back(s1);
for(int i=0; i<vectStud.size(); i++)
cout<<vectStud[i]<<" ";
cout<<endl;
////////////////////////////////////////////////////////////////

// lista de tip de data predefinit


list<int> listInt;
listInt.push_back(10);//inserare la sfarsit cu ajutorul metodei
push_back()
listInt.push_front(1);//inserare la inceput prin push_front()
listInt.insert(listInt.end(),25);//inserare prin insert()
listInt.insert(listInt.begin(),12);//inserare prin insert()

//declararea unui iterator cu care va fi traversata lista


//operatorul [] nu este implementat pentru list
list<int>::iterator itInt;
for(itInt=listInt.begin(); itInt!=listInt.end(); itInt++)
cout<<*itInt<<" ";
cout<<endl;
listInt.sort();
cout<<"Lista sortata"<<endl;
for(itInt=listInt.begin(); itInt!=listInt.end(); itInt++)
cout<<*itInt<<" ";
cout<<endl;
//-----------------------------------------------------------// lista de tip de data definit de programator
list<Student> listStud;
//inserare la sfarsit cu ajutorul metodei push_back()
listStud.push_back(s3);
listStud.push_front(s1);//inserare la inceput prin push_front()
listStud.insert(listStud.end(),s2);//inserare prin insert()

//declararea unui iterator cu care va fi traversata lista


//operatorul [] nu este implementat pentru list
list<Student>::iterator itStud;
for(itStud=listStud.begin(); itStud!=listStud.end(); itStud++)
cout<<*itStud<<" ";
cout<<endl;
listStud.sort();
cout<<"Lista de studenti sortata"<<endl;
for(itStud=listStud.begin(); itStud!=listStud.end(); itStud++)
cout<<*itStud<<" ";
cout<<endl;

///////////////////////////////////////////////////////////////////
//deque pentru tipuri de date predefinite
deque<int> deqInt;
deqInt.push_front(10);
deqInt.push_back(1);
deqInt.push_front(25);
deqInt.push_back(12);

deque<int>::iterator iInt;
for(iInt = deqInt.begin(); iInt!= deqInt.end(); iInt++)
cout<<*iInt<<" ";
cout<<endl;
//----------------------------------------------------------//deque pentru tipuri de date definite de programator
deque<Student> deqStud;
deqStud.push_front(s1);
deqStud.push_back(s3);
deqStud.push_front(s2);
deque<Student>::iterator iStud;
for(iStud = deqStud.begin(); iStud!= deqStud.end(); iStud++)
cout<<*iStud<<" ";
cout<<endl;
}

Pentru tipul de date int i tipul definit Student sunt construite cte
un container de tip vector, unul de tip list i unul de tip deque. In fiecare
dintre acestea sunt adugate elemente de tip int sau Student, dup caz,
utiliznd metode specifice acestor abloane.
Clasa vector permite adugarea elementelor pe la un singur capt,
pe cnd clasele list i deque permit operaii pe la ambele capete. Metoda
push_back() pentru toate cele trei clase i metoda push_front() pentru list
i deque copiaz obiectul ntr-unul dintre elementele clasei. Dac pentru
tipurile predefinite de date copierea valorii obiectului nu pune nici o
problem, copierea unui tip definit de programator necesit suprancrcarea
constructorului de copiere, n special n cazul n care obiectul conine
atribute cu extensie n memoria dinamic. Clasa Student suprancarc
constructorul de copiere i operatorul >>, pentru tiprirea pe ecran a unui
obiect de tip Student. Vectorul de elemente int i cel de elemente Student
pot fi parcuri cu ajutorul unui contor ce ia valori de la 0 la dimensiunea
masivului, dimensiune dat de metoda size() a vectorului. Aceast
parcurgere se datoreaz faptului c vectorul ocup un spaiu contiguu de
memorie dinamic, ceea ce permite calculul deplasrii unui element fa de
nceputul vectorului. Operatorul [] nu este definit i pentru clasele list i
deque, de aceea este necesar definirea unui iterator pentru a realiza
accesul la date. Acest iterator poate parcurge masivul de la nceput dat de
metoda begin() pn la sfrit, dat de metoda end().
Clasa list permite sortarea elementelor containerului (sortare
cresctoare implementat de metoda sort() sau sortare descresctoare,
implementat de metoda reverse()), motiv pentru care, n moment ce
containerul este utilizat pentru stocarea unor obiecte de tip definit de
programator, trebuie suprancrcat operatorul<. Pe baza acestuia este
stabilit relaia de ordine n clasa respectiv.
ablonul deque combin facilitile oferite de vector cu cele oferite de
list. Acesta este proiectat ca un vector ce poate crete n ambele direcii,
deci metodele push_back()/pop_back() i push_front()/pop_front() sunt
valide. Adugarea de elemente n interiorul unui deque este mai lent dect

pe o poziie interioar ntr-o list ntruct n cazul deque toate elementele


trebuie mutate.
Pentru exemplificarea modului de lucru cu containerele asociative se
consider clasa Student i prelucrrile corespunztoare din exemplul
urmtor.
#include <iostream>
#include <set>
using namespace std;
class Student
{
private:
int nrMat;
int varsta;
char nume[20];
public:
Student(int nr = 0, int v=0, char* n = "Student"):nrMat(nr),
varsta(v)
{
strcpy(nume, n);
cout<<"Constructor clasa Student"<<endl;
}
int getNrMat(){return nrMat;}
char* getNume(){return nume;}
int getVarsta(){return varsta;}
void setNume(char* n){strcpy(this->nume,n);}
Student(const Student& s){
this->nrMat = s.nrMat;
this->varsta = s.varsta;
strcpy(this->nume, s.nume);
cout<<"Constructor de copiere clasa Student"<<endl;
}
bool operator<(const Student& s)const{
return (this->nrMat < s.nrMat);
}
friend ostream& operator<<(ostream& o, Student s){
o<<s.getNrMat()<<" "<<s.getNume()<<endl;
return o;
}
};
void main()
{
Student
Student
Student
Student

s1(1,
s2(2,
s3(3,
s4(4,

19,
20,
19,
19,

"Gigel");
"Ionel");
"Balanel");
"Ilie");

/////////////////////////
//sablon set //
/////////////////////////
set<Student> setStud;
setStud.insert(s4);
setStud.insert(s2);

setStud.insert(s3);
setStud.insert(s1);
cout<<"Comparatie dupa cheie:";
cout<<s4.getNume()<<" este"<<(setStud.key_comp()(s4,s2)?" inaintea ":"
dupa ")<<s2.getNume()<<endl;
cout<<"Comparatie dupa valoarea elementului:";
cout<<s4.getNume()<<" este"<<(setStud.value_comp()(s4,s2)?" inaintea
":" dupa ")<<s2.getNume()<<endl;
cout<<"Comparatie dupa cheie:";
cout<<s3.getNume()<<" este"<<(setStud.key_comp()(s3,s1)?" inaintea ":"
dupa ")<<s1.getNume()<<endl;
cout<<"Comparatie dupa valoarea elementului:";
cout<<s3.getNume()<<" este"<<(setStud.value_comp()(s3,s1)?" inaintea
":" dupa ")<<s1.getNume()<<endl;
cout<<"Comparatie dupa cheie:";
cout<<s1.getNume()<<" este"<<(setStud.key_comp()(s1,s4)?" inaintea ":"
dupa ")<<s4.getNume()<<endl;
cout<<"Comparatie dupa valoarea elementului:";
cout<<s1.getNume()<<" este"<<(setStud.value_comp()(s1,s4)?" inaintea
":" dupa ")<<s4.getNume()<<endl;
set<Student>::iterator itStud;
itStud = setStud.find(2);
while(itStud!=setStud.end())
{
cout<<itStud->getNume()<<" ";
itStud++;
}
}

ntruct toate containerele asociative rein elementele sortate pe


baza cheilor, clasa Student suprancarc operatorul< pentru a defini relaia
de ordine. Clasa Student utilizat n lucrul cu ablonul set definete relaia
de ordine pe baza numrului matricol al studentului, numr ce identific n
mod unic un student. Metodele key_comp() i value_comp() au acelai efect
n lucrul cu abloane set ntruct fiecare valoare a unui element corespunde
unei singure chei. Aadar, valoarea i cheia sunt identice. Rezultatele rulrii
programului confirm ordinea impus prin suprancrcarea operatorului<.
Containerele asociative dispun i de metode de regsire direct a
elementelor, adic dup valoare cheii. Aceast metod se numete find() i
primete ca parametru valoarea cheii, ntorcnd un pointer ctre elementul
respectiv. In exemplul prezentat, este definit un iterator care adreseaz
ntr-o prim faz elementul ce are valoarea cheii egal cu 2. Acest element
este obinut cu ajutorul metodei find(). De la respectiva poziie i pn la
sfritul mulimii aceasta este parcurs, iar toate numele ce aparin acestui
interval sunt tiprite pe ecran.
Pentru a exemplifica lucrul cu multiset va fi stabilit o nou relaie de
ordine a clasei Student, pe baza vrstei. Astfel, o cheie nu va identifica n
mod unic neaprat doar un element.

class Student
{
...
//supraincarcare operator< a clasei Student modificata
bool operator<(const Student& s)const{
return (this->varsta < s.varsta);
}
...
}; //sfarsit clasa Student
//////////////
void main()
{
Student
Student
Student
Student
Student
Student

s1(1,
s2(2,
s3(3,
s4(4,
s5(5,
s6(6,

19,
20,
19,
19,
20,
20,

"Gigel");
"Ionel");
"Balanel");
"Ilie");
"Maricica");
"Lenuta");

Student vectStud[] = {s6, s1, s5, s4, s2, s3};


//////////////////////////////
//lucrul cu sablon multiset //
//////////////////////////////
multiset<Student> mSetStud;
for(int i=0; i<6; i++)
mSetStud.insert(vectStud[i]);
multiset<Student>::iterator itStud;
std::pair<multiset<Student>::iterator,multiset<Student>::iterator>
itVarsta;
cout<<"Persoane de aceeasi varsta cu "<<s1.getNume()<<": ";
itVarsta = mSetStud.equal_range(s1);
for(itStud=itVarsta.first; itStud!= itVarsta.second; itStud++)
cout<<itStud->getNume()<<" ";
cout<<endl;
cout<<"Persoane de aceeasi varsta cu "<<s2.getNume()<<": ";
itVarsta = mSetStud.equal_range(s2);
for(itStud=itVarsta.first; itStud!= itVarsta.second; itStud++)
cout<<itStud->getNume()<<" ";
cout<<endl;
}

Pentru a putea identifica n cadrul mulimii de studeni pe ci cu


aceeai vrst este definit o pereche de iteratori ai multisetului. Aceast
pereche este ncrcat cu valori prin apelul metodei equal_range(obiect),
primul dintre ei adresnd primul obiect ce satisface condiia, iar al doilea
adresnd primul obiect ce numai satisface condiia. Cu ajutorul unui nou
iterator, itStud, sunt parcurse toate adresele ce se ncadreaz n intervalul
precizat, iar numele studenilor sunt tiprite pe ecran.
Containerele set i multiset sunt implementate n C++ cu ajutorul
arborilor de cutare, astfel regsirea fiind foarte rapid.
Utilizarea containerul asociativ map este prezentat ntr-un exemplu
de agend telefonic. Se consider o adres telefonic, n care vor fi
inserate perechi de iruri de caracter sub forma <nume, numr de telefon>.
Pentru a putea insera ntr-un container map valori, acestea trebuie s fie

furnizate mai nti ca parametru constructorului clasei pair<string, string>.


Numele persoanei este cheie unic, iar fiecrei persoane i este asociat un
numr de telefon. Agenda permite cutarea unui numr de telefon dup
numele persoanei. Pentru cutarea n agend este folosit un iterator.
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
map<string, string> agenda;
typedef pair<string, string> String_pair;
agenda.insert(String_pair("Gigel", "021111111"));
agenda.insert(String_pair("Georgiana", "023666777"));
agenda.insert(String_pair("Ionel", "021333456"));
agenda.insert(String_pair("Balanel", "021999888"));
agenda.insert(String_pair("Maricica", "021333444"));
agenda.insert(String_pair("Lenuta", "023444555"));
map<string, string>::iterator itAgenda;
cout<<"Numele valabile: ";
itAgenda
!=
agenda.end();
for(itAgenda
=
agenda.begin();
itAgenda++)
cout<<(*itAgenda).first<<" "; /* scrie prima valoare
stocata, adica numele */
cout<<endl;
cout<<"----------------------------------------------"<<endl;
cout<<"Introduceti numele: ";
string nume;
cin>>nume;
while(nume != "IESIRE")
{
cout<<endl;
map<string, string>::iterator itNume = agenda.find(nume);
if(itNume != agenda.end()){
cout<<(*itNume).first<<"
"<<(*itNume).second
<<endl<<endl;
}
else{
cout<<"Acest
contact
nu
exista
in
agenda"
<<endl<<endl;
}
cout<<"Introduceti numele: ";
cin>>nume;
}
}

Numele persoanei, care este cheie, este prima valoare stocat din
cadrul perechii i poate fi referit ca (*numeIterator).first. Numrul de
telefon este a doua valoare memorat i poate fi referit cu ajutorul
iteratorului astfel: (*numeIterator).second. Cutarea unui numr de telefon
dup
numele
persoanei
este
realizat
cu
ajutorul
metodei
find(valoare_cheie) a ablonului map. Aceast funcie returneaz un

iterator, a crui valoare indic fie elementul pentru care valoarea cheii este
egal cu valoarea cutat, fie o adres egal cu cea furnizat de metoda
end() a clasei map, n cazul n care nu exist nici un element n container
pentru care valoarea cheii s fie egal cu valoarea dorit.
Containerul asociativ multimap permite identificarea unei sau mai
multor valori pe baza unei valori a cheii. Mai exact, cheia nu este unic, pot
exista oricte perechi pentru care cheia s fie egal.
Modul de lucru cu containere adaptive este pus n eviden de
exemplul urmtor.
#include
#include
#include
#include
#include
#include

<iostream>
<stack>
<queue>
<vector>
<deque>
<list>

using namespace std;


template <class T> popStiva(T& st)
{
while(!st.empty()){
cout<<st.top()<<" ";
st.pop();
}
}
template <class T> popCoada(T& cd)
{
while(!cd.empty()){
cout<<cd.front()<<" ";
cd.pop();
}
}
void main()
{
//stiva adaptata pentru container deque -> implicit
stack<char, deque<char> > stivaDeq; /* echivalent cu stack<int>
stivaDeq; */
//stiva adaptata pentru container vector
stack<char, vector<char> > stivaVect;
//stiva adaptate pentru container list
stack<char, list<char> > stivaList;
//coada adaptata pentru container deque
queue<char, deque<char> > coadaDeq;
//coada adaptata pentru conatainer list
queue<char, list<char> >coadaList;
for(char i=57; i>47; i--)
{
stivaDeq.push(i);
coadaDeq.push(i);
stivaVect.push(i+17);
stivaList.push(i+49);
coadaList.push(i+49);
}

cout<<"Stiva deque: ";


popStiva(stivaDeq);
cout<<endl;
cout<<"Coada deque: ";
popCoada(coadaDeq);
cout<<endl;
cout<<"Stiva vector: ";
popStiva(stivaVect);
cout<<endl;
cout<<"Stiva list: ";
popStiva(stivaList);
cout<<endl;
cout<<"Coada list: ";
popCoada(coadaList);
cout<<endl;
}

In exemplul de mai sus este creat un container adaptiv de tip stiv


pentru fiecare container secvenial. Aadar, stivaDeq corespunde
implementrii unei stive pentru un container de tip deque, stivaVect este o
adaptare a clasei vector la o structur de stiv iar stivaList este
implementarea stivei pentru o list. Totodat au fost create i dou
containere de tip coad pentru cele dou containere secveniale ce permit
aceast adaptare: coadaDeq adaptarea unui deque pentru a implementa o
structur de tip coad i coadaList adaptarea unei liste. In stiva i coada
corespunztoare ablonului deque sunt introduse cifrele de la 0 la 9.
Afiarea elementelor din cadrul stivei i cozii se face cu ajutorul metodelor
top(), respectiv front(). Elementele din cadrul cozii vor fi afiate n ordine
invers fa de cele din cadrul stivei. In stiva corespunztoare ablonului
vector sunt inserate caracterele de la J la A. Afiarea se face n ordine
invers, deci aceste vor aprea pe ecran de la A la J. Similar, n coada i
stiva corespunztoare ablonului list sunt inserate caracterele de la j la a.
Afiarea pe baza cozii se face n ordinea n care au fost introduse, deci de la
j la a, iar afiarea pe baza stivei se face n ordine invers, deci de la a la j.
Urmtorul program exemplific utilizarea containerului asociativ
multimap.
#include
#include
#include
#include

"stdafx.h"
<map>
<string>
<iostream>

using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
multimap<string, string> agenda;
agenda.insert(pair<string,string>("Alina","0711111111"));
agenda.insert(pair<string,string>("Alina","0211111111"));

agenda.insert(pair<string,string>("Alina","0311111111"));
agenda.insert(pair<string,string>("Dragos","0711111112"));
agenda.insert(pair<string,string>("Dragos","0311111112"));
agenda.insert(pair<string,string>("Vlad","0711111112"));
agenda.insert(pair<string,string>("Ionut","0711111114"));
agenda.insert(pair<string,string>("Marian","0711111115"));
multimap<string, string>::iterator it;
//Parcurgere intreaga agenda
for (it = agenda.begin(); it != agenda.end(); it++)
cout << "Persoana de contact " << (*it).first << ", are
numarul: " << (*it).second<< endl;

cout<<"Dragos are " <<agenda.count("Dragos")<<" intrari in


agenda"<<endl;

cout<<"Numerele de telefon ale lui Dragos sint:" << endl;


for (it = agenda.lower_bound("Dragos");it !=
agenda.upper_bound("Dragos"); it++)
cout << "\t" << it->second << endl;
agenda.erase("Dragos");
cout<<"Dragos are " <<agenda.count("Dragos")<<" intrari in
agenda"<<endl;
cin.get();
return 0;
}

n programul de mai sus s-a utilizat clasa multimap pentru a crea o


agend de contacte. Cheia de cutare este numele persoanei, o persoan
putnd avea asociate mai multe numere de telefon. Sunt exemplificate
pentru clasa multimap operaiile de inserare, numrare a elementelor,
tergere i parcurgere.
Un adaptor priority-queue furnizeaz un subset restrns de
funcionaliti containerului secvenial corespunztor. Aceste funcionaliti
se refer la inseria de noi elemente, consultarea i extragerea/tergerea de
elemente. O coad cu prioriti nu poate fi parcurs prin intermediul unui
iterator. Containerul asociat n mod implicit este vector. Elementul din vrful
unei cozi cu prioriti este elementul cu cea mai mare prioritate.
#include <iostream>
#include <queue>
using namespace std;
void main()
{
priority_queue<char> Q;
Q.push('A');
Q.push('C');
Q.push('B');
Q.push('E');
Q.push('D');
Q.push('F');

while(!Q.empty())
{
cout<<Q.top()<<" ";
Q.pop();
}
//iesire: F E D C B A
}

Metoda top() returneaz o referin ctre elementul cu valoarea cea


mai mare din coad. Acest lucru este realizat astfel datorit definirii
implicite a unui Comparator. n exemplu, dei literele nu au fost introduse n
coad astfel nct s fie tiprite de la mare la mic de la F la A, stocarea lor
coada cu prioriti a rezultat n afiarea lor de la F la A.

20.3 Iteratori STL


Iteratorii se aseamn cu pointerii, dar sunt de fapt obiecte ce
adreseaz alte obiecte. Cu ajutorul lor pot fi adresate elemente ale
containerelor care aparin anumitor intervale. Iteratorii reprezint interfaa
de comunicaie ntre algoritmi i containere, fiind preluai ca parametrii de
ctre algoritmi. Containerele le furnizeaz algoritmilor o cale de acces ctre
elementele lor prin intermediul iteratorilor.
In exemplele prezentate anterior au fost utilizai pentru parcurgerea
containerelor
iteratori
definii
de
programator
dup
modelul
container<tip_data>::iterator numeIterator;. STL furnizeaz i iteratori
predefinii, a cror utilizare presupune includerea fiierului iterator.
Un iterator de tip istream, notat istream_iterator<T,distanta>
formateaz intrarea provenit de la un obiect de tip T dintr-un flux de
intrare. In momentul n care fluxul ajunge la sfrit iteratorul ia o valoare de
sfrit special pentru a specifica sfritul datelor. Acest iterator permite
modificarea elementului pe care l refer.
Un iterator de tip ostream, notat ostream_iterator<T> este un
iterator de ieire ce realizeaz legtura unui iterator cu un flux de ieire. De
menionat c elementul referit de acest tip de iterator nu poate fi modificat,
ci doar consultat.
#include <iostream>
#include <iterator>
#include <vector>
using namespace std;
void main()
{
vector<char> vect;
istream_iterator<char>inIterator(cin);
ostream_iterator<char>outIterator(cout, "\n");
//varianta a - stream intrare
copy(istream_iterator<char>(cin),
istream_iterator<char>(), back_inserter(vect));
//varianta b - stream intrare

/* while(*inIterator)
{
vect.push_back(*inIterator);
inIterator++;
} */
//varianta a - stream iesire
copy(vect.begin(),
vect.end(), ostream_iterator<char>(cout, "\n"));
//varianta b - stream iesire
/* while(!vect.empty())
{
*outIterator = vect.back();
vect.pop_back();
} */
}

n exemplul de mai sus este prezentat modul de lucru cu iteratori de


tip istream_iterator i ostream_iterator. La declararea unui iterator de
intrare, respectiv de ieire, trebuie specificate tipul dedate al elementelor
din colecie (exemplul utilizeaz elemente de tip char) i fluxul de date
cin, respectiv cout. \n este o constant care va fi afiat automat dup
afiarea unui element. Practic elementele vor fi afiate cte unul pe linie.
Varianta a din exemplul, att pentru fluxul de intrare ct i pentru cel de
ieire este echivalent cu varianta b. In varianta a ns, nu s-au folosit
iteratori declarai anterior. Funcia copy(InputIterator prim, InputIterator
ultim, OutputIterator rezultat) copiaz elementele care aparin intervalului
[prim, ultim) n intervalul [rezultat, rezultat + (ultim - prim)).
Un iterator invers, de tip reverse_iterator este folosit pentru
parcurgerea n ordine invers a elementelor unei colecii.
#include <iostream>
#include <vector>
#include <iterator>
using namespace std;
void main()
{
vector<char> vect;
vector<char>::reverse_iterator reverseIt;
for(int i=57; i>47; i--)
vect.push_back(i);
for(reverseIt=vect.rbegin(); reverseIt != vect.rend();
reverseIt++)
cout<<*reverseIt<<" ";
}

Metoda rbegin() este echivalent cu metoda end() a vectorului, iar


metoda rend() este echivalent cu begin(). Metodele rbegin() i redn() sunt
utilizate pentru a lucra cu iteratori inveri, iar operatorul ++ semnific de
fapt decrementarea acestuia. In vector sunt introduse cifrele de la 9 la 0, iar
parcurgerea elementelor cu ajutorul iteratorului invers afieaz pe ecran

cifrele de la 0 la 9. Practic, afiarea se face ca i cum vectorului i-ar fi


aplicat un adaptor de tip stiv, aceasta fiind golit prin metoda pop().
Un iterator de inserare, insert_iterator<container<T> > este un
iterator specializat pe inserri n cadrul containerelor. Parametrul primit de
ctre acest tip de iterator este tipul containerului n care se face inserarea,
iar tipul containerului va primi ca parametru tipul de dat al elementelor.
Exist i dou forme specializate ale acestui iterator, back_insert_iterator
pentru inserri la sfritul containerelor i front_insert_iterator pentru
inserri la nceputul containerelor.
#include <iostream>
#include <list>
#include <iterator>
using namespace std;
void main()
{
list<char> lista;
lista.push_front('D');
//definesc
un
iterator
cu
care
inserez
la
inceputul
containerului
insert_iterator<list<char>
>
insertItBegin(lista,
lista.begin());
//inserez in lista
*insertItBegin = 'A'; *insertItBegin = 'B';*insertItBegin = 'C';
//afisez pe ecran
copy(lista.begin(),lista.end(),ostream_iterator<char>(cout,"
"));
}

Constructorul iteratorului de inserare primete ca parametru


containerul n care vor fi inserate elemente i poziia din cadrul
containerului unde se vor efectua inserrile.

20.4 Algoritmi STL


Pe lng metodele care aparin de clasele container, STL pune la
dispoziie funcii globale ce ofer posibilitatea efecturii unor prelucrri
asupra mai multor containere prin includerea fiierului algorithm. Aceste
funcii primesc ca parametri iteratori ai diferitelor containere, acesta fiind
modul de realizare a accesului la elementele containerelor.
Algoritmii STL se mpart n patru mari categorii:
A. Algoritmi care modific ordinea elementelor n container
modifying sequence operations. Dintre cei mai folosii algoritmi care fac
parte din aceast categorie sun amintii: copy(), replace(), transform() i
remove().

#include
#include
#include
#include
#include

<iostream>
<list>
<vector>
<iterator>
<algorithm>

using namespace std;


void main()
{
vector<char> vect;
list<char> lista(6);
vect.push_back('A');
vect.push_back('B');
vect.push_back('C');
vect.push_back('D');
vect.push_back('A');
vect.push_back('A');
//functia COPY - pentru copierea elementelor
//dintr-un container in altul
copy(vect.begin(), vect.end(), lista.begin());
//functia COPY pentru tiparirea pe ecran
//a elementelor copiate
copy(lista.begin(), lista.end(), ostream_iterator<char>(cout, "
"));
cout<<endl;
//functia REPLACE - pentru inlocuirea
//caracterelor A cu a
replace(lista.begin(), lista.end(), 'A', 'a');
cout<<"Lista dupa efectuarea inlocuirilor:"<<endl;
copy(lista.begin(), lista.end(), ostream_iterator<char>(cout, "
"));
}

Funciilor le sunt furnizate ca parametrii intervale de aciune asupra


elementelor prin intermediul a doi iteratori: unul care indic de unde ncepe
aciunea funciei, unul unde se termin. Funcia copy() este utilizat n
cadrul exemplului pentru a copia elemente dintr-un container n altul i
pentru a afia pe ecran elementele containerului n care s-au efectuat
modificri. Pe lng cei doi iteratori ce definesc intervalul cruia i aparin
elementele, funcia mai primete ca parametru i un iterator ce definete
destinaia copierilor. Pentru copierea elementelor dintr-un container n altul,
al treilea parametru este poziia din containerul destinaie unde se doresc s
fie inserate elementele, adic lista.begin() (la nceputul listei). Pentru
tiprirea pe ecran a elementelor, al treilea parametru este dat de un iterator
de ieire, care leag copierea valorilor de fluxul standard de ieire, ecranul:
ostream_iterator<char>(cout, " "). Funcia replace() efectueaz nlocuirea
unei valori cu alt valoare, pentru elementele ce aparin unui interval dat.
Aadar, n lista din exemplu, valoarea A este nlocuit cu a, de la nceputul
listei i pn la sfritul ei.
B. Algoritmi care nu modific ordinea elementelor n container nonmodifying sequence operations. Cei mai ntlnii din aceast categorie sunt:
for_each(), find(), count() i equal().

#include
#include
#include
#include
#include

<iostream>
<list>
<vector>
<iterator>
<algorithm>

using namespace std;


void scrie(char c)
{
cout<<++c<<"/";
}
void main()
{
list<char> lista;
lista.push_back('A');
lista.push_back('B');
lista.push_back('C');
lista.push_back('D');
lista.push_back('A');
lista.push_back('A');
for_each(lista.begin(), lista.end(), scrie);
cout<<"Numar de elemente egale cu A: "<<count(lista.begin(),
lista.end(), 'A')<<endl;
cout<<"Elemente dupa D:";
list<char>::iterator it = find(lista.begin(), lista.end(), 'D');
it++;
for( ;it != lista.end(); it++)
cout<<*it<<" ";
cout<<endl;
}

Funcia for_each() itereaz de-a lungul intervalului delimitat de cei


doi iteratori primii ca primi doi parametrii i prelucreaz fiecare element
inclus n acest interval. In exemplul de mai sus, funcia for_each() tiprete
pe ecran caracterele incluse n lista, incrementate. Dac lista conine A,
atunci n locul acestuia va fi tiprit B. Funcia count() numr cte elemente
dintr-un interval dat al containerului au o valoare egal cu o valoare dat.
Funcia find() returneaz un iterator ctre poziia din cadrul containerului
unde exist primul element ce are o valoare egal cu valoarea primit ca
parametru. Aceast funcie este utilizat n exemplu pentru a tipri toate
caracterele din list care i urmeaz caracterului D.
C. Algoritmi de sortare i operaii similare. Dintre cei mai cunoscui
algoritmi inclui n aceast categorie, i reamintim pe urmtorii: sort(),
equal_range(), merge() i includes().
#include
#include
#include
#include

<iostream>
<vector>
<iterator>
<algorithm>

using namespace std;


bool less(int n1, int n2){return n1<n2;}

bool greater(int n1, int n2){return n1>n2;}


void main()
{
vector<int> vect;
vect.push_back(10);
vect.push_back(5);
vect.push_back(7);
vect.push_back(1);
vect.push_back(13);
sort(vect.begin(), vect.end());
copy(vect.begin(), vect.end(), ostream_iterator<int>(cout, "
"));
cout<<endl;
sort(vect.begin(), vect.end(), less);
copy(vect.begin(), vect.end(), ostream_iterator<int>(cout, "
"));
cout<<endl;
sort(vect.begin(), vect.end(), greater);
copy(vect.begin(), vect.end(), ostream_iterator<int>(cout, "
"));
cout<<endl;
}

Funcia sort() sorteaz elementele unui container dup o condiie


dat prin intermediul unui pointer la o funcie. Aceast funcie trebuie s
primeasc dou variabile de tipul elementelor containerului ca parametrii i
s ntoarc un boolean. Dac ceea ce ntoarce este egal cu false, funcia
sort() interschimb elementele. In exemplul de mai sus, sunt definite dou
funcii, less i greater, cu ajutorul crora elementele vectorului vect sunt
sortate cresctor, respectiv descresctor. Funcia sort() sorteaz elementele
n mod implicit (dac nu primete i pointer ctre o funcie ca parametru)
cresctor. Dac tipul obiectelor stocate n container este unul definit de
programator, este necesar suprancrcarea operatorului < pentru a defini o
relaie de ordine n cadrul containerului. Totodat, dac se dorete tiprirea
pe ecran a elementelor containerului, este necesar suprancrcarea
operatorului <<.
D. Algoritmi generali pentru operaii numerice, precum min() i
max().
#include <iostream>
#include <algorithm>
using namespace std;
void main()
{
int a = 10;
int b = 11;
int c = 12;
cout<<"Minimul
dintre
"<<a<<"
si
min(a,b)<<endl;
cout<<"Minimul dintre "<<a<<", "<<b<<"
min(min(a,b),c)<<endl;

"<<b<<"
si

"<<c<<"

este:
este:

"<<
"<<

cout<<"Maximul
dintre
"<<a<<"
si
max(a,b)<<endl;
cout<<"Maximul dintre "<<a<<", "<<b<<"
max(max(a,b),c)<<endl;
}

"<<b<<"
si

"<<c<<"

este:
este:

"<<
"<<

Funciile min() i max() puse la dispoziie de STL returneaz minimul,


respectiv maximul dintre dou numere. Ele pot fi utilizate i n cascad,
pentru a calcula minimul, respectiv maximul dintre mai multe numere.
n practic, biblioteca standard STL este suportat de toate
compilatoarele C++ (Intel, Microsoft, Unix) pe toate tipurile de platforme
UNIX, Windows.

21. CONVERSII ALE STRUCTURILOR DE DATE


21.1 Procese de conversie
Dezvoltarea domeniului IT a dus la intensificarea concurenei pe piaa
produselor software. Apariia la momentul potrivit a pachetului de aplicaii
care s satisfac cerinele clienilor este elul fiecrui concurent. Din pcate
unii reuesc fructificarea momentului oportun la maximum, chiar dac
eficiena produsului respectiv nu este dintre cele mai bune.
Eficiena produsului software este mrit prin mai multe metode,
capitolul de fa oprindu-se la metoda conversiei structurilor de date. n
dezvoltarea unui program, la un moment, este necesar realizarea
conversiei, de exemplu, de la tipul real la tipul ntreg. Acest lucru este
folositor desfurrii normale a algoritmului gndit de programator. Sub
mediul de programare Microsoft Visual C++, conversia de la un tip la altul
are o importan deosebit, fapt demonstrat de existena operatorului de
cast. Acest operator se suprancarc n funcie de necesitile ulterioare ale
programatorului.
Aparent, necesitatea operatorului cast este nesemnificativ. n
schimb programarea sub Windows utilizeaz foarte des conversiile, cele mai
des uzitate fiind cele ale pointerilor ctre anumite clase. Deci, existena
conversiei fluidizeaz procesul de programare, mrete eficiena i
simplitatea programului.
Aa cum se afirm mai sus, acest capitol se oprete asupra conversiei
structurilor de date. Asemnarea dintre economie i conversie este dat de
nivelul la care se face analiza. Conversia tipurilor de date este asociat
nivelului microeconomic, iar conversia structurilor de date este asociat
nivelului macroeconomic. Deci, conversia structurilor de date este tratat ca
un factor important n creterea eficienei produsului final.
Eficiena sporit este dat i de creterea gradului de reutilizare a
anumitor structuri de date. S presupunem c numrul matricol al unor
elevi este memorat ntr-un vector. Acest vector este folosit ntr-o aplicaie
de gestionare a elevilor colii respective. La un moment dat se dorete
schimbarea aplicaiei. Programatorul care realizeaz noua aplicaie dorete
pstrarea numrului matricol ntr-o structur de date de tip list. Cum se
poate micora efortul de programare? Simplu, prin folosirea procedurii care
realizeaz conversia de la un vector ctre o list.
n acest caz programatorul nu mai este nevoit s reintroduc toate
numerele matricole asociate elevilor, ceea ce i reduce foarte mult efortul de
programare. Existena unei astfel de biblioteci de funcii i proceduri este
foarte folositoare. n continuarea capitolului sunt prezentate proceduri i
funcii de conversie a structurilor de date realizate n limbajul de
programare C.
Reprezentarea n limbajul de programare C a structurilor folosite
este:
//lista simplu nlnuit
struct lista
{
//informaia elementului
int info;

//lista dublu nlnuit


struct listad
{
//informaia elementului
int info;

//pointer elementul urmtor


lista *next;
};

//elementul din lista arcelor


struct arc
{
//referin ctre nod destinaie
struct nodgraf * destinatie;
//elementul urmtor din list
struct arc * next_arc;
//greutatea arcului
int weight;
};

//pointer elementul urmtor


lista* next;
//pointer elementul anterior
lista* prev; };
//nodul unui graf
struct nodgraf
{
//informaia nodului
int info;
//nodul urmtor
struct nodgraf * next;
//capt lista arcelor
struct arc * capat;
};

// nodul arborelui binar


struct arbbin
{
// informaia nodului
int info;
// referin ctre nodul stng i cel drept
arbbin *ss,*sd;
};

ntr-o aplicaie informatic se utilizeaz mai multe structuri de date.


Pentru a prelucra informaii referitoare la elementele unei colectiviti,
stocarea este realizat sub forma de fiiere.
Pentru toate structurile de date sunt definite articole ce includ
variabile pointer specifice legturilor dintre elementele acestora i
informaia util care descrie elementele unei colectiviti format din
persone care lucreaz ntr-o organizaie, astfel:
- cod numeric personal, dat de tip vector cu 13 elemente de tip
caracter;
- nume i prenume, dat de tip vector de caractere;
- adresa, structur de tip articol format 3 cmpuri: ora, strad,
numr;
- vrsta, dat de tip ntreg, definit pe 4 baii;
- salariu, dat de tip ntreg, definit pe 4 baii.
Aplicaia informatic pentru rezolvarea sistemului de ecuaii AX=B de
mari dimensiuni presupune:
crearea unui fiier ce conine elementele matricei A;
crearea unui fiier ce conine elementele termenului liber B;
ncarcarea n memorie sub forma unei matrici rare a elementelor
matricii A (n blocuri/totalitate);
ncarcarea n memorie a elementelor vectorului de termeni liberi B
(ncrcarea se efectueaz parial)
se deruleaz calcule cu aceste informaii;
se obine soluia sistemului de ecuaii i se memoreaz ntr-un
fiier.
Problema se reduce la conversie de date organizate sub forma de
fiier n matrice/vector, respectiv conversie din matrice/vector n fiier.

n acest capitol nu sunt descrise toate combinaiile posibile de


conversii din dou motive: sunt lsate n grija cititorului sau acestea se pot
obine combinnd tipurile prezentate. De exemplu conversia unui graf n
vector se realizeaz combinnd conversia graf matrice i matrice vector.

21.2 Conversie masiv unidimensional list simpl sau


list dubl
Conversia unei structuri de tip masiv unidimensional n list simpl
sau list dublu nlnuit este operaia prin care se formeaz unul din cele
dou tipuri de liste cu elementele vectorului. Operaia const n citirea
vectorului element dup element i construirea dinamic a listei n mod
dinamic insernd la sfrit noile elemente.
Avantajul operaiei const n prelucrarea ulterioar a datelor care se
afl n list, prelucrare care se face mai rapid i utiliznd mai puine
resurse. n unele cazuri, elementele listelor nu trebuie s conin numai
valorile vectorului, acest lucru fiind un avantaj, deoarece se adaug
informaii care s uureze prelucrarea ulterioar, de exemplu un index.
Vector
1

Conve rsie

NULL

List simplu nlnuit

Figura 21.1 Conversia unui vector n list simplu nlnuit


Funcia care transform un vector ntr-o list simpl primete ca
parametrii vectorul respectiv i dimensiunea sa. Se apeleaz funcia
ajuttoare lista *inssf(lista *cap, int info), pentru a crea dinamic lista
simpl.
void vector_to_lista(int v[10], int n)
{
for (int i=0; i<n; i++) l=inssf(l,v[i]);
}
lista *inssf(lista *cap, int info)
{
lista *temp;
lista *nou=(lista*) malloc(sizeof(lista)); /*se aloc dinamic
spaiu pentru element*/
nou->info=info;
nou->next=NULL;
if (cap==NULL) return nou; /*dac lista era vid, noul element
devine captul ei*/

temp=cap;
while (temp->next) temp=temp->next;
temp->next=nou;
return cap;
}

n cellalt caz, transformarea vectorului ntr-o list dublu nlnuit,


se folosete funcia lista* vector_to_lista_dubla(int v[10], int n) care
returneaz captul listei i primete ca parametrii vectorul de transformat i
dimensiunea sa. Lista este construit dinamic direct n corpul acestei funcii.
lista* vector_to_lista_dubla(int v[10], int n)
{
listad *temp;
for (int i=0; i<n; i++)
{
listad *nou=(lista*) malloc(sizeof(lista)); /*se aloc dinamic
spaiu pentru element*/
nou->info=v[i];
nou->next=NULL;
if(cap==NULL) {nou->prev=NULL; cap= nou; temp = cap;}
nou->prev=temp;
temp->next=nou;
temp=nou;
}
return cap;
}
Vector
1

Conve rsie

NULL
1

NULL

List dublu nlnuit

Figura 21.2 Conversia unui vector n list dublu nlnuit


Conversia n cellalt sens const doar n parcurgerea listei i
iniializarea elementelor vectorului. Problema care se pune n acest caz este
alegerea modului de lucru. Ori se declar vectorul static cu un numr mai
mare de elemente dect al listei, din motive de risip de spaiu i chiar de
incapacitate de realizarea a conversiei, dac lista are mai multe elemente,
sau se numr elementele listei i se aloc dinamic spaiu pentru vector.

21.3
Conversie
unidimensional

masiv

bidimensional

masiv

O mare aplicabilitate are i vectorizarea unei matrice sau conversia


acesteia la vector. Aceasta s-a concretizat prin funcia void
matrice_to_vector(int a[30][30], int n, int m, int x[], int *k).
Aceast funciei preia o matrice i o transform ntr-un vector de
n*m+2 componente. Traversarea matricei se face pe linii, deci n vector vor
fi regsite elementele matricei ca i cnd am cap la cap fiecare linie.
Dimensiunea vectorului este cu dou componente mai mare dect numrul
de elemente ale matricei deoarece pe ultimele dou poziii se pstreaz
numrul de linii, respectiv numrul de coloane ale matricei.
Este important pstrarea dimensiunilor matricei pentru o eventual
operaie invers, de la vector la matrice, sau pentru operaiile de adunare,
scdere, transpunere i nmulire cu matrice vectorizate.
Funcia primete urmtorii parametrii:
int a[30][30] parametru de intrare i reprezint matricea care va fi
vectorizat;
int n, m
parametrii de intrare; reprezint dimensiunile matricei
a;
int x[]
parametru de ieire i reprezint vectorul n care va fi
transformat matricea a;
int *k
parametru de ieire i reprezint dimensiunea vectorului
x; transferul se face prin valoare.
Funcia are urmtoarea structur:
void matrice_to_vector (int a[30][30], int n, int m, int x[], int *k)
{
int i,j;
*k=0;
for(i=0; i<n; i++)
for(j=0; j<m; j++)
{
x[*k]=a[i][j];
(*k)++;
}
x[*k]=n;
(*k)++;
x[*k]=m;
*k=(n*m)+2;
}

Pentru a nelege mai bine algoritmul, se consider urmtorul masiv


de 3 linii i 3 coloane:

1 2 3
M 4 5 6
7 8 9

(21.1)

n urma apelrii funciei matrice_to_vector se obine urmtorul


vector:

Vector rezultat
1

Figura 21.3 Vectorul rezultat prin conversie


Dup cum se observ, apar dou componente diferite de elementele
matricei. Ele reprezint dimensiunile matricei i vor ntotdeauna memorate
pe ultimele dou poziii din vector, de unde i dimensiunea 3*3+2
componente. Dac operaia nu se vrea a fi reversibil, atunci se renun la
ultimele dou elemente ale vectorului.

21.4 Conversie list simpl fiier


Conversia unei liste simplu nlnuit ntr-un fiier este echivalent cu
scrierea elementelor listei ntr-un fiier, el reprezentnd la rndul su o
structur de date. Aceast operaie este diferit de memorarea unei liste
ntr-un fiier, pentru c n acest caz se scriu n fiier i informaiile de
legtur dintre elementele listei.
Funcia care transform o list ntr-un fiier este void
lista_to_fisier(lista *cap) i are ca parametru de intrare adresa de nceput a
listei. Aceasta se parcurge i pentru fiecare element se scrie n fiier
informaia sa.
void lista_to_fisier(lista *cap)
{
FILE *f;
if ((f=fopen("elem.dat","wb"))==NULL)
{
printf("\n Fisierul nu se poate deschide ");
exit(1);
}
lista *temp=cap;
while(temp) fwrite (&temp->info, sizeof(temp->info) ,1, f);
temp=temp->next;
fclose(f);
}
List simplu nlnuit

NULL

Conversie
Date aflate n fiier

1234567

Figura 21.4 Conversia unei liste simplu nlnuite n fiier


Lund lista cu elementele 1, 2, 3, 4, 5, 6, 7 i convertind-o n fiierul
elem.dat atunci acesta conine doar numerele ntregi 1, 2, 3, 4, 5, 6, 7 i
are dimensiunea de 7 baii.

21.5 Conversie fiier list simpl


Conversia unui fiier ntr-o list simplu nlnuit este echivalent cu
crearea unei liste de elemente care s conin datele din fiier. Aceast
operaie este inversa conversiei list fiier.
Lista este creat dinamic, pe msur ce sunt citite elementele
fiierului. Funcia ntoarce adresa de nceput a listei.
lista * fisier_to_lista()
{
FILE *f;
int i, temp;
lista *cap=NULL;
if ((f=fopen("elem.dat","rb"))==NULL)
{
printf("\n Fisierul nu se poate deschide ");
exit(1);
}
for (i=0; i<n; i++) fread(&temp,sizeof(temp),1,f);
cap=inssf(cap,temp);
fclose(f);
}

Lucrul cu structuri dinamice presupune opera ia de creare a


structurii de date cu alocare dinamic i temporar de memorie, efectuarea
de prelucrri n condi ii de performan crescut, folosind elementele
structurii dinamice i, n final, ncheierea lucrului cu structura dinamic,
nso it de dealocarea de memorie, cu pierderea informa iei utile
rezultate din prelucrare.
Pentru a asigura conditiile continurii ciclului de prelucrri este
necesar s se salveze informa ia util din structura de date alocat dinamic
ntr-un fisier.
Dac aplica ia utilizeaz liste simple sunt necesare apeluri pentru:
- copierea informa iei utile dintr-un fiier ntr-o list simpl, n
vederea
prelucrrilor
eficiente
folosind
procedurile
corespunztoare opera iilor pe liste simple;
- salvarea informa iei utile din lista simpl alocat dinamic n fiier
la sfritul prelucrrilor corespunztoare aplica iei, asigurnd n
acest fel condi iile de reluare a prelucrrilor n alte momente de
timp.

21.6 Conversie arbore binar fiier


Conversia unui arbore binar ntr-o alt structur de date de tip fiier,
const n crearea unui fiier care s conin informaia nodului arborelui. Ca
i n cazul conversiei list fiier, conversia nu nsemn copierea sau
scrierea arborelui ntr-un fiier, fapt care implic scrierea nodului
(informaia i legturile ctre celelalte noduri).

Arbore binar

Date aflate n fiier

1243567
Conversie

6
7

Figura 21.5 Conversia unui arbore binar n fiier


Rezultatul conversiei depinde n primul rnd de modul n care este
traversat arborele. Cum exist trei moduri de parcurgere: preordine,
inordine i postordine i datele din fiier vor reflecta acest lucru.
Funcia care realizeaz acest tip de conversie este void
arbore_binar_to_fisier(arbbin *rad). Ea primete ca parametru de intrare
adresa nodului printe a arborelui.
void arbore_binar_to_fisier(arbbin *rad)
{
FILE *f;
if ((f=fopen("elem.dat","wb"))= =NULL) //se creeaz fiierul
{
printf("\n Fisierul nu se poate deschide ");
exit(1);
//dac nu se deschide fiierul
}
if(rad)
{
//arborele se parcurge utiliznd unul din cele 3 moduri
fwrite(&rad->info,1,sizeof(rad->info),fis);
arbore_binar_to_fisier(rad->ss);
arbore_binar_to_fisier(rad->sd);
//n acest caz, se parcurge n preordine
}
}

n exemplul din figura 21.5, arborele este traversat n preordine.


Problemele complexe presupun procese de cutare i regsire a
informa iei dup o cheie n care structura dinamic arbore binar se
dovedete a fi deosebit de eficient.
Probleme economice reale, care reflect colectivit i formate din
sute i mii de persoane, conduc la construirea unor arbori binari cu sute sau
mii de noduri, organiza i pe zeci de niveluri.
Informa ia util din arborii binari, care este supus prelucrrii
provine dintr-un fiier, iar la sfritul prelucrrilor, este necesar salvarea
acestei informa ii, de asemenea, ntr-un fisier.

21.7 Conversie graf matrice


Conversia unui graf ntr-o matrice se definete ca fiind operaia de
creare a matricei de adiacen asociat grafului respectiv.

0
5

0
2

Figura 21.6 Graf orientat cu greutate i fr greutate


Acest tip de conversie este utilizat n cazul lucrului cu grafuri
reprezentate prin intermediul listelor de liste i nu au asociate matrice de
adiacen. Un motiv al necesitii conversiei graf matrice este dat de
urmtorul fapt: n cazul grafului cu un numr de mic de noduri, matricea de
adiacen ocup un spaiu relativ mic i este mai uor de lucrat cu aceasta.
Funcia primete ca date de intrare pointer la captul grafului i
construiete matricea de adiacen asociat grafului. Codul surs se
bazeaz pe logica faptului c nodurile grafului sunt numerotate ncepnd cu
0. Dac de exemplu graful este compus din trei noduri notate cu 99, 100,
106, n final s-ar obine o matrice de adiacen cu 107 linii i 107 coloane.
void graf_to_matrice(nodgraf *cap)
{
nodgraf* p,*q;
int max,i,j;
int **matr;
if(cap==NULL) printf("\n Graful nu exista !");
else
{
max=cap->info;
for(p=cap; p!=NULL; p=p->next) //se caut nodul notat
{
//cu cel mai mare numr de ordine
if (max<p->info) max=p->info;
}
matr=alocmatr(max+1,max+1); /*se aloc dinamic memorie ptr.
Matrice*/
for(i=0; i<=max; i++)
for(j=0; j<=max; j++)
matr[i][j]=0; //se iniializeaz elementele sale cu 0

}
}

//Matricea de adiacenta asociata grafului este:


for(p=cap; p!=NULL; p=p->next)
for(q=cap; q!=NULL; q=q->next)
if(q->info!=p->info)
{
//dac exist arc ntre 2 noduri se gsete
//valoarea arcului
matr[p->info][q->info]=verif_arc(p,q);
}

Funcia int verif_arc(nodgraf *s, nodgraf *d) este apelat n interiorul


funciei precedente pentru a verifica dac exist arc ntre dou noduri
indicate prin pointer i pentru a ntoarce greutatea lui n caz afirmativ. n
caz contrar funcia returneaz valoarea 0. Astfel dac ntre nodurile i i j

exist arcul cu greutatea g, atunci elementul matricei matr[i][j] ia valoarea


g.
int verif_arc(nodgraf *s,nodgraf *d)
{
arc * p,*aux;
int gasit=0;
for(p=s->capat; p!=NULL; p=p->next_arc)
if(p->destinatie==d) {
gasit=1;
aux=p;
return aux->weight;
//ntoarce greutatea arcului
}
if (gasit==0) {
return 0;
}
else return aux->weight;
}

Funcia int ** alocmatr(int n,int m), dup cum s-a observat, este
folosit pentru a aloca dinamic spaiu matricei de adiacen, ea ntorcnd un
pointer la matrice. Este un mod economic de a lucra cu matrice mai ales n
cazul n care nu se tie de la nceput dimensiunile ei.
int ** alocmatr(int n,int m)
{
int **x;
int i,j;
x=(int **)malloc(n * sizeof(int));
for(i=0;i<n;i++) x[i]=(int *)malloc(m * sizeof(int));
return x;
}

Matricele de adiacen corespunztoare grafurilor sunt :

0 7 5 0
0 0 8 1

M1
0 0 0 3

0 0 0 0

0 1 1 0
0 0 1 1

(21.2) i M 2
0 0 0 1

0 0 0 0

(21.3)

Funcia prezentat este folosit n cazul grafului cu greutate, dar


fcnd mici modificri ea este aplicat i grafului fr greutate. Aceste
modificri constau n schimbarea funciei verif_arc astfel nct s ntoarc
valoarea 1 dac exist arc ntre cele dou noduri cercetate.

21.8 Omogenitatea prelucrrilor


Alegerea tipului de dat este determinat de domeniul cruia aparin
nivelele consemnate pentru fiecare caracteristic la descrierea elementelor
unei mulimi.

Dac de exemplu, pentru specificarea mediului din care provin


persoanele unui eantion se nregistreaz rspunsurile mediu rural sau
mediu urban, domeniul asociat celor dou nivele este format din elementele
0 i 1. D = {0, 1}, unde:
0 pentru mediul rural;
1 pentru mediul urban.
Caracteristica aceasta se reprezint pe un bit.
Pentru specificarea vrstei persoanelor, domeniul este cuprins ntre 0
i 130, DV=[0,130]. Reprezentarea vrstelor se efectueaz pe zone de
memorie de un bait, domeniul D = [0, 255], cu DV D. De asemenea,
numrul de copii aflai n ngrijirea unei persoane se reprezint pe 5 bii,
domeniul caracteristicii numr_copii fiind DC=[0,31]. Aceleai aprecieri se
efectueaz i n legtur cu alte caracteristici ale altor colectiviti.
Se obine o diversitate de domenii, care n final determin
constituirea de structuri de tip articol, cu cmpuri neomogene.
Necesitatea omogenitii apare n procesul prelucrrii.
Pentru calculul totalului unei facturi se utilizeaz membri definii n
structura:
struct factura {
int numar;
struct data_emiterii dat;
struct rand y[10];
float total_tva;
double total;
} f;
struct data_emiterii {
int ziua;
int luna;
int anul;
};
struct rand {
int nr_crt;
char denum[30];
char um[5];
int cantitate;
float pret_unitar;
double pret;
int tva;
float tva_calculat;
};

float pret_vanzare;

Se construiesc expresiile:
f.y[i].pret = f.y[i].cantitate * f.y[i].pret_unitar;
f.y[i].tva_calculat = f.y[i].pret * f.y[i].tva / 100;
f.y[i].pret_vanzare = f.y[i].pret + f.y[i].tva_calculat;

Cmpurile care intervin sunt neomogene. Astfel, cantitate are tipul


ntreg, reprezentat pe 2 baii. Cmpul pret_unitar este de tip float,
reprezentat pe 4 baii. Rezultatul nmulirii, pret, este de tip double.
Pentru evaluarea acestei expresii se efectueaz conversiile din figura
21.7.
pre unitar

cantitate

conversie ntreg
spre float

*
produsul cu
reprezentare
float

conversie float
spre double

pre

Figura 21.7 Omogenizarea expresiei de calcul pret.


Pentru obinerea tva_calculat se face conversia din figura 21.8.

pret

tva

conversie ntreg
spre double

float

*
100

conversie float
spre double

rezultat intermediar

conversie
double spre float
tva_calculat

Figura 21.8 Conversii necesare obinerii tva_calculat


21.9.

Pentru calculul pret_vanzare se efectueaz conversiile din

figura

tva

pret

conversie float
spre double

rezultat intermediar

pret_vanzare

conversie
double spre float

Figura 21.9 Conversii necesare calculului pret_vanzare


Dac toate datele se definesc cu acelai tip, avnd un nivel de
omogenitate maxim, de la ncrcarea fiierelor se utilizeaz structura:
struct rand_omogen {
int nr_crt;
char denum[30];
char um[5];
double cantitate;
double pret_unitar;
double pret;
double tva;
double tva_calculat;
double pret_vanzare;
};

Fiierul FACTURI, avnd iniial articole de lungimea sizeof(struct


facturi), o dat cu definirea masivului y[ ] prin struct rand_omogen y[10];
va avea articole de lungimea sizeof(struct factura_omogena), unde:
struct factura_omogena {
int numar;

struct
struct
double
double

data_emiterii dat;
rand_omogen y[10];
total_tva;
total;

} g;

devenind fiierul FACTURI_OMO.


Dac se consider un numr n de facturi, prelucrrile din fiierul
FACTURI_OMO nu necesit conversii datorit omogenitii datelor, n timp
ce prin utilizarea fiierului FACTURI sunt necesare conversiile:
1. ntreg (cantitate) spre float;
2. float (rezultat) spre double (pret);
3. ntreg (tva) spre double;
4. constant float (100) spre double;
5. double (rezultat) spre float (tva_calculat);
6. float (tva_calculat) spre double;
7. double (rezultat) spre float (pret_vanzare);
pentru fiecare articol prelucrat, deci un total de 7*n conversii.
Apare problema lurii unei decizii n legtur, pe de o parte pentru a
economisi spaiul de memorie cu:

sizeof ( factura )
*100
G 1
sizeof ( factura _ omogena)

(21.4)

dar n schimb cu un volum de 7*n conversii, iar pe de alt parte, fr


economie
de
spaiu
pe
suport,
fiierul
FACTURI_OMO
avnd
n*sizeof(factura_omogena) baii i prelucrrile necesitnd zero conversii.

21.9 Resursele conversiilor


Se consider dou alfabete A i B, A= {a1, a2, ,an }, B= {b1, b2,
,bm }, unde ai reprezint simbolul i al alfabetului A, iar bj simbolul j al
alfabetului B. Alfabetul A are n simboluri i alfabetul B are m simboluri.
Cu alfabetul A se construiesc cuvinte, alctuindu-se vocabularul VA,
cu simbolurile alfabetului B se alctuiesc cuvinte care formeaz vocabularul
VB. Fiecare cuvnt are o lungime dat de numrul simbolurilor din care
este alctuit.
Se numete algoritm de conversie succesiunea de prelucrri care
pornind de la un cuvnt xVA conduce la obinerea cuvntului yVB i
succesiunea de prelucrri care pornind de la cuvntul yVB conduce la
obinerea cuvntului xVA.
Cnd cuvintele x i y se reprezint sub forma unor configuraii de bii,
lungimile cuvintelor ca numr de bii sunt lg(x) i, respectiv, lg(y).
n acest fel se realizeaz legarea cuvintelor de zone de memorie, iar
algoritmii de conversie de restriciile acestei resurse, respectiv zona de
memorie.
Definiiile care antreneaz ca resurse alfabete, zone de memorie i
seturi de prelucrri dau o maxim generalitate conversiilor.

Procesul de compresie de date este un caz particular de conversie, n


care singurul obiectiv urmrit este ca pornind de la un alfabet A s se
construiasc un alfabet W bazat pe particulariti de construire a cuvintelor
din vocabularul VA astfel nct, pentru orice cuvnt xVA s se obin un
cuvnt yVW aa fel nct lg(x) >> lg(y).
Algoritmul de compresie este operaional dac este posibil
construirea i a algoritmului de decompresie care permite ca pornind de la
cuvntul yVW s se obin cuvntul xVA.
n cazul n care cuvntul x este chiar fiierul nsui, y devine fiierul
compresat.
Manipularea zonelor de memorie impune gestionarea parametrului de
definire a acesteia, lungimea, ca numr de baii.
Conversie de lungime
Fie un cuvnt xVA i lg(x) = k bii.
Dac se definete o zon de memorie Z avnd m bii, m > k, se pune
problema memorrii cuvntului x n Z.
Instruciunile de atribuire sau de transfer din limbajele evoluate
realizeaz conversiile n diferite moduri.
Dac n limbajul COBOL era definit:
77 x1 PIC 9(5) VALUE 12345.
77 x2 PIC 9(7).

instruciunea:
MOVE

x1 TO x2

realizeaz conversia de lungime cu generare de zerouri n partea stng a


configuraiei iniiale de bii, figura 21.10:
x1
1

x2
0

generat
Figura 21.10 Conversie pe lungime date numerice zecimal despachetat
n cazul n care tipul de dat este alfanumeric la conversia de lungime
se genereaz configuraia de bii asociat caracterului spaiu 32h.
Pentru definirea n limbajul COBOL:
77 y1 PIC X(4) VALUE 'ABCD'.
77 y2 PIC X(7).

instruciunea:

MOVE y1 TO y2

conduce la obinerea configuraiei de bii din figura 21.11:


y1
A

y2
A

Figura 21.11 Conversie pe lungime ir de alfanumerice


Conversia numeric
Cele mai frecvente conversii sunt cele numerice.
Se consider urmtoarele tipuri de date:
ntreg
virgul mobil simpl precizie
virgul mobil dubl precizie
zecimal mpachetat
zecimal despachetat
caractere numerice
hexazecimal
Pentru toate numerele exist o reprezentare sub form de
configuraie de bii. Reprezentarea unui numr n zone de memorie este
dat n tabelul 21.1.
Tabelul nr. 21.1 Reprezentarea valorilor numerice
Valori
Tip
ntreg
virgul
mobil
simpl
precizie
virgul
mobil dubl
precizie
zecimal
mpachetat
zecimal
despachetat
caractere
numerice

25
0019

-25
FFE7

1
0001

-1
FFFF

0
0000
00000000

000000000
0000000
0205

S0205

01

S01

00

0025

S0025

01

S01

00

3235

2D3235

31

2D31

30

De la tastatur de introduc n zonele de memorie iruri de caractere.


Funciile de I/E realizeaz conversii. n acest scop se folosesc descriptori.
Astfel, descriptorul %d realizeaz conversia de la ir numeric-caracter spre
ntreg binar.

Descriptorul %f activeaz realizarea conversiei de la ir de caractere


numerice spre tipul float.
Pentru realizarea conversiei unui ir de simboluri cifrice ntr-un ntreg
binar este utilizat funcia:
int atoi( char sir[] )
{
int i,numar,semn;
for(i=0;sir[i]<'0' || sir[i] >'9' ||
sir[i]!='-' || sir[i]!='+';i++);
if(sir[i]=='-') { semn=-1; i++;}
if(sir[i]=='+') { semn=1; i++;}
for(numar=0; sir[i]>='0' && sir[i]<='9'; i++)
numar=numar*10+(sir[i]-'0');
numar*=semn;
return numar;
}

Pentru conversia de la ntreg binar spre sir de simboluri numerice


este folosit funcia:
itoa(int numar,char sir[])
{
int i, j, semn, lungime, carc;
if(n<0) {
semn=-1;
numar=-numar;
}
i=0;
do
{
sir[i++]=numar%d+'0';
}
while((n/10)>0 );
if(semn <0) sir[i++]='-';
else sir[i++]='+';
sir[i]='\0';
lungime=strlen(sir)-1;
for(i=0, j=lungime; i<j; i++, j--)
{
carc=sir[i];
sir[i]=sir[j];
sir[j]=carc;
}
}

Exist o diversitate de funcii pentru conversie.


Ipotezele de lucru pentru realizarea conversiei i ceea ce returneaz
reprezint un aspect esenial. Astfel, pentru conversia unui ir de simboluri
numerice se consider irul declarat prin:
char sir[];

a crui lungime trebuie impus s nu depeasc k poziii.


Dac este vorba de o conversie de la ir de simboluri numerice spre
ntregul binar numar, definit pe cuvnt i -32768 cont(numar) 32767,

irul de tip char trebuie s conin secvene de simboluri numerice care s


nu depeasc lungimea 5.
sir[ ]

'9'

'2'

'4'

'a'

'b'

'c'

'4'

'2'

'9'

'x'

'8'

'1'

'\0

Figura 21.12 Secvene de simboluri numerice


Traversarea de la stnga la dreapta a masivului unidimensional
permite extragerea simbolurilor 9, 2, 4 i constituirea numrului ntreg 924
n reprezentarea binar din zona de memorie asociat variabilei x.
Delimitatorul de sfrit al secvenei este un caracter diferit de simbol
cifric.
De asemenea, poate fi interpretat delimitator de sfrit de secven
nsui delimitatorul de sfrit de ir \0.
Funcia iniializeaz cu zero variabila y n cazul n care irul are
structura dat n figura 21.13.

sir[ ]
'x'

'y'

'z'

'a'

'b'

'd'

'+'

'\0

y
00

00

Figura 21.13 Iniializarea cu zero n cazul secvenei vide


de simboluri cifrice
Se introduce o ambiguitate, n ceea ce privete imposibilitatea de a
face o conversie i cazul real n care secvena conine o succesiune de
simboluri de 0.
Lipsa testelor privind simbolurile cifrice i lungimea secvenei face
imposibil introducerea n limitele specifice tipului int.
Este util construirea funciei de evaluare a lungimii secvenei
consecutive de simboluri numerice nstrlen( ).
int nstrlen (char sir[])
{
int i,k;
i=k=0;
while(sir[i+1]!='\0')
{
if(sir[i]>='0' && sir[i]<='9'
(sir[i+1]>='0' && sir[i+1]<='9'))
k++;
i++;
}
return ((k) ? k++ : k);
}

&&

Funcia returneaz lungimea secvenei de simboluri cifrice i se


utilizeaz pentru testarea din interiorul funciei atoi( ):

if(nstrlen(sir)>4)
if(nstrlen(sir)==4
if(nstrlen(sir)==4
if(nstrlen(sir)==4
if(nstrlen(sir)==4
if(nstrlen(sir)==4

return 0;
&& sir[i]-'0'>3)
&& sir[i]-'0'>2)
&& sir[i]-'0'>7)
&& sir[i]-'0'>6)
&& sir[i]-'0'>7)

return
return
return
return
return

0;
0;
0;
0;
0;

Conversia ncepe numai dup ce s-a efectuat ncadrarea ntre limitele


domeniului asociat tipului int, respectov [-32768, 32767].
n acest caz, funcia nu returneaz i o informaie privind modul n
care s-a efectuat conversia.
Este de dorit s se construiasc funcii de conversie care s ofere mai
multe informaii, mai ales c funciile nu sunt apelate oricum.
Astfel, este posibil identificarea urmtoarelor situaii:
secven vid de simboluri cifrice;
secven de simboluri cifrice mai mare ca 4;
secvena de 4 simboluri cifrice care prin conversie conduce la o
valoare din afara intervalului [-32768, 32767 ].
Procedura care efectueaz conversia de la ir de simboluri cifrice,
sir[] spre tipul zecimal despachetat n irul upk[], este:
int atoupk(char sir[],char upk[])
{
int i;
i=0;
while(sir[i]!='\0')
{
upk[i] = sir[i] - '0';
i++;
}
upk[i]='*';
return i;
}

Funcia returneaz lungimea irului convertit.


Funcia care efectueaz conversia numrului zecimal despachetat
spre ir de simboluri cifrice este:
int upktoa(char upk[],char sir[])
{
int i;
i=0;
while(upk[i]!='*')
{
sir[i]=upk[i]+'0';
i++;

}
sir[i]='\0';
return i;
}

S-a ales delimitatorul de sfrit al numrului zecimal despachetat


caracterul '*' pentru a nu se denatura conversia din cauza coincidenei
caracterului '\0' cu zero binar.
Funcia care efectueaz conversia unui ir de simboluri cifrice ntr-un
numr zecimal mpachetat este:
int atopk(char sir[],char pk[])
{
int i,k;
k=i=0;
while(sir[i]!='\0')
{
pk[k]=sir[i]-'0';
if(sir[i+1]!='\0')
{
pk[k]<<4;
pk[k]|=sir[i+1]-'0';
k++;
i++;
}
}
return k;
}

Masivul unidimensional sir[] trebuie s conin n mod obligatoriu


simboluri cifrice pentru ca rezultatul conversiei s fie corect. Pentru irul 'A2
B4F8\0' se va obine prin conversie irul:
pk[0]=0xA2
pk[0]=0x20 prin pk[0]<<4
pk[0]=0xA4 prin 0xB4-0x30=0x84
0x84+0x20=0xA4.
O ameliorare const n operarea cu masca 0x0F, funcia devenind:
int atopk(char sir[],char pk[])
{
int i,k;
k=i=0;
while(sir[i]!='\0')
{
pk[k]=sir[i]&0x0F;
if(sir[i+1]!='\0')
{
pk[k]<<4;
pk[k]|=sir[i+1]&0x0F;

k++;
i++;
}
}
return k;
}

Puterea unor limbaje de programare este dat n primul rnd de


diversitatea de tipuri de date i de diversitatea conversiilor acceptate.
Cnd se prezint limbajele este necesar definirea tabelelor de
conversie, de forma tabelului 21.2.
Tabelul nr. 21.2 Conversii posibile
TIP
Tip1
Tip2

Tipi

Tipn

Tip1

Tip2

Tipj

Tipk

Tipn

Simbolul D arat c este permis conversia de la tipul Tipi la tipul de


dat Tipj. Simbolul N arat c nu este permis conversia de la tipul de dat
Tipi la tipul de dat Tipk.
Conversia matricelor
Matricele n care un numr important de elemente, peste 70%, sunt
nule, necesit un alt fel de stocare.
O posibilitate de conversie a matricelor revine la a considera o
matrice A rar ncrcat n memorie i de a trece de la aceasta la o list
simpl ale crei elemente conin numai informaii care prezint valori
nenule.
Dac se consider o matrice de mari dimensiuni cu m linii i n
coloane, mai nti aceasta este stocat n fiierul F, dup care programul de
conversie realizeaz citirea de date din fiier i transpunerea acestora ntruna din forme de prezentare n memorie a matricei de mari dimensiuni,
figura 21.14.

Figura 21.14 Conversia fiier-matrice


Pentru conversia matricei sub forma de fiier, figura 21.15, trebuie
specificat modul n care sunt preluate elementele matricei, fie linie de linie,
fie coloan de coloan.

Figura 21.15 Conversia matrice-fiier


Problemele de optimizare, probleme de calcule inginereti complexe,
presupun lucrul cu matrice de dimensiuni mari, matrice pentru care trebuie
asigurat completitudinea i corectitudinea datelor.
Exist foarte mari riscuri legate de procesul de asigurare a calit ii
con inutului unei matrice, fapt pentru care ncrcarea elementelor matricei
i verificarea acestora trebuie s respecte reguli stricte de management a
con inutului.
Matricea ncrcat n memoria intern este supus prelucrrilor,
ob inndu-se o nou matrice sau valori agregate.
Este necesar ca matricele complete i corecte s fie salvate n fiier.
Fiierele care con in matrice sunt folosite pentru a ncrca matricea
n memorie n vederea utilizrii acestei proceduri de prelucrare.
Conversia listelor simple i duble
n cazul n care se dorete conversia de la structur de tip list simplu
nlnuit, la cea de tip list dublu nlnuit, se parcurg nodurile listei i se
copiaz informaia util n nodurile listei duble, figura 21.16.

Figura 21.16 Conversia list simplu nlnuit- list dublu nlnuit


Pentru lista simplu nlnuit definit mai jos:
struct lista_simpla
{
float info;
lista_simpla* next;
};

se va implementa o procedur de conversie n list dublu nlnuit, cu


urmtoarea structur:
struct lista_dubla
{
float info;
lista_dubla *prev,*next;
};

Dac se consider structurile de date S1, S2, , SN problema


conversiei revine la a scrie proceduri de reprezentare a informa iei utile din
structura Si ntr-o structur Sj.
Procedura are rolul de a crea i ini ializa variabile pointer care s
ajute la traversarea corect n structura Sj folosind reguli deja definite
pentru structura Si.
Sunt necesare n*n-n proceduri, ntruct conversia de la Si la Si nu se
justifica.
Dac se consider o structur de baz Sb i se dorete conversia de la
structura Si la structura Sj prin intermediul structurii de baz, se vor scrie n1 proceduri de conversie de la structura Si la structura Sb, respectiv, tot n-1
proceduri de conversie de la structura Sb la structura Sj. n total, vor fi
necesare 2(n-1) proceduri de conversie, i nu n*n-n.
Conversia listei simple spre arbore binar se implementeaz folosind o
procedur care copiaz informa ia util a elementului din lista simpl n
partea destinat informa iei utile din nodul arborelui binar. Variabilele
pointer ale arborelui binar se ini ializeaz la construirea acestuia, astfel
nct s se asigure traversarea corect a nodurilor.

Variabila pointer din lista simpl nu este preluat n arborele binar


datorit volatilit ii procesului de alocare dinamic a memoriei, nefiind
utilizabil la o conversie de la arborele binar spre lista simpl.
Pentru conversia listei duble n list simpl, se utilizeaz procedura
care:
traverseaz lista dubl;
creeaz un element al listei simple;
asigur legatura cu elementul precedent al listei simple;
copiaz informa ia util din elementul listei duble n elementul
creat al listei simple;
asigur ini ializarea variabilei pointer de legatur;
reia procesul.
Pentru conversia listei duble n fiier este necesar ca procedura s
realizeze:
traversarea listei duble;
preluarea informa iei utile din lista dubl n articolul fiierului;
scrierea articolului n fiier;
reluarea procesului pn la ncheierea procesului de traversare.
Conversia listei duble n arbore binar const n:
traversarea listei duble;
crearea unui nod din arborele binar prin alocare dinamic de
memorie;
asigurarea legturii cu nodul printe;
copierea informa iei utile din lista dubl n zona de memorie
aferent nodului creat;
reluarea procesului de traversare a listei duble pn la epuizarea
elementelor acesteia.
Conversia de fiiere
n cazul n care exist dou sisteme de operare S1 i S2, avem
algoritmi de conversie pentru tipurile de date diferite, moduri diferite de
gestionare a pozi iei semnelor, a mantisei, a caracteristicii, lungimi diferite
ale cuvintelor i modalit i specifice de construire a etichetelor, a
delimitatorilor de articole.
Fiecare suport tehnic de stocare a fiierelor are caracteristicile sale.
Conversia de suport revine la a realiza un fiier F2 pe suportul X
pornind de la fiierul F1 aflat pe suportul Y. Astfel de probleme apar la
trecerile spre noi generaii de purttori tehnici de informa ie.
Recensmintele anilor 70 au fost realizate cu genera ia a III-a de
calculatoare, iar fiierele care con in fondul de date se afl pe benzi
magnetice. Filosofia noilor calculatoare impune o alt abordare. Pentru a
efectua compara ii, ori se reintroduc datele din documentele primare, dac
mai exist, ori se face conversie fiiere, de suport.
Pornind de la structura fiierelor pe band magnetic, de la
reprezentarea informa iei pentru genera iile de calculatoare din anii 70,
se vor scrie programe care s converteasc aceste fiiere.
Un alt mod de a privi conversia de fiiere const n elaborarea unui
program care are ca intrare un fiier F1 realizat pentru a efectua anumite
prelucrri i a prelua din fiier anumite articole, anumite cmpuri, a le
modifica aa nct s se ob in la ieire un fiier F2. Fiierul F2 este fiier de
intrare pentru un program care efectueaz prelucrri.

Situa ia este des ntlnit. Fiiere realizate n FoxPro trebuie


prelucrate n programe C++.
Pentru conversia de fiiere, sistemul FoxPro dispune de comanda
IMPORT FROM.
Se consider un program care efectueaz calcule de salarii. Fiierul
de intrare con ine:
numrul de intervale pentru diferenierea procentului de
impozitare: int n; 0<n<20;
limitele superioare ale intervalelor: float x[20];
articolele fiierului ce conin date despre salariai, avnd
structura:
struct salariat {
int marca;
char nume[30];
int vrsta;
int timp;
float salariu_orar;
float reineri;
float sporuri;
};

Fiierul iniial conine numai articole cu date ale salariailor construite


dup structura:
struct sal {
int marca;
char nume[20];
int timp_lucrat;
int salariu_orar;
int reineri;
};

Lipsesc cmpurile vrsta i sporuri.


La conversia de fiiere nu se realizeaz completarea cu valori
cmpurilor, ci se aloc numai spaiu, eventual iniializat cu zero. Procedurile
de conversie efectueaz trecerea de la structura articolului sal la structura
articolului salariat, figura 21.17:
marca

nume

timp_lucrat

salariu_orar retineri

sal

salariat

0
marca

nume

varsta

0
timp

salariu_orar

retineri

sporuri

Figura 21.17 Conversia de structur a articolelor unui fiier

Programul C++ care efectueaz conversia acestor fiiere este :


#include <stdio.h>
#include <string.h>
struct salariat {
int marca;
char nume[30];
int varsta;
int timp;
float salariu_orar;
float retineri;
float sporuri;
};
struct sal {
int marca;
char nume[20];
int timp_lucrat;
int salariu_orar;
int retineri;
};
void main()
{
sal S1;
salariat S2;
FILE *pf1,*pf2;
pf1=fopen("sal.dat","rb");
pf2=fopen("salariat.dat","wb");
S2.varsta=0;
for(int i=20; i<30; i++)
S2.nume[i]=' ';
while(fread(&S1,sizeof(struct sal),1,pf1))
{
S2.marca=S1.marca;
strncpy(S2.nume,S1.nume,20);
S2.timp=S1.timp_lucrat;
S2.salariu_orar=S1.salariu_orar;
S2.retineri=S1.retineri;
fwrite(&S2,sizeof(struct salariat),1,pf2);
}
fclose(pf1);
fclose(pf2);
}

Pentru a reduce volumul conversiilor n timpul execuiei programelor


se efectueaz conversia de cmpuri n fiiere.
Se consider seriile de timp T1, T2, T3, T4 avnd n termeni. Un articol
din fiierul F1 care le descrie are forma:
struct articol {
int anul;
int t1;
float t2;
int t3;
float t4;
};

Pentru calculul valorilor medii ale celor patru serii se impun conversii.
Cel mai simplu este s se efectueze conversiile cmpurilor din articole,
ajungndu-se la structura:
struct art {
int anul;
float t[4];
};

n loc de efectua prelucrrile cu dou funcii din programul:


#include <stdio.h>
#define N 10
struct articol {
int anul;
int t1;
float t2;
int t3;
float t4;
};
float media(int x[], int n)
{
int s=0;
for(int i=0; i<n; i++)
s+=x[i];
return (float)s/n;
}
float media(float x[], int n)
{
float s=0.0;
for(int i=0; i<n; i++)
s+=x[i];
return s/n;
}
void main()
{
float medii[4];
int T1[N],T3[N];
float T2[N],T4[N];
articol A;
FILE *pf;
pf=fopen("serii.dat","rb");
for(int i=0;i<N;i++)
{
fread(&A,sizeof(struct articol),1,pf);
T1[i]=A.t1;
T2[i]=A.t2;
T3[i]=A.t3;
T4[i]=A.t4;
}
fclose(pf);
medii[0]=media(T1,N);
medii[1]=media(T2,N);
medii[2]=media(T3,N);

medii[3]=media(T4,N);
for(i=0;i<4;i++)
printf("\n media seriei %d este %f",i+1,medii[i]);
}

se va efectua aceeai prelucrare cu programul:


#include <stdio.h>
#define N 10
struct art {
int anul;
float t[4];
};
float media(float x[], int n)
{
float s=0.0;
for(int i=0; i<n; i++)
s+=x[i];
return s/n;
}
void main()
{
float medii[4];
float T[4][N];
art A1;
FILE *pf;
pf=fopen("serii1.dat","rb");
for(int i=0; i<N; i++)
{
fread(&A1,sizeof(struct art),1,pf);
T[0][i]=A1.t[i];
T[1][i]=A1.t[i];
T[2][i]=A1.t[i];
T[3][i]=A1.t[i];
}
fclose(pf);
for(i=0; i<4; i++)
{
medii[i]=media(T[i],N);
printf("\n media seriei %d este %f",i+1,medii[i]);
}
}

n limbajul C++ delimitatorul de sfrit de ir de caractere este \0.


n limbajul Pascal un ir este structurat printr-un bait ce conine numrul de
poziii urmat de irul propriu-zis de lungimea indicat.
Conversia fiierului F1 realizat n C++ la fiierul F2 realizat n Pascal
presupune:
alocare un bait;
copierea irului din F1 n F2 cu contorizarea n variabila x a
caracterelor;
la apariia \0 se pune n baitul alocat valoarea contorului x.
Conversia fiierului F2 Pascal n fiierul F1 C++ presupune :
preluarea n variabila x a lungimii irului;

copierea unui numr de caractere ct arat contorul x din fiierul


F2 n fiierul F1;
inserarea n F1 n continuare a simbolului \0 pentru a marca
sfritul fiierului.
Programul este:
#include <stdio.h>
void main()
{
char c,x;
int i=0;
FILE *pf1,*pf2;
pf1=fopen(F2.txt,r);
pf2=fopen(F1.txt,w);
x=fgetc(pf1);
while(i<x)
{
c=getc(pf1);
putc(c,pf2);
i++;
}
putc(\0,pf2);
fclose(pf1);
fclose(pf2);
}

22. UTILIZAREA STRUCTURILOR DE DATE N


CLONAREA INFORMATIC
22.1 Conceptul de ortogonalitate a elementelor
Indiferent de forma de existen a produselor i serviciilor n
domeniul informaticii, evidenierea i gestionarea procesului de clonare
reprezint o activitate important ntruct limitarea clonelor creeaz
premise obinerii de software performant i de atragere de resurse
financiare pentru dezvoltarea produciei i calificarea personalului. n acest
scop, se impune dezvoltarea de instrumente software care s analizeze
arhitecturi hardware, produse software i baze de date existente ntr-o arie
de utilizare precis delimitat i s identifice clonele existente. n cazul n
care clonele i procesul de clonare depesc limitele definite printr-un cadru
legal existent la un moment dat se procedeaz la efectuarea de corecii pe
toate planurile.
Astfel de produse software sunt realizate deja pentru stabilirea
prilor comune din texte redactate cu procesoarele de tip Word i pentru
identificarea clonelor din programe C++ precum i depistarea elementelor
de proporionalitate care genereaz clone n tabele construite prin prelucrri
de tip matriceal. Absena clonelor este specific datelor ortogonale.
Dou drepte sunt ortogonale dac unghiul format la intersecia
acestora are cosinusul egal cu zero, cu alte cuvinte cele dou drepte sunt
perpendiculare. Un ansamblu de drepte este ortogonal dac dreptele ce-l
compun sunt perpendiculare dou cte dou.
Dou plane sunt ortogonale dac unghiul format la intersecia lor are
cosinusul egal cu valoarea zero, adic cele dou plane sunt perpendiculare.
Un ansamblu format din mai multe plane este ortogonal dac planele care l
formeaz sunt ortogonale dou cte dou.
Doi vectori sunt ortogonali dac produsul scalar al acestora este nul.
n continuare o dat este reprezentat uzual ca un numr (125 sau 0
sau 400.72 sau +25.3e-4) sau un ir de caractere (maina sau 125 sau
Cluj-Napoca-2000).
Extinznd, rezult c datele D1 i D2 sunt ortogonale semantic, n
cazul n care coninutul informaional al acestora, sensul lor, difer ntr-o
manier categoric i semiotic, dac acestea au o formalizare matematic
total diferit.
Ortogonalitatea reprezint diferena dintre dou entiti. Dac datele
sunt complet diferite, atunci ele sunt ortogonale. Forma pe care o iau
indicatorii de ortogonalitate este strns legat de tipul i complexitatea
datelor comparate i specificul analizei.
Se consider colectivitatea KOL = {ek1, ek2, , eknek}, avnd nek
elementele omogene. Elementele colectivitii sunt descrise prin intermediul
a nca caracteristici ale colectivitii, Ch1, Ch2, , Chnca.
Pentru un element eki, ce aparine colectivitii KOL, nivelurile
caracteristicilor sunt evideniate de urmtorul tuplu:
eki = (VChi1 , VChi2 , , VChi,nca)

(22.1)

unde Vchik este nivelul caracteristicii Chk pentru elementul eki.

dac:

Un element x este ortogonal pe colectivitatea KOL dac i numai


< x, eki > = 0

(22.2)

oricare ar fi eki, element al colectivitii.


Dou seturi de date sunt ortogonale dac ele nu au nimic n comun.
Elementele eki i ekj sunt ortogonale dac valorile situate pe poziiile
similare sunt distincte. Identitatea a dou seturi de date se definete n mod
contrar ortogonalitii. Elementele eki i ekj sunt identice dac au acelai
volum, iar valorile de pe poziiile similare sunt egale.
Dac se consider I indicatorul asociat identitii i O indicatorul
asociat ortogonalitii elementelor, atunci:
I=1O

(22.3)

Ortogonalitatea seturilor de date joac un rol important n


dezvoltarea modelelor cu un nivel ridicat al calitii. Identificarea unic a
unui element dintr-o colectivitate are loc pe baza conceptului de amprent.
Amprenta datelor reprezint o succesiune de caracteristici obiectiv
msurabile. Caracteristicile individualizeaz datele i determin ca n cazul
n care nivelurile lor sunt diferite toate pentru date diferite sau sunt identice
pentru date identice.
n cazul datelor reprezentate ca entiti text, amprenta se
construiete prin efectuarea sondajului statistic.
Contribuia original la dezvoltarea domeniului Di este rezultatul
activitii de creaie. Ea vizeaz urmtoarele aspecte [Popa05]:
- introducerea i definirea de concepte noi care completeaz sau
mbuntesc cadrul conceptual al domeniului;
- dezvoltarea de tehnici i metode noi de analiz prin introducerea
de noi instrumente, sisteme de evaluare, algoritmi de
sistematizare i prelucrare a datelor;
- punerea la punct, completarea sau introducerea de noi
metodologii de descriere a ciclului de via pentru entitile
dezvoltate n domeniul Di;
- elaborarea de studii de caz cu un puternic impact asupra lucrului
cu entiti n domeniul Di.
Este aproape imposibil s se construiasc entiti cu ortogonalitate de
100%. Plusul de valoare este adus pe baza elementelor existente la care se
adaug, se dezvolt sau se corecteaz noi aspecte care prezint importan
n domeniu.
Noile entiti cuprind o foarte mare parte din elementele deja
construite. n analiza ortogonalitii partea cea mai important o constituie
identificarea i cuantificarea elementelor cu un ridicat nivel de noutate.
Conceptul de clonare informaional este strns legat de cel al
reutilizrii.
Termenul clonare i are originile n cuvntul grecesc klon.
Semnificaia cuvntului este cea de reproducere a sistemelor, produselor
sau informaiei prin constituirea de entiti separate, dar cu acelai coninut.
A clona o entitate ET nseamn a derula procese prin care se genereaz noi
entiti care au acelai coninut i ndeplinesc aceleai funcii.

De exemplu, pentru entitile software, clonarea presupune obinerea


de noi produse, diferite din punct de vedere sintactic, dar identice din cel al
funciilor ndeplinite. n acest caz, procesele de clonare presupun efectuarea
transformrilor [Ivan03]:
- nlocuirea cuvintelor cu sinonime; exist operaii care se
implementeaz prin mai multe modaliti, utiliznd cuvinte
diferite; n acest caz, procesele de clonare se refer la funciile
entitii software;
- eliminarea secvenelor care nu joac un rol critic; secvenele
vizeaz implementarea operaiilor de normalizare, sistematizare,
pregtire apel de subrutine; pentru a asigura caracterul de
generalitate pentru entitatea software dezvoltat, este necesar s
se efectueze validri; pentru o entitate concret nu este
obligatoriu ca validrile s fie implementate;
- nlocuirea secvenelor de cod cu secvene echivalente; const n
schimbarea succesiunii pailor concomitent cu detalierea sau
generalizarea secvenelor de cod asociate; procesele de clonare
privesc natura prelucrrilor care, pentru aceleai intrri, conduc la
obinerea acelorai rezultate;
- optimizarea subexpresiilor comune; subexpresiile comune sunt
incluse ntr-o structur creia i se atribuie un nume generic; n
entitatea software, subexpresiile comune sunt nlocuite cu noua
structur; se modific doar modul n care subexpresiile sunt
referite;
- eliminarea secvenelor redundante; nu afecteaz prelucrrile
specificate n algoritm, ci numai modul de reprezentare a
programului surs ca structur i coninut;
- concatenarea structurilor repetitive; pentru structurile repetitive
avnd acelai numr de iteraii sau aceeai condiie de oprire sunt
reunite expresiile care se execut n cadrul acestora; entitatea
software rezultat este identic sub aspectul prelucrrilor i
funciilor implementate, dar diferit ca structur;
- conversia formatului de stocare a datelor; const n modificarea
structurilor utilizate n stocarea datelor; coninutul structurilor este
identic; n schimb, difer modul de organizarea extern a datelor;
- schimbarea structurilor de date utilizate; const n modificarea
modului de organizare intern a datelor; datele i prelucrrile sunt
identice, dar difer modul de reprezentare a lor;
- aducerea programelor la o form comun; presupune translatarea
n plan matematic a caracteristicilor entitilor software; scopul
este de a efectua analize comparative pe entiti software, prin
asigurarea omogenitii reprezentrii acestora;
- restructurarea programelor; presupune inventarierea structurilor
implementate n entitatea software i stabilirea succesiunii lor;
pentru secvene de structuri, se dezvolt structuri echivalente ca
prelucrri efectuate;
- translatarea software; const n transformarea unei entiti scris
ntr-un limbaj de programare ntr-o entitate dezvoltat cu un alt
limbaj.
Cel mai frecvent, procesele de clonare sunt derulate pe baza operaiei
de copiere a coninutului. Copierea entitilor reprezint operaia de

duplicare a coninutului acestora. n funcie de tipului suportului de stocare,


exist mai multe forme de copiere.

22.2 Indicatori ai diferenelor msurate dintre proceduri


Procedurile dintr-un program sunt asemntoare din urmtoarele
puncte de vedere:
- problema pe care o rezolv;
- instrumentele folosite;
- funciile pe care le realizeaz;
- tehnicile de proiectare;
- performanele.
Este preferabil sa se construiasc proceduri i biblioteci de proceduri
n scopul reutilizrii acestora pentru a crete productivitatea programatorilor
i pentru a reduce volumul de munca vie ncorporat n programele noi care
se elaboreaz. n cazul n care pentru o aceeai tipologie de prelucrri se
scriu mai multe proceduri, costurile aplicaiei informatice cresc nejustificat.
Pentru a controla coninutul textelor surs din produsele software
elaborate i utilizate n vederea reducerii ponderii componentelor identice
realizate accidental este necesar s se evalueze permanent ortogonalitatea
procedurilor existente n aplicaiile aflate n uz curent sau n curs de
elaborare.
Astfel, se impune definirea unui sistem de indicatori cu scopul de a
evalua asemnarea existenta ntre proceduri.
Se consider dou proceduri Pi i Pj. Gradul de asemnare ntre cele
dou componente ale colectivitii format din produsele software
elaborate, se determin ca o medie geometric ntre indicatorii ce
caracterizeaz asemnarea procedurilor n funcie de criteriile considerate:
- dimensiune ca numr de instruciuni;
- frecvena de apariie a caracterelor alfabetice;
- frecvenele de apariie a cuvintelor unui vocabular dat de ctre
utilizator;
- frecvenele de apariie a cuvintelor care formeaz cele dou
proceduri;
- frecvenele de apariie a diferitelor tipuri de date;
- complexitatea secvenelor program.
Primul indicator J1 caracterizeaz asemnarea procedurilor Pi i Pj din
punctul de vedere al lungimii acestora i se calculeaz ca raport ntre
lungimea procedurii Pi i lungimea procedurii Pj .

J1

min( Li , L j )
max( Li , L j )

(22.4)

unde:
Li lungimea n octei a procedurii Pi;
Lj lungimea n octei a procedurii Pj;
Aceasta se realizeaz pentru a determina un grad de asemnare
relevant a crui valoare s fie cuprins n intervalul [0; 1].

Al doilea indicator J2 are n vedere surprinderea asemnrii dintre


cele dou proceduri Pi i Pj n funcie de frecvenele de apariie a
caracterelor alfabetice.
NTC

J2

nca
i 1

(22.5)

NTC

unde:
NTC
ncai

reprezint numrul total de caractere alfabetice (mari i mici);


ia una din urmtoarele valori:
0, dac pentru caracterul i frecvenele de apariie n cele
dou proceduri sunt diferite;
1, dac pentru caracterul i frecvenele de apariie n cele
dou proceduri sunt identice.
Al treilea indicator J3 reflect gradul de asemnare a celor dou
proceduri n funcie de un vocabular furnizat de ctre utilizator.
NCV

J3

ncv
i 1

(22.6)

NCV

unde:
NCV

numrul de cuvinte al vocabularului pentru care frecvenele de


apariie n cele dou proceduri nu sunt egale i nule pentru un
cuvnt dat;
ia una din urmtoarele valori:
ncvi
0, dac frecvenele pentru cuvntul i sunt diferite sau egale,
dar nule;
1, dac frecvenele pentru cuvntul i sunt identice i diferite
de zero.
Urmtorul indicator, J4, are n vedere criteriul de comparare a
elementelor privind frecvenele de apariie a totalitii cuvintelor care
compun cele dou proceduri.
NTCV

J4
unde:
NTCV

ncvf
i 1

NTCV

(22.7)

numrul total de cuvinte al celor dou elemente, cuvinte care


sunt distincte;
ntcvi ia una din urmtoarele valori:
0, dac frecvenele pentru cuvntul i sunt diferite sau
cuvntul i nu se regsete n cealalt procedur;
1, dac pentru cuvntul i frecvenele sunt identice n cele
dou proceduri.
Al cincilea indicator, J5, pune n eviden asemnarea a dou
elemente n funcie de structura lor.

NESC
NMES

J5

(22.8)

unde:
NESC

numrul de elemente structurale comune celor dou proceduri


considerate, respectiv Pi i Pj;
NMES numrul minim de elemente structurale a celor dou proceduri
Pi i Pj.
Urmtorul indicator, J6, determin gradul de asemnare a
elementelor prin utilizarea unei matrice de preceden. Acest grad de
asemnare se folosete pentru analiza gramatical a elementelor.

J6

min( NCI i , NCI j )

(22.9)

max( NCI i , NCI j )

unde:
NCIi numrul de cuvinte interschimbabile din procedura Pi;
NCIj numrul de cuvinte interschimbabile din procedura Pj;
Aceasta se realizeaz pentru a determina un grad de asemnare
relevant a crui valoare s fie cuprins n intervalul [0; 1].
Ultimul indicator, J7, are n vedere determinarea gradului de
asemnare a celor dou elemente din punctul de vedere al complexitii
acestora.
Gradul de complexitate se determin conform relaiei:
cmplx = n*ln(n)

(22.10)

unde n reprezint numrul de operatori ale elementului.

J7

min(cmplxi , cmplx j )
max(cmplxi , cmplx j )

(22.11)

unde cmplxi i cmplxj reprezint complexitile celor dou proceduri Pi i Pj.


Gradul de asemnare a celor dou proceduri, J, reprezint, de fapt, o
sintez a gradelor de asemnare calculate dup cele apte criterii amintite,
realiznd o omogenizare a acestor indicatori.

J 7

(22.12)

i 1

n final, pentru un produs software se determin indicatorul agregat


Jn care msoar gradul de asemnare a procedurilor ca medie geometric
ntre gradele de asemnare dintre toate componentele ce formeaz un
produs software, componentele fiind luate dou cte dou.

Jn

C n2

J
i 1

ni
i

(22.13)

unde:
Cn 2 numrul de combinaii posibile de proceduri luate dou cte dou;
Ji
valori indicatori calculate pentru fiecare dou proceduri
considerate;
frecvena de apariie a valorii respectivului indicator.
Ni
De exemplu, se consider procedurile:
P1 destinat calculului sumei ptratelor elementelor unui masiv
unidimensional al crui text surs este:
int sume(int x[],int n)
{
int S;
S=0;
for(int j=0; j<n; j++)
S=S+x[j]*x[j];
return S;
}

P2 destinat calculului produsului scalar a elementelor a dou


masive unidimensionale al crui text surs este:
int prod_sc(int x[],int y[],int n)
{
int S;
S=0;
for(int j=0;j<n;j++)
S=S+x[j]*y[j];
return S;
}

este:

P3 alegerea minimului dintre trei elemente ntregi al crui text surs

int min(int a,int b,int c)


{
int min;
min=a;
if(min>b)
min=b;
if(min>c)
min=c;
return min;
}

P4 alegerea maximului dintre trei elemente ntregi al crui text


surs este:

int max(int a,int b,int c)


{
int max;
max=a;
if(max<b)
max=b;
if(max<c)
max=c;
return max;
}

Textele surs ale celor patru proceduri


frecvenelor fij i gij din tabelele nr. 22.1 i nr. 22.2.

conduc

la

obinerea

Tabelul nr. 22.1 Frecvenele de apariie a cuvintelor cheie n procedurile


P1, P2, P3 i P4
Cuvinte cheie
int
}
}
(
)
=
+
[
]
;
++
return
-for
<
>
if
,
*

P1
5
1
1
2
2
3
1
0
3
3
6
1
1
0
1
0
0
0
1
1

Frecvene de apariie
P2
P3
6
5
1
1
1
1
2
3
2
3
3
3
1
0
0
0
4
0
4
0
6
5
1
0
1
1
0
0
1
0
0
0
0
2
0
2
2
2
1
0

P4
5
1
1
3
3
3
0
0
0
0
5
0
1
0
0
2
0
2
2
0

Tabelul nr. 22.2 Frecvenele de apariie a cuvintelor nespecifice din


procedurile P1, P2, P3 i P4
Cuvinte nespecifice
P1
1
3
2
5
2
0
0
0
0
0
0
0

sume
x
n
S
0
prod_sc
y
min
a
b
c
max

Frecvene de apariie
P2
P3
0
0
2
0
2
0
5
0
2
0
1
0
2
0
0
8
0
2
0
3
0
3
0
0

P4
0
0
0
0
0
0
0
0
2
3
3
8

Se studiaz ortogonalitatea ntre P1 i P2 i se obine:


I = I1 * I2 = 0,8 * 0,7916 = 0,6(3)

(22.14)

i ntre P3 i P4 i se obine:
I = 0,8(3) * 0,9 = 0,75 (22.15)
Din aceste dou comparaii rezult c gradele de ortogonalitate dintre
P1 i P2, respectiv dintre P3 i P4 sunt foarte sczute.
Se studiaz ortogonalitatea ntre P1 i P3 i se obine:
I = I1 * I2 = 0,68(3) * 0,6916 = 0,4769

(22.16)

i ntre P2 i P4 i se obine:
I = 0,75 * 0,6875 = 0,515

(22.17)

Din aceste dou comparaii rezult c gradele de ortogonalitate dintre


P1 i P3, respectiv dintre P2 i P4 sunt ridicate.

22.3 Structura software pentru analiza calitativ a noilor


elemente incluse n baze de date
Aplicaiile informatice complexe includ module program intercorelate
i fiiere independente sau interdependente. n cazul folosirii unor SGBD-uri
aplicaiile informatice includ texte surs de baz, structuri generate i date
care fac obiectul prelucrrilor precum i date necesare accelerrii proceselor
de selecie i de regsire.
Este important s se gestioneze redundana n bazele de date mai
ales atunci cnd se impune ca textele stocate s fie diferite ntre ele.

De exemplu, se consider mulimea ofertelor pentru obinerea de


fonduri de finanare i se impune ca ntre oferte sa existe diferene
semnificative, n sensul nedepunerii aceleiai oferte pentru dou licitaii sau
n sensul depunerii de oferte asemntoare n cadrul aceluiai program.
De asemenea, tezele de doctorat, lucrrile de licen, articolele i
crile prezentate de autori pentru concursuri trebuie s fie diferite unele de
celelalte.
n acest sens, este necesar construirea unui produs software care
msoar gradul de ortogonalitate n cadrul unui fiier sau a unei baze de
date precum i dintre fiiere, respectiv, baze de date.
Determinarea printr-o aplicaie software a valorilor caracteristicilor de
calitate i indicatorilor de ortogonalitate a entitilor proiect presupun
derularea urmtoarelor activiti:
- definirea obiectivului aplicaiei prin stabilirea rezultatelor necesare
fundamentrii actului decizional;
- stabilirea inputurilor prin studiul sistemului caracteristicilor de
calitate i a modelelor asociate metricilor de ortogonalitate a
proiectelor;
- construirea arhitecturii sistemului prin analiza inputurilor i a
corelaiilor dintre acestea;
- culegerea, normalizarea i sistematizarea datelor conform
cerinelor metricilor;
- implementarea sistemului de metrici;
- proiectarea interfeei utilizator n asistarea procesului de stabilire
a ortogonalitii bazei de proiecte;
- testarea sistemului de metrici, urmrindu-se cu precdere
comportamentul produsului software n cazurile extreme.
n [Popa02] este prezentat arhitectura i funciile produsului Cloning
Analysis Software CAS. Aceast aplicaie implementeaz metricile ale
caracteristicilor de baz pentru texte i date organizate n masive
bidimensionale.
Aplicaia solicit utilizatorului date privind:
- fiierele de lucru trebuie s se specifice numrul i numele
fiierelor care se analizeaz din punctul de vedere al
ortogonalitii;
- vocabularul utilizator trebuie s se furnizeze dimensiunea i
coninutul unui vocabular utilizat n modele asociate metricilor;
vocabularul utilizator este stocat pe disc ntr-un fiier;
- devizele de cheltuieli exist trei modaliti n care devizele de
cheltuieli asociate proiectelor sunt ncrcate n aplicaie:
completarea formularului n care trebuie s se specifice:
structura devizului ca numr de linii i coloane, denumirile
categoriilor de cheltuieli i valorile asociate;
completarea formularelor asociate devizelor cu structur
predefinit; devizul are o structur impus, iar utilizatorul
trebuie s furnizeze valorile asociate categoriilor de cheltuieli;
ncrcarea devizelor asociate ofertelor dintr-o baz de proiecte;
au o structur impus, iar valorile sunt completate de
ofertani; aceste devize sunt stocate n fiiere.
- fiiere din anexe proiectele TIC se concretizeaz n aplicaii
informatice; se analizeaz ortogonalitatea aplicaiilor dezvoltate

prin analiza codului surs; sunt definite i calculate metrici ale


programelor surs.
n continuare, se prezint o serie de metode definite n clasa de
obiecte OrtoMetric dezvoltate n vedere implementrii metricilor de evaluare
a similaritii entitilor text. Clasa OrtoMetric are urmtoarea definiie:
class OrtoMetric{
public:
unsigned char NrFis;
char **F;
OrtoMetric(unsigned char);
//[1] Grad de asemanare al
double OrtoLength();
//[2] Grad de asemanare al
// alfabetice
double OrtoCAlfa();
//[3] Grad de asemanare al
// utilizator
double OrtoUserVoc();
//[4] Grad de asemanare al
double OrtoFisVoc();
//[5] Grad de asemanare al
// precedenta a cuvintelor
double OrtoMatVoc();
};

fisierelor dupa lungimea lor


fisierelor dupa caracterele

fisierelor dupa vocabular definite

fisierelor dupa vocabularul acestora


fisierelor dupa matricea de

Metodele implementate utilizeaz structuri de date externe, respectiv


fiiere, pentru determinarea indicatorilor intermediari i structurarea
variabilelor de intrare n modelele asociate metricilor.
Constructorul clasei OrtoMetric are urmtorul coninut:
OrtoMetric::OrtoMetric(unsigned char n){
NrFis=n;
F=new char*[NrFis];
for(int i=0; i<NrFis; i++)
{
char DenFis[30];
cout<<"Denumirea fisierului "<<i+1<<":";
cin>>DenFis;
F[i]=new char[strlen(DenFis)];
strcpy(F[i],DenFis);
}
}

Se folosete o structur de date dinamic pentru stocarea denumirilor


de fiiere supuse analizei de similaritate pe baza indicatorilor implementai.
Determinarea similaritii textelor din punctul de vedere al lungimii
fiierelor n care acestea sunt stocate este implementat cu ajutorul
metodei double OrtoMetric::OrtoLength().
Fiierul GrPartLg.txt memoreaz gradele de asemnare ntre fiierele
lotului introdus prin analiza n pereche. Indicatorul final se determin ca
medie geometric a gradelor de asemnare pariale.
Codul surs al metode OrtoLength() este:
double OrtoMetric::OrtoLength(){
unsigned char i,j;
FILE *f,*g,*h;

float gr;
h=fopen("GrPartLg.txt","w+");
for(i=0; i<NrFis; i++){
for(j=0; j<NrFis; j++){
f=fopen(F[i],"r");
g=fopen(F[j],"r");
if(f&&g){
fseek(f,0,SEEK_END);
fseek(g,0,SEEK_END);
if(ftell(f)<ftell(g)){
gr=(float)ftell(f)/ftell(g);
fprintf(h,"%5.3f ",gr);
}
else{
gr=(float)ftell(g)/ftell(f);
fprintf(h,"%5.3f ",gr);
}
}
else
if(!f)
cout<<"Fisierul "<<F[i]<<" nu se
deschide!"<<endl;
else
cout<<"Fisierul "<<F[j]<<" nu se
deschide!"<<endl;
fclose(f);
fclose(g);
}
fprintf(h,"\n");
}
//DETERMINAREA GRADULUI DE ASEMANARE A LOTULUI DE FISIERE
fseek(h,0,SEEK_SET);
long double produs=1;
unsigned int NrComp=0;
for(i=0; i<NrFis; i++){
for(j=0; j<NrFis; j++){
fscanf(h,"%f",&gr);
if(i<j){
if(gr){
produs*=gr;
NrComp++;
}
}
}
}
fclose(h);
return pow(produs,1./NrComp);
}

Indicatorul de asemnare al fiierelor text din punctul de vedere al


caracterelor alfabetice coninute este implementat cu ajutorul metodei
double OrtoMetric::OrtoCAlfa() al crei cod surs C++ este:
double OrtoMetric::OrtoCAlfa(){
unsigned char i,j,k;
FILE *f,*g,*h;
float gr;
h=fopen("GrPartAlfa.txt","w+");

for(i=0; i<NrFis; i++){


for(j=0; j<NrFis; j++){
f=fopen(F[i],"r");
g=fopen(F[j],"r");
if(f&&g){
unsigned
int
contor1m[26],contor2m[26],
contor1M[26],contor2M[26];
char c;
for(k=0; k<26; k++){
contor1m[k]=0;
contor2m[k]=0;
contor1M[k]=0;
contor2M[k]=0;
}
//citire caractere alfabetice din primul fisier
fscanf(f,"%c",&c);
while(!feof(f)){
if(c==13)
fscanf(f,"\n");
else
{
if(c>=65&&c<=90)
contor1M[c-'A']++;
else
{
if(c>=97&&c<=122)
contor1m[c-'a']++;
}
}
fscanf(f,"%c",&c);
}
//citire caractere alfabetice din al doilea
// fisier
fscanf(g,"%c",&c);
while(!feof(g))
{
if(c==13)
fscanf(g,"\n");
else
{
if(c>=65&&c<=90)
contor2M[c-'A']++;
else
{
if(c>=97&&c<=122)
contor2m[c-'a']++;
}
}
fscanf(g,"%c",&c);
}
int nca=0;
for(k=0; k<26; k++){
if(contor1m[k]==contor2m[k])
nca++;
if(contor1M[k]==contor2M[k])
nca++;
}

float gr;
gr=(float)nca/52;
fprintf(h,"%5.3f ",gr);
}
else
if(!f)
cout<<"Fisierul

"<<F[i]<<"

nu

se

"<<F[j]<<"

nu

se

deschide!"<<endl;
else
cout<<"Fisierul
deschide!"<<endl;
fclose(f);
fclose(g);
}
fprintf(h,"\n");
}
//DETERMINAREA GRADULUI DE ASEMANARE A LOTULUI DE FISIERE
fseek(h,0,SEEK_SET);
long double produs=1;
unsigned int NrComp=0;
for(i=0; i<NrFis; i++){
for(j=0; j<NrFis; j++){
fscanf(h,"%f",&gr);
if(i<j){
if(gr){
produs*=gr;
NrComp++;
}
}
}
}
fclose(h);
return pow(produs,1./NrComp);
}

Fiierul GrPartAlfa.txt este utilizat pentru stocarea gradelor de


asemnare pariale. Fiierele sunt analizate n pereche pentru determinarea
gradului de asemnare parial, iar indicatorul sintetic se obine prin
determinarea mediei geometrice a indicatorilor pariali.
Metoda double OrtoMetric::OrtoUserVoc() implementeaz indicatorul
de asemnare al textelor n raport de un vocabular definit de utilizator.
Coninutul metodei este:
double OrtoMetric::OrtoUserVoc(){
unsigned char i,j,k;
FILE *f,*g,*h;
FILE *f1,*g1,*h1;
float gr;
struct FA{
char cuv[30];
unsigned int fr;
};
int opt=1;
FA str;
h1=fopen("UserVoc.dat","wb+");
while(opt){
cout<<"Introduceti cuvant vocabular:";
cin>>str.cuv;

str.fr=0;
fwrite(&str,sizeof(FA),1,h1);
cout<<"Continuati?(0/1) ";
cin>>opt;
}
h=fopen("GrPartUserVoc.txt","w+");
for(i=0; i<NrFis; i++){
for(j=0; j<NrFis; j++){
f=fopen(F[i],"r");
g=fopen(F[j],"r");
if(f&&g){
f1=fopen("FrecvF.dat","wb+");
g1=fopen("FrecvG.dat","wb+");
fseek(h1,0,SEEK_SET);
fread(&str,sizeof(str),1,h1);
int nrc=0;
while(!feof(h1)){
nrc++;
fwrite(&str,sizeof(str),1,f1);
fwrite(&str,sizeof(str),1,g1);
fread(&str,sizeof(str),1,h1);
}
//DETERMINARE FRECVENTE DE APARITIE PENTRU
// PRIMUL FISIER
char CuvF[30];
char size=sizeof(FA);
fseek(f1,0,SEEK_END);
long int dim=ftell(f1)/size;
fscanf(f,"%s",CuvF);
while(!feof(f)){
fseek(f1,0,SEEK_SET);
for(k=1;k<=dim;k++){
fread(&str,sizeof(FA),1,f1);
if(strcmp(CuvF,str.cuv)==0){
str.fr++;
fseek(f1,-size,SEEK_CUR);
fwrite(&str,sizeof(FA),1,f1);
}
}
fscanf(f,"%s",CuvF);
}
//DETERMINARE FRECVENTE DE APARITIE PENTRU
//AL DOILEA FISIER
fseek(g1,0,SEEK_END);
dim=ftell(g1)/size;
fscanf(g,"%s",CuvF);
while(!feof(g)){
fseek(g1,0,SEEK_SET);
for(k=1;k<=dim;k++){
fread(&str,sizeof(FA),1,g1);
if(strcmp(CuvF,str.cuv)==0){
str.fr++;
char size=sizeof(FA);
fseek(g1,-size,SEEK_CUR);

fwrite(&str,sizeof(FA),1,g1);
}
}
fscanf(g,"%s",CuvF);
}
int nfa=0;
fseek(f1,0,SEEK_SET);
fseek(g1,0,SEEK_SET);
FA strf,strg;
for(k=1;k<=dim;k++){
fread(&strf,sizeof(FA),1,f1);
fread(&strg,sizeof(FA),1,g1);
if(strf.fr==strg.fr){
if(strf.fr) nfa++;
else nrc--;
}
}
float gr;
gr=(float)nfa/nrc;
fprintf(h,"%5.3f ",gr);
fclose(f1);
fclose(g1);
}
else
if(!f)
cout<<"Fisierul

"<<F[i]<<"

nu

se

"<<F[j]<<"

nu

se

deschide!"<<endl;
else
cout<<"Fisierul
deschide!"<<endl;
fclose(f);
fclose(g);
}
fprintf(h,"\n");
}
fclose(h1);
//DETERMINAREA GRADULUI DE ASEMANARE A LOTULUI DE FISIERE
fseek(h,0,SEEK_SET);
long double produs=1;
unsigned int NrComp=0;
for(i=0; i<NrFis; i++){
for(j=0; j<NrFis; j++){
fscanf(h,"%f",&gr);
if(i<j){
if(gr){
produs*=gr;
NrComp++;
}
}
}
}
fclose(h);
return pow(produs,1./NrComp);
}

Analiza asemnrii fiierelor din lotul de fiiere este realizat i din


punctul de vedere al vocabularelor acestora. Metoda care implementeaz
aceast metric de asemnare este double OrtoMetric::OrtoFisVoc().
double OrtoMetric::OrtoFisVoc(){
unsigned char i,j,k;
FILE *f,*g,*h;
FILE *f1,*g1;
float gr;
struct FA{
char cuv[30];
unsigned int fr;
};
h=fopen("GrPartFisVoc.txt","w+");
for(i=0; i<NrFis; i++){
for(j=0; j<NrFis; j++){
f=fopen(F[i],"r");
g=fopen(F[j],"r");
if(f&&g){
f1=fopen("FrecvF.dat","wb+");
g1=fopen("FrecvG.dat","wb+");
FA str;
//DETERMINARE FRECVENTE DE APARITIE PENTRU
// PRIMUL FISIER
char CuvF[30];
char size=sizeof(FA);
long int dim;
fscanf(f,"%s",CuvF);
while(!feof(f)){
fseek(f1,0,SEEK_END);
dim=ftell(f1)/size;
fseek(f1,0,SEEK_SET);
char vb=0;
for(k=1;k<=dim&&(!vb);k++){
fread(&str,sizeof(FA),1,f1);
if(strcmp(CuvF,str.cuv)==0){
str.fr++;
fseek(f1,-size,SEEK_CUR);
fwrite(&str,sizeof(FA),1,f1);
vb=1;
}
}
if(!vb){
str.fr=1;
strcpy(str.cuv,CuvF);
fseek(f1,0,SEEK_END);
fwrite(&str,sizeof(str),1,f1);
}
fscanf(f,"%s",CuvF);
}
//DETERMINARE FRECVENTE DE APARITIE PENTRU
//AL DOILEA FISIER
fscanf(g,"%s",CuvF);

while(!feof(g)){
fseek(g1,0,SEEK_END);
dim=ftell(g1)/size;
fseek(g1,0,SEEK_SET);
char vb=0;
for(k=1;k<=dim&&(!vb);k++){
fread(&str,sizeof(FA),1,g1);
if(strcmp(CuvF,str.cuv)==0){
str.fr++;
char size=sizeof(FA);
fseek(g1,-size,SEEK_CUR);
fwrite(&str,sizeof(FA),1,g1);
vb=1;
}
}
if(!vb){
str.fr=1;
strcpy(str.cuv,CuvF);
fseek(g1,0,SEEK_END);
fwrite(&str,sizeof(str),1,g1);
}
fscanf(g,"%s",CuvF);
}
unsigned int nci=0,ncvf=0;
fseek(f1,0,SEEK_END);
dim=ftell(f1)/size;
fseek(g1,0,SEEK_END);
long int dim1=ftell(g1)/size;
FA strf,strg;
fseek(f1,0,SEEK_SET);
for(k=1;k<=dim;k++){
fread(&strf,sizeof(FA),1,f1);
fseek(g1,0,SEEK_SET);
for(int k1=1;k1<=dim1;k1++){
fread(&strg,sizeof(FA),1,g1);
if(strcmp(strf.cuv,strg.cuv)==0){
nci++;
if(strf.fr==strg.fr) ncvf++;
}
}
}
float gr;
gr=(float)ncvf/(dim+dim1-nci);
fprintf(h,"%5.3f ",gr);
fclose(f1);
fclose(g1);
}
else
if(!f)
cout<<"Fisierul

"<<F[i]<<"

nu

se

"<<F[j]<<"

nu

se

deschide!"<<endl;
else
cout<<"Fisierul
deschide!"<<endl;

fclose(f);
fclose(g);
}
fprintf(h,"\n");
}
//DETERMINAREA GRADULUI DE ASEMANARE A LOTULUI DE FISIERE
fseek(h,0,SEEK_SET);
long double produs=1;
unsigned int NrComp=0;
for(i=0; i<NrFis; i++){
for(j=0; j<NrFis; j++){
fscanf(h,"%f",&gr);
if(i<j){
if(gr){
produs*=gr;
NrComp++;
}
}
}
}
fclose(h);
return pow(produs,1./NrComp);
}

Metoda double OrtoMetric::OrtoMatVoc() implementeaz metrica de


asemnare a fiierelor ce conin texte privind poziia cuvintelor n cadrul
acestora. Codul surs C++ al metodei este descris n continuare:
double OrtoMetric::OrtoMatVoc(){
unsigned char i,j,k,l;
FILE *f,*g,*h;
FILE *f1,*g1,*f2,*g2;
float gr;
struct FA{
char cuv[30];
unsigned int *fr;
};
h=fopen("GrPartMatVoc.txt","w+");
for(i=0; i<NrFis; i++){
for(j=0; j<NrFis; j++){
f=fopen(F[i],"r");
g=fopen(F[j],"r");
if(f&&g){
f1=fopen("VocF.txt","w+");
g1=fopen("VocG.txt","w+");
f2=fopen("CuvMatF.txt","w+");
g2=fopen("CuvMatG.txt","w+");
FA str;
//CONSTRUIRE VOCABULAR PRIMUL FISIER
char CuvF[30],CuvD[30];
unsigned int nrcF=0;
char size=sizeof(CuvF);
fscanf(f,"%s",CuvF);
while(!feof(f)){

fseek(f1,0,SEEK_SET);
char vb=0;
fscanf(f1,"%s",str.cuv);
while(!feof(f1)){
if(strcmp(CuvF,str.cuv)==0)
fscanf(f1,"%s",str.cuv);

vb=1;

}
if(!vb){
fprintf(f1,"%s ",CuvF);
nrcF++;
}
fscanf(f,"%s",CuvF);
}
str.fr=new unsigned int[nrcF];
fseek(f1,0,SEEK_SET);
fscanf(f1,"%s",str.cuv);
unsigned int nri=0;
while(!feof(f1)){
nri++;
for(l=0;l<nrcF;l++) str.fr[l]=0;
fseek(f,0,SEEK_SET);
fscanf(f,"%s",CuvF);
while(!feof(f)){
if(strcmp(str.cuv,CuvF)==0){
fscanf(f,"%s",CuvF);
if(!feof(f)){
fseek(f1,0,SEEK_SET);
for(unsigned int l=0;l<nrcF;l++){
fscanf(f1,"%s",CuvD);
if(strcmp(CuvF,CuvD)==0)
str.fr[l]++;
}
}
}
else fscanf(f,"%s",CuvF);
}
//SCRIEREA IN FISIERUL TEXT A LINIEI k CU
// FRECVENTELE PERECHILOR
fprintf(f2,"%s ",str.cuv);
for(int unsigned l=0;l<nrcF;l++)
fprintf(f2,"%i ",str.fr[l]);
fprintf(f2,"\n");
fseek(f1,0,SEEK_SET);
for(l=0;l<=nri;l++)
fscanf(f1,"%s",str.cuv);
}
//CONSTRUIRE VOCABULAR AL DOILEA FISIER
unsigned int nrcG=0;
fscanf(g,"%s",CuvF);
while(!feof(g)){
fseek(g1,0,SEEK_SET);

char vb=0;
fscanf(g1,"%s",str.cuv);
while(!feof(g1)){
if(strcmp(CuvF,str.cuv)==0)
fscanf(g1,"%s",str.cuv);
}
if(!vb){
fprintf(g1,"%s ",CuvF);
nrcG++;
}
fscanf(g,"%s",CuvF);

vb=1;

}
str.fr=new unsigned int[nrcG];
fseek(g1,0,SEEK_SET);
fscanf(g1,"%s",str.cuv);
nri=0;
while(!feof(g1)){
nri++;
for(l=0;l<nrcF;l++) str.fr[l]=0;
fseek(g,0,SEEK_SET);
fscanf(g,"%s",CuvF);
while(!feof(g)){
if(strcmp(str.cuv,CuvF)==0){
fscanf(g,"%s",CuvF);
if(!feof(g)){
fseek(g1,0,SEEK_SET);
for(unsigned int l=0;l<nrcF;l++){
fscanf(g1,"%s",CuvD);
if(strcmp(CuvF,CuvD)==0)
str.fr[l]++;
}
}
}
else fscanf(g,"%s",CuvF);
}
//SCRIEREA IN FISIERUL TEXT A LINIEI k CU
// FRECVENTELE PERECHILOR
fprintf(g2,"%s ",str.cuv);
for(unsigned int l=0;l<nrcG;l++)
fprintf(g2,"%i ",str.fr[l]);
fprintf(g2,"\n");
fseek(g1,0,SEEK_SET);
for(l=0;l<=nri;l++)
fscanf(g1,"%s",str.cuv);
}

//DETERMINARE COMPONENTE GRAD DE ASEMANARE


// PENTRU FISIERELE F SI G
unsigned int nfm=0,ntcc=0;
FA str1;
str.fr=new unsigned int[nrcF];
str1.fr=new unsigned int[nrcG];
unsigned int frc;
for(k=0;k<nrcF;k++){

fseek(f2,0,SEEK_SET);
for(l=0;l<k;l++){
fscanf(f2,"%s",CuvF);
for(unsigned int m=0;m<nrcG;m++){
fscanf(f2,"%i",&frc);
str1.fr[m]=frc;
}
}
fscanf(f2,"%s",CuvF);
strcpy(str.cuv,CuvF);
for(l=0;l<nrcF;l++){
fscanf(f2,"%i",&frc);
str.fr[l]=frc;
}
fseek(g2,0,SEEK_SET);
fscanf(g2,"%s",CuvD);
char vb=0;
while(!feof(g2)&&!vb){
if(strcmp(CuvF,CuvD)==0){
ntcc++;
strcpy(str1.cuv,CuvD);
for(l=0;l<nrcG;l++){
fscanf(g2,"%i",&frc);
str1.fr[l]=frc;
}
vb=1;
}
else{
for(l=0;l<nrcG;l++){
fscanf(g2,"%i",&frc);
str1.fr[l]=frc;
}
}
fscanf(g2,"%s",CuvD);
}
if(vb){
unsigned int pozF=0;
fseek(f2,0,SEEK_SET);
fscanf(f2,"%s",CuvF);
while(!feof(f2)){
fseek(g2,0,SEEK_SET);
fscanf(g2,"%s",CuvD);
unsigned int pozG=0;
while(!feof(g2)){
if(strcmp(CuvF,CuvD)==0){
if(str.fr[pozF]==str1.fr[pozG])
nfm++;
}
for(l=0;l<nrcG;l++){
fscanf(g2,"%i",&frc);
str1.fr[l]=frc;
}
fscanf(g2,"%s",CuvD);
pozG++;
}
for(l=0;l<nrcG;l++){
fscanf(f2,"%i",&frc);

str.fr[l]=frc;
}
fscanf(f2,"%s",CuvF);
pozF++;
}
}
}
//DETERMINAREA GRADULUI DE ASEMANARE
float gr;
gr=(float)nfm/(ntcc*ntcc);
fprintf(h,"%5.3f ",gr);
fclose(f1);
fclose(g1);
fclose(f2);
fclose(g2);
}
else
if(!f)
cout<<"Fisierul

"<<F[i]<<"

nu

se

"<<F[j]<<"

nu

se

deschide!"<<endl;
else
cout<<"Fisierul
deschide!"<<endl;
fclose(f);
fclose(g);
}
fprintf(h,"\n");
}
//DETERMINAREA GRADULUI DE ASEMANARE A LOTULUI DE FISIERE
fseek(h,0,SEEK_SET);
long double produs=1;
unsigned int NrComp=0;
for(i=0; i<NrFis; i++){
for(j=0; j<NrFis; j++){
fscanf(h,"%f",&gr);
if(i<j){
produs*=gr;
NrComp++;
}
}
}
fclose(h);
return pow(produs,1./NrComp);
}

Pentru reprezentare intern a textului entitilor software, se propun


mai multe variante, [Sand05]:
- reprezentarea cu ajutorul a doi vectori pentru fiecare entitate se
asociaz un vector VD, cu nvd elemente, care memoreaz toate
cuvintele distincte din i un vector VF, cu nvd elemente, care
reine pe fiecare poziie k, k = 1, 2, , nvd, numrul de apariii
ale fiecrui cuvnt din VD;
- reprezentare prin utilizarea de pointeri se utilizeaz o list
simplu nlnuit care are ca nregistrare n nod perechea (vdi,

vfi); unde vdi este un element din vectorul VD, iar vfi este un
element din vectorul VF;
- reprezentarea prin arbore de cutare cheia din arbore este dat
de un cuvnt; ca informaie util, nodul are ataat numrul de
apariii ale cuvntului n titlul proiectului;
- reprezentarea prin tabele gestionate de un SGBD se definete o
tabel destinat memorrii textelor entitilor software, n care
cheia primar este un cod unic de identificare asociat fiecrei
entiti; aceasta devine cheie extern ntr-o tabel, care conine
coloanele: nr_proiect, cuvnt i apariii; n aceast tabel se
stocheaz cuvintele, mpreun cu numrul de apariii.
Analiza ortogonalitii programelor surs din anexele proiectelor din
baza de proiecte presupune construirea unei matrice ale indicatorilor de
ortogonalitate agregai pentru perechile de programe surs (PRGi, PRGj).
Indicatorul agregat de ortogonalitate asociat perechii (PRGi, PRGj) se obine
prin determinarea ortogonalitii urmtoarelor metrici primare, [Popa02]:
- lungimea programelor presupune determinarea mrimii pe disc
a fiierelor n care sunt stocate codurile surs ale aplicaiilor din
baza de proiecte;
- frecvenele de apariie a caracterelor alfabetice const n
ncrcarea elementelor masivelor unidimensionale cu numrul de
apariii al caracterelor utilizate n construirea programelor;
- vocabularul utilizator utilizatorul introduce cuvinte, rezervate
sau definite, pe care le urmrete n programele analizate din
punctul de vedere al frecvenelor de apariie;
- vocabularul programelor se identific cuvintele diferite din
textele surs i se determin frecvena lor de apariie;
- vocabularul comun const n identificarea cuvintelor comune din
vocabularele textelor surs i determinarea frecvenelor de
apariie;
- structura entitilor const n mprirea n paragrafe a
programelor surs i determinarea de frecvene de apariie pentru
fiecare paragraf;
- variabilele definite pe baza sintaxei limbajului se identific
variabilele utilizate i se determin frecvenele de apariie;
- matrice de preceden a variabilelor presupune specificarea
ordinii de folosire a variabilelor printr-o structur de tip masiv
bidimensional; compararea valorilor din matricea de preceden
conduce
la
determinarea
indicatorului
de
ortogonalitate
corespunztor acestui criteriu;
- poziia variabilelor presupune identificarea variabilelor i
reinerea poziiilor acestora n funcie de instruciunile asociate n
utilizare.
Modul n care este proiectat permite includerea de noi indicatori i
implementarea de noi mecanisme de analiz comparata a datelor.

23. COMPACTAREA DATELOR


23.1 Parametrii stocrii datelor
Volumul datelor este dat n mai multe feluri. Pentru o colectivitate ale
crei indivizi se descriu cu acelai ablon, volumul informaiilor este prezent
prin numrul indivizilor. Avem o imagine suficient de clar, despre un fiier
care conine informaii referitoare la 30.000 persoane sau despre o matrice
are 50 linii i 80 coloane, din punct de vedere al volumului de date.
Pentru o mai bun precizare, se vor lua n considerare:
N - numrul de indivizi ai colectivitii;
L
- lungimea structurii de date asociate unui individ;
B - factorul de blocare;
R - lungimea informaiilor reziduale;
Volumul de date V este dat de relaia:
V = f (N * L, B) + g(R)

(23.1)

unde f i g sunt forme analitice ale dependenei dintre factorii considerai,


forme ce se determin pentru tipuri de suport extern de date, n mod
corespunztor.
Volumul de date astfel calculat este exprimat n numr de baii.
Documentaiile tehnice consemneaz capacitatea de memorare pentru
fiecare tip de suport extern, ca numr maxim de baii.
Fie suportul extern i, avnd capacitatea Ci. Raportul:

pi

V
100
Ci

(23.2)

reprezint ponderea pe care o au datele memorate n volumul V, fa de


capacitatea suportului.
De exemplu, pentru un suport de 1200 ko capacitate, un fiier care
ocup 400 ko, ocup 33% din suport.
Dac un programator trebuie s stocheze informaii pe un suport de
capacitate Ci, sub forma unor volume V1, V2, . . . , Vn, el stocheaz numai k
volume, ntruct:
k

V C
j1

(23.3)

Apare ns problema existenei unei diferene, care descrie funcia


obiectiv:
k

[min] C i Vj
j1

(23.4)

a unui model de optimizare a combinaiei de volume, care s conduc la


acest obiectiv.

De exemplu, se consider:
Ci = 1000, V1 = 200, V2 = 500, V3 = 400, V4 = 300

(23.5)

Se calculeaz sumele:
S1 = V1 + V2 + V3 + V4 = 1400 > Ci
S2 = 200 + 500 + 400 = 1100 > Ci
S3 = 200 + 500 + 300 = 1000 = Ci
S4 = 500 + 400 = 900 < Ci
S5 = 500 + 400 + 300 = 1200 > Ci
S6 = 200 + 500 = 700 < Ci
S7 = 500 + 300 = 800 < Ci
S8 = 200 + 300 = 500 < Ci
S9 = 200 + 400 = 600 < Ci
S10 = 400 + 300 = 700 < Ci

(23.6)
(23.7)
(23.8)
(23.9)
(23.10)
(23.11)
(23.12)
(23.13)
(23.14)
(23.15)

Din toate combinaiile, S3 reprezint varianta care conduce la o bun


umplere a capacitii cu date.
Apar deci ca parametri de descriere ai utilizrii unui suport, urmtorii:
- gradul de ocupare;
- numrul de fiiere;
- volumul de informaii stocat n fiier exprimat prin intermediul
numrului de indivizi pentru care se face stocarea sau numrul de
cuvinte, depinznd de unitile de msur, de natura fiierelor.
Problema maximizrii, apare pentru:
- creterea gradului de umplere;
- creterea numrului de fiiere ce se stocheaz;
- creterea volumului de informaii.
Exist modaliti specifice, care vin s amelioreze unul sau altul
dintre parametrii considerai, ceea ce influeneaz ns asupra tuturor
parametrilor, este compactarea datelor.
Prin compactarea datelor, nelegem totalitatea metodelor care
conduc la reducerea lungimii exprimate n baii, a datelor. Fiind dat o
mulime de cuvinte:
A = {a1, a2, . . an}, de lungime l1, l2 , . . . ln

(23.16)

compactarea datelor revine la a gsi funciile:


f : AK

g : KA

(23.17)

unde:
k = { k1, k2, . . . , km}

(23.18)

este mulimea cuvintelor compactate, aa fel nct exist o pereche (i, j),
pentru care:
kj = f (ai)
ai = g (kj)

(23.19)
(23.20)

Funcia f( ) se numete funcie de compactare, iar funcia g( ) este


funcia de decompactare.
Deci, orice metod de compactare este complet definit, dac s-a
identificat i modalitatea de a reduce setul de date n forma iniial, prin
decompactare.
Pentru perechea (i , j):
lg( kj) < lg (ai)

(23.21)

Rezult c efectul compactrii, pentru un text format din cuvintele:


a1 a2 a3 a3 a3 a3

(23.22)

de lungime iniial:
L1 = 1g(a1 a2 a3 a3 a3 a3 a3) = 1g (a1) + 1g (a2) + 4*1g (a3)

(23.23)

prin compactare este transformat n textul:


k1 k2 k3 k3 k3 k3

(23.24)

de lungime:
L2 = 1g (k1) + 1g (k2) + 4*1g (k3)

(23.25)

cu indicile de eficien a compactrii:

L1 - L 2
100
L1

(23.26)

23.2 Compactarea la nivel de caracter


Sistemele de coduri asociate caracterelor pornesc de la urmtoarele
aspecte:
- mulimea caracterelor ce sunt reprezentate este finit; de
exemplu, codul ASCII permite reprezentarea unei mulimi de
caractere formate din 256 elemente;
- lungimea exprimat n bii a unui element al mulimii, este
constant; codul ASCII asociat unui caracter are 8 bii.
Pentru un ir de n bii, se asociaz o mulime format din 2n
elemente distincte, ce sunt puse n coresponden cu simboluri sau cuvinte,
ale unei mulimi cunoscute.
Vom considera un roman, n care apar numai litere mici i litere mari,
semne de punctuaie, separatorul blanc i liniua corespunztoare semnului
de dialog.
Analiza textului ce formeaz romanul, pune n eviden urmtoarele
aspecte:
- dintre literele mari sunt utilizate 15;
- dintre literele mici sunt utilizate 24;
- semnele de punctuaie cu liniua de dialog, sunt n numr de 8.

Alfabetul nou cu care operm, este format din 24+15+8 = 47


simboluri. Fiecare simbol are reprezentare pe 6 bii, ntruct cel mai mic
numr natural pentru care 2n > 47 este n = 6.
Se vor pune n coresponden cele 47 de caractere, cu coduri de cte
6 bii i textul romanului care avea o lungime iniial Li:
Li = 8 * m

(23.27)

unde m reprezint numrul de caractere al textului.


Dup compactarea la nivel de caracter, textul compactat are o
lungime final Lf:
Lf = 6 * m

(23.28)

Indicele de eficien al acestei compactri este:


p

Li - Lf
100 25%
Lf

(23.29)

Decompactarea, presupune interpretarea succesiunilor de 6 bii i


nlocuirea lor cu caractere ASCII corespunztoare din stiva caracterelor
utilizate n text.

23.3 Compactarea la nivel de cuvnt


Prin analiza textului, nelegem construirea unei stive a cuvintelor
diferite din text i nregistrarea frecvenei lor de apariie.
Dac meninnd codul ASCII pentru caracterele uzuale ale textului,
punem n coresponden cuvintele avnd frecvenele cele mai mari C1, C2, .
. ., Ck, cu coduri asociate unor caractere ce nu apar n text, indicile de
eficien al compactrii este:
k

L i - f i *1g(c j ) f j
j1

j1

Li

*100

(23.30)

Dac stiva cu cuvintele definite C1, C2, . . ., Ck, avnd frecvene


ridicate f1, f2, . . ., fk, este suficient de mare, i mulimea G a simbolurilor
neutilizate n text, este insuficient, este necesar construirea cuvintelor g1,
g2, . . ., gk formate din 1, 2, . . ., ng simboluri neutilizate, atunci
performana compactrii este:
k

L i - f j * [1g(C j ) 1g j )]
j1

Li

* 100

(23.31)

Decompactarea revine la a nlocui cuvintele gj din text, cu cuvintele


cj, ntruct att algoritmul de compactare ct i cel de decompactare
presupun existena stivei cuvintelor diferite ale textului iniial i cuvintele

din mulimea {g1,


neutilizate n text.

g2,

. . .,

gk}, a cuvintelor formate din simboluri

23.4 Compactarea prin analiza caracteristicilor textului


Vom exemplifica modul de analiz al unui text, folosind reprezentarea
n memorie a tabelului de mai jos ce trebuie imprimat:
Tabelul nr. 23.1 Situaia materialelor
5
NR.
CRT.
0
1
2
3
TOTAL

30
DENUMIRE

15
VALOARE

1
CUIE
TABLA
VAR

2
100
200
600
900

Pentru memorarea acestui tabel, se definete un articol de 74 baii i


n fiierul TABEL.DAT, vor fi memorate 20 de rnduri, incluznd i blancurile
dintre antet i capul de tabel. n fiierul TABEL.DAT vor fi ocupai
20*74=1480 baii cu acest tabel.
Prin convenie, ntruct asteriscul nu este utilizat, n continuare este
folosit ca separator, iar dou asteriscuri consecutive au semnificaia de CR.
27
30b*SITUATIA MATERIALELOR
23
30b*22 - **74b**10b*
27
54 = **10b*!*5b!*30b*!*15b**
60
10b*! NR.
!
DENUMIRE
!
!**
25
10b*! CRT
!*30b*!*15b*!**
22
10b*!*5b*!*30b*!*15b**
69
10b*54 = **10b*!
0
!
1
!
2
69
10b*54 = **10b*!
1
! CUIE !
100
!**
60
10b*!
2
!
TABLA
! 200 !**
60
10b*!
3
!
VAR
! 600 !**
9
10b*54 = **
65
10b*!
TOTAL
!
900
10b*54 =
---523 baii

Textul astfel codificat are lungimea de 523 baii.


Indicele de performan n acest caz este:

1480 523
* 100 64%
1480

(23.32)

VALOARE

!**

!**

Compactarea merge mai n profunzime, prin identificarea elementelor


invariante. Apar n mod repetat:
10b*54 = **
10b*!*5b*!*30b*!*15b*!**

Dac aceste succesiuni vor fi nlocuite, prima cu caracterul & iar a


doua cu @:

p'

1480 (523 4 * 8 21)


*100 68%
1480

(23.33)

Dac se pune n coresponden construcia **10b* cu :, care are 10


apariii, nc se obine o ameliorare a indicelui de eficien.

23.5 Compactare prin asocierea unor coduri ce permit


eliminarea separatorilor
n textele de orice tip ar fi ele, se utilizeaz diveri separatori, care
ocup un numr de poziii, depinznd de regulile acceptate de utilizare,
dintre care se enumer:
- separatorii punct i virgul sunt urmai de un spaiu;
- separatorul linie de dialog cnd este urmat de o liter mare sau
precedat de punct sau dou puncte, este precedat de 3-6 spaii
pentru a marca un dialog;
- cuvintele sunt separate prin cel puin un spaiu; n procesarea de
texte, variabilitatea numrului de spaii depinde de limea
textului i de dorina de a realiza o aliniere la extremitile definite
pentru un rnd.
Se consider de exemplu, nregistrarea profesiilor persoanelor ce
alctuiesc o colectivitate. Din nregistrrile efectuate, rezult mulimea de
profesii distincte:
forjor
strungar
frezor
economist
mecanic
supraveghetor
Fiierul ce se creeaz, conine niruirea acestor profesii, urmnd ca
programele pentru consultarea lui, s permit numrarea elementelor ce
aparin fiecrei meserii.
nregistrarea brut a informaiilor, conduce la ocuparea unei zone de
memorie:
Lb=n1*lg(forjor)+n2*lg(strungar)+n3*lg(frezor)+n4*lg(economist)+
(23.34)
+n5*lg(mecanic)+n6*lg(supraveghetor)

Dac fiecare meserie este pus n corespondena cu un mnemonic


precum:
fo
st
fr
ec
me
su

pentru
pentru
pentru
pentru
pentru
pentru

forjor
strungar
frezor
economist
mecanic
supraveghetor

n mod sever, lungimea ocupat se reduce.


(23.35)

L m 2 * (n 1 n 2 n 3 n 4 n 5 n 6 )

Pentru eliminarea ambiguitii generate de reducerea lungimii


mnemonicelor de la 2 caractere la un singur caracter, se efectueaz
punerea n coresponden a meseriilor cu caracterele:
f forjor
s strungar
r frezor
e economist
m mecanic
g supraveghetor
n aceste condiii, lungimea textului este:

L s n1 n 2 n 3 n 4 n 5 n 6

(23.36)

Forma brut a caracterelor, conduce la calculul lungimii fiierului n


baii. Lungimea efectiv, exprimat n bii, este n continuare redus dac
meseriile sunt puse n coresponden cu iruri de bii, ce se bucur de o
serie de proprieti suplimentare:
forjor
strungar
frezor
economist
mecanic
supraveghetor

1
101
1001
10001
10101
100001

Aceste construcii, se bucur de proprietatea ca prin concatenare,


locul unde s-a produs aceast operaie apar dou cifre binare de 1. Astfel:

L B n 1 *1 n 2 * 3 n 3 * 4 n 4 * 5 n 5 * 5 n 6 * 6
Observm c:

8 * L b 8 * Ls L B

(23.38)

(23.37)

n multe cazuri, lungimea cmpului este dimensionat n aa fel


nct, s poat cuprinde meserii cu cele mai multe caractere, fiierul avnd
articole de lungime fix.
n exemplul dat, lungimea este dat de cuvntul supraveghetor,
care are 13 caractere.

L F 8 13 (n 1 n 2 n 3 n 4 n 5 n 6 )

(23.39)

Toi indicii de performan, se calculeaz n raport cu lungimea LF.


Dac de exemplu, ntr-o colectivitate de 1000 persoane:
n1=100, n2=300, n3=100, n4=100, n5=300, n6=100
LF=8*13*1000=104000 bii

(23.40)

(23.41)

Lb=100*6+300*8+100*6+100*9+300*7+100*13=7900 baii
LB=100+300*3+100*4+100*5+300*5+100*6=4000 bii

(23.42)
(23.43)

n cazul n care lungimea codurilor asociate, iau n considerare


frecvenele aa fel nct, meseria cu cea mai mare frecven s fie codul de
lungime cel mai mic, se obine:
LB'=300*1+300*3+100*4+100*5+100*5+100*6=3000 bii

p1

104000 3000
*100 97%
104000

(23.45)

p2

7900 * 8 3000
*100 95%
7900 * 8

(23.46)

p3

23.6
repetitive

4000 3000
*100 25%
4000

Compactarea

prin

(23.44)

(23.47)

identificarea

de

subiruri

Pentru cele 27 litere ale alfabetului, se construiete matricea


frecvenelor de apariie a grupurilor de cte dou litere, X. Astfel, xij
reprezint numrul de apariii al literei cu poziia i, urmat de liter cu
poziia j din alfabet.
Construirea matricei X se realizeaz, prin parcurgerea unei diversiti
de texte i frecvenele depind de particularitile fonetice ale fiecrei limbi.
Dintre grupurile de cte dou litere, vor fi extrase acelea cu
frecvenele cele mai mari i vor fi dispuse pe linii ntr-un tabel, ale crui
coloane conin literele alfabetului.
Se construiete matricea Y, ale crei elemente yij, conin frecvenele
de apariie ale grupului de dou litere de pe linia i, urmat de litere de pe
coloana j a tabelului.

Se are n vedere c totalitatea grupurilor de litere ce vor fi selectate


n ordinea descresctoare a frecvenelor de apariie, s nu depeasc un
numr K aa fel nct:
K + L < 256

(23.48)

unde L reprezint numrul de caractere considerat necesar pentru


introducerea unui text ntr-un fiier.
n continuare, se vor considera cele 27 litere ale alfabetului, cele 10
simboluri ale cifrelor i caracterele: spaiu, plus, minus, egal, punct, virgul,
dou puncte, punct i virgul, semnul mirrii, semnul ntrebrii i asteriscul.
n total sunt 48 de caractere.
Din analizele statistice efectuate pe texte, se rein grupurile de litere
alctuind o mulime format din 64 de elemente dispuse n tabloul de mai
jos, care au n dreptul liniilor i coloanelor combinaii de bii care alctuiesc
codul asociat fiecrui ir.
Tabelul nr. 23.2 Combinaii de bii asociate irurilor de caractere

1000
1001
1010
1011
1100
1101
1110
1111

1000
u1
lor
it
am
cr
sa
nstr
asera

1001
1e
se
la
ar
sc
ma
eau
res

1010
pr
ut
ea
ei
st
ne
eam
tit

1011
mb
re
ta
ra
os
tre
esti
ros

1100
mp
tr
ti
ne
ti
ist
ndu
oasa

1101
ni
te
ca
un
at
tri
u-se
isem

1110
in
ta
oi
ns
ri
urile
ati
tit

1111
lui
nu
au
nt
oa
ind
nul
art

Aceste grupuri de litere au fost puse n coresponden cu codul unui


caracter, altul dect cele 48 considerate.
Astfel, versurile eminesciene:
Dintre sute de catarge
Care leag malurile
Cte oare le vor sparge
Vnturile, valurile
A fost odat ca-n poveti
A fost ca niciodat
Din rude mari mprteti
O prea frumoas fat.
care nsumeaz 177 caractere incluznd i spaiile care separ cuvintele,
vor fi compacte astfel:
Dx

x
17

x
26 24

sux
64

de
26

aga
12

x
rge
36 27

x lux x
62
57 12

Cix

x
26

Vix
A

x
x
vor spx ge
58 24 12
42

ux x
, valux x
48 57 12
57 12
fox odat ca-n povex
53
73

A fox

x
53

Dx

x
26

rude x
17

ciodat
16

x
ix aratx
62 57 15
74

o x x
fata
13 33

85

ceea ce conduce la un indice de performan:

177 137
* 100 22%
177

(23.49)

Construirea matricei generale a subirurilor, are avantajul c este


unic pentru orice text care se compacteaz, dar exist posibilitatea de
apariie a situaiei ca frecvenele grupurilor n textul de compact s nu
urmeze nivelurile de frecven a textelor care au stat la baza obinerii ei.
Dac pentru fiecare compactare se construiete o matrice de subiruri
proprie, performana este cu totul alta.
Pentru textul analizat, subirurile identificare se organizeaz ntr-un
tabel, cruia i se ataeaz o matrice C a codurilor, ce conduce la un text de
116 caractere, avnd un indice de performan:

177 116
* 100 34%
177

Dac pornim de la ideea c


succesiuni de 6 bii, pentru c el
corespunztoare grupurilor de litere
memorarea ca text avnd fiecare
performan este:

(23.50)

acest text este memorat folosind


conine 25 de combinaii de bii,
i cele 27 de litere, comparativ cu
caractere cte 8 bii, indicele de

177 * 8 116 * 6
* 100 52%
177 * 8

(23.51)

Se observ c algoritmii de compactare, vizeaz att lungimea


codului sub care se reprezint un caracter din text, ct i modul n care se
identific elementele invariante n texte.
Spaiul ocupat de textul compactat, i se adaug o zon cu informaii
care permit reconstituirea textului iniial. Aceste informaii conin:
- lungimea codurilor asociate caracterelor;

mulimea subirurilor i a codurilor cu care acestea se pun n


coresponden.
n cazul n care se identific pentru reguli complexe de scriere a
textelor proceduri, acestea se pun n coresponden cu coduri i ori de cte
ori apar codurile respective n text, vor fi activate procedurile care vor
prelucra textul adecvat.
De exemplu, dac un text este centrat pe un rnd, blancurile vor fi
eliminate, respectivul text fiind precedat de un cod care odat identificat,
preia textul, l centreaz, reconstituind blancurile eliminate la compactare.
n cazul dialogurilor, nceperii unui nou paragraf sau scrierii unei
formule, reconstituirea poziiei reale a textului, se efectueaz cu ajutorul
procedurilor activate odat cu apariia codurilor care semnalizeaz fiecare
dintre situaiile menionate.
-

23.7 Compactarea programelor date n form executabil


Pentru utilizatori, prezint importan programele executabile.
Obiectivele programelor care efectueaz compactarea acestui tip de text,
sunt gsirea unor modaliti care s determine stocarea unui numr ct mai
mare de programe executabile pe un suport. n acelai timp, se urmrete
i gsirea posibilitii de a efectua decompactarea textului transformat.
Programele de compactare a programelor executabile, iau n
considerare urmtoarele aspecte:
- limbajul de asamblare are o mulime finit de coduri de
instruciuni, dintre care programatorii folosesc sub 40%, ca
diversitate n programe;
- programele executabile sunt rezultate ale compilrii i editrii de
legturi; n cea mai mare parte, aceste operaii conduc la o
pondere ridicat a secvenelor construite mecanic, pe baza unor
reguli tip;
- programele executabile, au ca entitate instruciunea i aceasta
este plasat ntr-o zon de memorie de lungime i structur fix;
toate analizele, vizeaz componentele n numr restrns de pe o
zon restrns; frecvenele construite vin s ajute alturi de
celelalte consideraii, la dimensionarea codurilor cu care se pun n
coresponden, instruciunile programului executabil.
Se consider n continuare programul:

CICLU:

BETA:

ORG
MOV
PUSH
XRA
LDAX
ADC
DCR
JZ
STAX
INX
INX
JMP
MOV

420 H
D , E
B
A
B
M
E
BETA
B
B
H
CICLU

LOAX B
XRA M
MOV A , E
STAX B
STC
JM GAMA
MOV A , M
XRA E
STC
JM DELTA
GAMA: CMC
DELTA:

POP
MOV
RET
END

B
E , D

Acestui program i corespunde codul obiect:


0420
0421
0422
0423
0424
0425
0426
0429
042A
042B
042C
042F
0430
0431
0432
0433
0434
0435
0438
0439
0440
043B
043F
0440
0441
0000

53
C5
AF
OA
8E
1D
CA
02
03
23
C3
5F
OA
AE
7B
02
37
FA
7E
AB
37
FA
C1
5A
C9

2F

04

23

04

3F

04

3F

04

Textul obiect generat are 31 baii. Se vor scrie frecvenele de apariie


a elementelor din acest text.

Tabelul nr. 23.3 Frecvenele de apariie a elementelor textului


Element
0
1
2
3
4
5
6
7
8
9
A
B
C
D
E
F

Frecvena
8
2
4
9
4
4
4
1
1
6
1
3
1
4
6

mprtierea cifrelor hexazecimale, reduce masiv posibilitatea


efecturii unor prelucrri directe pe textul obiect.
Se procedeaz la efectuarea modificrilor:
a) se ncepe contorizarea de la zero i se memoreaz 0420 i textul
devine:
0000
0001
0002
0003

53
C5
AF
OA

0004

8E

0005

1D

0006

CA

0009
000A

02
03

000B

23

000C

C3

000F

5F

0010

0A

0011

AE

0012

7B

0013

02

0014
0015

37
FA

0018

7E

0019

AB

001A

37

001B

FA

001F

C1

00

OF

00

03

00

1F

00

1F

0020

5A

0021

C9

Cea mai mic valoare din prima cifr hexazecimal a prii de


instruciune, este 0 i cea mai mare este F.
Tabelul nr. 23.4 Frecvenele de apariie a elementelor textului
Element

Frecvena

0
1
2
3
4
5
6
7
8
9
A
B
C
D
E
F

5
1
2
2
2
1
2
5
2

Din 15 simboluri lipsesc 7. Construim pe 2 baii masca elementelor


absente:
0 1 2 3 4 5 6 7 8 9 A B C D E F
-----------------------------------------------------------------------1 1 0 1 0 1 0 1 1 0
1 0
1 0 0
1
Se analizeaz frecvena de apariie a cifrelor hexazecimale pe al IIlea bait al instruciunii.
Tabelul nr. 23.5 Frecvenele de apariie a cifrelor hexazecimale
pe baitul II al instruciunii
Element
0
1
2
3
4
5
6
7
8
9

Frecvena
1
2
4
1
2
1

A
B
C
D
E
F

6
2
1
3
2

Se construiete pe 2 baii masca elementelor absente:


0 1 2 3 4 5 6 7 8 9 A B C D E F
-----------------------------------------------------------------------0 1 1 1 0 1 0 1 0 1
1 1
0
1 1 1
Se observ c pentru un text de lungime redus, elementele
repetitive nu se identific i deci compactarea are efecte reduse, uneori nule
sau chiar pgubitoare.
Limbajele de asamblare moderne, au n definire operaii de tip RR
(registru-registru) i ntruct numrul registrelor este redus, sunt asociate
coduri diferite pentru combinaia de cod operaie R1, R2 ceea ce reprezint
o compactare la nivel de limbaj. Tot astfel, n cazul instruciunilor memorate
pe trei baii, deplasarea operandului este reprezentat pe un singur bai,
prin recalcularea ei.
n cazul unor texte mult mai lungi, dac resursele sunt folosite
convenabil i programatorii stabilesc reguli precise de realizare a unor
anumite prelucrri, compactarea este realizabil.
Astfel, n toate subprogramele, secvena de preluare a parametrilor
este standardizat i deci este pus n coresponden cu un cod i nlocuit
cu acesta.
Aplicnd principiile de analiz ale textului, compactarea se realizeaz
n principal prin tratarea elementelor repetitive i n programele scrise n
limbajul de asamblare sau generate de compilatoare, acestea nu au o
pondere important.

23.8 Compactarea datelor numerice


Reprezentarea matricelor rare, este un exemplu de compactare a
datelor. n cazul seriilor de date, problema compactrii este important
dac seriile au variaii mici sau dac seriile au o lungime foarte mare.
Fie seria de date X avnd termenii:

11120
11170
11121

11130
11135
11122

11131
11131
11120

11136
11109
11104

Cei 12 termeni ai seriei de date, dac sunt reprezentai binar, ocup


fiecare cte 2 baii, deci n total 24 baii. Observm c termenul minim al
seriei este 11104, iar termenul maxim este 11170.
D = Xmax Xmin = 66

(23.52)

iar:

60
D

0,005
Xmin 11108

(23.53)

ceea ce arat c datele se afl ntr-un interval foarte ngust n raport cu


mrimea lor.
Considerm Xmin = 11104. Se calculeaz o nou serie x':
xi' = xi xmin

(23.54)

al crei termeni sunt:


16
27

26
5

27
17

32
18

66
16

51
0

Numerele acestea se reprezint pe 7 bii. Deci, pentru cei 12 termeni


sunt necesari 84 de bii, iar pentru 11104 sunt necesari 2 bii.

84 2 * 8
100
100
100 52%
24 * 8
192

(23.55)

Compactarea la nivel de bii, se dovedete cu efecte mai importante


dac se combin cu alte procedee de compactare.
111
113

111
113

111
111

112
112

112
112

112
112

Elementul minim este 111, iar elementul maxim este 113. n


reprezentare binar a ntregilor, pentru fiecare termen este necesar un bai.
Seria aceasta necesit 12 baii.
Observm c exist elementele repetitive 111, 112 i 113, care vor fi
puse n coresponden cu caracterele a, b, c, respectiv 00, 01, 10. Pentru
cele trei numere sunt necesari 7 bii.

3 7 12 2
45
100
100 46%
12 8
96

(23.56)

23.9 Programe care efectueaz compactarea


Un program P de lungime L, se zice c este compactat de programul
X, dac forma obinut P', are o lungime L' cu peste 20% mai mic dect
lungimea L.
Programul X execut complet compactarea, dac ncercnd
compactarea programului P', se obine un program P" de lungime L" i dac
L' = L" i P" este identic cu P'.
Programul Y realizeaz decompactarea programului P', dac dup
executarea sa se obine programul P' ' ' de lungime L' ' ', aa fel nct L' ' '
= L i P' ' ' este identic cu P'.

Programele de compactare sunt specializate s aib ca intrare, fie


texte surs, fie texte obiect, fie componente executabile. Exist i programe
generale care efectueaz compactarea i decompactarea. Exemple de astfel
de programe sunt PKZIP . EXE, PKUNZIP . EXE, ARJ . EXE, PKARC . EXE.
Fiecare dintre acestea folosesc algoritmi specifici de compactare, n funcie
de tipul fiierelor supuse comprimrii.
Formatul general al unei etichete de fiier arhivat, l descriem prin
structura urmtoare n C:
struct fisier_compactat
{
int metoda_compactare[10];
char nume_fisier[8];
char extensie_fisier[3];
unsigned char atribut_fisier;
short int data;
short int timp;
short int cluster_start;
long int lungime_fisier_initial;
long int lungime_fis_compactat;
int suma_control;
};

Se asociaz fiecrei metode de compactare folosit un anumit cod, ca


de exemplu:
- 0, dac fiierul a fost memorat n forma sa iniial, compactarea
nefiind eficient;
- 1, dac fiierul a fost comprimat printr-un algoritm nerepetitiv;
- 2, dac fiierul a fost comprimat n mai multe etape.
Aceste probleme sunt nzestrate cu funcii specifice gestionrii de
fiiere arhivate, respectiv adugare, tergere, modificare, protecie.
Unul dintre algoritmii utilizai de aceste programe de compactare este
cel al lui Huffman. Acest algoritm are drept scop de a minimiza numrul de
bii necesar pentru reprezentarea oricrui simbol dintr-un alfabet. Astfel, se
atribuie simbolurilor cu o frecven de utilizare mare, coduri mai scurte, iar
simbolurilor mai rar folosite, coduri mai lungi. n acest mod, media lungimii
codului, este mai redus.
Aceast tehnic a utilizat-o i Samuel Morse cnd a definit alfabetul
Morse.
n codul Huffman, frecvena de apariie a caracterelor din textul
surs, trebuie cunoscut sau estimat. Apoi, se asociaz fiecrui simbol o
secven de bii unic, care este definit utiliznd un arbore binar.
Un cod Huffman este construit astfel:
- se ordoneaz descresctor, dup frecven sau probabilitatea de
apariie, simbolurile din textul surs;
- se consider fiecare simbol un nod n arbore, fiecrui nod fiindu-i
asociat o probabilitate (frecvena) de apariie;
- se leag dou noduri cu probabilitatea de apariie cea mai mic,
ntr-un nod a crui probabilitate este dat de suma probabilitilor
celor dou noduri acest proces se ncheie n momentul n care
rmne un nod nelegat.

Rezultatul acestei operaii este un arbore binar, n care ramura


stng a unui nod printe are eticheta 0, iar ramura din dreapta, eticheta
1.
S presupunem c textul nostru iniial este format din 40 de
caractere, utiliznd un alfabet din 8 simboluri; frecvenele de apariie ale
fiecrui simbol, sunt calculate ca raport ntre numrul de apariii a
simbolului respectiv i numrul total de caractere ale textului.
n tabelul de mai jos, presupunem numrul de apariii ale simbolurilor
i n funcie de acest numr calculm numrul total de bii pe care l ocup
simbolul respectiv, n reprezentarea normal (8 bii/caracter).
Tabelul nr. 23.6 Varianta I de construire a codului Huffman
Simbol

Nr. de apariii
simbol

0
1
2
3
4
5
6
7
Total

12
8
6
5
4
3
2
0
40

Total nr. de bii


pentru un simbol
(reprez. normal)
96
64
48
40
32
24
16
0
320
0

30%

Total nr. de bii


pentru un simbol
(cod Huffman)
12
16
18
20
20
18
14
0
118
0

0
0

20%

10

1
100 %

15%
12%
10%

110

2
3

35 %
0

5%

23%

13 %
5%

1
1110

50 %

8%

0%

70 %

11110
111110

1
1111110

1
1111111

Figura 23.1 Structura arborescent a codurilor Huffman


Dac ns, cuplm nodurile n felul urmtor:

30%

20%

15%

12%

110

100%

27%

70%
43%

8%

5%

6
7

11110
23%

13%
5%

101

1110

0
0

10%

100

0%

111110

111111

Figura 23.2 Structura arborescent a codurilor Huffman dup cuplarea


nodurilor
vom obine:
Tabelul nr. 23.7 Varianta II de construire a codurilor Huffman
Simbol
0
1
2
3
4
5
6
7
Total

Nr. de
apariii
simbol
12
8
6
5
4
3
2
0
40

Total nr. de bii


pentru un simbol
(reprez. normal)
96
64
48
40
32
24
16
0
320

Total nr. de bii


pentru un simbol
(cod Huffman)
12
24
18
15
16
15
12
0
112

i se observ c am obinut un numr total de bii mai mic dect n prima


variant, iar diferena este cu att mai mare, cu ct avem texte de lungime
mai mari.
Observm, c pe lng faptul c codul Huffman folosete n medie,
un numr de bii mai mic dect codul ASCII pentru reprezentarea unui
caracter, mai are proprietatea c secvena binar asociat fiecrui simbol,
nu este prefixul unui alt simbol i de aceea, secvenele de bii sunt
concatenate fr s se foloseasc nici o punctuaie de delimitare a acestora.

34. INSPECIA SOFTWARE


34.1 Auditul informatic
n prezent, utilizarea calculatorului i a programelor informatice a
devenit un element vital n cadrul sistemului informaional al
ntreprinderilor. Odat cu integrarea noilor tehnologii informaionale n
procesele de prelucrare, transmitere i stocare a datelor, au aprut o serie
de ameninri i vulnerabiliti ale sistemului informaional. Astfel,
managerii i-au pus problema garantrii corectitudinii i securitii
operaiilor din sistemul informatic i convergenei acestora cu obiectivele i
strategiile organizaiei.
Pentru a contracara aceste aspecte negative s-a impus elaborarea
unor proceduri de control a sistemelor informaionale la nivelul fiecrei
ntreprinderi. Aceste obiective i proceduri de control intern au ca scop
asigurarea securitii sistemelor informaionale i reducerea riscului pe care
l-ar putea avea orice ameninare i vulnerabilitate asupra sistemului.
n literatura i practica din Romnia se ntlnesc termenii de auditul
sistemelor informaionale, auditul sistemelor informatice sau auditul
informatic, auditul IT. Diferena conceptual dintre acesti termeni este dat
pe de o parte de coninutul i nivelul la care se desfoar activitatea de
audit i pe de alt parte de diferena conceptual dintre noiunile de sistem
informaional i sistem informatic. Astfel, auditul sistemului informaional
este, conceptual, cel mai cuprinztor, acoperind prin obiectivele sale toate
nivelurile sistemului informaional, de la evaluarea proiectrii i utilizrii
sistemului informatic, pn la evaluarea politicilor i procedurilor de
securitate de la nivelul operaional i strategic. Auditul sistemului informatic,
respectiv auditul informatic, acoper prin obiectivele sale doar sistemul
informatic.
Auditul informatic se refer la evaluarea riscurilor informatice ale
securitii fizice, securitatea logic, managementul schimbrilor, planul de
asisten etc. n cazul general auditul informatic reprezint un ansamblu de
procese informatice pentru a rspunde la o cerere precis a clientului.
Principalele tipuri de audit informatic sunt [Ivan05]:
auditul sistemului operaional de calcul se refer la revizia
controalelor sistemelor operaionale de calcul i reelelor, la
diferite niveluri: sistem de operare, reea, software de aplicaie,
baze de date;
auditul instalaiilor IT este legat de securitatea fizic,
controalele mediului de lucru, sistemele de management i
echipamentele IT;
auditul sistemelor aflate n dezvoltare, care se ocup cu
controalele
managementului
proiectului,
specificaiile,
dezvoltarea, testarea, implementarea i operarea controalelor
tehnice i procedurale;
auditul managementului IT include revizia organizaiei,
structurii, strategiei, planificrii muncii, planificrii resurselor,
stabilirii bugetului, controlul costurilor etc;

auditul procesului IT cuprinde revederea proceselor care au loc


n cadrul IT cum sunt dezvoltarea aplicaiei, testarea,
implementarea, operaiile, mentenana, gestionarea incidentelor;
auditul managementului schimbrilor se ocup cu verificarea
planificrii i controlului schimbrilor la sisteme, reele, aplicaii,
procese etc., cuprinde managementul configuraiei, controlul
codului de la dezvoltare, prin testare, la producie i
managementul schimbrilor;
auditul controlului i securitii informaiilor se refer la
verificarea
controalelor
referitoare
la
confidenialitatea,
integritatea i disponibilitatea sistemelor i datelor;
auditul conformitii cu legalitatea include copyright,
conformitate cu legislaia, protecia datelor personale;
auditul
accidentelor
dezastruoase/planificrii
continuitii
afacerii/refacerii dup dezastre cuprinde reviziile msurilor
propuse pentru restaurarea dup un dezastru care afecteaz
sistemul i verificarea modului n care organizaia abordeaz
managementul riscurilor;
auditul strategiei IT - include revizia aspectelor variate ale
strategiei IT, viziune i planuri, precum i relaiile cu alte
strategii, viziuni i planuri.
Un loc important n cadrul auditului informatic l ocup inspecia
software.

34.2 Necesitatea i obiectivele inspeciei software


Calitatea constituie un el principal n industria software. nc se
manifest prerea c nu exist un mijloc eficient pentru mbuntirea
calitii fr lungirea ciclului de dezvoltare i/sau creterea costurilor.
ncadrarea asigurrii calitii software ntr-un program i un buget
prestabilite este aproape imposibil de realizat. Scurtarea ciclului de
dezvoltare, limitarea resurselor i creterea complexitii software duc la o
scdere a calitii sale i la creterea numrului de defecte. Impactul
economic al defectelor este deosebit. Ele reprezint principala cauz a
eurii aplicaiilor i provoac pagube nsemnate att utilizatorilor ct i
organizaiei dezvoltatoare.
Este posibil ca anumite defecte i vulnerabiliti s treac
neobservate, inclusiv n etapa de testare, mai ales cnd consecinele lor nu
sunt att de vizibile. Ele pot rmne nedetectate pn n etapele de
implementare i de exploatare cnd, pentru remedierea lor, sunt necesare
resurse suplimentare foarte mari.
Un mijloc important pentru creterea calitii unui produs software l
constituie mbuntirea testrii sale. Testarea este recunoscut ca un punct
critic n asigurarea calitii totale, dar are limitele ei, precum:
crearea, execuia, validarea i ntreinerea seturilor de date i a
procedurilor de testare este scump i consumatoare de timp;
gradul de acoperire procentul de instruciuni testate scade
considerabil pe masura creterii complexitii produsului;
adesea poate fi dificil i de durat determinarea cauzei reale
care provoac o eroare, astfel nct dezvoltatorii s localizeze
exact secvena de cod ce trebuie modificat;

testarea nu poate acoperi toate bug-urile posibile studii


realizate n acest sens arat c, de obicei, testarea duce la
nlturarea a mai puin de 50% din defecte; chiar i cele mai bune
procese de testare nu nltur mai mult de 85% din defecte
[Reas**].
n concluzie, dei testarea este o etap important n asigurarea
calitii unui produs software, nu reprezint un panaceu. Testarea singur
nu poate garanta eliminarea tuturor defectelor i nici nu asigur un nivel
suficient de nalt al calitii.
Testarea nu se termin niciodat, este doar abandonat [Onei01a].
Pentru a nelege acest citat trebuie fcut distincia ntre conceptul de cod
i cel de acoperire. Chiar dac este asigurat ntreaga acoperire a codului,
acest lucru nu garanteaz nlturarea tuturor defectelor. Testarea complet
cere acoperirea tuturor ramurilor, nu numai a instruciunilor ca atare.
Acoperirea tuturor ramurilor este mai dificil de realizat dect acoperirea
total a codului. Codul unui sistem complex poate conine milioane de
ramuri. Chiar dac ar fi posibil construirea unui sistem complet de testare,
timpul i costul necesar execuiei ar fi prohibite.
Deoarece testarea nu asigur acoperirea total i necesit
dezvoltarea de instrumente scumpe, sunt necesare alte tehnici pentru
asigurarea unei nalte caliti a produselor software. Una dintre aceste
tehnici este inspecia software, care acoper multe din limitele testrii.
Inspecia software este un proces de revizuire tehnic realizat de-a
lungul etapelor de dezvoltare a produselor, cu scopul de a identifica i
elimina defectele. Ea se poate aplica oricrui produs rezultat n diversele
etape ale procesului de dezvoltare: determinarea cerinelor, proiectare,
codificare, chiar i n etapa de testare.
Comunitatea dezvoltatorilor de software tie de mult c inspecia
software constituie o tehnic eficient pentru nlturarea defectelor, cu
beneficii substaniale pe termen lung. Inspecia software este benefic
deoarece identific i nltur erorile critice nc din primele etape ale
ciclului de dezvoltare, nainte de a se ajunge n etapa de testare sau
implementare.
Inspecia software procesul de examinare a fiecrei linii a codului
surs pentru identificarea defectelor i vulnerabilitilor este o practic
obinuit n multe organizaii. Ea are ca scop detectarea i corectarea
defectelor i prevenirea propagrii lor de-a lungul ciclului de dezvoltare.
Specialitii cu experien tiu c dezvoltarea produselor software este
un proces de experimentare care implic descoperirea continu de
informaii tehnice asociate cu funcionalitatea, forma i corectitudinea
produsului. Inspecia software reprezint o practic integrat n acest
proces de experimentare. Ea eficientizeaz etapele de dezvoltare i testare
prin reducerea efectelor cauzate de defecte i vulnerabiliti, n paralel cu
creterea nivelurilor de securitate, integritate, ncredere, disponibilitate,
viabilitate i mentenan ale produsului.
Inspecia software este o tehnic de detectare a defectelor din
produsele software nainte ca acestea s fie implementate. Ea a fost
introdus la compania IBM de ctre Michael Fagan n 1976 [Faga76], cu
scopul de a mbuntii calitatea produselor software i de a crete
productivitatea. De atunci s-a dezvoltat continuu i se refer la un proces
structurat prin care se ncearc identificarea defectelor n documentele
ntocmite n diversele etape ale procesului de dezvoltare. Procesul de

dezvoltare a produselor software const dintr-o serie de etape care se


finalizeaz cu un anumit produs: definirea cerinelor, proiectare, codificare,
testare i mentenan. Identificarea defectelor este necesar s se fac ct
mai devreme posibil, deoarece costurile de remediere a unui defect sunt de
10 pn la 100 ori mai mici n primele etape fa de remedierea lor n etapa
de mentenan. Acest lucru se realizeaz prin inspectarea ieirilor din
fiecare etap n parte i compararea lor cu cerinele corespunztoare, sau
prin ndeplinirea unui criteriu de terminare a etapei respective.
Inspecia este cea mai eficient metod de evaluare software i se
deosebete de alte forme de evaluare prin aceea c este un proces riguros,
bine definit pentru examinarea n detaliu a produselor software i
identificarea defectelor lor. Numit i inspecie Fagan sau inspecie formal,
ea definete un proces ca fiind o activitate specific cu criterii de intrare i
de ieire predefinite. Criteriile de intrare sunt cerinele ce trebuie ndeplinite
pentru a se ncepe un anumit proces, iar criteriile de ieire sunt cerinele ce
trebuie ndeplinite pentru terminarea procesului. Pentru fiecare activitate,
inspecia software stabilete dac ieirea din proces este conform cu
criteriile de ieire aferente procesului [Faga86]. Orice abatere de la aceste
criterii este considerat un defect.
Inspecia software sau evaluarea codului este o examinare vizual a
codului surs cu scopul identificrii defectelor i/sau nerespectrii
standardelor de codificare. Ea urmrete garantarea completitudinii i
corectitudinii codului surs i asigurarea faptului c implementarea codului
este n concordan cu specificaiile software. n acest fel se asigur
eliminarea erorilor nc din fazele incipiente ale ciclului de dezvoltare i
creterea calitii produselor software.
Este important de subliniat faptul c inspecia software nu este
acelai lucru cu testarea, dei ambele urmresc asigurarea unei nalte
caliti a produsului, ntre ele existnd anumite diferene, dintre care se pot
aminti:
cnd se testeaz, se execut cod; cnd se inspecteaz, se
evalueaz cod;
testarea se face n etapa de testare; inspecia software se face n
etapa de codificare, dar i n etapele anterioare (determinarea
cerinelor, proiectare);
prin testare nu se parcurg toate ramurile programului; prin
inspecie software se descoper defecte pe ramuri executate
foarte rar i puin probabil de inclus n strategia de testare;
defectele identificate n etapa de inspecie software sunt
nlturate imediat; testarea se caracterizeaz printr-o abordare
serial: nti se observ efectele, apoi se caut cauza pentru
nlturarea ei;
inspecia software nu execut cod, deci este independent de
hardware,
nu cere resurse sistem sau modificri n
comportamentul programului i poate fi aplicat cu mult timp
nainte ca resursele hardware s fie disponibile pentru testare.
n figura 34.1 se prezint ncadrarea inspeciei n ciclul de dezvoltare
a produsului software.

Etape ale
ciclului de
dezvoltare

Inspecie

DA

Produsul
respect
specificaiile?

Integrare

NU
NU
Continuare proces
dezvoltare

Costuri
mai
mari?

Testare

DA
Abandonare

Figura 34.1 Locul inspeciei n ciclul de dezvoltare a software.


Inspecia software implic interaciunea urmtoarelor elemente:
etapele inspeciei software;
rolurile participanilor;
colecia de procese i date;
produsul inspectat;
infrastructura corespunztoare.
Ea ajut organizaiile productoare de software s realizeze produse
mai bune. Prin aplicarea ei n fiecare etap a ciclului de dezvoltare, se
asigur o baz tehnic corect pentru etapele urmtoare. Descoperirea i
corectarea timpurie a defectelor reduce resursele necesare pentru depanare
n etapele ulterioare ale proiectului, ducnd la reducerea costurilor totale de
realizare a produsului. Rezultatele obinute la compania IBM [Faga86] arat
c pot fi detectate 80-90% din defecte, obinndu-se o reducere cu pn la
25% a costurilor. n acelai timp se asigur o eficien sporit testrii,
timpul necesar acestei activiti reducndu-se substanial. Un alt avantaj l
reprezint evaluarea imediat i feedback-ul primit de autor dup fiecare
etap a procesului de dezvoltare.
Calitatea codului care a fcut subiectul inspeciei software este de
nivel nalt, ceea ce duce la reducerea timpului i efortului de testare.

Deoarece inspecia software este o examinare a codului, n aceast etap


nu este necesar execuia i testarea sa.
Dupa definiia dat de Fagan [Faga86], un defect este o situaie n
care o anumit cerin nu este ndeplinit.
Defectele identificate n procesul de inspecie software se clasific n
dou categorii: defecte majore i defecte minore. Defectele care constau n
funcionaliti sau specificaii incorecte sau lips se ncadreaz n categoria
defectelor majore. Dac nu sunt nlturate, produsul nu va funciona corect.
Spre deosebire de defectele majore, cele minore nu afecteaz
funcionarea corect a produsului. Exemple de astfel de defecte sunt: erori
de ortografie n documente, poziionarea incorect a controalelor n
programele de interfa cu utilizatorul etc.
Inspeciile software sunt examinri stricte i complete impuse de
cerinele, specificaiile, arhitectura, design-ul, codul, planurile i procedurile
de testare ale produsului [Eben94]. Principalii indicatori de performan
pentru fiecare produs prevd criterii de terminare a fiecrei activiti din
ciclul su de dezvoltare. Aceti indicatori includ completitudine,
corectitudine, stil, reguli de construcie i viziuni multiple [Onei88].
Completitudinea se bazeaz pe transpunerea tuturor cerinelor n cod,
lucru esenial pentru mentenan. Corectitudinea se bazeaz pe specificarea
clar a funciilor i transpunerea lor n cod, lucru esenial pentru ncrederea
i disponibilitatea produsului [Ling79]. Stilul se bazeaz pe consistena
nregistrrilor, esenial pentru mentenan. Regulile de construcie se
bazeaz pe arhitectura i protocoalele aplicaiei, abloanele i conveniile
utilizate pentru realizarea ei, lucru esenial pentru disponibilitate i
ncredere. Viziunile multiple se bazeaz pe o varietate de perspective i
puncte de vedere necesar a fi reflectate n produsul software, lucru esenial
pentru mentenan. Prin detectarea timpurie a defectelor i prevenirea
propagrii lor n urmtoarele activiti se elimin nevoia de evaluri i a
coreciilor ulterioare, lucru esenial pentru reducerea timpului i costurilor
de realizare.
Adoptarea inspeciei software duce la creterea competenei i este
foarte apreciat de practicienii pregtii pentru utilizarea sa. Organizaiile
care folosesc aceast tehnic beneficiaz de mbuntirea predictibilitii
costurilor i a performanei proiectate, reducerea costurilor de dezvoltare i
ntreinere, reducerea defectelor i creterea satisfaciei utilizatorilor.
Recuperarea investiiei cu inspecia software este definit ca raportul
dintre economia net i costurile cu detecia [Onei01b]. Economiile obinute
prin detectarea i corectarea timpurie a defectelor previn creterea
costurilor care pot s apar dac acestea ar fi detectate n etape ulterioare
ale ciclului de dezvoltare a produsului. Un defect major nedetectat care se
propag n etapele ulterioare poate crete costurile cu detecia i corectarea
de dou pn la zece ori [Basi01]. Un defect minor poate crete costurile cu
detecia i corectarea de dou pn la patru ori. Economiile nete sunt deci
de pn la nou ori n cazul defectelor majore i de pn la trei ori n cazul
celor minore.
Inspecia software este o activitate laborioas, necesitnd adesea
evaluarea formal a codului, parcurgeri structurate i alte tehnici similare.
Rezultatele inspeciilor software pot fi impresionante, dar adesea ele nu
sunt bine realizate sau nu sunt aplicate de loc. Unii manageri consider
inspeciile software ca fiind consumatoare de resurse, iar programatorii se
simt adesea ncorsetai n formalitatea procesului. Volumul total de cod

implicat este o alt constrngere, deoarece programele actuale, de o


complexitate deosebit, implic adesea milioane de linii surs. De aceea
este unanim recunoscut faptul c inspecia manual se poate aplica efectiv
numai pentru programe relativ simple.
Cu toate acestea, inspecia software este cea mai eficient metod de
evaluare i garanteaz faptul c produsul final funcioneaz conform
specificaiilor. n acelai timp este util i proiectanilor i programatorilor
care, pe viitor, vor fi n msur s evite erorile constatate.
Pentru stabilirea componentelor de evaluat i a metodelor ce se vor
folosi trebuie avui n vedere urmtorii factori de risc [Onei01a]:
componentele care folosesc tehnologii, tehnici sau instrumente
noi;
componentele critice;
componentele ce folosesc algoritmi compleci, care trebuie s fie
coreci i optimizai;
componentele cu multe condiii de excepie, care nu sunt uor de
testat;
componentele care se vor reutiliza;
componentele care vor servi drept model pentru alte
componente;
interfeele cu utilizatorul;
componentele realizate de dezvoltatori mai puin experimentai.
Componentele care se ncadreaz n aceste categorii sunt considerate ca
avnd un risc ridicat. Pentru acestea este necesar folosirea inspeciei
software. Componentele ale cror eventuale defecte neidentificate nu vor
afecta semnificativ planul de realizare, calitatea, costurile i obiectivele
urmrite sunt considerate ca avnd un risc sczut. Pentru evaluarea lor se
pot folosi i metode mai puin formale.

34.3 Locul
informatic

inspeciei

software

cadrul

auditului

Auditarea software const n activiti prin care se verific gradul de


concordan dintre specificaii i programul elaborat. Auditul software d
msura siguranei pe care trebuie s o aib utilizatorul de programe atunci
cnd obine rezultate. Sigurana se refer la corectitudinea i
completitudinea rezultatelor finale atunci cnd i datele de intrare sunt
corecte i complete [Ivan05].
n urma verificrilor realizate se identific diferene ntre cerine i
ceea ce s-a realizat, abateri fa de standarde, norme i restricii impuse de
tehnicile utilizate. n cazul cel mai fericit, se constat o suprapunere
perfect a acestora. Dac diferenele constatate nu sunt semnificative n
raport cu criteriile de exigent stabilite, produsul informatic este validat.
Inspecia se realizeaz asupra tuturor produselor livrabile realizate
de-a lungul ciclului de dezvoltare a aplicaiilor informatice. n cadrul acestui
proces, un rol special l are inspecia textului surs, care const n
parcurgerea acestuia pas cu pas i stabilirea concordanei cu specificaiile de
programare.
Pornind de la specificaiile de programare unde se prezint datele de
intrare, rezultatele, modelele de calcul i seturile de date de test i

rezultatele ce trebuie obinute, activitatea de inspecie compar


componentele specificaiilor software cu secvenele corespondente din
program.
n urma inspeciei se constat una din urmtoarele situaii n care se
pot afla specificaiile de programare i textul surs:
a) tuturor secvenelor de texte din specificaii le corespund secvene
de instruciuni n programul surs. n acest caz programul
realizeaz toate cerinele definite n specificaii;
b) numai anumitor secven e de text din specificaii le corespund
secvene de instruciuni n programul surs. n acest caz
programul nu realizeaz toate cerinele din specificaii;
c) exist mai multe secven e de cod dect cele definite n
specificaii. Este cazul n care programatorul intuiete o serie de
prelucrri necesare programului, care nu au fost incluse n
specificaii;
d) exist o diferen total ntre specificaii i textul surs.
Programul realizeaz cu totul altceva dect s-a cerut prin
specificaii.
Toate aceste rezultate posibile ale comparrii specificaiilor de
programare cu textul surs al programelor sunt redate n figura 34.2.
Specificaii de programare

Text surs program

a) specificaiile sunt identice cu programul

b) programul realizeaz parial cerinele din specificaii

C
c) Programul realizeaz mai multe funcii dect au fost prevzute
n specificaii

d) Programul face cu totul altceva dect a fost prevzut n specificaii


Figura 34.2 Rezultatele posibile ale comparrii specificaiilor de
programare cu textul surs al programului
n continuare se prezint secvene din specificaii mpreun cu textul
surs aferent, realizat n limbajul C#, pentru exemplificarea cazurilor
prezentate n figura 34.2.
a) Specificaii identice cu programul corespund exemplului urmtor.
Constructorul clasei PhotoManager este o metod public, fr parametri.
Se iniializeaz cmpul connection cu o nou conexiune la SqlServer. irul
de conectare se gsete n fiierul de configurare al aplicaiei i este cel
corespunztor bazei de date Personal. Cmpul command se iniializeaz
cu o nou comand. Comanda se va executa pe conexiunea connection i
este de tip procedur stocat. Pe constructor se deschide conexiunea. Se
iniializeaz cmpul filter cu true sau false, n funcie de rolul utilizatorului.
Dac utilizatorul este din grupul Administrators sau Friends, atunci filter
devine false.
public PhotoManager() {
Connection = new SqlConnection
(ConfigurationManager.ConnectionStrings[Personal].ConnectionString);
command = new SqlCommand();
command.Connection = conection;
command.CommandType = CommandType.StoredProcedure;
connection.Open();
filter = true;
if (HttpContext.Current.User.IsInRole(Friends) ||
HttpContext.Current.User.IsInRole(Administrators)) {
Filter = false;
}
}

b) Programul realizeaz parial cerinele din specificaii. Operatorul de


nmulire a dou obiecte de tip Matrice este o metod public i static din
clasa utilitar Matrice. Numele predefinit este operator*. Parametrii sunt

dou referine la obiecte de tip Matrice, m1 si m2. Metoda returneaz


rezultatul nmulirii lui m1 cu m2 sub forma unei referine la obiect de tip
Matrice alocat dinamic n corpul metodei. Se verific dac numrul de
coloane al lui m1 este egal cu numrul de linii ale lui m2. Dac este
adevrat, se nmulesc matricele, altfel se returneaz valoarea null.
public static Matrice operator *(Matrice m1, Matrice m2)
{
Matrice c = null;
c= new Matrice(m1.lin, m2.col);
for (int i = 0; I < m1.lin; i++)
for (int j = 0; i < m2.col; j++)
for (int k = 0; k < m1.col; k++)
{
c[i,j] = c[i,j] + m1[i,k] * m2[k,j];
}
return c;
}

Aici, n cazul n care dimensiunile nu corespund se produce o excepie


care ntrerupe funcionarea execuiei programului. Blocul cu alocare
dinamic a lui c i cele trei instruciuni for trebuie tratate ntr-un bloc al
instruciunii
if ((m1.col == m2.lin))
{}

c) Programul realizeaz mai multe funcii dect n specificaii.


GetPhoto este o metod public a clasei PhotoManager. Parametrii sunt
photoid de tip int, reprezentnd identificatorul imaginii n baza de date, i
dimensiunea size de tip int. Metoda returneaz un obiect de tip Stream cu
irul de octei care alctuiete o imagine. Metoda apeleaz procedura
stocat GetPhoto prin intermediul obiectului command. La comanda se
adaug parametrii procedurii, @PhotoID, @Size si @IsPublic, cu valorile,
respectiv, photoid, size i filter. Rezultatul execuiei procedurii stocate se
reine n memorie ntr-un obiect result i se construiete obiectul stream pe
baza acestuia.
public Stream GetPhoto(int photoid, int size) {
command.CommandText = GetPhoto;
command.Parameters.Add(new SqlParameter(@PhotoID, photoid));
command.Parameters.Add(new SqlParameter(@Size, size));
command.Parameters.Add(new SqlParameter(@IsPublic, filter));
object result = command.ExecuteScalar();
try {
return new MemoryStream((byte[]result);
} catch (ArgumentNullException e) {
Return null;
}
}

n acest exemplu, dei nu este specificat n documentaia primit,


programatorul intuiete c exist posibilitatea ca obiectul returnat de
procedura stocat s fie vid. Acest lucru ar produce n linia:
return new MemoryStream((byte[])result);

o excepie ce ar ntrerupe funcionarea programului. De aceea el mbrac


codul cu o seciune try catch, tratnd i cazul omis de specificaii. n cazul
n care rezultatul este vid, programul nu se ntrerupe, ci returneaz un
obiect vid.
n concluzie, auditul pe textul surs analizeaz modul n care au fost
introduse datele de test, ce proceduri au activat i stabilete caracterul
parial sau caracterul complet al prelucrrilor. Auditul pe textul surs
descoper care sunt minusurile din textul surs sau care sunt definirile n
plus, fr a da soluii, adic fr a genera secvenele lips, pentru a arta
cum trebuie rescris programul.
Cnd se analizeaz secvenele de program trebuie cutate sursele de
erori care afecteaz fluxurile de prelucrare, dintre care se enumr
[Ivan05]:
lucrul cu variabile neiniializate;
traversarea unor structuri de date n afara limitelor pentru care
au fost definite;
neincluderea de teste care s evite mprirea prin zero sau
blocarea unor funcii care au restricii asupra intervalelor
parametrilor;
construirea unor expresii care filtreaz parial sau incorect
submulimi de elemente;
restructurarea expresiilor prin schimbarea ordinii de evaluare a
unor subexpresii diferite de cele date de specificaii;
efectuarea de conversii intermediare care deformeaz rezultatele
finale;
construirea expresiilor de referire a elementelor unei structuri
care dezvolt procese de cutare/regsire neconcordante cu
specificaiile;
absena manipulrii variabilelor de stare a prelucrrilor;
atribuirea de coduri variabilelor de stare dup alte reguli dect
cele date n specificaiile de programe;
alocarea dinamic a unor variabile fr a exista i procesul de
dealocare;
introducerea unor elemente de neomogenitate, cum ar fi citiri
scrieri de date;
definirea de invariani n structuri repetitive n principal prin
inexistena incrementrilor sau decrementrilor;
neincluderea n secvene a unor instruciuni corespunztoare
evalurii unui model sau filtrrii, ceea ce conduce la o tratare
parial a problemei;
atribuirea altei semnificaii variabilelor cu consecine directe
asupra rolului i locului pe care acestea le au n program;
introducerea de expresii de atribuire care anuleaz prelucrrile
precedente;
compunerea unor construcii fr a exista o echivalen ntre
formulele finale i cele iniiale ale acestora.
Inspecia textului surs presupune i punerea n coresponden a
variabilelor, ecuaiilor i inecuaiilor, enumerrilor, definirilor de mulimi i
intervale de existen coninute n specificaiile de programare i n textul
surs.

n ceea ce privete variabilele, n cazul respectrii riguroase a


specificaiilor se constat c lista variabilelor din specificaii are aceeai
dimensiune ca lista variabilelor din program (fiecrei variabile din specificaii
i corespunde o variabil i numai una din program).
Dac lista variabilelor din program este mai mare dect cea din
specificaii, trebuie analizat cauza acestei situaii: i) n program sunt
definite mai multe variabile intermediare dect este necesar, sau ii)
specificaiile sunt incomplete, caz n care trebuie identificate efectele asupra
programului.
Dac lista variabilelor din program este mai mic dect cea din
specificaii, acest lucru se poate datora faptului c i) programatorul a fcut
redefiniri, caz ce nu constituie o eroare, sau ii) n program nu au fost
abordate toate aspectele din specificaii, ceea ce impune refacerea sa.
O ecuatie simpl din specificaii trebuie s se regseasc n program
sub forma unei instruciuni de atribuire:
n specificaii: a = b + c + d;
n program: a = b + c + d.
O ecuaie complex trebuie s se regseasc sub forma unei structuri
IF-ELSE-ENDIF sau a unei structuri CASE-ENDCASE, precum:
n specificaii:

x 2 daca x 0
1
a = 2 daca x 0
x
1 daca x 0

(34.1)

n program:
if (b > 0) a = x * x;
else
if (b < 0) a = 1 / (x * x);
else
a = 1;

Cazul inecuaiilor este similar cu cel al ecuaiilor.


Indiferent de cele constatate, inspecia textului surs doar
semnaleaz abaterile fa de specificaii, fr s dea soluii. Va fi sarcina
echipei de programare sa remedieze erorile identificate.
Un alt aspect pe care inspecia trebuie s l aib n vedere este cel al
structurilor de date. Programele lucreaz cu fiiere, cu matrice, liste, stive,
arbori binari, arbori B sau alte structuri de date agregate. Rolul inspeciei
software orientat pe structuri de date este de a vedea dac:
au fost bine alese structurile de date;
implementarea procedurilor respect cerinele impuse de
rezolvarea problemei;
complexitatea structurilor de date utilizate este n concordan cu
complexitatea problemei;
expresiile de referire a elementelor din structuri sunt ct mai
simple posibil.
Pentru aceasta trebuie analizate tipurile de structuri folosite. n cazul
n care se cunoate numrul componentelor care alctuiesc colectivitatea,
trebuie s se lucreze cu fiiere. n caz contrar, cnd nu se cunoate numrul

componentelor care alctuiesc colectivitatea, trebuie s se lucreze cu


structuri dinamice. Inspectorul analizeaz ipotezele din specificaii i verific
dac acest lucru este respectat. Inspecia are rolul de a evidenia dac
structura de date folosit este cea adecvat. n cazul n care aceasta nu
este cea adecvat, se va demonstra cu calcule care este structura cea mai
potrivit.
O cerin fundamental a prelucrrii datelor este aceea de a memora
date aleatoare. Dac un ir de valori este constant, atunci trebuie s se
memoreze numrul de elemente i valoarea constantelor. Dac irul are
elemente ce rezult din calcul, atunci se memoreaz valoarea de start,
expresia analitic i numrul de termeni. Pentru aceasta se analizeaz zona
de memorie cu informaia util n care se descriu caracteristicile elementelor
colectivitii pentru care se efectueaz prelucrarea, zonele unde se definesc
legturile dintre elemente i care sunt variabilele pointer.
Pentru structurile de date existente n programme se definete
volumul de prelucrri legat de numrul de comparri necesare pentru a
ajunge la elementul cutat din structur. Pentru aceasta, inspectorul
analizeaz structurile definite n program, estimeaz volumul necesar de
comparri i verific dac structura de date utilizat este cea care
determin volumul cel mai redus de comparri.

34.4 Structura echipei de inspecie software


Cnd dezvoltatorii i revizuiesc propria munc nu observ toate
defectele produsului. De aceea este necesar revizuirea produsului de ctre
o nou echip.
Echipa de inspecie software este format dintr-un grup de oameni
care lucreaz mpreun pentru a analiza fiecare produs al unei activiti de
dezvoltare, cu scopul de a identifica i nltura defectele. Acest lucru este
realizat prin atribuirea de cinci roluri procedurale diferite pentru membrii
echipei: autor, moderator, lector (reader), nregistrator (recorder) i
inspector [Fran**].
Autorul este persoana care a realizat produsul sau un stadiu al
acestuia i este ultimul responsabil cu actualizarea acestuia dup inspecia
software. De regul este persoana care a creat iniial produsul. Lui i se vor
adresa ntrebri specifice cu privire la coninutul produsului i i va folosi
cunotinele speciale pentru a ajuta la detectarea defectelor reale i pentru
a preveni ca simple nenelegeri s fie considerate defecte. El va corecta
toate defectele majore identificate precum i, n limita timpului i resurselor
disponibile, defectele minore.
Moderatorul conduce echipa de inspecie software i este responsabil
cu asigurarea faptului c procedurile inspeciei sunt respectate de-a lungul
ntregului proces de inspecie. Aceasta include asigurarea c toi membrii
echipei i ndeplinesc rolurile la nivelul sarcinilor. Sarcinile sale sunt:
verificarea faptului c produsul este pregtit pentru inspecia
software;
verificarea ndeplinirii criteriilor de nceput;
alegerea efectiv a echipei de inspecie software;
nregistrarea ntlnirilor de inspecie software;
verificarea ndeplinirii criteriilor de terminare.

Moderatorul este implicat activ n toate etapele inspeciei software,


mai puin n cea de refacere.
Lectorul este responsabil cu conducerea echipei n timpul ntlnirilor
de inspecie software, prin prezentarea unor uniti logice mici.
nregistratorul documenteaz toate defectele identificate n timpul
ntlnirilor de inspecie software. Aceast documentaie include locul unde a
fost identificat fiecare defect, o scurt descriere a sa, precum i numele
inspectorului care l-a gsit. n plus, fiecare defect este asociat unei categorii
i unui tip de defecte. Toate defectele sunt nregistrate ntr-o list de
defecte.
Independent de rolul atribuit, toi membrii echipei au i rolul de
inspectori. Rolul de inspector este responsabil cu analiza produsului i
identificarea defectelor sale. n funcie de aspectele urmrite, inspectorii pot
avea urmtoarele roluri [Dona**]:
arhitect de software, cu rolul de a inspecta proiectul din punct de
vedere al calitii i conformitii cu arhitectura software;
programator, cu rolul de a inspecta componentele software din
punct de vedere al conformitii cu standardele de codificare;
inginer de testare, cu rolul de a inspecta produsul software pe
componente i integrat;
inginer de calitate, cu rolul de a inspecta componentele software
din punct de vedere al calitii i conformitii cu conveniile
utilizate;
inginer de securitate, cu rolul de a inspecta componentele
software din punct de vedere al cerinelor i mecanismelor de
securitate;
reprezentantul utilizatorilor, cu rolul de a inspecta interfaa cu
utilizatorul din punct de vedere al facilitilor de utilizare.
n funcie de pregtirea pe care o are, un inspector poate ndeplini
unul sau mai multe din aceste roluri. Important este ca un anumit aspect s
fie urmrit de o singur persoan, s nu existe suprapuneri n activitatea de
inspecie i, n acelai timp, echipa s nu fie prea numeroas.
Fiecare inspector va ntocmi un raport de inspecie cu cele constatate.
Aceste rapoarte vor sta la baza ntocmirii raportului final, care va
concluziona cele constatate de ntreaga echip.
Echipa realizeaz inspecia software pe parcursul unui set de etape
predefinite, a cror nlnuire este prezentat n figura 34.3.

Produsul
inspectat

Etapa de
planificare

Pachet de
inspecie

Etapa de
prezentare

Lista de
defecte

Etapa de
pregtire

Nota de
ntlnire

Etapa de
ntlniri

Raport
intermediar
de inspecie

Etapa de
refacere

Etapa de
verificare

Produsul
revizuit

Produsul
verificat

Raport final
de inspecie

Figura 34.3 Etapele inspeciei software.


Pentru unele produse software se definete un singur tip de inspecie,
n timp ce pentru alte produse sunt necesare mai multe tipuri de inspecie.
Un produs software are tipuri diferite de inspecie pentru specificaii, design,
cod surs. Fiecare tip de inspecie este unic i complet definit de apte
parametrii, prezentai n figura 34.4.

Scop

INSPECIE
Criterii
de intrare

Liste verificare
Participani
Proceduri

Criterii
de ieire

Clasificarea
defectelor

Figura 34.4 Parametrii inspeciei


Scopul descrie ceea ce va realiza inspecia. Sopul general al inspeciei
este verificarea produsului software.
Criteriile de intrare se refer la datele de intrare pentru etapa de
ntrunire. Acestea includ produsul software asupra cruia se va realiza
inspecia, cerinele i specificaiile produsului, precum i alte rapoarte
necesare. Criteriile minime de intrare:
codul produsului codul a fost compilat cu succes, far mesaje de
eroare;
documentaia produsului ortografie corect.
Listele de verificare
conin indicaii i recomandri adresate
inspectorilor pentru a gsi cu uurin defectele produsului analizat.
Participanii sunt reprezentai de persoanele care vor ndeplini
procesul de inspecie.
Procedurile explic cum trebuie abordat fiecare tip de inspecie.
Clasificarea defectelor descrie ceea ce trebuie asociat cu un defect i
cum se nregistreaz acestea. De obicei exist trei caracteristici care sunt
atribuite unui defect: tipul, circumstana i gradul de impact. Aceste
caracteristici sunt denumite tipul defectului, clasa i gravitatea. mpreun
acestea ajut la procesul de identificare a defectelor i la transmiterea
rezultatelor inspeciei.
Criteriile de ieire indic inspectorilor momentul n care procesul de
inspecie este terminat. Cel mai ntlnit criteriu de ieire este faptul c toate
defectele identificate n etapa de ntrunire sunt corectate. Criteriile de ieire
includ, de obicei, produsul software verificat i corectat, precum i un raport
de inspecie.
Scopul principal al procesului de inspecie software este de a elimina
defectele dintr-un anumit produs. Produsul poate fi: specificarea cerinelor,
manualul de proiectare sau module program. Pentru a determina dac un
produs este pregtit pentru inspecie se folosesc anumite criterii de intrare.
Sfritul inspeciei se realizeaz la atingerea criteriilor de terminare a
procesului. Criteriile de intrare depind de fiecare produs n parte, dar n

general se refer la a determina dac produsul este suficient de matur


pentru a fi folosit dup nlturarea defectelor. n general, criteriile de ieire
trebuie s asigure c au fost nlturate toate defectele identificate n timpul
procesului de inspecie. Este posibil ca dintre aceste criterii s se exclud
corectarea defectelor minore, care nu au impact negativ n utilizarea
produsului.
Indiferent ce este produsul analizat, procesul de inspecie va urma
aceleai etape. Aceste etape sunt prezentate n tabelul 34.1, [Fran**].
Tabelul nr. 34.1 Etapele procesului de inspecie
Etapa
Planificare
Prezentare
Pregtire
ntlniri
Refacere
Verificare

Descriere
Identificarea produsului de inspectat i stabilirea
planului de inspecie
Etap opional n care membrii echipei care nu
sunt familiarizai cu produsul de inspectat se
familiarizeaz cu problema
Membrii echipei inspecteaz individual produsul
pentru gsirea defectelor
Membrii echipei se ntlnesc pentru a discuta
posibilele defecte gsite
Produsul este revizuit conform cerinelor i
specificaiilor
Produsul refcut este verificat, datele finale ale
inspeciei sunt colectate i centralizate i procesul
de inspecie este nchis

34.5 Planificarea inspeciei software


Exist prerea c inspeciile sunt realizate n ultimul moment astfel
nct produsul poate fi considerat terminat i livrat clientului sau trecut n
urmtoarea etap de dezvoltare. Nu aceasta este realitatea atta timp ct
inspeciile trebuie planificate cu grij i membrii echipei trebuie coordonai
pentru atingerea scopului comun.
Primul pas n iniierea unei inspecii apare atunci cnd autorul
consider c produsul ndeplinete toate condiiile de intrare n etapa de
planificare.
Autorul
notific
acest
lucru
moderatorului
i
este
responsabilitatea acestuia s verifice ndeplinirea condiiilor. Criteriile de
intrare specific materialele ce vor fi inspectate i condiiile pe care acestea
trebuie s le ndeplineasc. Dac aceste criterii nu sunt ndeplinite,
moderatorul informeaz autorul despre ce mai trebuie fcut pentru
satisfacerea lor. Aceast verificare este important pentru procesul de
inspecie deoarece ofer o garanie c produsul se afl ntr-o etap potrivit
pentru nceperea inspeciei.
Dup verificarea criteriilor de intrare, moderatorul selecteaz membrii
echipei i le atribuie roluri. Dup alegerea acestora, moderatorul poate
determina dac inspectorii dein suficiente informaii despre produs pentru
ndeplinirea rolurilor atribuite. Dac acest lucru nu este realizat, se va
programa o ntlnire de prezentare pentru ca inspectorii s primeasc toate
informaiile necesare.

Scopul principal al etapei de planificare este de a asigura eficiena


necesar procesului de inspecie. Pentru ndeplinirea acestui obiectiv, n
etapa de planificare trebuie avute n vedere urmtoarele aspecte:
echipa de inspecie va fi format din urmtoarele persoane:
moderator, nregistrator, inspector, lector i autor;
produsul ce urmeaz a fi inspectat va fi ngheat pe durata
procesului de inspecie;
ntlnirile echipei nu vor depi dou ore;
managerii de proiect vor identifica punctele de verificare n planul
proiectului pentru conducerea inspeciei;
lista de verificare va fi folosit de inspectori pentru a ajuta la
identificarea defectelor;
trebuie asigurate cel puin dou ore de pregtire;
la un moment dat se vor inspecta maximum 20 pagini ale
produsului;
autorul nu va ndeplini rolul de moderator, lector sau
nregistrator.
n tabelul 34.2 se prezint scopul, activitile i rolurile procedurale
ale etapei de planificare a inspeciei software.
Tabelul nr. 34.2 Activitile i rolurile etapei de planificare
Scop
Intrri

Organizarea i planificarea resurselor;


Forma final a produsului;
Materiale suport;
Criterii de demarare a inspeciei;
Datele de inspectat (dac sunt disponibile);

Sarcini

Verificarea criteriilor de demarare;


Selectarea echipei;
Determinarea necesitii unei prezentri;
ntocmirea planului de inspecie;
Completarea i distribuirea pachetului de inspecie;

Msurtori

Planificarea efortului;
Dimensiunea produsului;
Definitivarea i distribuirea pachetului de inspecie
Planificarea unei prezentri (opional).
Moderatorul:
verific ndeplinirea criteriilor de demarare;
selecteaz membrii echipei de inspecie;
decide dac este necesar organizarea unei
prezentri;
planific ntlnirile i ntocmete notele de
ntlnire;
Autorul:
revede produsul cu moderatorul;
propune membrii echipei;
recomand, dac este cazul, o ntlnire de
prezentare;

Ieiri
Roluri

34.6 Etapa de prezentare


Obiectivul tuturor inspeciilor software este de a analiza produsul i
de a detecta i nltura defectele. Pentru aceasta este necesar un anumit
nivel de cunoatere a produsului. Dac moderatorul consider c inspectorii
nu dein suficiente cunotine, poate organiza o ntlnire de prezentare
pentru punerea lor n tem. Autorul va explica funcionalitatea produsului i
va face o descriere a tuturor tehnicilor i conveniilor folosite.
n tabelul 34.3 se prezint scopul, activitile i rolurile procedurale
ale etapei de prezentare.
Tabelul nr. 34.3 Activitile i rolurile etapei de prezentare
Scop
Intrri

Informare;
Produsul de inspectat;
Prezentarea materialului;

Sarcini

Msurtori
Ieiri

Roluri

Moderatorul conduce ntlnirea de prezentare;


Autorul prezint produsul;
Timpul de pregtire necesar autorului;
Timpul necesar ntlnirii;
nelegerea mai bun a produsului;
Atribuirea responsabilitilor;
Pachetul de inspecie a produsului;
Materialul ntlnirii de prezentare;
Nota de ntlnire;

Moderatorul:
conduce ntlnirea astfel nct s maximizeze
schimbul de informaii;
dac este necesar, atribuie responsabiliti (teste,
standarde, claritate) pentru fiecare inspector;
Autorul:
pregtete ntlnirea;
prezint materialul;
rspunde la ntrebri;
Inspectorul:
pune ntrebri pentru o mai bun nelegere a
produsului;
nu revizuiete i nu discut alternative de
proiectare/implementare;

34.7 Etapa de pregtire a inspeciei


Aceasta este cea mai important etap a procesului de inspecie. n
timpul etapei de pregtire, fiecare membru al echipei examineaz individual
produsul, cutndu-i defectele. Pentru o mai bun analiz a produsului,
fiecrui inspector i sunt atribuite anumite categorii de defecte.
De notat c defectele nu apar doar n etapa de codificare a
proiectului. Cele mai multe produse software trec prin mai multe etape i
produc diferite produse. De cte ori rezult un produs exist posibilitatea de

apariie a unor defecte. Pentru fiecare din aceste produse procesul de


inspecie trebuie s prevad o list de verificare care descrie defectele fazei
respective. n tabelul 34.4 se prezint un exemplu cu cerinele listei de
verificare.
Tabelul nr. 34.4 Cerinele listei de verificare
Categoria de
defecte
Claritate
Standarde
Completitudine
Nivelul de detaliu
Consisten
Funcionalitate

Performan
Testabilitate
Fezabilitate

Maleabilitate
Capacitate de
modificare

ntrebri
Cerinele sunt clare si neambigue?
Toate cerinele au standarde ce trebuie
urmrite?
Toate cerinele sunt complete?
Cerinele sunt independente de proiectare?
Cerinele sunt consistente?
Datele sunt structurate i funciile au nume i
sunt folosite consistent?
Funciile sunt corect specificate?
Intrrile i ieirile sunt specificate clar?
Funciile sunt logic independente i formeaz
o structur bine organizat?
Sunt clar definite cerinele de performan
pentru sincronizare, folosirea memoriei i a
resurselor?
Exist cerine pentru testare i verificare?
Fiecare component poate fi implementat cu
tehnicile,
instrumentele,
resursele
i
personalul disponibil i cu restriciile de
costuri i planificare specificate?
Cerinele sunt unic identificate?
Cerinele sunt unic structurate astfel nct
orice modificare necesar s poat fi fcut
uor, complet i consistent?

Toate defectele posibile identificate de inspectori n etapa de


pregtire trebuie nregistrate. De notat c n acest moment acestea nu vor fi
considerate defecte ale produsului. Fiecare inspector va nregistra
potenialele defectele gsite, dar numai n etapa de ntlnire se va stabili
care dintre ele sunt defecte reale i care nu.
n tabelul 34.5 se prezint scopul, activitile i rolurile procedurale
ale etapei de pregtire.
Tabelul nr. 34.5 Activitile i rolurile etapei de pregtire
Scop
Intrri
Sarcini

nelegerea produsului i identificarea potenialelor


defecte;
Pachetul de inspecie;
nelegerea produsului;
Materialul revizuit al inspeciei;
Studierea produsului;
Identificarea defectelor:

Dac este necesar o inspecie a cerinelor, se


folosete lista de inspecie a cerinelor
software;
Dac este necesar o inspecie la nivelul
proiectrii de detaliu, se folosete lista de
inspecie a proiectrii detaliate;
Dac este necesar inspecia codului, se
determin dac exist o list pentru limbajul n
care este scris codul. Dac exist, se folosete
lista respectiv. Dac nu, se adapteaz lista
principal pentru inspecia codului la cerinele
limbajului de inspectat;
nregistrarea timpului de pregtire;
Completarea listei de defecte;
Timpul de pregtire;
Numrul de defecte;
Lista complet de defecte;
nregistrarea timpului de pregtire;

Msurtori
Ieiri
Roluri

Inspectorul:
studiaz produsul;
identific defectele;
nregistreaz timpul de pregtire;
Moderatorul:
realizeaz toate sarcinile de pregtire
inspeciei;
Lectorul:
stabilete cum s prezinte produsul;

34.8 Etapa de ntlnire


n etapa de ntlnire ntreaga echip este responsabil cu
identificarea defectelor produsului. Toi membrii echipei particip la aceste
ntlniri i prezint defectele identificate n etapa de pregtire.
n primul rnd moderatorul revizuiete agenda ntlnirii, prezint
participanii i rolurile lor. Apoi lectorul prezint inspectorilor produsul,
mprit n mici uniti logice (de exemplu: paragrafe de text, blocuri de
cod, etc.). Pe msura prezentrii unitilor logice, fiecare inspector caut
defectele din unitatea respectiv. Cnd sunt gsite posibile defecte, ntregul
grup discut dac reprezint sau nu defecte adevrate.
Este necesar realizarea unui consens n ceea ce privete fiecare
defect, deoarece uneori este posibil ca un potenial defect s fie o greeal
din partea inspectorului sau o nenelegere care poate fi clarificat de autor.
Cnd se realizeaz consensul n ceea ce privete un defect, lectorul l trece
n lista de defecte ntr-o manier clar i concis. Este important ca
moderatorul s menin atenia participanilor asupra detectrii defectelor i
s nu lase discuia s devieze spre ncercarea de a determina modul de
corecie a lor. Refacerea produsului se va realiza de ctre autor dup
ntlnirea de inspecie. Lista de defecte are rolul de a ajuta autorul s refac
produsul i este folosit n etapele ulterioare ale procesului de inspecie

pentru a se verifica c toate defectele identificate la ntlnirea de inspecie


au fost corectate.
Pentru folosirea judicioas a timpului, o ntlnire de inspecie trebuie
s urmeze o agend de lucru predefinit. Se recomand ca agenda s
conin urmtoarele capitole:
1. Introducere
2. Stabilirea pregtirii inspectorilor
3. Citirea produsului, identificarea i nregistrarea defectelor
4. Analiza defectelor
5. Determinarea dispoziiei produsului
Echipa de inspecie folosete lista de defecte pentru a determina
dispoziia produsului. Dispoziia se refer la procedura de folosit pentru
verificarea refacerii produsului de ctre autor. Robert Ebenau [Eben94]
propune urmtoarele trei categorii de dispoziii:
a) dispoziie de tip A: acceptarea produsului ca fiind complet, fr
nici o alt verificare a refacerii sale. Aceasta nu presupune c
produsul nu mai are nici un defect, ci c nu exist defecte care s
determine abaterea de la specificaiile sale i c sunt doar cteva
defecte minore care pot fi lsate la latitudinea autorului.
b) dispoziie de tip C: acceptarea condiionat a produsului, cu
verificarea refacerii de ctre moderator, mpreun cu autorul.
Aceasta este situaia cnd exist unele defecte majore, puine la
numr, iar corectarea lor nu va duce la modificri majore n
premisele de proiectare ale produsului.
c) dispoziie de tip R: reinspectarea produsului refcut de autor.
Aceasta presupune ca produsul refcut s fie examinat de ctre
moderator, autor i cel puin un membru al echipei de inspecie
ntr-o nou ntlnire de inspecie. Acesta este cazul n care exist
un numr nsemnat de defecte majore, sau cnd refacerea va
modifica substanial premisele de proiectare ale produsului.
n tabelul 34.6 se prezint scopul, activitile i rolurile procedurale
ale etapei de ntlnire.
Tabelul nr. 34.6 Activitile i rolurile etapei de ntlnire
Scop
Intrri
Sarcini

Msurtori
Ieiri

Atingerea consensului privind defectele; verificarea


produsului;
Lista complet cu defecte;
Timpul de pregtire nregistrat;

Introducere;
Citirea produsului;
Identificarea i nregistrarea defectelor;
Rezumatul revizuirii defectelor;
Determinarea dispoziiei produsului;
Gradul de corectitudine a datelor;
Datele defecte;
Dispoziia produsului;
Lista complet de defecte;
Sumarul complet al defectelor;
Nota complet a ntlnirii de inspecie;
Raportul parial de inspecie;

Roluri

Autorul:
rmne un asculttor obiectiv i activ, ia
notie;
nu adopt o atitudine defensiv;
Inspectorii:
obiectivi i impersonali;
bine pregtii i participani activi;
nu trebuie s ridice probleme de stil i soluii
de dezvoltare;
Moderatorul:
ncepe i termin edina la timp, meninnd o
atmosfer adecvat;
urmrete meninerea sinergiei ntlnirii;
se asigur c defectele ntrunesc consensul
echipei;
Lectorul:
stabilete cea mai bun strategie de
prezentare a produsului;
rspunde la ntrebrile puse de membrii
echipei;
nregistratorul:
trebuie s fie familiarizat cu schema de
clasificare a defectelor;
nregistreaz toate datele n lista principal de
defecte;
sintetizeaz datele defecte n sumarul de
inspecie;

34.9 Etapa de refacere


Defectele identificate n etapa de ntlnire sunt revzute de autor n
etapa de refacere. Autorul folosete lista de defecte pentru a determina care
sunt acestea i unde sunt ele localizate. Cnd toate defectele sunt
rezolvate, autorul prezint din nou produsul moderatorului pentru
urmtoarea etap de inspecie.
Exist situaii n care un defect identificat de echipa de inspecie nu
este nlturat n aceast etap. Oricare ar fi motivul pentru care corecia
este amnat, decizia pentru acest lucru trebuie s aparin managerului de
proiect. Defectul va fi notat n sistemul de control al modificrilor proiectului
i va fi urmrit de managerul de proiect pn cnd va fi posibil de corectat
efectiv.
n tabelul 34.7 se prezint scopul, activitile i rolurile procedurale
din etapa de refacere.
Tabelul nr. 34.7 Activitile i rolurile etapei de refacere
Scop
Intrri

Corectarea i rezolvarea defectelor;


Lista complet cu defecte;
Timpul de pregtire nregistrat;

Sarcini

Msurtori
Ieiri

Roluri

Autorul:
rezolv toate defectele;
notific moderatorului terminarea refacerii;
raporteaz
managerului
de
proiect
defectele
nerezolvate;
Numrul de defecte;
Efortul de refacere;
Defecte rezolvate;
nregistrarea timpului de refacere n lista principal de
defecte;
Notificarea ctre moderator a terminrii refacerii;
Produsul revizuit;
Autorul:
rezolv defectele;
raporteaz
managerului
de
proiect
defectele
nerezolvate;
notific moderatorului terminarea refacerii;

34.10 Etapa de verificare a refacerii


Toate revizuirile produsului fcute de autor n etapa de refacere
trebuie verificate formal, deoarece este posibil ca autorul s fi provocat noi
defecte n ncercarea de corectare a defectelor identificate anterior. n
funcie de dispoziia atribuit produsului n timpul ntlnirilor de inspecie,
moderatorul decide:
pentru dispoziie de tip C (acceptare condiionat) moderatorul va
verifica reviziile fcute. El se va concentra att pe coninutul
reviziilor, ct i pe interfaa acestora cu restul documentului.
pentru dispoziie de tip R (reinspectarea produsului) se va relua
ntreg procesul de inspecie, dar cu focalizarea pe reviziile fcute
i pe interdependenele dintre ele.
Dup ce autorul realizeaz toate modificrile cerute i moderatorul
verific reviziile, acesta completeaz celelalte rapoarte de inspecie.
n tabelul 34.8 se prezint scopul, activitile i rolurile procedurale
din etapa de verificare.
Tabelul nr. 34.8 Activitile i rolurile etapei de verificare
Scop
Intrri
Sarcini

Terminarea i certificarea inspeciei;


Produsul revizuit, dac dispoziia sa a fost de tip C;
Produsul, dac dispoziia sa a fost de tip A;
Toate situaiile de inspecie;
Moderatorul verific produsul:
dac dispoziia produsului a fost de tip R, se
reia de la etapa de planificare;
dac dispoziia produsului a fost de tip C, se
verific refacerea i, dac este necesar, se reia
etapa de refacere;
dac dispoziia produsului a fost de tip A, se
certific inspecia;

Moderatorul completeaz raportul de concluzii;


Moderatorul transmite managerului de proiect
fiierul de inspecie;
Efortul de verificare;
Raportul de concluzii;
Produsul verificat;
Certificarea inspeciei;
Raportul complet de concluzii;
Fiierul de inspecie;
Autorul:
planific reinspecia, dac dispoziia produsului
a fost de tip R;
realizeaz i reface ceea ce a identificat
moderatorul;
Moderatorul:
ntocmete planul reinspeciei, dac dispoziia
produsului a fost de tip R;
examineaz refacerea mpreun cu autorul,
dac dispoziia produsului a fost de tip C;
completeaz raportul de concluzii;

Msurtori
Ieiri

Roluri

34.11 Elaborarea rapoartelor de inspecie


Sunt patru rapoarte standard care trebuie folosite cnd este
programat o ntlnire de inspecie, cnd sunt identificate defecte sau cnd
sunt pregtite rapoarte pentru manageri. n timpul procesului de inspecie,
aceste rapoarte constituie un mecanism de comunicare ntre membrii
echipei i manageri. Cele patru rapoarte standard, al cror flux este
prezentat n figura 34.5, sunt: nota de ntlnire, lista de defecte, raportul de
concluzii i raportul pentru management.
Avizul de
ntlnire

INSPECIE

Lista de
defecte

Rezumatul
defectelor

Raportul de
management

Figura 34.5 Fluxul rapoartelor inspeciei.


Nota de ntlnire este ntocmit de moderator n etapa de planificare
i scopul su este de a informa membrii echipei despre inspecia care
urmeaz. De obicei raportul este trimis membrilor echipei ca parte a
pachetului de lucru, care include toate materialele necesare pentru

demararea etapei de planificare. Pachetul poate include produsul, nota de


ntlnire, lista de defecte i alte liste de control.
Nota de ntlnire trebuie s conin urmtoarele informaii ce vor fi
completate de moderator nainte de a fi transmise membrilor echipei:
antetul notei informaii administrative care includ: numele
proiectului, data distribuirii, activitatea, componenta, versiunea,
documentul, numele moderatorului;
tipul ntlnirii inspecie, reinspecie, mentenan;
tipul inspeciei cerine, proiectare de ansamblu, proiectare de
detaliu, cod;
programul ntlnirii data, durata, timpul de pregtire,
dimensiunea produsului;
membrii echipei de inspecie numele, categoriile de defecte
pentru care sunt responsabili, rolurile procedurale;
comentarii.
Lista de defecte este raportul folosit de inspectori n etapa de
pregtire. Raportul conine un antet i seciuni pe tipuri de ntlniri, la fel ca
nota de ntlnire descris anterior. Lista de defecte conine urmtoarele
informaii care ajut la nregistrarea defectelor identificate:
locaia este completat de inspectori n etapa de pregtire;
aceast zon este folosit pentru localizarea defectelor i conine:
numrul defectului: este numrul de identificare atribuit
defectului;
numrul de pagin: este numrul paginii produsului unde a
fost identificat defectul;
numrul de seciune: este numrul seciunii produsului unde a
fost identificat defectul;
numrul de paragraf: este numrul paragrafului unde a fost
identificat defectul;
numrul de propoziie: este numrul propoziiei unde a fost
identificat defectul;
descrierea defectului este completat de inspectori n etapa de
pregtire, ntr-o form scurt i concis.
defectul aceast zon este completat de moderator n etapa de
ntlnire. Este folosit pentru clasificarea defectelor i se compune
din:
acceptare D/N: se bifeaza D dac exist consensul
inspectorilor c defectul identificat reprezint un defect real;
tipul defectului: este un numr care identific categoria de
defecte din lista de control a inspectorilor;
clasa defectului: echipa de inspecie poate decide c defectul
a fost cauzat de ceva care lipsete, este eronat sau este din
afara produsului; suplimentar, echipa poate preciza nu este
sigur;
gravitatea defectului: se specific dac defectul este
considerat unul major, mediu sau minor, corespunztor
impactului potenial asupra produsului dac nu este nlturat;
timpul de refacere este completat de autor n etapa de refacere
i reprezint timpul necesar pentru remedierea defectului.
Raportul de concluzii este completat dup etapa de ntlniri. El
sumarizeaz tipurile, clasele i gravitatea tuturor defectelor identificate n

timpul ntlnirilor de inspecie i conine un antet i seciuni pe tipuri de


ntlniri, la fel ca n raportul de ntlniri descris anterior.
Raportul trebuie s conin, pentru fiecare categorie de defecte
(majore, medii i minore), descrierea consecinelor, prin specificarea clasei
de defecte: lips, eronat, din afara produsului, nu este sigur.
Raportul de management este completat de moderator la sfritul
etapei de ntlniri. Conine un antet i seciuni pe tipuri de ntlniri, la fel ca
la raportul de ntlniri prezentat anterior. Raportul conine urmtoarele
informaii care ajut la nregistrarea datelor de identificare i performan
ale procesului de inspectie software:
dispoziia se refer la procedura ce va fi folosit pentru
verificarea refacerii produsului de ctre autor; valorile posibile
sunt acceptare, acceptare condiionat i reinspecie.
informaii despre produs se nscriu urmtoarele informaii:
numrul de inspectori;
numrul total de pagini inspectate;
numrul total de defecte gsite;
cine a realizat refacerea;
informaii despre efortul total depus se descrie efortul n ore,
pentru urmtoarele activiti ale procesului de inspecie:
efortul pentru planificare;
efortul pentru pregtire;
efortul total;
durata ntlnirilor de inspecie;
efortul de completare a rapoartelor;
efortul de verificare;
timpul de pregtire pentru inspectori se prezint timpul necesar
fiecrui inspector pentru pregtire.
certificarea moderatorului este autorizarea dat de moderator
precum c procesul de inspecie a fost terminat.

34.12 Inspecia software automat


n ultima vreme se afirm tot mai mult tehnologiile de inspecie
software automat. Aceste tehnologii, livrate ca instrumente i servicii
comerciale, pot localiza multe erori de programare, erori care pot fi cauza
celor mai multe eecuri. Strategia acestor tehnologii este de a analiza codul
surs nainte de testarea sa i de a identifica potenialele probleme nainte
ca acestea s se manifeste ca bug-uri de programare. Cel mai important
aspect al inspeciei automate este capacitatea de depanare a codului nainte
de execuia sa. Ea este superioar testrii, care implic necesitatea
executrii codului i a construirii, ntreinerii i rulrii de seturi de date de
test.
Inspecia software automat reprezint o nou abordare bazat pe
utilizarea unor programe care realizeaz pri din acest proces. Ea este mult
mai eficient dect inspecia manual i asigur creterea productivitii n
activitatea de dezvoltare de software. Acest lucru se realizeaz prin:
reducerea costurilor, printr-o detectare mai puin costisitoare a
defectelor;
reducerea timpului, printr-o detectare mai rapid a defectelor;

creterea calitii, prin depistarea defectelor ce nu pot fi


identificate prin testare.
Unele instrumente folosite pentru inspecia software automat, cum
ar fi Flexelint de la Gimpel Software i QAC de la Programming Research,
verific respectarea standardelor de codificare i genereaz mesaje de
atenionare privind posibilele defecte. Unele instrumente genereaz un
volum mare de mesaje de atenionare care sunt fals pozitive. Cu alte
cuvinte, instrumentul crede c a gsit un defect, dar la o analiz mai
profund a contextului se dovedete c acesta nu constituie o problem
real. Toata lumea cunoate avertizrile nerelevante generate de
compilatoare. Aceast problem fals pozitiv este destul de sever ntr-un
instrument de inspecie automat. n Flexelint de exemplu, apar peste 50
mesaje fals pozitive la fiecare defect real.
n anumite cazuri mesajele fals pozitive pot fi eliminate prin crearea
de filtre capabile s nlture automat un subset de astfel de mesaje. Totui
este necesar un proces manual pentru a elimina mesajele fals pozitive
nereinute de filtru. Dezvoltatorii au nevoie de un mijloc de a evalua fiecare
mesaj de atenionare pentru a determina dac este ntr-adevar un defect
sau un mesaj fals pozitiv. Pentru a folosi efectiv instrumente de inspecie
automat, firmele dezvoltatoare de software trebuie s angajeze sau s
pregteasca experi n inspecie i s implementeze o metodologie pentru
evaluarea i nlturarea mesajelor fals pozitive, pentru a fi sigure c
rezultatele inspeciei automate conin defecte reale i nu un numr mare de
mesaje fals pozitive.
Pentru asigurarea unui beneficiu maxim, inspecia automat trebuie
realizat imediat dup terminarea etapei de codificare i nainte ca produsul
s intre n testare. Deoarece inspecia automat nu necesit ca aplicaia s
fie compilat, programele pot fi inspectate chiar nainte de integrarea lor cu
alte componente ale aplicaiei.
Multe din eforturile de dezvoltare din zilele noastre sunt globale, cu
echipe aflate n diferite locaii lucrnd n etapa de codificare. Cu inspecia
automat firmele de software pot asigura calitatea fiecrui segment de cod
n fiecare etap a procesului de dezvoltare. Fiecare component poate fi
inspectat independent, apoi poate fi inspectat i integrarea
componentelor. Rezultatul va fi o soluie curat de la nceput pn la
sfrit.
O alternativ la utilizarea direct a unor instrumente de inspecie
automat este apelarea la serviciile unei firme specializate. Astfel, compania
Reasoning Inc. pune la dispoziie servicii prin care se identific
vulnerabilitile i defectele din programe realizate n limbajele C, C++ i
Java:
zonele sensibile la accese neautorizate sau atacuri din afar;
defecte care pot cauza cderi ale sistemului, coruperea datelor
sau comportri anormale ale programelor;
secvene de cod nefolosite sau incomplete, care pot avea impact
negativ asupra integritii aplicaiei i a costurilor cu mentenana.
Realizarea unui produs software presupune un consum apreciabil de
resurse umane i financiare. Este necesar ca produsul s fie realizat n
timpul planificat, cu nivelul de calitate stabilit prin cerinele formulate i n
limita bugetului alocat [Ivan05]. Nici o echip de dezvoltare software nu i
poate permite ca produsul livrat s manifeste defecte i/sau vulnerabiliti

n faza de exploatare curent. nlturarea lor n acest moment ar fi deosebit


de costisitoare, att n plan financiar ct i n cel al imaginii.
Inspecia software reprezint cea mai eficient tehnic utilizat n
scopul asigurrii unei nalte caliti a produselor realizate. Este un proces de
revizuire tehnic realizat de-a lungul etapelor de dezvoltare a produselor
software, cu scopul de a identifica i nltura defectele. Ea se poate aplica
oricrui produs rezultat n diversele etape ale procesului de dezvoltare: de
la determinarea cerinelor, pn la etapa de testare. Scopul su este de a
identifica i nltura defectele nc din primele etape ale procesului de
dezvoltare, tiind c remedierea lor n etapele ulterioare poate duce la
creterea costurilor de cteva zeci de ori.
Alturi de testarea convenional, inspecia automat permite
proiectelor software s obin beneficiile combinrii metodelor tradiionale
cu cele noi ntr-un mod echilibrat i cu costuri reduse, astfel nct s se
asigure o calitate superioar produselor software.

32. GRADUL DE OCUPARE A ZONEI DE MEMORIE


32.1 Zone de memorie
Zona de memorie ocupat de structurile de date include:
- cmpul pentru informaia util n care se definesc cmpuri ce fac
obiectul prelucrrilor;
- cmpul pentru definirea de variabile pointer n vederea stabilirii
legturilor ntre componente.
Dac se noteaz:
- numrul de elemente din structura de date;
x
Liu
- lungimea zonei de memorie ocupat de informaia util dat ca
numr de baii;
Lgp
- lungimea zonei de memorie ocupat de o variabil pointer;
Lgcpl - lungimea zonei de memorie ocupat de variabila pointer spre
primul element al structurii de date.
Se obine indicatorul Gru gradul de utilizare a zonei de memorie, ca
raport ntre lungimea total a zonei de memorie ocupat de informaia util
n structura de date i totalul zonei de memorie prin care se definete
structura de date.
Toate relaiile sunt elaborate pentru luarea n calcul a reprezentrii pe
32 de bii pentru variabilele de tip ntreg.
Se consider codul surs pentru structura care definete informaia
util inclus n definirea elementelor din lista simpl, lista dubl, arbori,
graf, pentru care se calculeaz gradul de utilizare a zonei de memorie:
struct info{
int cod;
char nume[30];
char adresa[40];
int varsta;
float salariu;
};

n aceast structur variabila cod este utilizat pentru a identifica


fiecare element din colectivitatea creia i se asociaz o structur dinamica.
Cmpul cod joac rol de cheie unic n procedurile de cutare a elementelor
i de definire a nodurilor n structura de tip graf.

32.2 Zone de memorie asociate listelor


n cazul listei simple cu x elemente, gradul de utilizare este dat de
relaia:
Gru=

x Liu
x ( Lgp Liu ) Lgcpl

(32.1)

Pentru lista simpl cu 142 de elemente definite prin:

struct element{
info * inf;
element * urm;
};

rezult:
x
Liu

numrul de elemente din structura de date este 142;


lungimea zonei de memorie ocupat de informaia util este 78
baii;
lungimea zonei de memorie ocupat de o variabil pointer este
Lgp
2 baii;
Lgcpl lungimea zonei de memorie ocupat de variabila pointer spre
primul element al structurii de date este de 2 baii.
i rezult c gradul de utilizare pentru lista simplu nlnuit are valoarea:
Gru=

142 78
=0.9748
142 (78 2) 2

(32.2)

n cazul listei duble n care apare n plus variabila pointer pentru


referirea elementului precedent, numit prec gradul de ocupare a zonei de
memorie este dat de relaia:
Gru=

x Liu
x (2 Lgp Liu ) 2 Lgcpl

(32.3)

Dac se consider lista dubl avnd elementele definite prin:


struct element{
info * inf;
element * urm;
element * prec;
};

rezult:
x
Liu
Lgp
Lgcpl

numarul de elemente din structura de date este 142;


lungimea zonei de memorie ocupat de informaia util este 78
baii;
lungimea zonei de memorie ocupat de o variabil pointer este
2 baii;
lungimea zonei de memorie ocupat de variabilele pointer spre
primul i spre ultimul element al structurii de date este n total de
4 baii, onsiderai pointeri spre capul de lista dubl, respectiv
pointer spre sfarit de lista dubl.

Se obine gradul de utilizare pentru lista dublu nlnuit care are


valoarea:
Gru=

142 78
=0.9508
142 (78 4) 4

(32.4)

32.3 Zone de memorie asociate arborilor


n cazul arborilor binari cu x elemente, gradul de utilizare este dat de
relaia:
Gru=

C++:

x Liu
x (2 Lgp Liu ) Lgcpl

(32.5)

Pentru arborele binar ale crui noduri sunt definite prin secvena
struct element{
info * inf;
element * fius;
element * fiud;
};

avnd 142 de noduri gradul de utilizare a memoriei este:


Gru=

142 78
=0.9510
142 (4 78) 2

(32.6)

Dac arborele binar este organizat pe m niveluri, iar pe fiecare din


cele K niveluri se afl ni noduri, numrul total de noduri NrN din structura
arborescent, dat de relaia:
K

NrN ni

(32.7)

i 1

Se determin gradul de utilizare a zonei de memorie n arborele binar


organizat pe niveluri ca fiind:
Gru=

NrN Liu
NrN ( Liu 2 Lgp ) Lgcpl

(32.8)

Pentru un arbore binar, n care nodurile sunt definite prin secvena de


program C++:
struct nod{
info * inf;
element * fiu_stang;
element * fiu_drept;
};
se consider:
- numrul de noduri din arborele binar este 7 noduri;
x
Liu
- lungimea zonei de memorie ocupat de informaia util a unui
nod este de 78 baii;
Lgp
- lungimea zonei de memorie ocupat de variabilele pointer
asociate nodului descendent stng i nodului descendent drept

este n total de 4 baii;


- lungimea zonei de memorie ocupat de variabila pointer spre
nodul rdcin este de 2 baii;
ceea ce conduce la valoarea gradului de ocupare:
Lgcpl

Gru=

NrN Liu
NrN ( Liu 2 Lgp) Lgcpl

(32.9)

Rezult c valoarea gradului de utilizare este:


Gru=

7 78
=0.9479
7 (78 2 2) 2

(32.10)

Pentru structura de date arbore B de grad m cu x noduri n care


structura unui nod este reprezentat n figura 32.1:
Informaie
util 1

Informaie
util 2

Informaie
util 2*m

descendent
stng

descendent
drept

Figura 32.1 Modelul grafic al elementului dintr-un arbore B

Figura 32.2 Reprezentarea grafic a arborelui B


Pentru arborele B din figura 32.2 gradul de utilizare al memoriei este:

Gru=

x 2 m Liu
x (2 m Liu 2 Lgp) Lgp

(32.10)

Arborele B de ordin m are noduri ce conin:


- pointer spre descendenii corespunztori informaiei utile;
- informaie util;
aa cum sunt definii n capitolul 13 de arbori B. Dac se consider definirea
nodului din capitolul 13, atunci pentru un arbore B a carui structur este:

struct element{
info * inf[2*m];
element * desc[2*m+1];
};

gradul de utilizare a memoriei se calculeaz:


Gru=

x 2 m Liu
x 2 m Liu x (2 m 1) Lgp Lgcpl

(32.11)

rezult pentru arborele B de gradul 1:


- numarul de noduri din arborele B este 9;
x
Liu
- lungimea zonei de memorie ocupat de informaia util a unui
nod este de 78 baii;
- lungimea zonei de memorie ocupat de variabilele pointer
Lgp
asociate nodurilor descendente este 2 baii;
Lgcpl - lungimea zonei de memorie ocupat de variabila pointer spre
nodul rdcin este de 2 baii.
ceea ce conduce la valoarea gradului de ocupare:

Gru=

9 2 1 78
= 0.9499
9 2 1 78 9 (2 1 1) 2 2

(32.12)

32.4 Zone de memorie asociate grafurilor


Pentru structura dinamica graf definit ca list de liste, cu o list
principal asociat celor n noduri ale grafului i, respectiv, tot attea liste
secundare, fiecare avnd mi elemente corespunztoare arcelor incidente
spre exterior nodurilor din graf. Se consider graful din figura 32.3.

Figura 32.3 Structur de tip graf

Pentru acest graf se construiete lista de liste din figura 32.4.

pb

Figura 32.4 Reprezentarea grafului prin liste de liste


Gradul de utilizare a zonei de memorie dintr-o structur de tip graf
reprezentat ca list de liste n care:
- numrul de noduri din graf;
x
Liu
- lungimea zonei de memorie ocupat de informaia util din
fiecare nod i care se memoreaz n lista de baz; informaia
util conine obligatoriu codul nodului n raport cu care se
realizeaz identificarea arcelor incidente spre exterior;
- lungimea zonei de memorie din lista de baz ocupat de
Lgps
variabila pointer de 2 baii ce reprezinta pointer spre primul
element al listei secundare asociat arcelor descendente spre
exterior nodului;
Lgpb
- lungimea variabilei pointer de 2 baii care refer primul
element din lista de baz;
Lgpp
- lungimea asociat spre urmatorul element din lista principal
de 2 baii;
Lgspu - lungimea pointer-ului spre elementul urmtor din lista
secundar;
Lgcd
- lungimea codului pentru identificarea nodului destinaie;
Narc
- numarul total de arce din graf.
GruLP=

x Liu
x ( Liu Lgpp Lgps ) Lgpd

GruLS=

Narc L gcd
Narc ( L gcd Lgspu )

(32.13)

(32.14)

Gru=

GruLP GruLS
2

(32.15)

Pentru graful din figura 32.3 ale crui noduri sunt definite n lista de
baz prin secvena C++:
struct elemente_Lista_Baz{
info * inf;
elementLS * arc;
elementLP * urm;
};

i urmtoarea structur pentru lista de arce:


struct elementLS{
info * inf;
elementLS * urm;
};

rezult:
x
Liu
Lgps

Lgpb
Lgpp
Lgspu
Lgcd
Narc

numrul de noduri din graf este 6;


lungimea zonei de memorie ocupat de informaia util din
fiecare nod este 78;
lungimea zonei de memorie din lista de baz ocupat de
variabila pointer de 2 baii ce reprezint pointer spre primul
element al listei secundare asociat arcelor descendente spre
exterior nodului;
lungimea variabilei pointer de 2 baii care refera primul element
din lista de baz;
lungimea asociat spre urmtorul element din lista principal
de 2 baii
lungimea pointer-ului spre elementul urmtor din lista
secundar de 2 baii;
lungimea codului pentru identificarea nodului destinaie este de
4 baii;
numarul total de arce din graf este 10
GruLP=

6 78
=0.9473
6 (78 2 2) 2

GruLS=

Gru=

10 4
=0.6667
10 (4 2)

0.9473 0.6667
=0.807
2

(32.16)

(32.17)

(32.18)

n cazul reprezentrii grafurilor prin vectori de pointeri gradul de


utilizare al zonei de memorie se calculeaz diferit.
Pentru graful definit n figura 32.3, reprezentarea prin vectori de
pointeri este:

PVEC

Figura 32.5 Reprezentarea prin vectori de pointeri


rezult c trebuie luate n considerare urmtoarele variabile:
x
- numarul de elemente din vectorul de pointeri;
Lgve
- lungimea unui element din vectorul de pointeri care se obine
din lungimea informaiei utile Liu la care se adaug pointerul
spre capul de list a arcelor incidente spre exterior nodului, adica
Lgve=Liu + Lgp;
Lgpv
- lungimea variabilei pointer de 2 baii ctre vectorul de baz;
Lgspu - lungimea pointer-ului spre elementul urmtor din lista
secundar de 2 baii;
Lgcod - lungimea zonei de memorie n care se stocheaz codul nodului
destinaie;
Lgpa
- lungime pointer din lista de arce;
Narc
- numarul de arce din graf.
Gradul de ocupare a zonei de memorie se obine din relaia:
GruVP=

GruLS=

x Lgve
x Lgve Lgpv

(32.19)

Narc L gcd
Narc ( L gcd Lgspu )

Gru=

GruVP GruLS
2

(32.20)

(32.21)

Pentru graful dat n figura 32.3 cu urmtoarea structur:


struct elementVP{
info * inf;
elementLS * urm;
};

i urmtoarea structur pentru lista de arce:

struct elementLS{
info * inf;
elementLS * urm;
};

rezult ca pentru:
x
- numarul de elemente din vectorul de pointeri egal cu 6;
Lgve
- lungimea unui element din vectorul de pointeri care se obine
din lungimea informaiei utile Liu la care se adaug pointerul spre
capul de lista a arcelor incidente spre exterior nodului, adic
Lgve=Liu + Lgp = 80 de baii;
Lgpv
- lungimea variabilei pointer de 2 baii ctre vectorul de baz;
Lgspu - lungimea pointer-ului spre elementul urmtor din lista
secundar de 2 baii;
Lgcod - lungimea zonei de memorie n care se stocheaz codul nodului
destinaie egal cu 4 baii;
Lgpa
- lungimea pointerului din lista de arce de 2 baii;
Narc

- numrul de arce din graf este 10;

gradul de utilizare este:


GruVP=

6 80
=0.9958
6 80 2

(32.22)

GruLS=

10 4
=0.6667
10 (4 2)

(32.23)

0.9958 0.6667
=0.8312
2

(32.24)

Gru=

Graful din figura 32.2 definit ca list de pointeri are modelul


reprezentat n figura 32.6.

<6,5>

<1,3>

<1,2>

<6,1>

cpl
NULL

NULL

NULL

<4,2>

NULL

<3,4>

<3,5>

NULL
NULL

<2,3>

<2,6>

<6,4>

Figura 32.6 Reprezentarea grafului prin list de pointeri


Se iau n considerare variabilele:
- numarul de noduri din graf;
x
- lungimea zonei de memorie ocupat de informaia util din
Liu
fiecare nod i care se memoreaz n lista de baz; informaia util
conine obligatoriu codul nodului n raport cu care se realizeaz
identificarea arcelor incidente spre exterior;
- lungimea zonei de memorie din lista de baz ocupat de
Lgps
variabila pointer de 2 baii ce reprezint pointer spre primul
element al listei secundare asociat arcelor descendente spre
exterior nodului;
Lgpb
- lungimea variabilei pointer de 2 baii care refera primul element
din lista de baz ;
Lgpp
- lungimea asociat spre urmtorul element din lista principal de
2 baii;
Lgspu - lungimea pointer-ului spre elementil urmtor din lista
secundar;
Lgcd
- lungimea pointerului pentru identificarea nodului destinaie;
Narc
- numarul total de arce din graf.

GruLP=

x Liu
x ( Liu Lgpp Lgps ) Lgpb

GruLS=

Narc Lgpd
Narc ( Lgpd Lgspu )

(32.25)

(32.26)

De exemplu, fie urmtoarea structur:


struct elementLP{
info * inf;
elementLS * arc;
elementLP * urm;
};

i urmtoarea structur pentru lista de arce:


struct elementLS{
elementLP * urm;
elementLS * arc;
};

rezult c pentru:
- numrul de noduri din graf este 6;
x
Liu
- lungimea zonei de memorie ocupat de informaia util din
fiecare nod i care se memoreaza n lista de baz; informaia util
conine obligatoriu codul nodului n raport cu care se realizeaz
identificarea arcelor incidente spre exterior este 78;
- lungimea zonei de memorie din lista de baz ocupat de
Lgps
variabila pointer de 2 baii ce reprezint pointer spre primul
element al listei secundare asociat arcelor descendente spre
exterior nodului;
Lgpb
- lungimea variabilei pointer de 2 baii care refer primul element
din lista de baz;
Lgpp
- lungimea asociat spre urmatorul element din lista principal de
2 baii;
Lgspu - lungimea pointer-ului spre elementul urmtor din lista
secundar este de 2 baii;
Lgcd
- lungimea pointerului pentru identificarea nodului destnaie; este
de 2 baii;
Narc
- numarul total de arce din graf este 10.
gradul de utilizare este:
GruLP=

6 78
=0.9473
6 (78 2 2) 2
10 2
= 0.5
10 (2 2)

(32.28)

0.9473 0.5
=0.7236
2

(32.29)

GruLS=

Gru=

(32.27)

Atunci cnd se dorete creterea vitezei de referire a elementelor prin


includerea de variabile pointer se va obine scderea gradului de utilizare a
memoriei
n cazul n care se dorete sortarea cresctoare i sortarea
descresctoare a elementelor unei liste simple cu memorarea vechilor poziii
ale elementelor, sunt necesari 3 pointeri cap de list:
- un pointer pentru a referi elementele cu vechea ordine;
- un pointer pentru a referi elementele dipuse n ordine cresctoare;
- un pointer pentru a referi elementele n ordine descresctoare.
n mod corespunztor, vor fi definii n fiecare element 3 pointeri
pentru a conserva legturile listei sortate cresctor i descresctor, precum
i legturile listei nesorate. Atunci:
Gru=

x Liu
(32.30)
x ( Liu 3 Lgp ) 3 Lgcpl

Pentru a gestiona modificarile din structura se definete o variabil


care are valoarea 0 daca nu s-a produs nici o modificare dup realizarea
sortrii i, respectiv, 1 n caz contrar. nainte de a se trece la o noua sortare
a elementelor din structur se va testa aceast variabil. n cazul n care
are valoarea 0 sortarea anterioar este considerat operaie valid i
elementele structurii sunt folosite ca atare. n cazul n care variabila este 1
se va trece la efectuarea unei noi sortri ncat structura a suferit modificari
n raport cu sortarea precedent.

32.5 Zone de memorie asociate masivelor


n cazul masivelor definite static cu x elemente, prin una din
instruciunile:
tip nume[x];
tip nume[m][n];
cu x = m*n, gradul de utilizare este:
Gru=

x Liu
=1
x Liu

(32.31)

Pentru masivele alocate dinamic, cu x elemente gradul de utilizare


este:
Gru=

x Liu
x Liu Lgcpl

(32.32)

n cazul unui masiv unidimensional cu 142 de elemente alocat


dinamic, avand modelul grafic din figura 32.7.

142

Figura 32.7 Masiv unidimensional alocat dinamic


avnd definirea:
int * vector = new int [142];

gradul de ocupare este:


Gru=

142 4
=0.9965 (32.33)
142 4 2

Masivul bidimensional alocat dinamic este caracterizat


variabilele:
Liu
- lungimea informaiei utile;
n
- numrul de linii;
m
- numrul de coloane;
Lgpm - lungimea pointerului ctre primul elment al matricii;
Lgpvc - lungimea pointerului ctre vectorii linie.
avnd definirea:

prin

int ** matrice;

matrice = new int [n];


for(i=0;i<n;i++)
matrice[i] = new int[m];
Gradul de utilizare al memoriei este dat de relaia:
Gru=

m n Liu
m n Liu n Lgpvc Lgpm

(32.34)

Pentru un masiv bidimensional alocat dinamic de 142 de elemente, cu


modelul grafic dat n figura 32.8.

1
2
3

142

Figura 32.8 Masiv bidimensional alocat dinamic


i cu definirea din secvena:
int ** matrice;
matrice = new int [71];
for(i=0;i<71;i++)
matrice[i] = new int[2];

gradul de utilizare este dat de relaia:


Gru=

2 71 4
=0.7977
2 71 4 71 2 2

(32.35)

Acest indicator, mpreun cu indicatorii de performanta ai aplicatiei


informatice privind comportamentul estimat al acesteia la utilizator
contribuie la completarea imaginii pentru fiecare varianta de program
bazata pe o anumita structura de date inclusa in el. Se prefera realizarea
unui echilibru intre dimensiunea resurselor utilizate, complexitatea
expresiilor de referire si viteza de acces la informatia solicitata de fiecare
beneficiar in parte.

S-ar putea să vă placă și