Capitole de Program Are Procedural A

download Capitole de Program Are Procedural A

of 119

Transcript of Capitole de Program Are Procedural A

  • 8/4/2019 Capitole de Program Are Procedural A

    1/119

    Prof.dr.Valer Roca ULBS 1

    Universitatea "Lucian Blaga" din SibiuFacultatea de tiine

    Prof.univ.dr. Valer Roca

    CAPITOLE

    DE

    PROGRAMARE PROCEDURAL

    Sibiu 2008

  • 8/4/2019 Capitole de Program Are Procedural A

    2/119

    Prof.dr.Valer Roca ULBS 2

    Prefa

    Prezenta lucrare, intitulat Capitole de programare procedural, se

    adreseaz studenilor de la specializarea Informatic, de la Facultatea de

    tiine, Universitatea Lucian Blaga din Sibiu.

    Lucrarea a fost conceput ca manual n sprijinul cursului de

    Programare procedural, care se pred n semestrul I al anului I, studiii de

    licen, pentru a acoperi cteva capitole fundamentale de programare.

    In elaborarea lucrrii s-a avut n vedere c exist o bogat literatura de

    specialitate, inclusiv cri on-line, n limba romni n limba englez, care

    pot s fie utilizate de studeni n studiul programrii C/C++, dar se simte

    nevoia, cel puin pentru o tematic de baz, s se adopte o tratare orientat,

    mai cu seam, spre tehnica programrii.

    Cele cinci capitole acoper aspectele fundamentale cu privire la

    programarea structurilor dinamice de date (cu referire la liste i arbori), la

    lucrul cu fiiere, inclusiv tratarea algoritmicii pentru probleme reprezentative,la modul n care se poate realiza prezentarea grafic a datelor (cu unele

    elememte de animaie), la controlul monitorului n modul consol, cu cteva

    elemente de programare a sunetelor, pentru a realiza o interfa atractiv a

    programelori, lucrarea se ncheie, cu un capitol de faciliti pe care le ofer,

    n plus, limbajul C++ pentru realizarea programrii procedurale.

    Desigur c

    lucrarea presupune cunotinele de baz

    pentru programare

    n limbalul C/C++ i, prin studiul ei, studenii pot aprofunda, ntr-un numr

    relativ redus de pagini, prile corespunztoare din programa disciplinei

    menionate. Lucrarea poate fi parcurs secvenial sau prin selecie, avnd la

    dispoziie un cuprins sub form de linkuri interne.

    Autor:Prof.univ.dr. Valer Roca

  • 8/4/2019 Capitole de Program Are Procedural A

    3/119

    Prof.dr.Valer Roca ULBS 3

    CUPRINS

    1. Structuri dinamice de date1.1 Pointeri

    1.1.1 Declararea i utilizarea pointerilor1.1.2 Operaii asupra pointerilor1.1.3 Echivalena indexrii i expresiei cu pointeri. Scderea pointerilor1.1.4 Pointerii i parametrii funciilor1.1.5 Declaraii cu pointeri i interpretarea lor

    1.2 Variabile dinamice1.3 Array-uri dinamice1.4 Liste nlnuite i arbori

    1.4.1 Liste nlnuite1.4.2 Arbori binari

    1.5 Realizarea operaiilor de baz pe liste nlnuite 1.5.1 Traversarea unei liste

    1.5.2 Inserarea ntr-o list1.5.3 Stergere ntr-o list1.5.4 Distrugerea unei liste

    1.6 Realizarea operaiilor de baz pe arbori binari de cutare1.6.1 Traversarea arborilor binari1.6.2 Cutarea n arbori binari1.6.3 Inserare n arbori binari de cutare

    1.7 Realizarea programelor care utilizeaz structuri dinamice1.8 Un exemplu de utilizare a structurilor dinamice

    2. Utilizarea fiierelor2.1 Tipuri de fiiere admise2.2 Implementarea lucrului cu fiiere

    2.2.1 Streamuri2.2.2 Controlul unui stream2.2.3 Modul de lucru cu streamul

    2.3 Citire din streamuri text2.3.1 Citire la nivel de caracter2.3.2 Citire la nivel de cmp2.3.3 Citire la nivel de linie

    2.4 Scriere n streamuri text2.4.1 Scriere la nivel de caracter2.4.2 Scriere la nivel de cmp

  • 8/4/2019 Capitole de Program Are Procedural A

    4/119

    Prof.dr.Valer Roca ULBS 4

    2.4.3 Scriere la nivel de linie

    2.5 Citire i scriere n streamuri binare2.6 Controlul sfritului de fiieri poziionarea n fiier2.7 Algoritmica lucrului cu fiiere

    2.7.1 Prelucrarea secvenial2.7.2 Controlul paginilor n realizarea rapoartelor

    2.7.3 Construirea fiierelor binare pentru aplicaii de eviden2.7.4 Indexarea fiierelor2.7.5 Prelucrare pe grupe de articole

    2.8 Un exemplu de aplicaie de lucru cu fiier binar

    3. Prezentarea grafic a datelor3.1 Regimul grafic al monitorului3.2 Utilizarea modului grafic3.3 Grafic n culori3.4 Vizor pentru desen3.5 Desen prin puncte i vectori3.6 Raportul de aspect3.7 Scrierea textelor n modul grafic3.8 Primitive pentru figuri3.9 Transformarea coordonatelor3.10 Elemente de animaie

    3.5.1 Aspecte generale3.5.2 Facilitii pentru animaie date de sistemul grafic

    4. Controlul monitorului i al difuzorului4.1 Regimul text al monitorului4.2 Faciliti de lucru cu texte n modul pagin

    4.2.1 Stabilirea unui mod text al consolei4.2.2 Utilizarea ferestrelor de scriere

    4.3 Faciliti de sunet

    5. Faciliti n C++ pentru programare procedural5.1 Declararea de articole

    5.2 Referine5.3 Operatorii de alocare i de eliberare5.4 Funcii cu argumente implicite5.5 Suprancrcarea funciilor5.6 Tipuri abstracte de date

  • 8/4/2019 Capitole de Program Are Procedural A

    5/119

    1. Structuri dinamice de date

    Limbajul C, prin mecanismul lucrului cu pointeri, ofer programatorilor facilitideosebite pentru construirea i utilizarea variabilelor dinamice, a listelori a arborilor.

    1.1 PointeriAa dup cum s-a artat ntr-un capitol anterior, pointerii sunt variabile de adres

    legate de un anumit tip de dat standard sau declarat de programator. Mecanismul utilizriipointerilor ofer o mai mare fexibilitate n folosirea memoriei, dect referirea direct prinvariabile statice. Figura 1.1 sugereaz modul n care, prin utilizarea adresei dat de unpointer, se acceseaz valoarea dintr-o alt locaie de memorie. Referirea locaiei se faceindirect i tehnica aceasta este denumitreferire prin indirectare.

    Adres Valoare

    numepointer

    Fig.1.1 Referirea locaiilor prin pointeri

    1.1.1 Declararea i utilizarea pointerilorDin punctul de vedere al limbajului, un pointer trebuie, mai nti, s fie declarat i apoi

    s fie ncrcat cu adresa locaiei, nainte de a putea fi utilizat. In secvenele care urmeaz, seutilizeaz pointeri la tipuri standard i la o structur de date.

    double x = 3.65, *px, z;int k = 5;struct {int *a;

    char t;

    double y;} s, *ps;px = &x;ps = &s;

    Prof.dr.Valer Roca ULBS 5

  • 8/4/2019 Capitole de Program Are Procedural A

    6/119

    Prof.dr.Valer Roca ULBS 6

    (*ps).y = 1.2;ps->a = &k;z = *px + ps->y + *(ps->a);

    Se remarc modul simplu de utilizare a pointeruluipx, sub forma *px, pentru a referivaloarea 3.65, din locaia variabilei x. In cazul utilizrii structurilor de date, limbajul oferforme alternative de scriere care sunt echivalente. In aceast secven, referirea la variabila

    y, de forma (*ps).y este echivalent cu formaps->y. Programatorii prefer forma ultimcare este mai uor de neles, mai ales atunci cnd apar indirectri n lan, aa cum este cazulcu referirea valorii la care trimite pointerul a: forma *(ps->a) este mai clar dect forma*((*ps).a).

    In aceste scrieri *i ->sunt considerai operatori de indirectarei, n expresii, ei sebucur de proprietatea de asociativitate la dreapta. De aceea, referirile de formap1->(p2->->(pn-1->(pn->a))) pot fi scrise, fr paranteze sub forma p1->p2->->pn->a idevin mai clare.

    1.1.2 Operaii asupra pointerilor

    Limbajul stabilete operaiile acceptate aupra variabilelor pointeri, avnd n vederestructura adresei de memorie ca un cuplu de forma (adres segment, deplasare n segment),aa cum este cazul modelelor de memorie cu segmente. De exemplu, o opera ie de adunare

    ntre doi pointeri nu este corect deoarece nu produce o adres valid, adunarea prilor deadres de segment ne avnd sens. Redm, n continuare, operaiile acceptate, cu modul descriere i semificaia acestuia.

    Adunarea/scderea unei constante ntregi pozitive se red prin expresiapointer ki este interpretat n raport de tipul pointerului. Adresa rezultat se determin

    sub formapointer k*sizeof(tip), unde tipeste tipul de care este legat pointerul.In particular, adunarea sau scderea luik=1 nu nseamn incrementare/decrementare, ci

    agugarea lungimii tipului. Atenie, deci, la forma de scriere i la semificaia expresieirespective ! In secvena:double x, *px, *pz;double *py;py = NULL;px = &x;px = px + 4;pz = px;

    se atribuie luipx adresa x, & fiind operatorul de adres. Apoi, noua valoare a luipx esteadresa lui x mrit cu 4*8 = 32i nu cu 4, cum s-ar putea crede din forma instruciunii deatribuire.

    Atribuirea de valoare are semificaie pentru oricare pointer, dac aceasta estevaloarea convenionaNULL, avnd semificaia de adres nul. In secvena de mai sus,pyprimete o astfel de valoare. Altminteri, unui pointer poate s i se atribuie numai adresa uneivariabile de acelai tip sau valoarea unui pointer de acelai tip. In secvena de mai sus, xipx se refer la acelai tip double, de aceea atribuireapx = &x este corect. Din aceleai

    motiv, este corect i atribuireapz = px.

    Compararea se poate face cu valoarea NULL pentru oricare pointer, utiliznd

    operatorii =i !=. Altminteri, compararea are sens numai pentru pointeri de acelai tip i nexpresiile respective pot fi utilizai oricare dintre operatorii relaionali. Pentru secvena de mai

    sus, se pot scrie expresii relaionale de formapx != py ,pz > px etc.

    1.1.3 Echivalena indexrii i expresiei cu pointeri. Scderea pointerilor

  • 8/4/2019 Capitole de Program Are Procedural A

    7/119

    Prof.dr.Valer Roca ULBS 7

    Aa dup cum se tie, un array n C are o structur aparte, numele de variabil datacestuia fiind considerat un pointer constant la primul element. In aceste condiii, limbajul aintrodus, urmtoarea echivalen:

    tab[k] *(tab+k)unde tab este numele variabilei array, iar k este indice. Aplicnd regula de semificaie aexpresiei cu pointeri din dreapta, rezult modul de calcul al adresei i faptul c, n ambelecazuri, este referit acelai element, dar prin forme diferite de scriere.

    Echivalena este valabil i pentru array-uri n-domensionale, ns expresia, fiindfuncie de n indici i de dimensiunile declarate, aa cum rezult din modul de liniarizare,prezentat n capitolul referitor la acest tip de date, este mai dificil de utilizat

    Echivalena pune n evideni posibilitatea de a scdea doi pointeri de acelai tip,dac, n plus, ei au i aceeai adres de segment, aa cum este cazul a doi pointeri ncrcaicu adresele unor elemente din acelai array. Dac se presupune c q=&tab[k] ip=&tab[k+n], atunci se deduce succesiv:

    p-q = *(tab+k+n)- *(tab+k) = *(tab+k+n-tab-k) = n.

    Se observ cum diferena, n acest caz, produce ca rezultat un numr natural,

    reprezentnd numrul de componente care se gsesc n array ntre cele dou ranguri.Trebuie semnalat aici c pointerii de tip array fiind pointeri constani, nu pot fi

    modificai prin operaii de adunare cu constante i nici prin atribuire, dar pot s fie supui laacele operaii care nu le afecteaz valoarea.

    1.1.4 Pointerii i parametrii funciilorIn multe situaii, transferul parametrilor la funcii se realizeaz prin adres, adic prin

    intermediul pointerilor. Dup cum se tie, o astfel de metod de transfer este obligatorie ncazurile n care funcia trebuie s modifice valoarea parametrului actual corespunztor, dar ease utilizeaz uneori i n cazul stucturilor de date, pentru a evita multiplicarea datelor princopiere i ncrcarea suplimentar a timpului de execuie.

    In aceste condiii, protecia datelor i a pointerilor mpotriva modificrilor accidentale esteesenial. In scrierea funciilor care se gsesc n astfel de situaii, programatorul poate sutilizeze facilitile pe care le ofer limbajul pentru a declara date constane i pointericonstani, prin utilizarea corespunztoare a modificatoruli const. Modificatorul const poate fiplasat, ntr-o declaraie cu pointeri, n patru poziii, cu semificaia care este prezentat cuajutorul declaraiilor care urmeaz:

    int* pv = 100; // pointer non-const la data non-constint* const pv = 100; // pointer const la data non-constconst int* pv = 100; // pointer non-const la data constconst int* const pv = 100; // pointer const la data const

    Dac transferul prin adres este fcut pentru structuri voluminoase, n vedereaeliminrii copierilor costisitoare, parametrul actual se recomand a fi protejat declarndparametrul formal corespunztor ca o dat constant. Declaraia trebuie s fie precedat demodificatorul const, aa cum se observ n secvena care urmeaz, unde, n funcia sum(),arrayul x este declarat constant i orice ncercare de modificare a componentelor sale va fisemnalat ca eroare de ctre compilator.

    double sum(const double* x, int n){ int k;double s;

    for(s=1, k=0; k

  • 8/4/2019 Capitole de Program Are Procedural A

    8/119

    Prof.dr.Valer Roca ULBS 8

    }

    Pentru pointeri, la fel ca i pentru alte variabile, se utilizeaz modificatorul const,pentru a declara c valoarea pointerului respectiv este nemodificabil. Caracterul de pointerconstant, nu afecteaz posibilitatea de a modifica valoarea din locaia referit de pointer. Inschia de funcie care urmeaz, pointeriipp i chp sunt declarai constani, n vreme cepointerulpmnu. Astfel, atribuireapm=p este corect, n vreme ce atribuireapp=pp+4 va

    determina compilatorul s semnaleze o eroare de sintax. Atribuirile constantelor5.25i'a' sunt corecte, ele referindu-se la valoarea pe care o va conine locaia respectiv,referit prin pointer constant.

    void func(double const* pp, char const* chp, double* pm){ *pp = 5.25;*chp = 'a';pp = pp +4; /* Atribuire incorecta */ pm = pp; /* Atribuire corecta */}

    Se atrage atenia asupra poziiei modificatorului const n declararea de pointer constant !Dac s-ar face o declaraie de forma const double* pp, atunci valoarea din locaie esteconstant i nu poate fi modificati compilatorul ar semnala eroare la atribuirea *pp =5.25.

    Pentru cazul parametrilor array, problema proteciei pointerului respectiv esteautomat rezolvat, dac declaraia parametrului este de forma tip numearray[]. Inbaza echivalenei indexrii cu o expresie cu pointer, aa cum s-a discutat mai sus, existi posibilitatea de a declara ca parametru formal un pointeri acesta s primeasc, laapelul funciei, adresa dat de un pointer la un array, cu elemente de acelai tip. In acestcaz, se recomand s se declare parametrul formal ca un pointer constant, pentru aproteja adresa array-ului parametru actual. In secvena care urmeaz, n funciaprodscal() pointerulpp este automat protejat la modificare, n timp cepmnu este, dei

    ambii au corespondent, la apel, adrese de array.

    ......long prodscal(int pp[], int n, int* pm){long s;int k;s = 0;for(k = 0; k < n; k++)s = s + pp[k] * pm[k];

    return s;

    }.....void main(){ int a[5] = {3, 4, 2, 1, 7};int b[5] = {-4, 3, 2, 5, 6};long psc;psc = prodscal(a, 5, b);.......

    }

    Pentru protecie, este recomandabil ca prototipul funciei s se declare sub forma:

    long prodscal(int pp[], int n, int const* pm).

  • 8/4/2019 Capitole de Program Are Procedural A

    9/119

    Prof.dr.Valer Roca ULBS 9

    O atenie deosebit trebuie acordat situaiei n care o variabil pointer din apelatortrebuie s poat fi modificat printr-o funcie. La fel ca n cazul variabileleor obinuite, estenecesar un parametu formal la care s se aplice transferul prin adres. Un astfel deparametru trebuie declarat ca un pointer, adic el este unpointer la pointer.

    In secvena care urmeaz, este schiat o funcie la care se transfer un astfel depointer, presupunnd cazul unei stive nlnuite la care se terge un nod i, prin aceastoperaie pointerul pentru topul stivei se modific. Deoarece parametrii de tipul pointer la

    pointer, sunt utilizai, mai ales, n legtur cu structurile dinamice de date, n aceastsecven, s-a ales un astfel de caz, funcia free() facnd parte din sistemul de alocare amemoriei dinamice. Detalii despre memoria dinamic, sistemul de alocare i structuriledinamice sunt date n alte paragrafe ale acestui capitol.

    /* Declararea functiei */int stergenod(NOD** top){NOD* p;if(!*top) return 1;p = (*top)->link;free(*top);

    *top = p;return 0;}/* Apelul functiei */. . . . .NOD *cap;. . . . .int r = stergenod(&cap);. . . . .

    In aceast secven, variabila pointer cap trebuie s se actualizeze ca urmare a

    tergerii nodului din vrful stivei. In acest scop, parametrul formal top este declaratpointer lapointer i trebuie s se supun regulii de indirectare discutate mai sus, pentru a accesavaloarea, aa cum se observ n instruciunile funciei. La apel, pentru variabila pointercap,se transmite subprogramului adresa, la fel ca pentru oricare variabil obinuit. TipulNOD,construit de programator, este un struct ce descrie structura unui element al stivei. Deregul, un element are cel puin un cmp pentru informaie i o adres care puncteaz spreelementul urmtor (link).

    In multe cazuri, este convenabil ca o funcie s ntoarc un pointer la o variabil ncare funcia a construit rezultatul, n locul rezultatului nsui. Trebuie, ns, acordat atenievaliditii pointerului, dup revenirea din funcie n apelator, avnd n vedere existenavariabilei pe care o desemneaz. Dup cum se tie, variabilele declarate ntr-o funcie sunt

    variabile locale, de clas automatic, crora li se aloc memorie pe stiva sistem n momentulintrrii n funcia respectivi memoria ocupat de acestea este automat eliberat nainte derentoarcerea la apelator. In aceste condiii, un pointer la o variabil local nu are sens napelator, deoarece zona de memorie pe care o adreseaz nu mai exist.

    O prim modalitate de rezolvare a acesteri situaii, este aceea a schimbrii clasei dememorie pentru variabila n cauz, prin declararea ei de clas static. In acest mod,variabilei respective i se aloc spaiu n segmentul de date al programului i aceasta rmnealocat pe toat durata execuiei programului. In secvena care urmeaz, se schieaz unastfel de caz i se sugereaz modul de utilizare a rezultatului.

    double* func(...)

    {static double x;double* px = &x;. . . . . . . .return px;

  • 8/4/2019 Capitole de Program Are Procedural A

    10/119

    Prof.dr.Valer Roca ULBS 10

    }. . . . . . . .double* d, y, z=5.25;d = func(...);y = *d + z;. . . . . . . .

    O alt modalitate este aceea a construirii unei variabile dinamice n funciarespectiv, deoarece zona de memorie alocat ei este persistent, adic aceasta esteaccesibil, pe baz de adresi dup ntoarcerea la apelator. In secvena care urmeaz sesugereaz acest mod de rezolvare, considernd cazul de mai sus.

    double* func(...){double* px;px = (double*)malloc(sizeof(double));. . . . . . . .return px;

    }. . . . . . . .double* d, y, z=5.25;d = func(...);y = *d + z;. . . . . . . .

    In aceast secven, pointeruluipx i se atribuie adresa unei zone de memoriedinamic, solicitat sistemului de alocare prin funciamalloc(). Rezultatul se construiete naceast zoni adresa ei este cea care se transmite apelatorului.

    Pe baza discuiei de mai sus, se pot face urm

    toarele recomand

    ri pentru transferul

    parametrilori a rezultatului la funcii: pentru variabile scalare nemodificabile, transfer prin valore; pentru structuri voluminoase, transfer prin adres. Arrayul se transfer implicit prin adres; pentru variabile modificabile, indiferent de mrime, transfer prin adres; pentru rezultat ca variabil local retunare prin valoare: pentru rezultat n heap, returnare prin pointer; pentru structuri voluminoase nemodificabile, ca parametru sau ca rezultat, se declar

    pointer la dat constanti/sau rezultat constant aa cum se sugereaz n declaraia:const tip* func(const tip* pv);

    1.1.5 Declara

    ii cu pointeri

    i interpretarea lor

    Utilizarea pointerilor, benefic altminteri, poate s aduc i neajunsuri, printre caresemnalm dificultatea de a descifra programe surs. Un caz tipic, l constiuie interpretareadeclaraiilor complexe, pentru care, n cele ce urmeaz se d, sub form de algoritm, un

    ndrumar. In continuare, se dau cteva exemple, succesiunea entitilor care se analizeazfiind marcate prin numere de ordine

    1. Se ncepe cu identificatorul din declaraie.2. Se merge spre dreapta cutnd o entitate sub forma unei perechi de paranteze

    rotunde ( ) sau drepte [ ]. O succesiune de dou sau mai multe perechi de paranteze dreptese considerat ca o entitate unic.

    3. Dac s-a selectat o entitate, se interpreteaz ca funcie, respectiv array.4. Se schimb sensul de deplasare nspre stnga cutnd o entitate *, dat de cel

    mai apropiat caracter de acest fel care nu a fost nc analizat. Dac * este precedat demodificatorul const, atunci aceast sucsesiune formeaz o entitate. Se interprezeazentitatea ca indicaie de pointer sau pointer constant i se continu cu deplasare spre stnga.

  • 8/4/2019 Capitole de Program Are Procedural A

    11/119

    5. Dac n cutarea spre stnga, se ntlnete o parantez rotund deschis (, atuncientitatea care este cuprins ntre aceast parantezi perechea ei dreapta este considerattratati algoritmul se reia de la pasul 2.

    6. Dac n cutarea spre dreapta, se ajunge la o parantez rotund nchis), atuncialgoritmul se reia de la pasul 4.

    2. Dac n cutarea spre dreapta, se ajunge la sfritul declaraiei, atunci se ncheiecutarea i se interpreteaz declaraia de tip.

    Exemplul 1:

    char* (* ( * var )(int))[10]

    Prof.dr.Valer Roca ULBS 11

    53247 6 1

    1. Identificatorul vareste declarat ca2. un pointer la3. o funcie care are un parametru ntreg i care returneaz4. un pointer la5. un array de 10 elemente care sunt6. pointeri la2. tipul char

    Exemplul 2:

    double ( * var (double (*)[3])) [5]

    42135

    1. Identificatorulvar desemneaz o funcie care are un

    parametru i returneaz2. un pointer la3. un array cu 3 componente4. de tip doubleParametrul funciei esteun pointer la un array cu 5componente de tipul double

  • 8/4/2019 Capitole de Program Are Procedural A

    12/119

    Exemplul 3:

    struct s * (* var[5])[5]

    6 15 43 2

    1. Idetificatorulvar este2. un array cu 5 componente care sunt3. pointeri la4. un array cu 5 componente care sunt5. pointeri la6. tipul struct s

    Exemplul 4:

    unsigned int * ( * const* var [5][10])(void)

    1 57 6 4 3 2

    1. Identificatorulvar este2. o matrice cu 5 linii i 10 coloane3. pointeri constani la4. un pointer la5. o funcie care nu are parametrii i returneaz6. un pointer la2. un ntreg fr semn

    Declaraiile de funcii, incluse, se analizeaz n acelai mod, depinznd de poziia lor.

    In exemplul care urmeaz sunt trei funcii, analiza ncepe cu funcia intermediar. Deremarcat prezena parantezelor care o delimiteaz, fr care scrierea ar fi fost eronatsintactic.

    Prof.dr.Valer Roca ULBS 12

  • 8/4/2019 Capitole de Program Are Procedural A

    13/119

    Exemplul 5:

    void ( * var (int,double(*)(int) ) )(int)

    35 1 2 4

    1. Identificatorulvar este2. o funcie 1 care are doi parametri i returneaz3. un pointer la4. o funcie 2 cu un parametru ntreg care returneaz5. un voidFuncia 1 are un parametru ntreg i un parametru care estepointer la o funcie -3 care are un parametru intreg ireturneaz

    un double

    1.2 Variabile dinamiceLa fel ca i alte limbaje, limbajul C implementeaz posibilitatea utilizrii spaiului de

    memorie disponibil, denumit spaiu de memorie dinamic sau spaiuheap, potrivit unui modelprestabilit pentru memoria intern. Utilizarea acestui spaiu se bazeaz pe pointeri, cavariabile prin care se pot accesa locaii de memorie, denumite blocuri. In plus fa de cazulstatic, blocurile de memorie pot s fie controlate - alocate i eliberate - la momentul execuieiprogramului, prin sistemul pe care limbajul l ofer, denumit sistem de gestiune a memorieiheap.

    Datorit posibilitiilor de utilizare i a posibilitilor de control, un cuplu (pointer,bloc heap) este denumit variabil dinamic. Variabilele dinamice pot fi utilizate independent

    sau pot fi asamblate n structuri de variabile, prin care se pot implementa structuri complexede date, cunoscute ca structuri dinamice de date.

    In fig. 1.2, este redat modelul logic al memoriei interne n sistemele de operare DOS

    (UNIX, MS-DOS), de referin pentru implementarea sistemului de gestiune heap.

    Prof.dr.Valer Roca ULBS 13

  • 8/4/2019 Capitole de Program Are Procedural A

    14/119

    Prof.dr.Valer Roca ULBS 14

    Sistemului de gestiune pentru heap pune la dispoziia programatorului o mulime defuncii, prin care programul poate aloca i elibera, la momentul execuiei programului, blocuride memorie, de lungimi variabile, la care s se refere prin pointeri. Mulimea de funcii,conform standardului ANSI, conine un nucleu obligatoriu, la care o anumit implementare alimbajului poate aduga i alte funcii. In acest capitol, se face referire numai la sistemul degestiune heap recomandat de standard, pentru a asigura construirea de programe portabile.

    Prototipurile funciilor sistemului de gestiune a memorie heap, la fel ca n cazul altor

    biblioteci, sunt declarate n fiierul headeralloc.h care trebuie s fie inclus n programulsurs, atunci cnd se face uz de acest spaiu. Alocarea de blocuri de memorie presupune apelul uneia din funciile de

    alocare/realocare urmtoare:

    void * malloc(size_t blocksize);void * calloc(size_t nitem, size_t blockitemsize);void * realloc(void* pblock, size_t blocksize);

    In aceste prototipuri, size_t este tipul intreg, ntlnit i la alte funcii, definit n maimulte fiiere header, printre care i fiierul alloc.h. Acest tip permite declararea unor ntregifoarte mari, ntregi care ar putea fi necesari n exprimarea unor cereri de blocuri, avnd nvedere c lungimile acestora se exprim n bytes. Lungimea blocului cerut este dat prinparametrulblocksize, n cazul funcieimalloc() sau este produsul dintre numrul deelemente nitem i lungimea unui element, n cazul funciei calloc(). Ultima funcie esteutilizat, mai ales, n legtur cu alocarea spaiului pentru array-uri cu numr variabil decomponente (array dinamic).

    Ambele funcii de alocare ntorc adresa blocului, ca pointer nelegat de un tip de dat,pointer void, care poate fi NULL, dac spaiul heap liber nu are disponibil un bloc de mrimeacerut. In acest mod, programatorul poate controla, testnd valoarea pointerului, dacalocarea s-a realizat sau nu i poate decide n consecin.

    Trebuie, de asemenea, remarcat faptul c programatorul trebuie s rein adresa

    returnat de alocator, ntr-o variabil pointer, legat de tipul de date pe care urmeaz s-lconin blocul, pentru utilizare ulterioar. Rezult, de aici, necesitatea de a converti, prin cast,pointerulvoidla tipul dorit, ceea ce, poate nsemna utilizarea unei alocri de forma:

    Spaiul heap

    Spaiul pentru stiva

    sistem

    Spaiul de date staticeale programului

    Spaiul pentru codulprogramului

    Spaiul pentru antetulprogramului

    Fig. 1.2 Modelul logic al memoriei interne pentru un program C

  • 8/4/2019 Capitole de Program Are Procedural A

    15/119

    Prof.dr.Valer Roca ULBS 15

    tip*pblock;if(pblock= (tip*)functiealocare(parametri))

    {/* Spatiu alocat. Utilizare pointerpblock*/}else

    { /* Rezolvare situatie de lipsa de spatiu */ }

    In textul care urmeaz, se aloc spaiu pentru un real dublu, pentru un articol, careurmeaz a fi utilizat ca nod al unei liste, i pentru un array intreg cu n componente, utiliznd osecven fr testare de reuit:

    #include #include void main(){........................./* Definiri de tipuri si de variabile */double* predab;typedef struct art {char info[50];

    art* next;} NOD;NOD* cap;int* arrayint;........................../* Alocare de blocuri */predab = (double*)malloc(sizeof(double));cap = (NOD*)malloc(sizeof(NOD));n = 80;arrayint = (int*)calloc(n, sizeof(int));........................../* Utilizarea blocurilor alocate */*predab = -5.75L;strcpy(cap->info, " ");cap->next = NULL;for(int k = 0; k < n; k++) arrayint[k] = 0;}

    Aa cum rezult din text, se recomand ca lungimea blocului sau a elementului debloc, n funciile de alocare, s se indice prin operatorul sizeof(), pentru a evitaspecificarea eronat a lungimii. Se remarc, de asemenea, modul de conversie i utilizare,avnd n vedere tipurile blocurilori a cmpurilor pe care acestea le conin.

    Funcia realloc() este utilizat n cazul n care un bloc alocat anterior trebuie s fiereajustat ca mrime, potrivit parametruluiblocksize specificat. Funcia ntoarce pointer nul,dac nu este posibil realocarea. Dac se utilizeaz valoarea NULL, ca parametru pentrupointerul la bloc, atunci aceast funcie este echivalent cumalloc(). Noul bloc poate fiobinut prin prelungirea vechiului bloc sau poate fi alocat pe un spaiu nou. In ambele cazuri,coninutul blocului este conservat pn la minimum ditre cele dou lungimi.

    Eliberarea blocuriloralocate se realizeaz prin funcia free() care are un prototipsimplu:

    void free(void* pblock);

    Pentru exemplele de mai sus, eliberarea se face sub forma:

    free(predab);free(cap);free(arrayint);

  • 8/4/2019 Capitole de Program Are Procedural A

    16/119

    Prof.dr.Valer Roca ULBS 16

    In ncheierea acesui paragraf, se face precizarea c implementrile limbajului Cutilizeaz, n general o metod de gestiune a spaiului heap, denumit garbage collection(colectarea rezidurilor). Potrivit acestei metode, evidena spaiului liber i ocupat se ine pemai multe liste, cu blocuri de mrimi diferite. Alocarea unui bloc, nseamn scoaterea lui dinlista de spaiu disponibil i trecerea lui ntr-o list de spaiu ocupat. Atunci cnd programulcere o eliberare de bloc, blocul respectiv este, de regul marcat ca disponibil, fr s fie

    returnat unei liste de spaiu liber. La anumite intervale de timp, sistemul de gestiunedeclaneaz procesul de eliberare proriu-zis, comasnd blocuri adiacente, marcate cadisponibile, pentru a construi blocuri libere de lungime ct mai mare, pe care le evideniaz nliste corespunztoare. Acest proces se aplici atunci cnd, la o cerere de alocare, nici unadin listele de spaiu liber nu poate oferi un astfel de bloc. Alocarea eueaz, dac, i dupcolectare (de reziduri), cererea nu poate fi satisfcut. Prin acest sistem, se asigur ofuncionare, ct mai ndelungat posibil, a programelor care utilizeaz memoria heap fr a a

    ncrca exagerat timpul de execuie al programului cu timp necesar procesului de gestiune aacestui spaiu.

    In fine, menionm c n C++, au fost introdui operatorii (new, dispose), pentru afacilita utilizarea spaiului heap, legat, mai ales, de tehnica programrii orientate pe obiecte,

    care sunt prezentai n lucrare, n capitolul de faciliti ale acestui limbaj.

    1.3 Array-uri dinamiceAa dup cum se cunoate, n limbaj exist posibilitatea declarrii i referirii, prin

    indexare, a array-urilor uni i multidimensionale. Acestea sunt variabile de clas automatic alcror spaiu de memorie le este atribuit n segmentul de date al programului sau pe stivasistem, la dimensiunile maxime declarate i acest spaiu, n timpul execuiei programului, numai poate fi ajustat (mrit sau micorat) i nici nu poate fi eliberat, atunci cnd rolul aceluiarray a ncetat. In programele mari, n condiiile n care determinarea spaiului maxim necesarpentru astfel de structuri este dificil, utilizarea eficient a memoriei trebuie s fie opreocupare a programatorului. O soluie posibil este aceea a construirii de structuri array n

    memoria heap care s poat fi dinamic ajustate dimensional i s poat fi eliberate n timpulexecuiei programului.

    Structurile array construite n heap poart denumirea de array dinamic. Acesteapstreaz proprietatea de spaiu compact, dar pierd, cu excepia celor unidimensionale,posibilitatea referirii directe prin indexare. Pentru array-urile unidimensionale, pe bazaechivalenei ntre expresia de indexare, cu un indice, i expresia de indirectare asociat, aacum s-a artat mai nainte n acest capitol, exist posibilitatea utilizarii alternative a celor doumodaliti.

    O prim abordare este aceea a construirii unui array cu spaiu la dimensiuni efective,care, n timpul execuiei programului nu mai trebuie ajustat dimensional, denumit arraydinamic neajustabil. Formal, un asfel de array este un tuplu de forma (n,psp,bloc), unde

    n este numrul efectiv (maxim) de elemente, iarpsp este pointerul la blocul de spaiu dinheap. Dup cum se observ, un astfel de array este o variabil dinamic i utilizarea luirevine la realizarea urmtoarei secvene de aciuni:

    - declararea unui pointer la tipul de dat pe care urmeaz s l conin elementele:tip* psp;

    - preluarea spaiului din heap:psp = (tip*)malloc(n*sizeof(tip));

    - utilizarea elementelor, fie prin indexarepsp[k] , fie prin indirectare*(psp +k),unde k este poziia( rangul) elementului, n convenia de referire cu indice ntreg, cu valori

    ncepnd zero. Controlul ncadrrii n blocul de spaiu se realizeaz prin raportarea lavaloarea n-1sau la adresa (psp + n-1), ca adres a ultimului element ;

    - eliberarea spaiului, atunci cnd array-ul nu mai este necesar:free(psp);

  • 8/4/2019 Capitole de Program Are Procedural A

    17/119

    Prof.dr.Valer Roca ULBS 17

    In listingul 1.1, utiliznd un array dinamic neajustabil, funciamain() introduce un irde n date ntregi, utiliznd adresarea cu indexare i le nsumeaz, folosind referirea prinindirectare.

    Listingul 1.1 Utilizarea unui array dinamic neajustabil#include #include

    void main(){int n, k, s;int *psp, *p;printf(" Numarul de componente: ");scanf("%d", &n);

    psp = (int*)malloc(n*sizeof(int));printf("Componentele: ");for(k=0; k < n; k++) scanf("%d", &psp[k]);s = 0;for(p = psp; p < (psp + (n-1)*sizeof(int)); p++)

    s = s + (*p);

    printf("Suma este %d", s);}

    Cazul general este cel al unui array dinamic ajustabil. Un astfel de array este untuplu de forma (nelem, pozelem, psp, bloc), unde nelemeste numrul maxim deelemente care pot fi nserate n array la dimensiunea curent a blocului de memorie alocat, iarpozelemd rangul ultimului element efectiv ncrcat. Iniial, este necesar ca aceste variabile

    s aib valorile:nelem = 0;pozelem = -1;psp = NULL;

    Lucrul cu un astfel de array este ceva mai complicat, deoarece operaiile de inserarea unui elemente n array pot cere o cretere a spaiului pe care acesta l posed la un anumitmoment, inclusiv momentul iniial, cnd un asfel de spaiu nu exist. De aceea, pentru unasfel de array este necesar s se construiasc funcii care s poat realiza nserri, curealocare de spaiu, precum i alte operaii, cum ar fi preluarea elementului de un anumitrang, cu controlul ncadrrii n limita numrului de elemente efectiv prezente n array. In celece urmeaz, se sugereaz o list de astfel de funcii care ar putea asigura o funcionalitate ctmai complet:

    - funie pentru nserarea unui element la sfrit, dup ultimul element

    efectiv prezent;- funcie pentru nserarea unui element pe o poziie interioark, cu

    deplasarea spre dreapta a elementelor pentru a face loc;- funcie de nlocuire a valorii unui elementde rang k;- funcie depreluare a valorii ultimului element;- funcie depreluare a valorii elementuli de rang k;- funcie de ncrcare de array prin citire de la consol;- funci de afiarede array pe monitor,- funcie de sortare de array etc.

    In continuare, cu titlu de exemplu, n listingul 1.2 este redat o funcie de nserare aunui element pe poziia ki una de preluare a valorii elementului de rang k. Aici s-a fcutipoteza, care va fi discutat mai n detaliu ntr-un paragraf urmtor, c tuplu care definete un

  • 8/4/2019 Capitole de Program Are Procedural A

    18/119

    Prof.dr.Valer Roca ULBS 18

    array dinamic ajustabil se definete ca un tip de datstruct, pentru date de tip double, deforma:

    typedef struct{int nelem;int pozelem;double *psp; } DINARRAY;

    In aceste condiii, programul apelator declar variabile de tipDINARRAY, atunci cnd doreteo astfel de structuri, le iniializeaz cum s-a artat mai sus i transfer funciilor de lucru cuarray pointeri de acest tip. De exemplu, aceste aciuni, cu apelul funciei de nserare, suntsugerate de secvena de instruciuni care urmeaz:

    DINARRAY vectdin, *pvect;vectdin.nelem = 0;vectdin.pozelem = -1;vectdin.psp = NULL;pvect = &vectdin;

    . . . . . . . . . . . . . . . . . . . .int err = setitemat(pvect, 5, -200.75);

    Listingul 1.2 Funcia de nserare i funcia de preluare pentru un array dinamic ajustabil/* Functia de inserare a unui element pe pozitia k */int setitemat(DINARRAY* pa, int k, double x){double *r;if(kpa->pozelem) return 2;if(pa->pozelem +1 > pa->nelem){r = (double*)realloc(pa->psp, (pa->nelem +50)*sizeof(double));if(!r) return 1;pa->psp = r;pa->nelem += 50;}

    for(int i= pa->pozelem + 1; i > k; i--) p[i]=p[i-1];pa->psp[k] = x;pa->pozelem++;return 0;

    }

    /* Functia de preluare a valorii elementului de rang k */int getitemat(DINARRAY* pa, int k, double *x){if(kpa->pozelem) return 2;

    *x = pa->psp[k];return 0;}

    Se observ c funcia de nserare verific, mai nti, corectitudinea indicelui ki dacacesta este n afara limitelor, returneaz codul 2. Cnd nserarea este corect definit, severific dac mai este spaiu pentru a putea deplasa spre dreapta elementele. Dac blocul nuare cel puin un element neocupat, se face o realocare, cu suplimentare a blocului cu 50 deelemente. Dac realocarea eueaz, funcia returneaz codul 1, pentru a arta c inserareanu s-a putut realiza, din lips de spaiu. Dac blocul a fost realocat, se rencarccorespunztor pointerul la spaiu, deoarece blocul ar fi putut s fie alocat pe alt amplasament

    i se modific numrul maxim de elemente posibile, n noile condiii. Apoi, se face deplasarea,din poziia k, spre dreapta cu o poziie, se nscrie, n poziia k, valoarea x, se actualizeaz

  • 8/4/2019 Capitole de Program Are Procedural A

    19/119

    Prof.dr.Valer Roca ULBS 19

    informaia cu privire la poziia ultimului element prezent i funcia returneaz codul 0, pentru amarca reuita.

    Funcia de preluare a elementului de rang k, verific indicile k. Dac acesta eteincorect, operaia de preluare eueaz, funcia ntoarce codul 2 i valoarea lui x estenedeterminat. Dac preluarea reuete, funcia returneaz codul 0 i x conine valoarecorect.

    1.4 Liste nlnuite i arboriFrecvent, n programele scrise n C, sunt necesare liste nlnuite i arbori binari, ca

    structuri dinamice de date care s faciliteze rezolvarea diferitelor clase de probleme. Cuscopul de a fixa elementele necesare nelegerii mecanismelor de implementare n limbaj, nacest paragraf, se face o scurt trecere n revist a acestor tipuri de structuri.

    1.4.1 Liste nlnuiteListele de date sunt compuse din articole, denumite noduri, dispuse ntr-o anumit

    ordine. In fiecare nod, rezid informaia propriu-zis, la care se adaug o informaie special,care s materializeze legtura nodului respectiv cu vecinii si. Aceasta nseamn c lista dedate trebuie s se construiasc ntr-un spaiu adresabil, de exemplu heap, informaia delegtur fiind adresa (adresele) blocului (blocurilor) vecinului (vecinilor).

    Dac se noteaz cu P = {p1, p2, , pn} mulimea adreselor din spaiul deconstrucie, la care se adaug valoarea NULL, pentru a desemna adresa vidi se noteazcu D = {d1, d2,, dn} mulimea informaiilor care vor fi coninute de de nodurile listei, atuncise pot defini formal listele simplu i dublu nlnuite, dup cum urmeaz.

    O list simplu nlnuit sau list asimetric este cuplu (cap, La) unde Laestemulimea{ (di, pi) | di D, pi P }, pe care s-a definit o relaie de ordine, cu cap= p0, ca adres a blocului care conine perechea (d1,p1), cupi, 1 i n-1 ca adresa blocului care conine perechea (di+1, pi+1)i cupn = NULL, pentru perechea (d , p ).n n

    O list dublu nlnuit sau list simetric este tripletul (prim, Ls, ultim),unde Lseste mulimea { (pi, di, si) | di D, pi, si P }, pe care s-a definit orelaie de ordine direct, cu Prim = p0, ca adres a blocului care conine tuplul (p1, d1,s1), cupi, 1 i n-1 ca adres a blocului care conine tuplul (pi+1, di+1, si+1)i cupn = NULL, pentru (pn, dn, sn) i o relaie de ordine invers, cu ultim = pn+1, ca

    adres a blocului care conine tuplul (pn, dn, sn), cusi, unde n i 2, ca adresablocului (pi-1, di-1, si-1)i cu s1 = NULL.

    Convenind s se figureze nodurile prin dreptunghiuri i legturile prin sgei, cuvaloarea NULL ca legare la pmnt, cele dou tipuri de liste se pot reprezenta grafic aa cumse arat n fig.1.3.

  • 8/4/2019 Capitole de Program Are Procedural A

    20/119

    d1 d2 dnd -

    cap

    d1dd1 d1

    prim ultim

    Fig.1.3 Liste nlnuite

    Figura sugereaz c nodurile unei liste simplu nlnuite pot fi "vizitate" numai ntr-unsingur sens, de la nodul de pointer cap spre nodul cu legtur la pmnt (ultimul nod),deoarece, pentru fiecare nod se cunoate numai succesorul su imediat. O adtfel de list, princonvenie, este vid, adic nu are nici un nod, atunci cnd pointerul cap = NULL.

    Similar, pentru o list dublu nlnuit, vizitarea se poate face de la nodul dat depointerulprimspre nodul ultim, dari invers, de la nodul dat de pointerul ultimspre primulnod, deoarece fiecare nod interior are legtur spre succesorul imediat, n ordine directispre predecesorul imediat, n ordine invers. Dac lista dublu nlnuit este vid, atunciprim= ultim =NULL.

    Listele cresc dinamic, plecnd de la situaia de list vid, adugnd mereu cte unnod i se micoreasz dinamic, tergnd cte un nod. Cele dou operaii tipice, denumiteadugare/tergere sau nserare/tergere, fac ca, n timpul execuiei programului respectiv,structura i numrul de noduri ale listei s sufere o permanent schimbare. Astfel, o listpoate trece de mai multe ori ntre situaia de list vid i nevid, n general, pe altamplasament, datorit faptului c, la nserare se solicit un nou bloc, iar la tergere seelibereaz cte un bloc, care, pn la o nou nserare, poate fi ocupat de alt structur. Duplocul unde se fac cele dou operaii tipice, se disting stivele i cozile, ca liste particulare,deosebit de utile n programare.

    Stiva nlniut (stack) este o list asimetric la care operaia de nserare i operaiade tergere se fac numai la primul nod (nodul de cap), care, n acest caz este denumit vrfulstivei sau

    top. Datorit

    acestui comportament sau disciplin

    de lucru, bine sugerat de modul

    n care se gareazi se scot vagoanele ntr-o linie nchis la un capt, stivele se mai numescliste LIFO (Last-In-First-Out = ultimulintratprimul-ieit). La o stiv, deregul se viziteaz numai nodul top, dar nu este exclus vizitarea i a altor noduri, caz ncare vizitarea presupune o operaie de traversare a stivei, total sau parial. Datorit acestuicomportament, stiva este o structur frecvent utilizat n diferite probleme care presupun oatare disciplin n lucrul cu datele.

    Coada nlnuit (queue) este o list asimetric la care operaia de nserare se facela captul dat de pointerul ultim, iar operaia de tergere se face la captul dat de pointerulprim. Coada este denumit list FIFO (First-In-First-Out=primul-intrat-primul-ieit), deoarece materializeaz comportamentul obinuit pentru tratarea cozilor

    cotidiene: cozi de persoane, cozi de automobile etc. Prin analogie cu aceste cozi, captul lacare se face tergerea este denumit faa cozii, operaia de tergere este denumit servire, iarpointerul spre acest nod este notat fata. Similar, nodul dup care se poate face o nserare

    Prof.dr.Valer Roca ULBS 20

  • 8/4/2019 Capitole de Program Are Procedural A

    21/119

    Prof.dr.Valer Roca ULBS 21

    este este denumit spatele cozii i pointerul se noteazspate. Tratarea corect a unei cozipresupune totdeauna doi pointeri, pentru oricare din operaiile care modific coada i estesuficent cunoaterea unui singur pointer, pointerul de fa, n cazul unei operaii detraversare.

    1.4.2 Arbori binariPstrnd notaiile introduse n paragraful anterior, se poate defini formal un arbore

    binar sau arbore de ordinul doi, utiliznd o schem recursiv.Un arbore binareste cuplul (cap, A2),undeA2 este o mulime de forma { (pi,

    di, si) | di D, pi, si P } care ndeplinete urmtoarele condiii:1) exist unic nodul de adresp0 care conine tripletul (p1, d1, s1), numit nodrdcina al arborelui i cap = p0;2) diferenaA2 {( p1, d1, s1)} se descompune n mulimileA21iA22, cuA21A22 = ;3) dacA21 = , atuncip1=NULL, iar dacA21 , atunci exist unic (pk, dk,sk)nA21astfel nctp1s fie adresa blocului acestui element, numit succesorstnga al lui ( p1, d1, s1) ;

    4) dacA22 = , atunci s1=NULL, iar dacA22 , atunci exist unic (pr, dr,sr) nA22astfel nct s1 s fie adresa blocului acestui element, numit succesordreapta al lui ( p1, d1, s1) ;5)A21iA22sunt arbori binari.

    Dac se reprezint nodurile arborelui binar la fel ca la liste, se poate obine o imagine

    de forma celei din fig.1.4, situaia de arbore vid fiind marcat convenional prin valoarea NULLa pointerului cap. Se observ c, ntr-un arbore binar, fiecare nod are cel mult doi succesoridireci, iar structura de noduri care deriv din acesta poate fi considerat un arbore, numitsubarbore, a crui rdcin este nodul considerat. Nodurile arborelui binar care nu ausuccesori direci sunt denumite frunze. De asemenea, trebuie remarcat faptul c n tehnica

    lucrului cu abori binari se utilizeaz adesea o terminologie mprumutat din structurile de tiparbore genealogic. Astfel, se vorbete de nod tati copii si, pentru a desemna un nod isuccesorii si direci, noduri frai i noduri veri, descendeni i ascendeni etc.

    La fel ca la liste, operaiile la arbori se refer la nserarea i tergerea de noduri carefac ca un arbore s creasc sau s descreasc dinamic, dar, din punctul de vedere alrealizrii, acestea sunt mai complicate. Ambele operaii solicit o operaie de traversareparial, pentru a depista nodul dup care trebuie fcut nserarea sau nodul care trebuieters, operaie care, de regul se bazeaz, pentru identificare, pe informaia coninut nnoduri. Aceast categorie de arbori binari, pentru care structura este dat prin relaia careexist ntre informaiile distincte pe care nodurile le conin sunt denumii arbori binari decutare. In continuare, pentru exemplificarea modului de implementare, se consider, n

    detaliu, numai aceast categorie.

  • 8/4/2019 Capitole de Program Are Procedural A

    22/119

    Prof.dr.Valer Roca ULBS 22

    Dac se noteaz cu info partea din structura unui nod care conine informaia, cullink cmpul de legtur spre succesorul stnga, iar cu rlink cmpul de legtur spresuccesorul dreapta, atunci un arbore binar este arbore de cutare dac, pentru oricare nod deadresp, diferit de o frunz, sunt adevrate urmtoarele relaii de dominare:

    p->info > p->llink->info;

    p->info < p->rlink->info.

    Relaiile de mai sus trebuie interpretate astfel: oricare nod i domin subarborelestnga i este dominat de subarborele su dreapta. In fig.1.5 este reprezentat un exemplude astfel de arbore, n care, pentru simplitate, informaia, de tip caracter, este nscris ncerculee, iar legturile sunt redate prin sgei.

    d

    d2 d3

    d4 d5

    d6 d7

    cap

    Fig.1.4 Arbore binar

  • 8/4/2019 Capitole de Program Are Procedural A

    23/119

    f

    d h

    gb e i

    a c

    Fig.1.5 Arbore binar de cutare

    Dac un astfel de arbore este traversat n ntregime, adic se viziteaz, ntr-o anumitordine, fiecare nod, se poate obine o list liniarizat a informaiilor coninute n arbore.Traversarea complet a unui arbore binar, inclusiv a arborilor binari de cutare, se poate face

    n mai multe moduri, dup ordinea n care se selecteaz sucesorii direci, n raport cumomentul selectrii nodului respectiv ca rddcin de subarbore. Teoretic, este comod s sedefineasc traversarea n mod recursiv, pe baza subarborelui stnga S, a subarboreluidreapta Di a rdcinii R a acestuia, astfel:

    traversare n preordine (RSD) pentru fiecare subarbore se viziteaz rdcina R,se traverseaz n preordine subarborele stnga S i apoi se traverseaz n preordinesubarborele dreapta D;

    traversare n ordine simetric (SRD) pentru fiecare subarbore se traverseaz nordine simetric subarborele stnga S, se viziteaz rdcina R, i apoi se traverseaz nordine simetric subarborele dreapta D;

    traversare n postordine (SDR) pentru fiecare subarbore se traverseaz npostordine subarborele stnga S, se traverseaz n postordine subarborele dreapta Di apoise viziteaz rdcina R.

    Pentru exemplul din fig.1.5 se obin urmtoarele liste liniare:

    f, d, b, a, c, e, h, g, i pentru preordine (RSD);

    a, b, c, d, e, f, g, h, i pentru ordine simetric (SRD);a, c, b, e, d, g, i, h, f pentru postordine (SDR).

    Se obsrv c lista care se obine printr-o traversare n ordine simetric este o listsortat, de aceea un arbore de cutare traversat complet, n aceast ordine, poate reprezentao metod de sortare, denumitsortare pe arbore binar.

    1.5 Realizarea operaiilor de baz pe liste nlnuiteAsupra listelor se pot realiza diferite operaii, dar, n cele ce urmeaz, prezentarea se

    limiteaz la operaiile de traversare, nserare i tergere, pentru care este recomandabil icomod s se construiasc funcii cu parametri.

    Prof.dr.Valer Roca ULBS 23

  • 8/4/2019 Capitole de Program Are Procedural A

    24/119

    Prof.dr.Valer Roca ULBS 24

    Oricare program care trebuie s implementeze astfel de operaii trebuie sdefineasc un tip de dat pentru nodul listei. In modul cel mai simplu, tipulNOD este un articol

    n care, pentru simplitate, informaia este considerat ca string. Pentru o list simplunlnuit, tipul poate fi definit astfel:

    #define n 50typedef char informatie[n];

    typedef struct art{informatie info;struct art* succesor;}NOD;

    Depinznd de situaie, definiia se poate adapta pentru liste dublu nlnuite i diferitetipuri de informaie, inclusiv pentru cazul n care informaia este un alt articol sau un arraydinamic. In ultimul caz, blocul de informaie fiind variabil, nodul trebuie s conin un pointerspre spaiul heap al blocului.

    1.5.1 Traversarea unei liste

    Operaia de taversare este o operaie de vizitare a nodurilor listei, n ordineaspecific acesteia. Aceasta se poate realiza, printr-o funcie, numitfuncie de traversare, cao traversare total sau poate fi o traversare parial de cutare.

    Traversarea total presupune vizitarea tuturor nodurilor, de la captul ales, pn lasfritul listei, fr ieire din funcia de traversare. Cu fiecare nod localizat, se pot executaprelucrri general valabile, pentru toate nodurile, i/sau prelucrri specifice, condiionate, deinformaia coninut de noduri. Prelucrrile care se fac asupra unui nod constituie operaia detratare de nod. Dac este necesar, n raport de informaia coninut n nod, nodurile care suntsupuse operaiei de tratare pot fi alese, operaia de traversare, n acest caz, fiind denumittraversare total cu selecie.

    In listingul 1.3 este redat, pentru exemplificare, structura unei funcii de traversaretotal cu selecie, pentru o list sumplu nlnuit, n care operaia de tratare s-a presupus ceste realizat de o alt funcie, denumittratare().

    Listingul 1.3 Structura general a funciei de traversare a unei liste sumplu nlnuit cuselecieint traversare(NOD* inceput, char * infsel){NOD* p;/* Rezolvarea situatiei de lista vida */if(!inceput) return 1;/* Traversarea listei nevide */p = inceput;do{if(conditie-de-selectie)

    tratare(p);p = p->succesor;}

    while (p);return 0;}

    In structura funcie de traversare, trebuie remarcat, n primul rnd, lista de parametri.Parametrul, denumit inceput este un pointer spre nodul cu care se ncepe traversarea, de

    regul, nodul de nceput - dat de pointerul cap al listei. Inceputul traversrii poate fi i un altnod, atunci cnd este necesar o traversare total a unei subliste terminal a acesteia.Parametrul pointer primit aici este un parametru prin valoare i este conservat de funcia de

  • 8/4/2019 Capitole de Program Are Procedural A

    25/119

    Prof.dr.Valer Roca ULBS 25

    traversare din motive de semantic, mai ales dac acesta este pointerul de cap i, nconsecin, este introdus un pointer de lucrup, numit pointer de traversare. Parametrulinfsel este destinat primirii informaiei de selecie, adic a informaiei cu care se stabilete,prin expresia denumit conditie-de-selectie, dac nodul curent va fi tratat sau nu.Condiia de selectie este specific, dar aici s-a meinut presupunerea c informaia din nodurieste un text i, prin urmare, parametrul trebuie s fie un pointer la char.

    In al doilea rnd, trebuie observat modul n care este conceput codul funciei detraversare, din punctul de vedere al asigurrii caracterului general al acestei funcii i anumetratarea strii listei. Aici, funcia distinge cele dou cazuri, prin rezultatul ntreg pe care lreturneaz apelatorului: valoarea 1, pentru situaia de list vidi valoarea 0, dac lista estenevid, avnd cel puin un nod i a fost traversat.

    In al treile rnd, se remarc bucla de ciclare do, pentru controlul traversrii, cnd listaeste nevid. Nodul curent, accesat prin pointerul de traversarep, este livrat sau nu funcieitratare(), n raport de valoarea de adevr a condiiei de selectie. Apoi, indiferent de caz,se trece la un alt nod, prin linia de avansp = p->succesor care obine adresa noduluisuccesor sau valoarea NULL, dac lista s-a terminat. Funcia de tratare, prin pointerulpprimit, i acceseaz informaiile din nod i realizeaz tratamentul adecvat.

    Dac, n listingul 1.3, se renun la selecie, se obine o funcie de traversare totalnormal, eventual de la ultimul nod spre nceput, dac lista este dublu nlnuit i senlocuiete legtura succesor cu legturapredecesor.

    Traversarea parial este, de regul, conceput ca o traversare de cutare ipresupune vizitarea n ordine a nodurilor, de la captul ales, spre sfritul listei, pentru alocaliza un nod care ndeplinete o condiie dat, n raport cu informaia coninut.Traversarea listei se termin cu succes, atunci cnd s-a localizat un astfel de nod (primul,dac pot exista mai multe noduri care ndeplinesc codiia impus) sau fr succes (insucces),dac un astfel de nod nu existi a fost atins sfritul listei.

    In listingul 1.4, este redat, pentru exemplificare, structura unei funcii de cutare, peo list

    simplu nl

    nuit, n ipoteza c

    informa

    ia din noduri este de tip text. Func

    ia returneaz

    apelatorului o adres sau pointer, prin care l informeaz asupra situaiei ntlnite, pe bazaurmtoarei convenii:

    - rezultat = NULLlista vid sau insucces;- rezultat NULLsuccesi rezutatul este adresa nodului gsit.

    Listingul 1.4 Structura general a funciei de traversare pentru cutare pe o list simplunlnuit NOD* cutare(NOD* inceput, char * infsel){NOD *p, *s;s = NULL;p = inceput;/* Rezolvarea situatiei de lista vida */if(!inceput) return s;/* Traversarea listei nevide */do{if(conditie-de-cautare)

    {s = p;break;}

    else p = p->succesor;}

    while (p);return s;}

  • 8/4/2019 Capitole de Program Are Procedural A

    26/119

    Prof.dr.Valer Roca ULBS 26

    Parametrii au aceeasi semificaie ca la procedura anterioar, iar conditie-de-cautare este expresia pe care trebuie s o ndeplineasc informaia din nodul curent, nraport cu informaia dat de parametrul infsel. Rezultatul cutrii se pregtete prinpointerul ajuttor s care, atunci cnd lista este nevid, n bucla do, rmne pe valoareaNULL, dac nu exist un nod care s satisfac cerina impus sau primete valoareapointeruluip, numitpointer de traversare, dac nodul curent este cel corespunztor.

    Traversarea parial cu pointer de urmrire este o variant a traversrii decutare, necesar, de multe ori, n lucrul cu listele simplu nlnuite cnd intereseaz nunumai adresa nodului care ndeplinete criteriul de cutare ci i adresa predecesorului imediatal acestuia. Aa, de exemplu este cazul tergerii unui nod, cnd refacerea legturilor nu esteposibil, dac nu se posedi adresa predecesoruuli nodului care se terge.

    In acest tip de traversare, fa cazul anterior, funcia de cutare trebuie s prevad nc unparametru, de tip pointer la pointer la nod, prin care s se returneze adresa predecesorului. Inlistingul 1.5, este redat structura funciei de traversare cu urmrire, unde acest pointer,denumit pointer de urmrire, este parametrul r, declarat sub formaNOD** r. In listingulmodel, s-au utilizat urmtoarele convenii, cu privire la valorile pointerilor:

    - rezultat NULL, r = NULLlista vid;- rezultat NULL, r NULL insucces, r conine adresa ultimului nod a listei;- rezultat NULL, r = NULLsucces, rezultatul este adresa primului nod al listei iacesta ndeplinete criteriul de cutare, nu exist predecesor;- rezultat NULL, r NULLsucces, rezultatul este adresa nodului care

    ndeplinete criteriul de cutare, iarr conine adresa predecesorului su.

    Listingul 1.5 Structura general a funciei de traversare pentru cutare, cu pointer deurmrire, pe o list simplu nlnuitNOD* traversarecuurmarire(NOD* inceput, char * infsel, NOD** r){NOD *p, *s, *pr;s = pr = NULL;p = inceput;/* Rezolvarea situatiei de lista vida */if(!p) return s;/* Verificarea primului nod */if(conditie-de-cautare){s = p;*r =pr;return s;}

    /* Traversarea listei care are cel putin doua noduri */pr = p;

    p = p -> succesor;do{if(conditie-de-cautare)

    {s = p;break;}

    else{pr = p;p = p->succesor;}

    }

    while (p);*r = pr;return s;

  • 8/4/2019 Capitole de Program Are Procedural A

    27/119

    Prof.dr.Valer Roca ULBS 27

    }

    In funciile de cutare, conditie-de-cautare este o expresie care trebuie scrispentru nodul curent, adic n raport de pointerul de traversarep. In ipotezele fcute aici,trebuie utilizat funcia de comparare de iruri de caractere i aceast condiie se scrie subforma:

    strcmp(p->info, infsel) 0;

    unde operatorul relaional se nlociuete cu unul din operatorii ==, , depinznd decerina pe care trebuie s o indeplineasc informaia din nod fa de informaia de selecie:

    - informaia din nod == informaia de selecie;- informaia din nod < informaia de selecie;- informaia din nod > informaia de selecie;Trebuie acordat atenie apelului unei astfel de funcii care, pe poziia parametrului r,

    trebuie s transfere o adres unei variabile pointer, aa cu sugereaz secvena urmtoare:

    NOD *inceput, *p, *r;

    .......p = traversarecuurmarire(inceput, "textde informatie", &r);

    1.5.2 Inserarea ntr-o listInserarea unui nod nou se poate realiza la unul din capetele listei sau n interiorul listei,

    dup sau naintea unui nod dat. Pentru ndeplinirea unei astfel de sarcini, pentru fiecare caz,se recomand construirea unei funcii, numitfuncie de nserare. O astfel de funcie trebuies cunoasc pointerul sau pointerii care definesc lista, informaia care trebuie s fie pus nnod i, adresa nodului referin, dac nserarea nu se face la un capt. De regul, apelatorulare sarcina s posede informaiile cerute de funcia de nserare, iar, dac este necesarapelatorul va realiza traversarea prealabil a listei, cu cutare, pentru a construi unele din

    aceste informaii. Funcia de nserare trebuie s actualizeze pointerii caracteristici ai listei,dac nserarea i afecteaz, i s realizeze corect nserarea i n situai n care lista este vid.

    La nserare, n general, trebuie avut n vedere situaia n care se modific pointeriicaracteristici. De aceea, toi pointerii care vor suferii modificri n funcia de nserare,modificri care trebuie s se regseasc ca atare n apelator, trebuie s fie declarai detipulpointer la pointer la nod. Corespunztor, la apel, argumentele trebuie s fie adresede variabile pointeri.

    Inserarea n capul listei este specific stivelor nlnuite, dar poate fi aplicati la olist oarecare. Din punct de vedere algoritmic, nu ridic probleme deosebite, aa cum sepoate deduce din listingul 1.6, n care se construiete un model de funcie pentru o list

    simplu nlnuit, cu informaie de tip text. Grafic, nserarea n fa se ilustreaz ca n fig.1.6 -a.

    Listingul 1.6 Structura general a funciei de nserare n capul unei liste simplu nlnuitint inserareinceput(NOD** inceput, char * infnod){NOD *p;/* Alocare si pregatire nod */p = (NOD*)malloc(sizeof(NOD));if(!p) return 1;strcpy(p->info, infnod);p->succesor = NULL;/* Legarea nodului nou, cu actualizarea pointerului de inceput*/if(!*inceput) *inceput = p; /* Lista a fost vida */else

    {p->succesor = *inceput;

  • 8/4/2019 Capitole de Program Are Procedural A

    28/119

    Prof.dr.Valer Roca ULBS 28

    *inceput = p; /* Lista a avut cel putin un nod */};

    return 0;}

    In acest listing, parametrul infnodaduce informaia de pus n nod care, dupalocarea de spaiu - adresa np - se copiaz n nod, legtura acestuia spre succesor fiind

    iniializat NULL. Dac lista este vid, atunci acest nod devine cap al listei i pointerulinceput devine egal cup. Dac lista are cel puin un nod, atunci nodul noup, trebuie spuncteze spre nodul dat de pointerul inceputi acest pointer se actualizeaz cu adresa luip, pentru a arta noul cap al listei. De remarcat faptul c funcia testeaz situaia de lips de

    spaiu i ntoarce, ca rezultat, valoarea 1 (adevrat), pentru a marca lipsa de spaiu ivaloarea 0, dac alocarea a reuit.

    Dac lista este dublu nlnuit, atunci funcia de nserarea are doi pointericaracteristici (inceput i sfarsit) care trebuie s figureze n lista de parametri. Funciatrebuie s iniializeze la NULL ambele legturi ale nodului nou i s fac legarea acestuia subforma:

    /* Lista a fost vida */*inceput = p;*sfarsit = p;

    /* Lista a avut cel putin un nod */p->succesor = *inceput;(*inceput)->predecesor = p;*inceput = p;

    Dac lista dublu nlnuit a fost vid, funcia actualizeaz ambii pointeri caracteristici,pentru a puncta spre nodul nou, ca unic nod al listei. Atunci cnd lista are cel pu in un nod,

    trebuie ca fostul nod de inceput s puncteze, prin legtura sapredecesor spre nodul noup,dar numai pointerul inceput trebuie actualizat, pointerul sfarsit trebuind s rmn cuvechea sa valoare.

    Inserarea la sfritul listei este specific cozilor, dar poate fi aplicat i la o listoarecare. Din punct de vedere algoritmic, nu ridic probleme deosebite, aa cum se poatededuce din listingul 1.7, n care s-a construit un model de funcie pentru o list simplu

    nlnuit, cu informaie de tip text. Grafic, nserarea n fa se ilustreaz ca n fig.1.6 - b. Deremarcat aici faptul c funcia trebuie s primeasc pointerii caracteristici ai listei, de inceputi sfrit, pe care trebuie s i actualizeze corespunztor. Dac lista a fost vid, ambii pointeripuncteaz spre noul nod. Dac lista a avut cel puin un nod, ultimul su nod trebuie s trimit

    spre nodul nou, iar pointerul de sfrsit trebuie s arate spre nodul nserat.

  • 8/4/2019 Capitole de Program Are Procedural A

    29/119

    a) Inserare n capul listei

    Prof.dr.Valer Roca ULBS 29

    Listingul 1.7 Structura general a funciei de nserare la sfritul unei liste simplu nlnuitint inseraresfarsit(NOD** inceput, NOD** sfarsit, char * infnod){NOD *p;/* Alocare si pregatire nod */

    p = (NOD*)malloc(sizeof(NOD));if(!p) return 1;strcpy(p->info, infnod);p->succesor = NULL;/* Legarea nodului nou, cu actualizarea pointerului de inceput*/if(!*inceput) /* Lista a fost vida */

    {*inceput = p;*sfarsit = p;}

    else /* Lista a avut cel putin un nod */{ (*sfarsit)->succesor = p;

    *sfarsit = p;};return 0;}

    p

    inceput

    1

    ince ut

    b) Inserare la sfarsitul listei p

    inceput sfarsit

    1

    sfarsit

    c) Inserare dup un nod dat

    dupanod

    pinceput 12

    Fig.1.6 Inserare n list asimetric

  • 8/4/2019 Capitole de Program Are Procedural A

    30/119

    Prof.dr.Valer Roca ULBS 30

    Pentru liste dublu nlnuite, este necesar s se modififice corespunztor secvenelee inst

    umit nod al listei se aplic la o list oarecare. In acest caz,i

    istingul 1.8 Structura general a funciei de nserare dup un nod dat la o list simplu

    Dac nu exist sigurana c pointerul conine o adres corect a unui nod

    Dac lista este dublu nlnuit, atunci funcia trebuie s primeasc ambii pointeri nevid, de exemplu, cnd nserarea nu se face dup ultimul nod,

    legturile trebuie modificate astfel:

    d ruciuni date n cazul nserrii la nceput, modificnd captul de referin, adicnlocuind pointerul spre predecesor cu pointerul spre succesor, astfel:

    /* Lista a fost vida */*inceput = p;

    *sfarsit = p;

    /* Lista a avut cel putin un nod */(*sfarsit)->succecesor = p;p->predecesor = *sfarsit;

    Inserarea dup un an

    *sfarsit = p;

    func a de nserare trebuie s cunoasc, n plus fa de pointerii caracteristici, adresa noduluidup care se face nserarea. In cele ce urmeaz, cu titlu de exemplu, se presupune c funciaprimete un pointer dupanod, cu adresa nodului dup care se face nserarea i pe care

    apelatorul o poate obine, de exemplu, prin traversare cu pointer de urmrire. Esterecomandabil ca funcia de nserare s considere, de asemenea, situaia n care se solicit

    nserarea dup un nod dar lista este vid, adic o situaie pentru care parametrii inceput=NULLi dupanod NULL. In listingul 1.8, n ipoteza c pointerul dupanodeste adresa unuinod al listei, este construit funcia model de nserare, iar situaia este ilustrat grafic n fig.1.6

    c.

    Lnlnuitint inseraredupa(NOD** inceput, NOD* dupanod, char * infnod){NOD *p;

    /* Alocare si pregatire nod */p = (NOD*)malloc(sizeof(NOD));if(!p) return 1;strcpy(p->info, infnod);p->succesor = NULL;/* Legarea nodului nou */if(!*inceput) *inceput = p; /* Lista a fost vida */

    else /* Lista a avut cel putin un nod */{p->succesor = dupanod->succesor;dupanod->succesor = p;};

    return 0;}

    dupanodal listei cnd aceasta este nevid, se poate introduce o secven de verificare prin traversare.Dac, n traversare, adresa nodului dupanodnu este egal cu adresa nici unui nod al listei,se refuz nserarea i funcia rentoarce valoare 2, ca mesaj de eroare.

    specifici. In cazul de list

  • 8/4/2019 Capitole de Program Are Procedural A

    31/119

    Prof.dr.Valer Roca ULBS 31

    nod;dupanod->succesor->predecesor = p;

    re la nodul din capul listei, la nodulultim al listei rul listei. Se pot construi funcii de tergere,pentru gaia funciei de a returna informaia pe care o conine

    seasc ca atare n apelator, trebuie s fie declarai de tipulpointer lapoint

    ea nodului din capul listei este tipic pentru stive i cozi, dar se poateiliza i n cazul listelor oarecare. Funcia de stergere trebuie s primeasc, ca parametri,ointerul sau pointerii caracteristici ai listei i un pointer la char pentru un spaiu al al

    infodinnod)

    NOD *p;

    spaiu pentru a primi informaia din nodul care seandabil, pentru ca blocul de memorie s nu rmn ne eliberat dup

    izare, aa cum, de regul, se ntmpl dac funcia de tergere aloc n heap spaiul i

    , t);

    ei este tipic pentru cozi, cnd funcia de tergere

    bui stici, dar se poate utiliza i n cazul listeloroarec e. Dac lista este simplu nlnuit, funcia de tergere trebuie s cunoasci adresanodul ecesor al ultimului nod. Pentru a simplifica sarcinile apelatorului, funcia de

    p->succesor = dupanod->succesor;p->predecesor = dupa

    dupanod->sucecesor = p;

    1.5.3 Stergere ntr-o listoate s se refeLa fel ca la nserare, tergerea p

    sau la un nod oarecare din interiofiecare caz, eventual cu obli

    nodul ce este ters. In continuare, se presupune aceast situaie i se consider c informaiaeste de tipul text.

    La fel ca la nserare, tergerea poate modifica valorile pointerilor caracteristici. Deaceea, toi pointerii care vor suferii modificri n funcia de tergere, modificri caretrebuie s se reg

    er la nod. Corespunztor, la apel, argumentele trebuie s fie adrese de variabilepointeri.

    Stergerutp

    apelatorului n care s primeasc informaia din nodul care se terge. Pentru a distinge dacinformaia returnat este semificativ, adic lista nu a fost vid, funcia trebuie s ntoarc unrezultat 1, dac lista a fost vidi un rezultat 0, n caz contrar. In listingul 1.9 este redat oastfel de funcie, pentru o list simplu nlnuit.

    Listingul 1.9 Structura general a funciei de tergere n capul unei liste simplu nlnuitint stergecap(NOD** inceput, char*

    {if(!*inceput) return 1; /* Lista a fost vida *//* Stergere nod de cap, lista nu este vida */

    eput)->succesor;p = (*incstrcpy(infodinnod, (*inceput)->info);free(*inceput);*inceput = p;return 0;}

    ca apelatorul s i aloceSoluiarge este recomte

    ilutreturneaz la apelator numai adresa acestuia. In acest caz, apelul s-ar putea realiza ca nsecvena care urmeaz:

    char t[10];int r;. . . . .r = stergecap(&cap

    Stergerea nodului ultim al list

    s primeasc ambii pointerii caracteritre earui pred

  • 8/4/2019 Capitole de Program Are Procedural A

    32/119

    Prof.dr.Valer Roca ULBS 32

    terg

    Lis

    char * infdinnod)NOD *p, *r;

    ) return 1; /* Lista a fost vida */

    erseaza */

    re */

    ne nevida */

    rmrire care rmne n urma pointerului dei, n final, d adresa predecesorului nodului de sfrit. Pe baza lui, se

    ualizeaz la NULL legtura, nodul r devenind nod ultim.

    ters i a predecesorului su. Instingul 1.11, se apeleaz funcia versarecuurmarire() , n care se presupune o

    tergeoarec nceput, char* infsel, char * infdinnod)*p, *r;

    && (r==NULL))return 1; /* Lista este vida */

    ere nsi poate s execute o traversare care s depisteze predecesorul nodului ultim.Dac lista este dublu nlnuit, aceast adres este coninut n structura nodului ultim, nlegtura denumitpredecesor. Deoarece cozile solicit operaii frecvente de tergere, elese pot proiecta ca liste dublu nlnuite (decozi - dequeue), pentru ca operaia de tergere sse realizeze uor, fr traversare.

    In continuare, pentru a arta n ce const, din punct de vedere algoritmic, o traversarede stabilire a acestei adrese, se presupune o list simplu nlnuit. Listingul 1.10 red

    structura funciei de tergere, n acest caz.

    tingul 1.10 Structura general a funciei de tergere a nodului ultim al unei listesimplu nlnuitint stergesfarsit(NOD** inceput,{infodinnod = NULL;if(!*inceput/* Lista are un singur nod si devine vida */

    *inceput)->info);strcpy(infdinnod, (free(*inceput);

    *inceput = NULL;return 0;trav/* Lista are cel putin doua noduri si se

    r = *inceput;p = (*inceput)->succesor;

    NULL) /* Traversawhile (p !={r =p;p = p->succesor;}

    /* Se sterge nodul sfarsit si lista ramafo);strcpy(infdinnod, p->in

    free(p);r->succesor = NULL;return 0;}

    In aceast funcie,r este pointerul de utraversarep act

    Stergerea unui nod oarecare, de regul, presupune c nodul de ters se d prin

    informaia pe care o conine. In acest caz, funcia de tergere trebuie s realizeze o traversarecu pointer de urmrire, pentru a obine adresa nodului de

    tralicondiie de selecie pe egal. Se observ modul de apel al acestei funcii care primetepointerul de nceput (*inceput), informaia pentru selecie i adresa variabilei pointer r(&r).

    Listingul 1.11 Structura general a funciei de tergere a unui nod oarecare al unei listesimplu nlnuitint s are(NOD** iNOD{p = traversarecuurmarire(*inceput, infsel, &r);if((p==NULL)if((p==NULL) && (r!=NULL))return 2; /* Nu exista nodul de sters */

    primul nod */if((p!=NULL) && (r==NULL)) /* Se sterge{strcpy(infdinnod, p->info);

  • 8/4/2019 Capitole de Program Are Procedural A

    33/119

    Prof.dr.Valer Roca ULBS 33

    /* Se sterge nodul p */o);

    = p->succesor;

    cie, s-a extins codificarea rezultatului ntors, pentru a sesiza i situaiare a fi ters nu exist n list, situaie ce poate apare, mai ales, datorit

    iei greite ce o conine parametrul infsel. De remarcat corecia care se aplicpra pointerului inceput, n cazul n care se terge primul nod. De asemenea, trebuie

    recare(&cap,"text", t);

    tfel de list, se ine i un pointer de sfarit, denumit sfarsit, atuncifuncia ebuie s disting cazul cnd nodulcare s secvena situaiei, marcat princoment ul , trebuie modificat astfel:

    if(p->succesor==NULL)*sfarsit = r;

    tergere se realizeaz uor, listaodurilor n ambele direcii. Lsm

    alizarea funciilor corespunztoare, avnd ca model funciile scrise late simplu nl

    cin, programul trebuie s elibereze spaiul ocupat. Aceast operaieste denumit distrugere de list i ea trebuie s elibereze spaiul nod cu nod. Operaia

    poate fi ecvat, aa cum rezult din listingul 1.12.

    while(!inceput)

    *inceput = p->succesor;free(p);return 0;}

    if((p!=NULL) && (r!=NULL))innod, p->inf{strcpy(infd

    r->succesor

    free(p);return 0;}

    In aceast funul care se cecnd nod

    rmainfoasu

    acordat atenie modului n care se apeleaz funcia, potrivit unei secvene de forma:

    char t[10];int n;. . . . . .n = stergeoaDac, pentru o astrebuie s l primeasc n lista de parametri i tr

    terge este ultimul. In mod corespunztor,eari /* Se sterge nodul p */if(p!=NULL && r!=NULL) /* Se sterge nodul p */

    {strcpy(infdinnod, p->info);r->succesor = p->succesor;

    return 0;free(p);

    Pentru cazul listelor dublu nlnuite, funciile de gtur care faciliteaz accesarea nconinnd informaii de

    titorulule

    i, ca exerciiu, renuite.

    cilis

    1.5.4 Distrugerea unei listeDup cum se poate uor constata, la terminarea lucrului cu o list, aceasta poate s

    fie nevidi, n consee

    programat printr-o funcie ad

    Listingul 1.12 Distrugerea unei listevoid ditrugelista(NOD* inceput){NOD* p;

    {p = inceput;inceput = inceput->succesor;

    free(p);}

    }

  • 8/4/2019 Capitole de Program Are Procedural A

    34/119

    Prof.dr.Valer Roca ULBS 34

    Realizarea operaiilor de baz pe arbori binari de cutareLa fel ca i la liste, un program care trebuie s implementeze operaiile pe arbori

    ari trebuie s defineasc un tip de dat pentru nodul arborelui. In modul cel mai simplu, ca string i

    ):

    {informatie info;k, *rlink;

    ntru diferite tipuri de informaie,. In ultimul caz, nodul trebuie s

    re numai tratarea modului n care se pot

    postordine. Traversarea parcurge nodurile,espective, indiferent de informaia pe care acestea o conin. In acest proces,

    plica operaiile de tratare dorite. La fel ca n

    zul lis

    arece legturile n arbore sunt totdeauna de la un nod printe spreuccesorii si direci, o traversare n ordine simetric ( ) presupune ca dup ce s-a vizitat,

    1.6bintipulNOD este un articol n care, pentru simplitate, informaia este consideratdefinete cele dou legturi spre succesorii direci (stnga = llink, dreapta = rlink

    #define n 50typedef char informatie[n];typedef struct art

    art *llin}NOD;

    Depinznd de situaie, definiia se poate adapta peinclusiv pentru cazul n care informaia este un array dinamic

    e aloc array-ului.conin un pointer spre spaiul heap care sIn cele ce urmeaz, avnd n vede

    implementa operaiile, se insist asupra operaiilor de traversare, cutare i nserare,considernd arborii binari de cutare. Problema distrugerii unui astfel de arbore se abordeaz

    n legtur cu exemplul de la sfritul capitolului.

    1.6.1 Traversarea arborilor binariTraversarea arborilor binari se realizeaz ca o traversare total, n una din ordinile

    definite anterior: preordine, ordine simetric ipotrivit metodei rfiecare nod este diponibil, pe rnd, pentru a i se a

    ca telor, se presupune c aceste operaii sunt realizate de funcia tratare(p), undepeste pointerul la nodul curent disponibil. Traversarea poate fi programat ntr-o funcietraversare() care poate fi realizat iterativ sau recursiv. In cele ce urmeaz se dicut, ndetaliu, numai traversarea n ordine simetric. Pentru celelalte tipuri de traversri, se potimplementa funcii, ntr-o manier analog, modificnd corespunztor funcia pentrutraversare simetric

    Traversarea iterativ n ordine simetric presupune o funcie care primete unpointer spre rdcina arborelului i, la fiecare nod vizitat, apeleaz funcia de tratare pe care oofer programul. Deos SRD

    n ordine simetric, subarborele stnga, s se revin la rdcina acestuia, adic s se fac orevenire n sens invers direciei legturii lleft. Acest lucru nu este posibil, dect dacrdcinile subarborilor, n ordinea n care se trece prin ele nainte, sunt memorate. Dacmemorarea acestora se realizeaz ntr-o stiv nlnuit, atunci drumul napoi se poatereconstitui extrgnd adresele de noduri n ordine invers memorrii, adic exact aa cumlucreaz o astfel de list.

    In listingul 1.13, n care se prezint codul funciei de traversare, stiva de lucru arepartea de informaie de tip adres, denumit adrnodarbore, partea de legtur estedenumiturmator, iat vrful stivei este gestionat prin pointerul top. Pentru a distinge celedou tipuri de noduri, tipul pentru nodul arborelui este denumitNODABORE, iar cel al stivei delucru esteNODSTIVA. In fine, se face ipotez simplificatoare, c exist spaiu heap suficient

    pentru a putea construi stiva i pentru cea mai lung ramur a arborelui. Dealtfel, ipoteza esteplauzibil, dac se are n vedere c la o ramur de 10000 de noduri, sunt necesari 80000de bytes sau cca. 80 KB, ceea ce, pentru memoriile actuale este un spaiu mic.

  • 8/4/2019 Capitole de Program Are Procedural A

    35/119

    Prof.dr.Valer Roca ULBS 35

    NODSTIVA *to

    * Pune adresa nodului arborelui in stiva */IVA*)malloc(sizeof(NODSTIVA));

    nodarbore = p;ator = top;

    al arborelui din stiva */

    e nod p */are spre dreapta */

    se urmrete codul funciei, se constat c stiva crete, iniial de la stiv vid,a n arbore (p = p->llink), pn la un nodnd un astfel de nod a fost introdus n stiv,

    sarea stnga se oprete, se extrage nodul din vrful stivei, acesta se trateaz, prin

    ru arbori voluminoi, avn n vedere s l deect spaiul heap i necesarul de memorie mai

    link != NULL) traversareSRD(k->llink);

    Listingul 1.13 Structura funcie de traversare iterativ n ordine simetric a unui arborebinarvoid traversareSRD(NODARBORE* cap)

    p, *r;{NODARBORE *p;p=cap;

    top=NULL;do

    ULL) /{ while(p!=N= (NODST{r

    r->adrr->urmtop = r;

    /p = p->llink; /* Deplasare spre stanga *}/* Extrage o adresa de nodif(!top)

    {p = top->adrnodarbore;top = top->urmator;r = top;

    ;free(r)tratare(p); /* Tratar

    plasp = p->rlink; /* De}

    }while(p != NULL || top != NULL);}

    Dacpe msur ce se face o deplasare spre stng

    are succesor stnga vid. In momentul ccareeplad

    funcia tratare(p)i apoi se face o schimbare a direciei de deplasare (p = p->rlink),spre subarborele dreapta al nodului tratat. Ciclul este reluat cu punerea n stiv a noduluirdcin al acestui subarbore i acesta este traversat spre stnga, pn la un nod cusuccesor stnga vid etc, atta timp ct cel puin unul dintre pointerulpi pointerul top suntnenuli, adic mai exist noduri ne vizitate.

    Traversarea recursiv n ordine simetric poate fi o tratare mai simpl, din punctde vedere algoritmic, aa cum rezult din listingul 1.14. O astfel de abordare, dei extrem desimpl, poate s nu dea satisfacie, pent d paiu

    i sistem, mult mai redus dmemoriei al stivemare pentru a memora n stiv strile de ntrerupere, potrivit tehnicii de execuie a funciilorrecursive.

    Listingul 1.14 - Structura funcie de traversare recursiv n ordine simetric a unui arborebinarvoid traversareSRD(NODARBORE* k)if(k->l{tratare(k);if((k = k->rlink)!= NULL) traversareSRD(k);

    }

  • 8/4/2019 Capitole de Program Are Procedural A

    36/119

    Prof.dr.Valer Roca ULBS 36

    r, la arborii binari de cutare,uctura este definit de o relaie de dominare definit pe informaia coninut n noduri.est ti de arbore i gsete utilizarea n programele care stocheaz iniial informaii care

    posed diferite momemte de timp, verific dac o anumitforma

    ie, n ipoteza cunoscut cu privire la structura de nod. Funcia primete pointerul cap ladcin

    int terminat, succes, rel;

    info);

    ia s-a gasit */

    rminat*/!= NULL) p = p->llink;

    exista */

    succes);

    zat o ciclare pe baza variabilei boolene terminat carete valoare 1 (adevrat), fie atunci cnd s-a gsit nodul ce conine

    maia, fie atunci cnd se ajunge la un nod terminal. Variabila succes, de acelai tip,ideniaz

    1.6.2 Cutarea n arbori binariAa dup cum s-a artat ntr-un paragraf anterio

    rstAc p

    o astfel de structur i apoi, lain ie este deja achiziionat. Datorit modului de memorare, pentru mulimi mari deinformaii, un arbore de cutare poate fi mai eficient, ca timp mediu de cutare, dect o

    stocare a acestor informaii ca un array, la care trebuie adugati facilitatea ca arborele screasc dinamic, prin inserare, atunci cnd o informaie nu se gsete, dar se vrea adugareaei.

    In acest paragraf, se consider cutarea ca un proces pur, ceea ce presupune carborele este deja creat i o funcie, denumitfuncie de cutare, stabilete dac o informaie,dat ca parametru, este sau nu n arborele de cutare. In listingul 1.15 este redat o astfel defunc

    r a arborelui i intoarce ca rezultat un pointer validp la ultimul nod vizitat. Din punct devedere algoritmic, cutarea se realizeaz prin deplasare alternativ, cu pointerulp, fie nsubarborele stnga fie n subarborele dreapta, n raport de relaia informaiei dat deparametrul infodat cu informaia info, coninut de nodul curent. Se constat cu uurin

    c informaia nu exist n arbore, dac n procesul deplasrii prin subarbori s-a ajuns primadat la un nod frunz (terminal).

    Listingul 1.15 - Structura funcie de cutare pe un arbore binar de cutareint cautarearb(NODARBORE* cap, char* infodat, NODARBORE** pp){NODARBORE *p;

    terminat = succes = 0;p = cap;do{rel = strcmp(infodat, p->

    matif(rel == 0) /* Infors = 1;{succeterminat = 1;}else /* Informatia inca nu s-a gasit */

    Deplasare stanga sau te{if(rel < 0) /*{if(p->llinkelse terminat = 1; /* Informatia nu}

    else /* Deplasare dreapta sau terminat */{if(p->rlink != NULL) p = p->rlink;else terminat = 1; /* Informatia nu exista */}

    }}while(!terminat);*pp = p;

    (return}

    s-a utiliIn codul funciei,pleac pe 0 (fals) i primein

    ev

    for

    eecul cutrii (valoarea 0) sau succesul acestui proces (valoarea 1). In final,funcia ntoarce valoarea variabilei succesi pointerulp al ultimului nod vizitat. In acest fel,

  • 8/4/2019 Capitole de Program Are Procedural A

    37/119

    Prof.dr.Valer Roca ULBS 37

    ebuiedecarat

    te, din aproaperoape, un arb are. Operaia de nserare se poate grama ca o funciese existent un nod nou. Inserarea unui nod

    u treb

    iderare situaia spaiului heapilimin

    har* infodat)

    u */

    ore vid, prima inserare */dupa o cautare */dat, &p) ;

    dat, p->info)p->llink = q;= q;

    arcat faptul c, n funcia de nserare s-a apelat funcia de cutarerb(). Variabila s recepioneaz rezultatul cutrii i, n cazul n care aceasta are

    a 1, informaia exist deja n nod, de aceea, nodul qpregtit deja se elibereaz,oarec

    procedura de cutare poate fi utilizat n operaii de nserare (vezi paragraful urmtor), nodulultim vizitat va fi cel de care se va lega un nod nou, atunci cnd informaia nu s-a gsit.

    Se atrage atenia asupra modului de declarare a parametruluipp, ca pointer lapointerla nod, adic sub formaNODARBORE** pp, pentru ca adresa nodului ultim vizitat spoat ajunge n apelator. In consecin, pentru apel, parametrul actual corespunztor tr

    sub formaNODARBORE *pi transferat prin adres, sub forma &p.

    1.6.3 Inserare n arbori binari de cutareOperaia de nserare este mecanismul general prin care se construie

    ore binar de cut pron ape nd rare care, la fiecare apel, adaug la arboreleon uie s implementeze relaia de dominare specific.

    In listingul 1.16 este prezentat o astfel de funcie care primete, n lista de parametri,pointerul la rdcina arborelui, la primul apel pointer NULL, i un pointer la informaia de pus

    n nod. La fel ca alte funcii de nserare, funcia ia n conse situaia cnd aceeai informaie se repetat. Situaiile acestea sunt codificate ctreapelator, prin rezultatul ntreg pe care funcia l ntoarce: valoarea 1, dac spaiul s-a epuizat,valoarea 0, dac a existat spaiu pentru nod i nserarea s-a realizat i valoarea 2, dacinformaia respectiv exist deja ntr-un nod.

    Listingul 1.16 - Structura funcie de nserare n arbore binar de cutareint inserarearb(NODARBORE** cap, cNODARBORE *p, *q;{int s;q = (NODARBORE*)malloc(sizeof(NODARBORE));

    * Lipsa spatiu */if(!q) return 1; /atire nod no/* Preg

    strcpy(q->info, infodat);q->llink = q->llink = NULL;

    //* Inserarea nodului q *if(!*cap) *cap = q; /* Arbelse /* Inserare

    nfo{s = cautarearb(*cap, iif(s == 1) /* Informatia exista deja */{free(q);return 2;}

    odului nou *//* Legarea nif(strcmp(info

    p->rlinkelse

    return 0;}}

    rebuie remTreacauta

    oareval

    ed e inserarea nu se face. Dac informaia nu exist, nodulp este cel la care trebuielegat nodul q, fie ca succesor stnga, fie ca succesor dreapta, pe baza relaiei de compararea informaiei infodat cu informaiainfo dinp.

  • 8/4/2019 Capitole de Program Are Procedural A

    38/119

    Prof.dr.Valer Roca ULBS 38

    .7 Realizarea programelor care utilizeaz structuri dinamicePentru a putea manipula, cu mai mult uurin, diferitele structuri de date, inclusiv n

    tuaiile n care se utilizeaz mai multe structuri de aceali fel, n programul respectiv, este

    rii

    mtrebuie Inacest p ficm definirea tipului prin dou astfel de structuri dinamice.

    ozelem} DINARRAY;

    pact alocat, la un anumit moment, acelei

    pot fi coninute n spaiul alocat, iaray, potrivit conveniei de numerotare

    lem,buie s fie vid, adic este necesar

    inamice trebuie s primeasc valorile sauestea nu vor fi sau vor fi modificate ndual, aa cum s-a artat ntr-un paragraf

    1sirecomandabil s se construiasc tipuri de date: list arbore, array etc.

    Un astfel de tip de dat, n tehnica programrii procedurale, poate fi conceput ca unarticol, descris prin struct, care s defineasc elemntele caracteristice drept cmpuri:pointeri, numr de noduri (elemente), unitatea de alocare i realocare etc. In aceste condiii,utiliznd aceleai funcii, se pot manevra structuri de acelai fel, prin declararea de variabilede acest tip. Fiecare astfel de structur dinamic este perfect identificat de o variabili, prinnotaia prefixat cu punct, cu numele acelei variabile, se disting elementele, fr confuzie.Tipurile struct definite pot s fie extinse ulterior, n tehnica programrii cu tipuri abstractesau n tehnica programrii orientat pe obiecte, astfel nct s ncorporeze i funciile demanipulare, prin utilizarera limbajului C++. Mai mult, se poate depi specificitatea impus detipul de dat al informaiei din noduri, dac se utilizeaz proprietatea de genericitate evitndu-se constucia sistemului de funcii pentru fiecare caz.

    In concluzie, pentru a construi un program bun pentru lucrul cu o structur dinamicde date, cu informaii de un anumit fel, este necesar s se in seama de urmtoarelerecomandri:

    s se defineasc un tip de dat pentru nodul (elementul) structurii care sintroduc tipul de dat al elementelori, dac este cazul, legturile; s se defineasc un tip de dat pentru structura respectiv care s cuprindelementele caracteristice ale acelui fel de structur; s se declare i s se defineasc funciile necesare, pentru utilizarea structurespective.

    In paragrafele anterioare, s-a artat cum se definete tipul de dat pentru nod i cuabordat construcia funciilor necesare manevrrii structurii dinamice respective.

    ct, exempliun

    Pentru un array dinamicse poate declara un tip de forma:

    ypedef structt{informatie* pblock;int nelem;int p

    l heap comunde pblock este pointerul la spaiu

    structurii, nelemreprezint numrul de elemente careozelem arat poziia ultimului element prezent n arrp

    dat de limbaj. Pe baza tipului declarat, se poate defini o variabilvectdin, sub forma:

    DINARRAY vectdin;

    i pot fi referite aceste elemente ca vectdin.pblock, vectdin.nevectdin.pozelem. La momentul de nceput, stuctura tre

    se iniializeze structura declarat, sub forma:s

    vectdin.pblock = NULL;vectdin.nelem = 0;vectdin.pozele = -1;

    In aceste condiii, funciile asociate structurii dadresele elementelor caracteristice, dup cum ac

    nciile respective. Ele pot s fie transferate indivifu

  • 8/4/2019 Capitole de Program Are Procedural A

    39/119

    Prof.dr.Valer Roca ULBS 39

    ea ce impune o ini . In funciileparametrului formal corespunztor,

    ementele necesare trebuie referite n notaia prefixat

    A* fata;

    NODCODA* spate;} COADA;

    respunztor, se

    ge una din variante, pentru transferulse poate utiliza, fr complicaii

    un alt fiier*.c. In felul acesta, se obin structuri

    u succes n cazul n care funciile care utilizeaz tipul respectiv nu sunt recursive.cazul

    varea de

    abetic, pentru o mulime

    vinte, a construi lista cerut revine la aaliza o sortare a acestora, pe baza ordinii lexicografice. Cum mulimea are un numr

    imi diferite, este preferabil s se utilizeze un arbore de sortare,a cum

    list sortat a cuvintelor. Trebuie inut seama c aceast list poate fi mare i, n plus, trebuie

    anterior sau, mai comod, global, ca un unic parametru, sub forma unui pointe