Sunteți pe pagina 1din 96

Academia de Studii Economice din Bucureti

Facultatea de Cibernetic, Statistic i Informatic Economic Catedra de Informatic Economic

Ctlina-Lucia Cocianu

Cristian Uscatu

Programarea calculatoarelor
Material didactic pentru ID
Acest material are la baz lucrarea Programarea calculatoarelor. Algoritmi n programare autori I. Gh. Roca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C. Uscatu, M. Mircea publicat la Editura ASE Bucureti, 2007

Editura ASE Bucureti 2010

Programarea calculatoarelor

Copyright 2011, Ctlina Cocianu, Cristian Uscatu Toate drepturile asupra acestei ediii sunt rezervate autorului

Editura ASE Piaa Roman nr. 6, sector 1, Bucureti, Romnia cod 010374 www.ase.ro www.editura.ase.ro editura@ase.ro

Refereni: Prof. univ. dr. Ion Gh. ROCA Prof. univ. dr. Gabriel ZAMFIR

ISBN 978-606-505-464-6

* Material didactic pentru ID *

Titlul cursului:

Programarea calculatoarelor

Introducere: Cursul Programarea calculatoarelor se adreseaz studenilor facultii de Cibernetic, Statistic i Informatic Economic din cadrul Academiei de Studii Economice din Bucureti. Conform planului de nvmnt, cursul se desfoar n semestrul 2 al anului 2 de studiu. Cursul Programarea calculatoarelor este destinat dezvoltrii cunotinelor studenilor n cteva aspecte fundamentale ale programrii calculatoarelor: lucrul cu structuri dinamice de date, elemente de teoria grafurilor cu aplicaii, noiuni de programare orientat obiect. Limbajul utilizat pentru demonstrarea conceptelor i aplicaiile practice este C standard (ANSI C). Obiectivele principale ale cursului vizeaz nsuirea de ctre studeni a cunotinelor teoretice i a abilitilor practice de lucru referitoare la urmtoarelor elemente din cadrul programrii calculatoarelor: structuri dinamice de date liniare, modul de lucru cu acestea i aplicabilitatea acestora; elemente de teoria grafurilor; structuri dinamice de date arborescente, modul de lucru cu acestea, utilitatea i aplicabilitatea lor; noiuni de programare orientat obiect. Cursul Programarea calculatoarelor este structurat n patru uniti de nvare, corespunztoare elementelor principale studiate prezentate mai sus. n cadrul procesului de instruire pot fi utilizate ca resurse suplimentare materialele puse la dispoziie de biblioteca facultii, att n cadrul slii de lectur ct i prin serviciul de mprumut. De asemenea, laboratoarele Catedrei de Informatic Economic pot fi utilizate pentru studiu individual pe toat durata programului de lucru, atunci cnd nu se desfoar ore (programarea orelor n laboratoare poate fi consultat la secretariatul Catedrei). Calculatoarele din laboratoare ofer medii de programare adecvate pentru dezvoltarea abilitilor de lucru n limbajul C i posibilitatea de accesare a bibliotecii virtuale a instituiei. Evaluarea cunotinelor se va realiza prin intermediul lucrrilor susinute pe parcursul semestrului astfel: o prob practic, constnd n rezolvarea a dou probleme de programare din cadrul tematicii cursului; o lucrare scris. Ambele teste se susin n timpul orelor aferente temelor de control din calendarul disciplinei. Cele dou teste contribuie la formarea notei finale astfel: proba practic constituie 40% din nota final; lucrarea scris constituie 50% din nota final; din oficiu se acord 10%.

Programarea calculatoarelor

CUPRINS
1. Grafuri ................................................................................................................................... 5 1.1 Definiii i reprezentri ale grafurilor .............................................................................. 5 1.2 Parcurgerea grafurilor n lime....................................................................................... 8 1.3 Metoda de parcurgere df (Depth First) .......................................................................... 10 1.4. Drumuri n grafuri. Conexitate ...................................................................................... 13 1.5. Drumuri de cost minim ................................................................................................... 17 1.6. Rspunsuri la testele de autoevaluare. Comentarii...................................................... 20 2. Structuri dinamice de date. Liste ...................................................................................... 26 2.1. Reprezentarea listelor ..................................................................................................... 26 2.2. Operaii primitive asupra listelor liniare ...................................................................... 27 2.3. Liste circulare .................................................................................................................. 32 2.4. Stive i cozi ....................................................................................................................... 35 2.5 Rspunsuri la testele de autoevaluare. Comentarii....................................................... 37 3. Structuri arborescente ....................................................................................................... 43 3.1. Definiii i caracterizri ale grafurilor arbori............................................................... 43 3.2. Reprezentri i parcurgeri ale arborilor orientai ....................................................... 45 3.3. Arbori pariali; algoritmul kruskal ............................................................................... 48 3.4. Arbori binari. Arbori binari de sortare......................................................................... 50 3.5. Arbori de structur ......................................................................................................... 54 3.6. Rspunsuri la testele de autoevaluare. Comentarii...................................................... 59 4. Elemente de programare orientat obiect........................................................................ 64 4.1. Modelul de date orientat obiect ...................................................................................... 64 4.2. Definirea claselor ............................................................................................................. 70 4.3. Constructori ..................................................................................................................... 73 4.4. Destructori........................................................................................................................ 76 4.5. Funcii prieten.................................................................................................................. 77 4.6. Derivarea claselor ............................................................................................................ 78 4.7. Rspunsuri i comentarii la testele de autoevaluare .................................................... 93 Bibliografie .............................................................................................................................. 96

* Material didactic pentru ID *

1. Grafuri
Obiective Principalele obiective vizeaz nsuirea i aprofundarea de ctre studeni a noiunilor fundamentale cu care se opereaz n studiul grafurilor: reprezentri i parcurgeri ale grafurilor, conectivitate, drumuri n grafuri.
Cuprins 1.1 Definiii i reprezentri ale grafurilor ........................................................................................... 5 1.2 Parcurgerea grafurilor n lime.................................................................................................... 8 1.3 Metoda de parcurgere df (Depth First) ....................................................................................... 10 1.4. Drumuri n grafuri. Conexitate ................................................................................................... 13 1.5. Drumuri de cost minim ................................................................................................................ 17 1.6. Rspunsuri la testele de autoevaluare. Comentarii ................................................................... 20 Timpul mediu de nvare pentru studiul individual: 8 ore.

1.1 Definiii i reprezentri ale grafurilor


Definiia 1.1.1. Se numete graf sau graf neorientat o structur G=(V,E), unde V este o mulime nevid iar E este o submulime posibil vid a mulimii perechilor neordonate cu componente distincte din V. Elementele mulimii V se numesc vrfuri, iar obiectele mulimii E se numesc muchii. Dac e E, e = (u,v) not.uv , vrfurile u i v se numesc extremiti ale lui e, muchia e fiind determinat de vrfurile u i v. Dac e=uv E se spune c vrfurile u, v snt incidente cu muchia e. Definiia 1.1.2. Fie G=(V,E) graf. Vrfurile u, v snt adiacente n G dac uv E. Definiia 1.1.3. Graful G=(V,E) este graf finit, dac V este o mulime finit. Definiia 1.1.4. Fie Gi =(V i,Ei), i=1,2 grafuri. G2 este un subgraf al grafului G1 dac V2 V1 i

E 2 E1 . G2 este este un graf parial al lui G1 dac V2=V1 i G2 este subgraf al lui G1.
Definiia 1.1.5. Un digraf este o structur D=(V,E), unde V este o mulime nevid de vrfuri, iar E este o mulime posibil vid de perechi ordonate cu componente elemente distincte din V. Elementele mulimii E snt numite arce sau muchii ordonate. Un graf direcionat este o structur D=(V,E), unde V este o mulime nevid de vrfuri, iar E este o mulime posibil vid de perechi ordonate cu componente elemente din V, nu neaprat distincte. Evident, orice digraf este un graf direcionat. Definiia 1.1.6. Se numete graf ponderat o structur (V,E,W), unde G=(V,E) este graf i W este o funcie definit prin W : E (0 , ) . Funcia W este numit pondere i ea asociaz fiecrei muchii a grafului un cost/ctig al parcurgerii ei. Definiia 1.1.7. Fie G=(V,E) un graf, u,vV. Secvena de vrfuri :u0,u1,..,un este un u-v drum dac u0=u, un=v, uiui+1E pentru toi i, 0 i n . Definiia 1.1.8. Fie G=(V,E) un graf. Elementul vV se numete vrf izolat dac, pentru orice e E, u nu este incident cu e. Cea mai simpl reprezentare a unui graf este cea intuitiv, grafic; fiecare vrf este figurat printr-un punct, respectiv muchiile snt reprezentate prin segmentele de dreapt, orientate (n cazul digrafurilor) sau nu i etichetate (n cazul grafurilor ponderate) sau nu, avnd ca extremiti punctele corespunztoare vrfurilor care o determin. Exemple 1.1.1. Fie G=(V,E) graf, cu V={1,2,3,4,5,6}, E={(1,2),(1,3),(2,5),(3,5),(5,6)}. O posibil reprezentare grafic este,

Programarea calculatoarelor

1 2 4 3 5 6

1.1.2. Fie D=(V,E) digraf, V={1,,5}, E={(1,2), (1,3), (1,5), (2,5), (3,5), (4,1), (5,4)}. Digraful poate fi reprezentat grafic astfel,
1

2 3

1.1.3. Fie G=(V,E,W) graf ponderat, V={1,2,3,4}, E={(1,2), (1,3), (1,4), (2,3), (2,4)}, W((1,2))=5, W((1,3))=1, W((1,4))=7, W((2,3))=4, W((2,4))=2. O posibil reprezentare grafic este:
1

1 4 7

2
2

n scopul reprezentrii grafurilor n memoria calculatorului snt utilizate n general urmtoarele structuri de date. Reprezentarea matriceal Grafurile, digrafurile i grafurile direcionate pot fi reprezentate prin matricea de adiacen. Dac G=(V,E ) este graf, digraf sau graf direcionat cu V = n , atunci matricea de adiacen A Mnxn({0,1}) are componentele aij =

1, dac (vi , v j ) E 0 , altfel

, unde vi, vj reprezint cel de-al i-lea,

respectiv cel de-al j-lea nod din V. n cazul unui graf neorientat, matricea de adiacen este simetric. Exemplu 1.1.4. Graful din exemplul 1.1.1 i digraful din exemplul 1.1.2sunt reprezentate prin,
0 1 1 A= 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 (1.1.1), A = 0 0 1 1 0 0
1 1 0 1 0 0 0 1 0 0 0 1 (1.1.2) 0 0 0 0 0 0 1 1

n cazul grafurilor ponderate, reprezentarea poate fi realizat prin matricea ponderilor. Dac G=(V,E,W) este graf ponderat, V =n, W Mnxn((0, )) are componentele,

W (( vi , v j )), dac (vi , v j ) E , unde vi, vj reprezint cel de-al i-lea, respectiv cel de-al j-lea wi , j = , altfel nod din V, = 0 , dac ponderea are semnificaia de ctig, respectiv = n cazul n care se dorete

reprezentarea costurilor ca ponderi ale grafului.

* Material didactic pentru ID *

Exemplu 1.1.5. Presupunnd c ponderile reprezint costuri, matricea de reprezentare a grafului din

5 exemplul 1.1.3. este W = 1 7

5 1 7 4 2 . 4 2

Reprezentarea tabelar Reinnd muchiile prin intermediul extremitilor i eventual valoarea ponderii ei, se obine reprezentarea tabelar, mai economic din punctul de vedere al spaiului de memorie necesar. Dac graful conine vrfuri izolate atunci este necesar pstrarea acestora ntr-un vector suplimentar VS. Mulimea muchiilor este reinut ntr-o matrice A cu E linii i c coloane, unde c=2 dac graful nu este ponderat, altfel c=3. n primele dou coloane se scriu perechile de vrfuri ce determin muchiile, n cazul grafurilor ponderate cea de-a treia coloan conine valoarea ponderii muchiei respective. Exemple
T

1 1 2 3 5 1.1.6. Graful din exemplul 1.1.1 poate fi reprezentat astfel, VS=(4), A = 2 3 5 5 6 1 1 1 2 3 4 5 1.1.7. Digraful din exemplul 1.1.2 este reprezentat prin A = 2 3 5 5 5 1 4 .
1 1 1.1.8. Graful ponderat din exemplul 1.1.3. este reprezentat prin A = 2 1 2 2 5 3 1 3 4 . 4 7 4 2
T

Reprezentarea prin intermediul listelor Aceast reprezentare permite utilizarea economic a spaiului de memorare i, n anumite cazuri, implementri mai eficiente pentru anumite clase de algoritmi. Vrfurile grafului snt memorate ntr-o list, fiecare nod al listei N coninnd o referin spre lista vecinilor vrfului memorat ca informaie n N. Dac graful nu este ponderat, el poate fi reprezentat prin structura list de liste, i anume: nodurile grafului se trec ntr-o list L_nod, fiecare celul avnd structura, Informaie legtur vecini legtur nod urmtor Unde cmpul informaie conine identificatorul nodului, legtur vecini reprezint referina spre nceputul listei vecinilor i legtur nod urmtor conine adresa urmtoarei celule din lista L_nod. Un graf ponderat poate fi reprezentat n mod similar, cu diferena c, fiecare celul din lista vecinilor conine i ponderea muchiei respective.

Teste de autoevaluare
1.1. Fie graful G=(V,E) graf, cu V={1,2,3,4,5,6}, E={(1,2),(1,3),(2,5),(2,6),(3,5),(3,6),(5,6)}. Care este matricea de adiacen a grafului? 1.2. Fie D=(V,E) digraf, V={1,,5}, E={(1,2), (1,3), (2,5), (3,5), (4,1), (5,1), (5,3), (5,4)}. Care este matricea de adiacen a digrafului? 1.3. Fie D=(V,E) digraf, V={1,,5}, E={(1,2), (1,3), (2,5), (3,5), (4,1), (5,1), (5,3), (5,4)}. Care este reprezentarea tabelar a digrafului? 1.4. Fie G=(V,E,W) graf ponderat, V={1,2,3,4}, E={(1,2), (1,4), (2,3), (2,4), (3,4)}, W((1,2))=5, W((1,4))=7, W((2,3))=4, W((2,4))=2, W((3,4))=1. Specificai matricea ponderilor.

Programarea calculatoarelor

1.2 Parcurgerea grafurilor n lime


Modalitatea de vizitare a tuturor vrfurilor grafului n care fiecare vrf al grafului este vizitat o singur dat se numete parcurgere sau traversare. n acest material snt prezentate metodele de parcurgere BF (n lime) i DF (n adncime). Metodele de parcurgere sunt aplicate grafurilor neorientate respectiv grafurilor direcionate i presupun selectarea unui vrf iniial v0 i identificarea acelor vrfuri ale grafului v cu proprietatea c exist cel puin un drum de la vrful iniial ctre v.

Grafurile cu proprietatea c oricare dou vrfuri sunt conectate printr-un drum se numesc grafuri conexe i sunt prezentate n 1.4. Dac graful este conex, atunci prin aplicarea metodelor de parcurgere vor fi identificate toate vrfurile grafului. Cele dou modaliti de parcurgere sunt prezentate n continuare n cazul grafurilor neorientate, extinderea la digrafuri i grafuri direcionate fiind imediat.
Traversarea BF presupune parcurgerea n lime a grafului, n sensul c, vrfurile grafului snt prelucrate n ordinea cresctoare a distanelor la vrful iniial. Distana de la u la v, notat (u , v ) , este numrul de muchii ale unui cel mai scurt u-v drum. La momentul iniial vrf curent este v0. Deoarece vrful curent la fiecare moment trebuie s fie unul dintre vrfurile aflate la distan minim de v0 se poate proceda n modul urmtor: iniial lui v0 i se asociaz valoarea 0, d [v0 ] = 0 i fiecrui vrf v v0 i se asociaz valoarea , d [v ] = . Dac valoarea asociat vrfului curent este m, atunci fiecruia dintre vecinii acestuia de valoare li se asociaz valoarea m+1. Se observ c, dac dup ce toate vrfurile de valoare m au fost considerate i nici unui vrf nu i-a fost recalculat valoarea, atunci toate vrfurile conectate cu v0 au fost vizitate, deci calculul se ncheie. Exemple 1.2.1. Fie graful,
2 1 3 6 4 5 7

i v0=1.Valorile calculate prin aplicarea metodei prezentate snt,


vrf d 0 1 2 0 0 0 0 1 2 3 4 5 6 7

1 1 1

1 1 1

1 1 1


2 2


2 2

1 1 1

Fie G=(V,E) un graf, V = n . O alternativ de implementare a metodei BF este construit prin utilizarea urmtoarelor structuri de date, A matricea de adiacen a grafului; o structur de tip coad, C, n care snt introduse vrfurile ce urmeaz a fi vizitate i procesate (n sensul cercetrii vecinilor lor); un vector c cu n componente, unde, 1, dac i a fost adugat n coad ci = 0, altfel Componentele vectorului c snt iniializate cu valoarea 0.

* Material didactic pentru ID *

Parcurgerea BF poate fi descris astfel, coada C este iniializat cu vrful v0; ct timp C , este extras i vizitat un vrf i din coad, apoi snt introdui n coad vecinii lui i care nu au fost deja introdui (acele vrfuri k cu proprietatea c c[k]=0 i a[i][k]=1). Vrfurile i ce au fost introduse n coad snt marcate prin c[i]=1. Exemplu 1.2.2. Pentru graful din exemplul 1.2.1., aplicarea metodei de traversare BF determin urmtoarea evoluie, c t t=1 t=2 t=3 t=4 t=5 t=6 t=7 t=8 c t t=1 t=2 t=3 t=4 t=5 t=6 t=7 t=8 1 2 3 4 7 5 6 3 4 7 5 6 4 7 5 6 7 5 6 1 1 1 1 1 1 1 1 1 2 0 1 1 1 1 1 1 1 3 0 1 1 1 1 1 1 1 4 0 1 1 1 1 1 1 1
5 0 0 1 1 1 1 1 1 6 0 0 0 1 1 1 1 1 7 0 1 1 1 1 1 1 1

Observaie Deoarece graful din exemplul 1.2.1. este conex, traversarea BF realizeaz vizitarea tuturor vrfurilor grafului. Aplicarea metodei BF unui graf neconex nu determin vizitarea tuturor vrfurilor. Cu alte cuvinte, metoda BF aplicat unui graf determin vizitarea tuturor vrfurilor care snt conectate cu vrful iniial selectat.

Teste de autoevaluare. Probleme


1.2.1. Fie graful,

1 2 3

4 5 7

9 11

10

i v0=1. Prezentai tabelul valorilor distanelor rezultat prin aplicarea metodei BF. 1.2.2. Scriei o surs C pentru implementarea metodei BF. 1.2.3. Fie graful de la testul 1.2.1. Care este ordinea de parcurgere BF pentru v0=3?

Programarea calculatoarelor

10

1.2.4. Fie graful


1 2 4 3 5 6

Care este ordinea de parcurgere BF pentru v0=2? 1.2.5. Fie graful, Prezentai evoluia determinat de aplicarea metodei de traversare BF pentru vrful iniial v0=3.

9 2 5 7 6 3 4 8

1.3 Metoda de parcurgere DF (Depth First)


Ideea metodei DF revine la parcurgerea n adncime a grafurilor. Considernd v0 vrf iniial i M mulimea vrfurilor vizitate de procedur, pentru vizitarea vecinilor este considerat unul din vrfurile din M cu proprietatea c lungimea drumului calculat de metod pn la vrful iniial v0 este maxim. Implementarea acestei metode poate fi realizat n mai multe moduri, pentru meninerea mulimii vrfurilor grafului disponibilizate pn la momentul curent fiind utilizat o structur de de date de tip stiv S. La momentul iniial se introduce n stiv v0. La fiecare pas, se preia cu tergere ca vrf curent vrful stivei S i se introduc n stiv vecinii nc nevizitai ai vrfului curent. Un vrf se marcheaz ca vizitat n momentul introducerii lui n S. Calculul continu pn cnd este efectuat un acces de preluare din stiv i se constat c S este vid. Pentru gestiunea vrfurilor vizitate, se utilizeaz un vector c cu n componente, unde n reprezint numrul vrfurilor grafului i, la fiecare moment, componentele snt:

1, dac i a fost vizitat ci = 0, altfel


Componentele vectorului c vor fi iniializate cu valoarea 0. Exemple 1.3.1. Pentru graful,
1 2 3 6 4 5 7

* Material didactic pentru ID *

11

i v0=1, prin aplicarea metodei descrise, rezult urmtoarea evoluie. c t t=1 t=2 t=3 t=4 t=5 t=6 t=7 t=8 1 1 1 1 1 1 1 1 1 2 0 1 1 1 1 1 1 1 S t t=1 t=2 t=3 t=4 t=5 t=6 t=7 3 0 1 1 1 1 1 1 1 4 0 1 1 1 1 1 1 1 5 0 0 0 0 1 1 1 1 6 0 0 1 1 1 1 1 1 7 0 1 1 1 1 1 1 1

1 7 6 4 5 3 2

4 4 3 3 2

3 3 2 2

2 2

Ordinea n care snt vizitate vrfurilor corespunztor acestei variante de parcurgere DF este: 1, 2, 3, 4, 7, 6, 5. O variant de implementare a metodei DF rezult prin gestionarea stivei S n modul urmtor. Iniial vrful v0 este unicul component al lui S. La fiecare etap se preia, fr tergere, ca vrf curent vrful stivei. Se introduce n stiv unul dintre vecinii vrfului curent nc nevizitat. Vizitarea unui vrf revine la introducerea lui n S. Dac vrful curent nu are vecini nc nevizitai, atunci el este eliminat din stiv i este efectuat un nou acces de preluare a noului vrf al stivei ca vrf curent. Calculul se ncheie n momentul n care este efectuat un acces de preluare a vrfului stivei ca vrf curent i se constat c S este vid. Evident, nici n cazul acestei variante nu vor fi vizitate vrfurile care nu snt conectate cu vrful iniial ales. Exemplu 1.3.2. Pentru graful,
1 2 3 6 4 5 7

Programarea calculatoarelor

12

i v0=1, prin aplicarea metodei descrise, rezult urmtoarea evoluie. c t t=1 t=2 t=3 t=4 t=5 t=6 t=7 t=8 t=9 t=10 t=11 t=12 t=13 t=14 S T t=1 t=2 t=3 t=4 t=5 t=6 t=7 t=8 t=9 t=10 t=11 t=12 t=13 t=14 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 0 1 1 1 1 1 1 1 1 1 1 1 1 1 3 0 0 0 1 1 1 1 1 1 1 1 1 1 1 4 0 0 1 1 1 1 1 1 1 1 1 1 1 1 5 0 0 0 0 0 0 0 0 0 1 1 1 1 1 6 0 0 0 0 1 1 1 1 1 1 1 1 1 1 7 0 0 0 0 0 1 1 1 1 1 1 1 1 1

1 2 4 3 6 7 6 3 4 5 4 2 1

1 2 4 3 6 3 4 2 4 2 1

1 2 4 3 4 2 1 2 1

1 2 4 2 1 1

1 2 1

Ordinea n care snt vizitate vrfurile corespunztor acestei variante este: 1, 2, 4, 3, 6, 7, 5.

Teste de autoevaluare. Probleme


1.3.1. Fie graful, 1

4 5 7

9 11

10

i v0=1. Care este ordinea de vizitare a vrfurilor prin aplicarea metodei DF n prima variant prezentat? 1.3.2. Scriei o surs C pentru implementarea metodei DF n cea de-a doua variant.

* Material didactic pentru ID *

13

1.4. Drumuri n grafuri. Conexitate


Una dintre cele mai importante proprieti ale grafurilor o constituie posibilitatea de accesare, prin intermediul unei secvene de muchii (arce), dintr-un vrf dat a oricrui alt vrf al grafului, proprietate cunoscut sub numele de conexitate sau conexiune. Aa dup cum a rezultat n 1.2. i 1.3, dac G=(V,E) este un graf conex, atunci pentru orice vrf iniial v0 considerat metodele BF i DF permit vizitarea tuturor vrfurilor din V. Definiia 1.4.1. Fie G=(V,E) un graf, u,vV. Secvena de vrfuri : u0, u1,..,un este un u-v drum dac u0=u, un=v, uiui+1E pentru toi i, 0 i n . Lungimea drumului, notat l() este egal cu n. Convenional, se numete drum trivial, un drum cu l()=0. Definiia 1.4.2. Fie : u0, u1,..,un un drum n graful G=(V,E). este un drum nchis dac u0=un; n caz contrar, se numete drum deschis. Drumul este elementar dac oricare dou vrfuri din snt distincte, cu excepia, eventual, a extremitilor. Drumul este proces dac, pentru orice 0 i j n 1 uiui+1 ujuj+1. Evident, orice drum elementar este un proces. Exemplu 1.4.1. Pentru graful,
v2

v4 v5

1: v1, v2, v3, v2, v5, v3, v4 este un v1- v4 drum care nu este proces; 2: v1, v2, v5, v1, v3, v4 este un v1- v4 proces care nu este drum elementar; 3: v1, v3, v4 este un v1- v4 drum elementar.
Definiia 1.4.3. Fie : u0, u1,..,un un drum n graful G=(V,E). : v0, v1,..,vm este un subdrum al lui dac este un drum i pentru orice j, 0 j m , exist i, 0 i n astfel nct ui=vj. Observaie Orice drum cu lungime cel puin 1 conine cel puin un drum elementar cu aceleai extremiti. ntr-adevr, dac : u0, u1,..,un nu este elementar, atunci exist 0 i < j n i i 0 sau j n astfel nct ui=uj. Atunci drumul

v1

v3

u j u j +1 ...u n , dac i = 0 : u0 u1 ...u i , dac j = 0 u u ...u u ...u , dac i 0 , j n 0 1 i j +1 n


'

este de asemenea un u0-un drum. Aplicnd n continuare eliminarea duplicatelor vrfurilor n modul descris, rezult n final un u0-um drum elementar.

Programarea calculatoarelor

14

v2 v1

Exemplu 1.4.2. n graful,

v6 v5 v9

v7 v8 v10

v4 v3

dac : v1, v2, v4, v5, v3, v1, v2, v5, v6, v7, v8, v9, v5, v9, v8, v10, atunci 1: v1, v2, v5, v9, v8, v10, 2: v1, v2, v4, v5, v9, v8, v10 snt v1-v10 subdrumuri elementare.

Matricea existenei drumurilor; algoritmul Roy-Warshall


Lema 1.4.1. Fie G=(V,E) un graf, V = n . Dac A este matricea de adiacen asociat grafului,
( atunci, pentru orice p1, a ijp ) este numrul vi-vj drumurilor distincte de lungime p din graful G, unde

( A p = a ij p ) .

Definiia 1.4.4. Fie Mn({0,1)} mulimea matricelor de dimensiuni nxn, componentele fiind elemente din mulimea {0,1}. Pe Mn({0,1)}se definesc operaiile binare, notate i , astfel: pentru orice A=(aij), B=(bij) din Mn({0,1)}, A B=(cij), A B=(dij), unde 1 i , j n , cij=max{aij, bij} dij=max{min{aik, bkj}, 1 k n }. Dac A=(aij) Mn({0,1)}, se noteaz A = a ij

A a ij
(k )

(1 )

= A, A = A A

( k 1 )

{ ( ); k 1} secvena de matrice definit prin:


k (k )

, k 2 .

Dac A este matricea de adiacen a unui graf G=(V,E), atunci pentru fiecare k, 1 k n 1 ,

1, dac exist drum de la i la j de lungime k = 0 , altfel


(1) (2) ( n 1 )

Matricea M = A A K A se numete matricea existenei drumurilor n graful G. Semnificaia componentelor matricei M este:

0 , dac nu exist vi v j drum n G 1 i , j n , mij = 1, altfel


Exemplu 1.4.3. Pentru graful,

2 1 3

4
0 1 A= 1 1 1 1 1 0 0 0 , 0 0 1 0 1 0 1 2 0 A = 1 1 0 1 1 1 1 1 , 1 1 1 1 1 1 1 3 1 A = 1 1 1 1 1 0 1 1 , 1 1 1 1 1 1 1 1 M = 1 1 1 1 1 1 1 1 1 1 1 1 1 1

* Material didactic pentru ID *

15

Observaie Calculul matricei existenei drumurilor permite verificarea dac un graf dat este conex. Graful este conex dac i numai dac toate componentele matricei M snt egale cu 1. Algoritmul Roy-Warshall calculeaz matricea existenei drumurilor ntr-un graf G cu n vrfuri.
void Roy_Warshall (unsigned char a[10][10],unsigned n,unsigned char m[10][10]) {int i,j,k; for (i=0;i<n;i++) for (j=0;j<n;j++) m[i][j]=a[i][j]; for (j=0;j<n;j++) for (i=0;i<n;i++) if(m[i][j]) for (k=0;k<n;k++) if (m[i][k]<m[k][j]) m[i][k]=m[k][j];}

Datele de intrare snt: n, numrul de noduri i A, matricea de adiacen corespunztoare grafului. Matricea M calculat de algoritm constituie ieirea i este matricea existenei drumurilor n graful G. Definiia 1.4.5. Fie G=(V,E) graf netrivial. Vrfurile u,v V snt conectate dac exist un u-v drum n G. Definiia 1.4.6. Dac G este un graf, atunci o component conex a lui G este un subgraf conex al lui G, maximal n raport cu proprietatea de conexitate. Exemplu 1.4.4. Componentele conexe ale grafului

Componente conexe ale unui graf

1 2

6 4 3
snt: C1={1,2,3}, C2={4,5}, C3={6}. Observaii Un graf este conex dac i numai dac numrul componentelor sale conexe este 1. Mulimile de vrfuri corespunztoare oricror dou componente conexe distincte snt disjuncte. Rezult c mulimile de vrfuri corespunztoare componentelor conexe ale unui graf formeaz o partiie a mulimii vrfurilor grafului. Problema determinrii componentelor conexe corespunztoare unui graf poate fi rezolvat n modul urmtor. Iniial, este selectat drept vrf curent un vrf al grafului pentru care este calculat componenta conex care l conine. Dac exist vrfuri care nu aparin componentei conexe determinate, este ales drept vrf curent unul dintre aceste vrfuri. n continuare este aplicat aceeai metod, pn cnd au fost gsite toate componentele conexe ale grafului. Determinarea componentei conexe care conine un vrf v0 dat poate fi realizat pe baza urmtorului algoritm. Pentru G=(V,E), V = n , n 1 i v0 V, paii algoritmului snt: Pas1: V0={v0}; E0= ; i=0; Pas 2: repet Pas 3 pn cnd Vi=Vi-1 i Ei=Ei-1 Pas 3: i=i+1;

E i = E i 1 {e / e E, u Vi 1 , u incident cu e};

Vi = Vi 1 {v / v V, u Vi 1 , uv E};

Programarea calculatoarelor

16

Ieirea este Gi=(Vi,Ei), componenta conex din care face parte v0. Exemplu 1.4.5. Pentru graful,

Aplicarea algoritmului descris pentru v0=1, determin urmtoarea evoluie: I i=0 i=1 i=2 Vi {1} {1,2,4} {1,2,4,7,8,5} Ei {(1,2),(1,4)} {(1,2),(1,4),(2,7),(2,8),(7,8),(4,5),(4,7),(5,8)}

Teste de autoevaluare.
1.4.1. Fie graful, 1

4 5 7

9 11

10

Cte component conexe are graful? 1.4.2. Pentru graful de mai sus, determinai componenta conex care conine vrful 5, aplicnd algoritmul de mai sus. 1.4.3.Fie graful,

1 2 4 3 5 6

Care este matricea existenei drumurilor?

* Material didactic pentru ID *

17

1.5. Drumuri de cost minim


Definiia 1.5.1. Fie G=(V,E,w) un graf ponderat. Costul drumului : u1,u2,..,un, notat L(), este definit prin:

L( ) = w(u i ,u i +1 ) .
i =1

n 1

Pentru orice u i v vrfuri conectate n G, u v, w-distana ntre u i v, notat D(u,v), este definit prin,

D(u ,v ) = min{L( ), Duv } , unde Duv desemneaz mulimea tuturor u-v drumurilor

elementare din G. Dac Duv este astfel nct D(u,v)=L(), drumul se numete drum de cost minim. Observaie Cu toate c este utilizat termenul de w-distan, n general D nu este o distan n sensul matematic al cuvntului.n particular, dac funcia pondere asociaz valoarea 1 fiecrei muchii a grafului, atunci pentru fiecare pereche de vrfuri distincte ale grafului, costul D(u,v) este lungimea unui cel mai scurt drum ntre cele dou vrfuri. n acest caz D este o distan pe mulimea vrfurilor. Algoritmul Dijkstra Urmtorul algoritm a fost propus de ctre E. W. Dijkstra pentru determinarea w-distanelor D(u0,v) i a cte unui u0-v drum de cost minim pentru fiecare vrf vu0 ntr-un graf ponderat, unde u0 este prestabilit. Fie G=(V,E,w) un graf conex ponderat, u0V, SV, u0S. Se noteaz S = V \ S i

D u 0 , S = min D(u 0 , x ); x S . Fie v S astfel nct D(u0,v)=D(u0, S ), : u0, u1,,upv un u0-v D u 0 , S = min D(u 0 ,u ) + w( uv ); u S , v S ,uv E .
xS, y S astfel nct

drum de cost minim. Evident, 0ip uiS i : u0, u1,,up un u0- up drum de cost minim. De asemenea,

Dac

D(u0 , y ) = D(u0 , x ) + w( xy ) .

D u 0 , S = D(u 0 , x ) + w( xy ) ,

rezult

Pentru determinarea a cte unui cel mai ieftin u0-v drum, algoritmul consider o etichetare dinamic a vrfurilor grafului.Eticheta vrfului v este (L(v),u), unde L(v) este lungimea unui cel mai ieftin u0-v drum determinat pn la momentul respectiv i u este predecesorul lui v pe un astfel de drum. Pentru (V,E,w) graf conex ponderat, V = n i u0V, calculul implicat de algoritmul Dijkstra poate fi descris astfel: Pas 1: i=0; S0={u0}; L(u0)=0, L(v)= pentru toi v V, vu0. Dac n=1 atunci stop Pas 2: Pentru toi v S i , dac L(v)>L(ui)+w(uiv), atunci L(v)=L(ui)+w(uiv) i etiche-teaz v cu (L(v),ui). Pas 3: Se determin d=min{L(v), v S i } i se alege ui+1 S i astfel nct L(ui+1)=d. Pas 4: Si+1=Si {ui+1} Pas 5: i=i+1. Dac i=n-1, atunci stop. Altfel, reia Pas 2. Observaie Dac (V,E,w) graf ponderat neconex, atunci, pentru u0V, algoritmul lui Dijkstra permite determinarea w-distanelor D(u0,v) i a cte unui u0-v drum de cost minim pentru toate vrfurile v din componenta conex creia i aparine u0.

Programarea calculatoarelor

18

Exemplu 1.5.1. Fie graful ponderat,


1 5 2 9 16 2 4 5 5 1

Considernd u0=1, etapele n aplicarea algoritmului Dijkstra snt: P1: i=0; S0={1}; L(1)=0, L(i)= pentru toi i = 2,5 . P2: S 0 ={2,3,4,5}, u0=1 L(2)= >L(1)+5=5 L(2)=5, eticheteaz 2 cu 1 L(3)= >L(1)+1=1 L(3)=1, eticheteaz 3 cu 1 L(4)= >L(1)+9=9 L(4)=9, eticheteaz 4 cu 1 L(5)= , w(1,5)= , deci L(5) nu se modific P3: selecteaz u1=3, L(3)=1, cea mai mic dintre w-distanele calculate la P2 P4: S1={1,3} P5: i=i+1=1 4, reia P2 P2: S1 ={2,4,5}, u1=3 Nu se modific nici o etichet i nici o w-distan (w(3,i)= , pentru toi i din S1 P3: selecteaz u2=2, L(2)=5, cea mai mic dintre w-distanele calculate la P2 P4: S2={1,3,2} P5: i=i+1=2 4, reia P2 P2: S 2 ={4,5}, u2=2 L(4)= 9>L(2)+2=7 L(4)=7, eticheteaz 4 cu 2 L(5)= >L(2)+16=21, eticheteaz 5 cu 2 P3: selecteaz u3=4, L(4)=7, cea mai mic dintre w-distanele calculate la P2 P4: S3={1,3,2,4} P5: i=i+1=3 4, reia P2 P2: S3 ={5}, u3=4 L(5)= 21>L(4)+5=12, eticheteaz 5 cu 4 P3: selecteaz u4=5, L(5)=12, cea mai mic dintre w-distanele calculate la P2 P4: S3={1,3,2,4,5} P5: i=i+1=4, stop. Algoritmul calculeaz urmtoarele rezultate: Vrful v pn la care este calculat w-distana D(1,v), eticheta lui v 1 0, 1 2 5, 1 3 1, 1 4 7, 2 5 12, 4

Drumurile de cost minim de la vrful 1 la fiecare dintre vrfurile grafului se stabilesc pe baza sistemului de etichete astfel: drumul de la 1 la un vrf v este dat de: v1, eticheta lui v, v2 eticheta lui v1 amd, pn se ajunge la eticheta 1. Astfel, v0 -drumurile de cost minim snt: pn la 2: 2,1; pn la 3: 3,1; pn la 4: 4,2,1; pn la 5: 5,4,2,1.

* Material didactic pentru ID *

19

n anumite aplicaii este necesar exclusiv determinarea w-distanelor D(v0,v), pentru toi vV. n acest caz algoritmul Roy-Floyd permite o rezolvare a acestei probleme mai simplu de implementat dect algoritmul Dijkstra. Algoritmul Roy-Floyd Pentru (V,E,w) graf ponderat, V = n i W matricea ponderilor, sistemul de w-distane D(v0,v), vV, poate fi calculat pe baza urmtoarei funcii (similar algoritmului Roy-Warshall),
void Roy_Floyd (float w[10][10],unsigned n,float d[10][10],float MAX) {int i,j,k; for (i=0;i<n;i++) for (j=0;j<n;j++) d[i][j]=w[i][j]; for (j=0;j<n;j++) for (i=0;i<n;i++) if(d[i][j]<MAX) for (k=0;k<n;k++) if (d[i][k]>d[i][j]+d[j][k]) d[i][k]=d[i][j]+d[j][k]; }

Matricea D calculat de algoritm este matricea w-distanelor D(u,v) n graful ponderat conex (V,E,w); pentru orice 1 i , j n

D( vi , v j ), vi , v j sunt conectate d ij = , altfel


ntr-adevr, procedura realizeaz calculul dinamic al w-distanei ntre oricare dou vrfuri i i k, astfel: dac exist un drum i-k drum ce trece prin j ( 1 j n ), cu costul corespunztor (dij+djk) inferior costului curent (dik), atunci noul drum de la i la k via j este de cost mai mic dect costul drumului vechi, deci w-distana ntre i i k trebuie reactualizat la dij+djk.

Teste de autoevaluare.
1.5.1. Fie graful ponderat,
2 2

1 1 9 1 12 4 1 5

Considernd u0=1, descriei rezultatele aplicrii algoritmului Dijkstra acestui graf. 1.5.2. Scriei o surs C pentru implementarea algoritmului Dijkstra

1.5.3. Fie graful,


5 2

1 1 9 16 2 4 5 5

Considernd u0=4, descriei rezultatele aplicrii algoritmului Dijkstra acestui graf.

Programarea calculatoarelor

20

1.5.4. Fie graful ponderat,

4 5 7 1 4 2

3 2 5 3

Care este matricea w-distanelor n graf?

1.6. Rspunsuri la testele de autoevaluare. Comentarii


0 1 1 1.1.1. A = 0 0 0
0 0 1.1.2. A = 0 1 1

1 0 0 0 1 1

1 0 0 0 1 1

0 0 0 0 0 0

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

1 1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 1 0
T

1 2 3 4 5 5 5 1.1.3.Numrul de vrfuri este 5. A = 2 5 5 1 1 3 4 5 7 5 4 2 1.1.4. W = 4 1 7 2 1


1.2.1. Valorile rezultate prin aplicarea metodei snt:
vrf d 0 1 2 1 0 0 0 0 2 3 4 5 6 7 8

10

11

1 1 1

1 1 1


2 2

1 1 1


2 2

1 1 1

* Material didactic pentru ID *

21

Se observ c valorile lui d calculate n final reprezint numrul de muchii corespunztor unui cel mai scurt drum care conecteaz vrful iniial cu vrful respectiv, pentru vrfurile neconectate cu v0 valoarea d[v0] rezultat la terminarea calculului este . 1.2.2 O surs C pentru implementarea metodei BF este urmtoarea.
#include <stdio.h> #include <conio.h> #include <alloc.h> typedef struct nn { int inf; struct nn *leg; } nod,* pnod; int insereaza_coada(pnod *head,pnod *tail,int info) { pnod nou; if(nou=(pnod)malloc(sizeof(nod))){ nou->inf=info; nou->leg=NULL; if(*head==NULL) *head=nou; else (*tail)->leg=nou; *tail=nou; return 1; } else return 0; } int extrage_coada(pnod *head,pnod *tail, int *info) { if(*head){ pnod aux=*head; *info=(*head)->inf; (*head)=(*head)->leg; free(aux); if(*head==NULL)*head=*tail=NULL; return 1; } else return 0;} void breadth_first(int v0,int a[10][10],int n) { pnod head=NULL; pnod tail=NULL; int c[10]; for(int i=0;i<n;c[i++]=0); int r=insereaza_coada(&head,&tail,v0); c[v0]=1; while(head){ r=extrage_coada(&head,&tail,&i); printf("\n%i",i+1); for(int k=0;k<n;k++) if((a[i][k]==1)&&(c[k]==0)){ r=insereaza_coada(&head,&tail,k); c[k]=1; } } } void main() { int n,v0,a[10][10]; clrscr(); printf("Numarul de varfuri:"); scanf("%i",&n); printf("\nMatricea de adiacenta\n"); for(int i=0;i<n;i++) for(int j=0;j<i;j++){ scanf("%i",&v0); a[j][i]=a[i][j]=v0; } for(i=0;i<n;i++)a[i][i]=0; printf("\nVarful initial "); scanf("%i",&v0); printf("\nParcurgerea BF a grafului este"); breadth_first(v0,a,n); }

Programarea calculatoarelor

22

1.2.3. Ordinea n care snt vizitate vrfurilor corespunztor BF este: 3, 1, 4, 6, 2, 5, 7. 1.2.4. Ordinea n care snt vizitate vrfurilor corespunztor BF este: 2, 1, 5, 3,6.
1.2.5. Aplicarea metodei de traversare BF determin urmtoarea evoluie,
c t t=1 t=2 t=3 t=4 t=5 t=6 t=7 t=8 C t t=1 t=2 t=3 t=4 t=5 t=6 t=7 t=8 1 0 0 1 1 1 1 1 1 3 2 4 6 1 5 7 2 0 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 4 0 1 1 1 1 1 1 1 5 0 0 1 1 1 1 1 1 6 0 1 1 1 1 1 1 1 7 0 0 0 0 0 0 1 1 8 0 0 0 0 0 0 0 0 9 0 0 0 0 0 0 0 0

4 6 1 5

6 1 5

1.3.1. Ordinea n care snt vizitate vrfurilor corespunztor DF n prima varianta prezentat este: 1, 2, 3, 4, 6, 7, 5. 1.3.2 Urmtoarea surs C implementeaz varianta a doua a parcurgerii DF.
#include <stdio.h> #include <conio.h> #include <alloc.h> typedef struct nn{ int inf; struct nn *leg; }nod,* pnod; int insereaza_stiva(pnod *head,int info) { pnod nou; if(nou=(pnod)malloc(sizeof(nod))){ nou->inf=info; nou->leg=*head; *head=nou; return 1; } else return 0; } int extrage_stiva(pnod *head,int *info) { if(head){ pnod aux=*head; *info=(*head)->inf; (*head)=(*head)->leg; free(aux); return 1; } else return 0;} void depth_first(int v0,int a[10][10],int n) {

* Material didactic pentru ID *

23

pnod head=NULL; int c[10]; for(int i=0;i<n;c[i++]=0); int r=insereaza_stiva(&head,v0); c[v0]=1; printf("\n%i",v0+1); while(head){ r=extrage_stiva(&head,&i); for(int k=0;k<n;k++) if((a[i][k]==1)&&(c[k]==0)){ r=insereaza_stiva(&head,k); c[k]=1;printf("\n%i",k+1); } } } void main() { int n,v0,a[10][10]; clrscr(); printf("Numarul de varfuri:");scanf("%i",&n); printf("\nMatricea de adiacenta\n"); for(int i=0;i<n;i++) for(int j=0;j<i;j++){ scanf("%i",&v0); a[j][i]=a[i][j]=v0; } for(i=0;i<n;i++)a[i][i]=0; printf("\nVarful initial ");scanf("%i",&v0); printf("\nParcurgerea DF a grafului este"); depth_first(v0,a,n); }

1.4.1. Graful are 2 componente conexe. 1.4.2. Aplicarea algoritmului descris pentru v0=5, determin urmtoarea evoluie:
I i=0 i=1 i=2 i=3 Vi {5} {1,4,5} {1,2,3,4,5,7} {1,2,3,4,5,6,7} Ei {(1,5),(4,5)} {(1,5),(4,5),(1,2),(1,3),(2,4),(3,4),(1,7),(4,7)} {(1,5),(4,5),(1,2),(1,3),(2,4),(3,4),(1,7),(4,7),(3,6),(3,7)}

1 1 1 1.4.3. Matricea existenei drumurilor este A = 0 1 1

1 1 1 0 1 1

1 1 1 0 1 1

0 0 0 0 0 0

1 1 1 0 1 1

1 1 1 0 1 1

Programarea calculatoarelor

24

1.5.1. Algoritmul calculeaz urmtoarele rezultate: Vrful v pn la care este calculat w-distana D(1,v), eticheta lui v 1 0, 1 2 2, 1 3 1, 1 4 4, 5 5 3, 2

Drumurile de cost minim de la vrful 1 la fiecare dintre vrfurile grafului se stabilesc pe baza sistemului de etichete astfel: drumul de la 1 la un vrf v este dat de: v1, eticheta lui v, v2 eticheta lui v1 amd, pn se ajunge la eticheta 1. Astfel, v0 -drumurile de cost minim snt: pn la 2: 2,1; pn la 3: 3,1; pn la 4: 4,5,2,1; pn la 5: 5,2,1. 1.5.2. Urmtoarea surs C implementeaz algoritmul Dijkstra.
#include<stdio.h> #include<conio.h> #include<alloc.h> typedef struct{ int predv; float L; } eticheta; void creaza(int *s,int *sb,int nv,int u0) { s[0]=u0; for(int j=0,i=0;i<nv;i++) if(i-u0)sb[j++]=i; } void modifica(int *s,int *sb,int ui, int *ns, int *nb) { s[*ns]=ui; (*ns)++; for(int i=0;i<*nb;i++) if(sb[i]==ui){ for(int j=i+1;j<*nb;j++) sb[j-1]=sb[j]; (*nb)--; return; } } eticheta *Dijkstra(float w[][50],int nv,int u0) { eticheta *r=(eticheta *)malloc(nv*sizeof(eticheta)); for(int i=0;i<nv;i++)r[i].L=1000; r[u0].L=0; r[u0].predv=u0; int s[50],sb[50],ns=1,nb=nv-1; creaza(s,sb,nv,u0); for(i=0;i<nv-1;i++){ float dmin=1000; for(int j=0;j<nb;j++) for(int k=0;k<ns;k++) if(r[sb[j]].L>r[s[k]].L+w[sb[j]][s[k]]){ r[sb[j]].L=r[s[k]].L+w[sb[j]][s[k]]; r[sb[j]].predv=s[k]; } int ui; for(j=0;j<nb;j++) if(r[sb[j]].L<dmin){ dmin=r[sb[j]].L; ui=sb[j]; } modifica(s,sb,ui,&ns,&nb); } return r; } void main() {

* Material didactic pentru ID *

25

int n,i,j; clrscr(); printf("Numarul de varfuri"); scanf("%i",&n); printf("Matricea ponderilor:\n"); float w[50][50]; for(i=0;i<n;i++) for(j=0;j<n;j++) scanf("%f",&w[i][j]); int u0; printf("\nVarful initial:"); scanf("%i",&u0);u0--; eticheta *rez=Dijkstra(w,n,u0); for(i=0;i<n;i++){ printf("Distanta de la vf. %i la vf. %i este %7.2f\n",u0+1,i+1,rez[i].L); printf("Un drum de cost minim este:"); printf("%i, ",i+1); j=rez[i].predv; while(j-u0){ printf("%i, ", j+1); j=rez[j].predv; } printf("%i\n\n",u0+1); } free(rez); getch(); }

1.5.3. Algoritmul calculeaz urmtoarele rezultate: Vrful v pn la care este calculat w-distana D(4,v), eticheta lui v 1 7, 2 2 2, 4 3 8, 1 4 0, 4 5 5, 4

Astfel, v0 -drumurile de cost minim snt: pn la 1: 1,2,4; pn la 2: 2,4; pn la 3: 3,1,2,4; pn la 5: 5,4

3 1.5.4. Matricea este: D = 2 7 4

3 2 6 3 5 5 4 1 4 1

Bibliografie 1. I. Gh. Roca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C. Uscatu, M. Mircea Programarea calculatoarelor. Algoritmi n programare, Ed. ASE Bucureti, 2007 2. I. Gh. Roca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C. Uscatu Programarea calculatoarelor. tiina nvrii unui limbaj de programare. Teorie i aplicaii, Ed. ASE Bucureti, 2003 3. Liviu Negrescu Limbajele C i C++ pentru nceptori, vol. I, II, Ed. Microinformatica, Cluj-Napoca, 1994

Programarea calculatoarelor

26

2. Structuri dinamice de date. Liste


Obiective Principalele obiective vizeaz nsuirea i aprofundarea de ctre studeni a noiunilor
fundamentale legate de definirea, necesitatea i utilizarea structurii de list cu legturi, precum i a operaiilor primitive asupra acestora.

Cuprins 2.1. Reprezentarea listelor ..................................................................................................... 26 2.2. Operaii primitive asupra listelor liniare ...................................................................... 27 2.3. Liste circulare .................................................................................................................. 32 2.4. Stive i cozi ....................................................................................................................... 35 2.5 Rspunsuri la testele de autoevaluare. Comentarii....................................................... 37
Timpul mediu de nvare pentru studiul individual: 7 ore

2.1. Reprezentarea listelor


Organizarea de tip list corespunde unei structurri lineare a datelor, n sensul c la nivelul fiecrei componente exist suficient informaie pentru identificarea urmtoarei componente a coleciei. Datele unei mulimi structurate prin intermediul listelor sunt referite de obicei prin termenii de noduri, celule, componente etc. Reprezentarea unei liste poate fi realizat static, prin intermediul structurii de date vector, n acest caz ordinea componentelor fiind dat de ordinea pe domeniul de valori corespunztor indexrii i, n consecin, urmtoarea component este implicit specificat. Memorarea unei mulimi de date {d1, d2,, dn} prin intermediul unei structuri statice poate fi realizat n limbajul C utiliznd un masiv unidimensional. Principalele dezavantaje ale utilizrii reprezentrii statice rezid din volumul de calcule necesare efecturii operaiilor de inserie/eliminare de noduri i din necesitatea pstrrii unei zone de memorie alocat, indiferent de lungimea efectiv a listei. Aceste dezavantaje pot fi eliminate prin opiunea de utilizare a structurilor dinamice. Componentele unei liste dinamice sunt eterogene, fiecare nod coninnd o parte de informaie i cmpuri de legtur care permit identificarea celulelor vecine. Cmpurile de legtur sunt reprezentate de date de tip referin (adres). n cazul listelor cu un singur cmp de legtur (simplu nlnuite), valoarea cmpului indic adresa nodului urmtor, n timp ce, n cazul listelor cu dubl legtur (dublu nlnuite), valorile memorate n cmpurile de legtur sunt adresele componentelor care preced i, respectiv, urmeaz celulei. n ambele situaii, cmpul de legtur pentru indicarea celulei urmtoare corespunztor ultimei componente a listei are valoarea NULL n cazul listelor deschise (lineare) i respectiv indic adresa primei componente din list n cazul listelor nchise (circulare). Declararea tipurilor de date C pentru definirea structurilor de liste dinamice simplu i respectiv dublu nlnuite este, a) List simplu nlnuit typedef struct nod{ tip_informatie inf; struct nod *leg; } list, *lista;

* Material didactic pentru ID *

27

b) List dublu nlnuit typedef struct nod{ tip_informatie inf; struct nod *ls, *ld; } list, *lista; unde tip_informatie este numele tipului de date C utilizat pentru memorarea fiecrei date din mulimea{d1, d2,, dn}. n cele ce urmeaz vom considera c tip_informatie este tipul C int. Teste de autoevaluare 2.1.1 Definii structura de list dinamic simplu nlnuit, unde tipul de date informaie este float. 2.1.2 Definii structura de list static pentru o mulime cu n elemente date caracter.

2.2. Operaii primitive asupra listelor liniare


Accesul la informaia stocat ntr-o variabil de tip list revine la efectuarea uneia sau mai multe dintre operaiile primitive: regsirea (dac exist) nodului care corespunde unei chei date (condiie impus asupra valorii cmpului de informaie), inserarea unei noi componente n list, eliminarea componentei (componentelor) cu proprietatea c valorile cmpurilor de informaie satisfac o anumit cerin i nlocuirea cmpului de informaie corespunztor unei componente printr-o informaie dat. Accesarea componentelor unei liste reprezentat printr-o structur static poate fi realizat att secvenial, ct i direct, utiliznd valorile indicelui considerat pentru indexare, n timp ce accesarea componentelor unei liste dinamice se realizeaz de regul numai secvenial, ncepnd cu prima component i continund cu urmtoarele, pe baza valorilor cmpurilor de legtur. Convenional, numim cap al listei dinamice pointerul a crui valoare este adresa primei componente a listei. n continuare ne vom referi exclusiv la liste dinamice, studiul listelor reprezentate prin intermediul vectorilor fiind propus cititorului. 1. Parcurgerea datelor memorate ntr-o list Funcia C parc implementeaz parcurgerea unei liste dinamice n varianta simplu nlnuit i n cazul listelor dublu nlnuite. Se presupune c declarrile de tip pentru definirea structurilor de liste menionate anterior sunt globale, relativ la procedurile descrise n continuare. a) Lista reprezentat prin structur dinamic simplu nlnuit

void parc(lista cap) { if(cap){ printf("%i ",cap->inf); parc(cap->leg); } } b) Lista reprezentat prin structur dinamic dublu nlnuit void parc(lista cap) { if(cap){ printf("%i ",cap->inf); parc(cap->ls); } }

Programarea calculatoarelor

28

2. Regsirea unei date d ntr-o colecie memorat ntr-o list Funcia C cauta calculeaz adresa nodului n care este gsit elementul cutat. Dac valoarea cutat nu se regsete printre elementele listei, funcia returneaz valoarea NULL. a) Lista reprezentat prin structur dinamic simplu nlnuit

lista cauta(lista cap,int info) { if(cap==NULL)return NULL; else if(cap->inf==info) return cap; else return cauta(cap->leg,info); } b) Lista reprezentat prin structur dinamic dublu nlnuit

lista cauta(lista cap,int info) { if(cap==NULL)return NULL; else if(cap->inf==info) return cap; else return cauta(cap->ls,info); } 3. Inserarea unei date, d, ntr-o list Includerea unei noi componente ntr-o list poate fi realizat, n funcie de cerinele problemei particulare, la nceputul listei, dup ultima component din list, naintea/dup o component cu proprietatea c valoarea cmpului de informaie ndeplinete o anumit condiie. Deoarece prin inserarea unei componente se poate ajunge la depirea spaiului disponibil de memorie, este necesar verificarea n prealabil dac este posibil inserarea sau nu (dac se poate aloca spaiu de memorie pentru componenta de inserat). n continuare ne vom referi la liste dinamice simplu nlnuite, operaiile de inserare n cazul listelor dinamice dublu nlnuite putnd fi realizate similar cazului listelor simplu nlnuite, cu specificarea ambelor cmpuri de adres ale nodurilor. Pentru exemplificarea operaiei de inserare sunt prezentate funciile de inserare la nceputul listei, inserare dup ultimul element al listei i inserarea unei celule dup un nod cu informaie dat. Inserarea la nceputul listei Funcia C inserare_la_inceput calculeaz valoarea 1 dac adugarea unui nou element este posibil (spaiul de memorie este suficient pentru o nou alocare), altfel returneaz 0. n cazul n care inserarea este posibil, prin apelul funcii este realizat adugarea unui nou nod la nceputul listei. int inserare_la_inceput(lista *cap,int info) { lista nou; if(nou=(lista)malloc(sizeof(list))){ nou->inf=info; nou->leg=*cap; *cap=nou; return 1; } return 0; } Inserarea dup ultima component a unei liste Funcia C inserare_la_sfarsit returneaz 1 dac i numai dac este posibil o inserare, altfel calculeaz 0. Pentru inserarea unui nou nod n list dupa ultima celul este necesar calculul ultimului nod al listei, notat p.

* Material didactic pentru ID *

29

int inserare_la_sfarsit(lista *cap,int info) { lista nou; if(nou=(lista)malloc(sizeof(list))){ nou->leg=NULL;nou->inf=info; if(cap==NULL)*cap=nou; else {for(lista p=*cap;p->leg;p=p->leg); p->leg=nou; } return 1; } return 0; } Inserarea unuei informaii dup o celul cu informaie cunoscut Inserarea unui nou nod ntr-o list identificat prin variabila cap dup o celul p cu informaie cunoscut, infod, poate fi realizat astfel. Este apelat funcia de cutare cauta, care calculeaz nodul p cu proprietatea c informaia memorat n p este infodat. Dac p este adresa vid sau dac spaiul de memorie disponibil nu este suficient, inserarea nu poate fi realizat. n caz contrar, este inserat un nou nod ntre celulele p i p->leg. int inserare_dupa_informatie(lista cap,int info,int infod) { lista nou,p; if(nou=(lista)malloc(sizeof(list))) if(p=cauta(cap,infod)){ nou->inf=info; nou->leg=p->leg; p->leg=nou; return 1; } return 0; } 4. Eliminarea unei date, d, dintr-o list. Modificarea coninutului unei liste prin eliminarea uneia sau mai multor componente poate fi descris secvenial, astfel nct este suficient s dispunem de o procedur care realizeaz eliminarea unei singure componente. Criteriile de eliminare pot fi formulate diferit, cele mai uzuale fiind: prima component, ultima component, prima component care ndeplinete o anumit condiie, respectiv componenta care precede/urmeaz primei componente care ndeplinete o condiie dat. n aceste cazuri este necesar verificarea existenei n lista considerat a componentei ce trebuie eliminat. Aceast verificare asigur i testarea faptului c lista prelucrat este vid sau nu. Informaia i aat nodului care este eliminat din list reprezint dat de ieire pentru orice modul de eliminare, n cazul n care i nu este cunoscut naintea eliminrii (de exemplu, atunci cnd este solicitat eliminarea unei celule care conine o informaie dat) . Eliminarea unui nod p poate fi realizat logic sau fizic. Eliminarea logic a celulei p este efectuat excluznd p din lista dinamic prin setarea legturii nodului care precede p pe adresa succesorului lui p, dac p nu este adresa primului element al listei, cap, respectiv prin atribuirea adresei primului element al listei cu cap->leg, n caz contrar. Eliminarea cu tergere fizic unui nod p presupune redenumirea acelui nod n scopul eliberrii memoriei ocupate de p i efectuarea operaiilor descrise n cadrul procesului de eliminare logic.

Programarea calculatoarelor

30

n continuare sunt prezentate urmtoarele tipuri de eliminri, cu tergere fizic. Eliminarea primei componente a unei liste Funcia C elimina_de_la_inceput calculeaz 1 dac lista nu este vid, deci eliminarea primului nod este posibil, altfel calculeaz 0. Dac lista conine mcar un nod, este eliminat prima celul. int elimina_de_la_inceput(lista *cap,int *info) { if(*cap){ lista aux=*cap; *info=aux->inf; *cap=(*cap)->leg; free(aux); return 1; } return 0; } Eliminarea ultimei componente a unei liste Similar operaiei de inserare a unui nod dup ultima celul a unei liste, eliminarea ultimului nod presupune determinarea acelei celule p cu proprietatea c p->leg este NULL. Funcia C elimina_ultim returneaz 1 dac lista nu este vid, caz n care este eliminat cu tergere ultimul nod al listei. Dac lista este vid, funcia calculeaz valoarea 0. int elimina_ultim(lista *cap,int *info) { if (*cap){ if((*cap)->leg){ for(lista p=*cap;p->leg->leg;p=p->leg); *info=p->leg->inf; free(p->leg); p->leg=NULL; } else{ *info=(*cap)->inf; free(*cap); *cap=NULL; } return 1; } return 0; } Eliminarea primei celule a unei liste care are informaia egal cu o informaie dat Pentru realizarea acestei operaii putem proceda astfel. Sunt calculate aux i p, unde aux este nodul care precede celulei cu informaie dat n lista din care este efectuat eliminarea (funcia C cautaprecedent) i p=aux->leg.. Dac p este NULL, atunci eliminarea este imposibil. Dac aux este NULL, atunci eliminarea revine la extragerea cu tergere a primului nod din list, altfel este eliminat celula p, succesoare a lui aux n list. Funcia C elimina_informatie implementeaz operaia de eliminare a unui nod cu informaie dat, info, din lista identificat prin parametrul cap. lista cautaprecedent(lista cap,int info, lista *aux) { lista p; if(cap==NULL)return NULL; else{

* Material didactic pentru ID *

31

} }

for(p=NULL,*aux=cap;(*aux)&& ((*aux)->inf-info);p=*aux,*aux=(*aux)->leg); if((*aux)==NULL)return NULL; return p;

int elimina_informatie(lista *cap,int info) { lista aux,p; p=cautaprecedent(*cap,info,&aux); if(aux==*cap){ *cap=(*cap)->leg; free(aux); return 1; } else if(p) { p->leg=aux->leg; free(aux); return 1; } return 0; } Eliminarea nodului care succede primei componente al crei cmp de informaie este cunoscut Funcia C elimina_dupa_informatie calculeaz valoarea 1 dac eliminarea este posibil, altfel calculeaz valoarea 0. n situaia n care informaia infodat a fost gsit n cmpul corespunztor nodului nodul p (p nu este NULL) i p are succesor n list, este realizat eliminarea nodului p->leg. int elimin_dupa_informatie(lista cap,int *info, int infodat) { lista aux,p; p=cauta(cap,infodat); if((p)&&(p->leg)){ aux=p->leg; p->leg=aux->leg; *info=aux->inf; free(aux); return 1; } return 0; } Teste de autoevaluare. Probleme Pentru problemele propuse n continuare recomandm utilizarea macrodefiniiei: #define NEW (TNOD*)malloc(sizeof(TNOD));
unde

typedef struct TNOD{float x; struct TNOD *next};

Programarea calculatoarelor

32

2.2.1. Scriei programul pentru crearea unei liste simplu nlnuite cu preluarea datelor de la tastatur. Sfritul introducerii datelor este marcat standard. Dup creare, se va afia coninutul listei apoi se va elibera memoria ocupat. 2.2.2 Scriei funcia pentru inserarea unui nod ntr-o list simplu nlnuit dup un nod identificat prin valoarea unui cmp. Dac nodul cutat nu exist, inserarea se face la sfritul listei. 2.2.3 Scriei funcia pentru inserarea unui nod ntr-o list simplu nlnuit naintea unui nod identificat prin valoarea unui cmp. Dac nodul cutat nu exist, se face inserare la nceputul listei. 2.2.4. Scriei funcia pentru tergerea unei liste simplu nlnuite. 2.2.5. Scriei funcia pentru tergerea nodului aflat naintea nodului identificat prin valoarea unui cmp. Pentru urmtoarele probleme cu liste dublu nlnuite se va folosi o list avnd ca informaie util n nod un numr real:
typedef struct TNOD{float x; struct TNOD *next,*pred;};

2.2.6. Scriei programul pentru crearea unei liste dublu nlnuite cu preluarea datelor de la tastatur. Sfritul introducerii datelor este marcat standard. Dup creare, se va afia coninutul listei apoi se va elibera memoria ocupat. 2.2.7. Scriei funcia pentru inserarea unui nod la nceputul unei liste dublu nlnuite. 2.2.8 Scriei funcia pentru inserarea unui nod ntr-o list dublu nlnuit, dup un nod identificat prin valoarea unui cmp. 2.2.9. Scriei funcia pentru tergerea unui nod identificat prin valoarea unui cmp dintr-o list dublu nlnuit. 2.2.10 Scriei funcia pentru tergerea ultimului nod dintr-o list dublu nlnuit.

2.3. Liste circulare


n anumite situaii este preferabil renunarea la structura de tip linear a listelor i utilizarea unei legturi de la ultima component ctre capul listei, rezultnd structura de list circular. Principalul avantaj al utilizrii acestui tip de structur rezid din posibilitatea de accesare dintrun element al listei al oricrui alt element, astfel. Dac nodul cutat este situat dup nodul curent, este iniiat un proces de cutare similar listelor lineare. n caz contrar, nodul poate fi accesat prin parcurgerea listei de la primul sau element, care, n procesul de cutare, este atins dup parcurgerea n ntregime a listei, ncepnd de la nodul curent. n continuare sunt prezentate funciile C pentru realizarea unor operaii de baz n lucrul cu liste circulare. #include<stdio.h> #include<conio.h> #include<alloc.h> typedef struct nod{ int inf; struct nod *leg; } list, *lista; int inserare_la_inceput(lista int stergere_la_inceput(lista int inserare_la_sfarsit(lista int stergere_la_sfarsit(lista void parc(lista); lista cauta(lista,int); *,int); *,int *); *,int); *,int *);

* Material didactic pentru ID *

33

void main() { clrscr(); int n,info;lista cap=NULL; printf("Numarul de noduri:");scanf("%i",&n); printf("Introduceti informatiile\n"); for(int i=0;i<n;i++){ scanf("%i",&info); if(inserare_la_inceput(&cap,info)); else {printf("\n Spatiu insuficient \n"); return;} } printf("\nLista rezultata\n"); parc(cap); printf("\n\nLista dupa extragerea primului element:\n"); if(stergere_la_inceput(&cap,&info)) parc(cap); else printf("\nEroare: lista vida"); printf("\n\nInformatia nodului de introdus la sfarsit:"); scanf("%i",&info); if(inserare_la_sfarsit(&cap,info)){ printf("Lista rezultata\n"); parc(cap); } else printf("\n Spatiu insuficient \n"); printf("\n\nLista dupa extragerea ultimului element:"); if(stergere_la_sfarsit(&cap,&info)){ printf("\nInformatia extrasa %i\nLista rezultata:",info); parc(cap); } else printf("\nEroare:Lista vida"); getch(); } void parc(lista cap) { lista p=cap; if(cap){ printf("%i ",cap->inf); for(p=p->leg;p-cap;p=p->leg) printf("%i ",p->inf); } else printf("\nLista vida"); } lista cauta(lista cap,int info) { if(cap==NULL)return NULL; if(cap->inf==info) return cap; for(lista p=cap->leg;p!=cap;p=p->leg) if(p->inf==info) return p; return NULL;} int inserare_la_inceput(lista *cap,int info) {

Programarea calculatoarelor

34

lista nou,ultim; if(nou=(lista)malloc(sizeof(list))){ nou->inf=info;nou->leg=*cap; if(*cap){ for(ultim=*cap;ultim->leg!=(*cap);ultim=ultim->leg); ultim->leg=nou; } else nou->leg=nou; *cap=nou;return 1; } return 0; } int stergere_la_inceput(lista *cap,int *info) { if(*cap){ lista aux=*cap;*info=aux->inf; for(lista ultim=*cap;ultim->leg!=(*cap);ultim=ultim->leg); if(ultim==(*cap)) *cap=NULL; else{ *cap=(*cap)->leg;ultim->leg=*cap;} free(aux); return 1; } return 0; } int inserare_la_sfarsit(lista *cap,int info) { lista nou,ultim; if(nou=(lista)malloc(sizeof(list))){ nou->leg=*cap;nou->inf=info; if(*cap==NULL){ *cap=nou;(*cap)->leg=*cap;} else{ for(ultim=*cap;ultim->leg!=(*cap); ultim=ultim->leg); ultim->leg=nou; } return 1; } return 0; } int stergere_la_sfarsit(lista *cap,int *info) { if (*cap){ if((*cap)->leg!=(*cap)){ for(lista pultim=*cap;pultim->leg->leg!=(*cap);pultim=pultim->leg); *info=pultim->leg->inf;free(pultim->leg); pultim->leg=(*cap);} else{ *info=(*cap)->inf;free(*cap);*cap=NULL;} return 1; } return 0; }

* Material didactic pentru ID *

35

Teste de autoevaluare. Probleme 2.3.1 Scriei funcia pentru inserarea unui nod ntr-o list circular simplu nlnuit

dup un nod identificat prin valoarea unui cmp. 2.3.2 Scriei funcia pentru inserarea unui nod ntr-o list circular simplu nlnuit naintea unui nod identificat prin valoarea unui cmp. 2.3.3. Scriei funcia pentru tergerea unei liste circulare simplu nlnuite.
2.3.4 Scriei funcia pentru tergerea nodului aflat dup un nod identificat prin valoarea unui cmp. Dac nodul cutat este ultimul, atunci se terge primul nod al listei. 2.3.5. Scriei funcia pentru tergerea nodului aflat naintea nodului identificat prin valoarea unui cmp. Dac nodul cutat este primul, se va terge ultimul nod al listei.

2.4. Stive i cozi


Aa cum a rezultat din seciunile precedente, operaiile de inserare i eliminare sunt permise la oricare dintre componentele coleciei. O serie de aplicaii pot fi modelate utiliznd liste lineare n care introducerea i respectiv eliminarea informaiilor este permis numai la capete. n acest scop au fost introduse tipurile de list stiv i coad prin impunerea unui tip de organizare a aplicrii operaiilor de inserare i eliminare. Stiva Se numete stiv o list organizat astfel nct operaiile de inserare i eliminare sunt permise numai la prima component. Acest mod de organizare corespunde unei gestiuni LIFO (Last In First Out) a informaiei stocate. Operaiile de baz efectuate asupra unei stive pot fi realizate similar cazului listelor dinamice lineare, innd cont c inserarea/extragerea unui element sunt posibile numai n prima poziie (vezi modulul de inserare la nceputul unei liste i respectiv funcia de extragere a primului nod dintr-o list, prezentate n 2.2). Coada Se numete coad o list organizat astfel nct operaia de inserare este permis la ultima component, iar operaia de eliminare este permis numai la prima component. Acest mod de organizare corespunde unei gestiuni FIFO (First In First Out) a informaiei stocate. Implementarea unei liste coad poate fi efectuat att printr-o structur static (masiv unidimensional), ct i printr-o structur dinamic de tip list. Pentru optimizarea operaiilor de inserare/extragere, n cazul implementrii cozilor prin structuri dinamice lineare, este necesar utilizarea a dou informaii: adresa primei componente i adresa ultimei componente. Aceste informaii pot fi meninute explicit prin utilizarea a doi pointeri sau prin utilizarea unui pointer i a unei structuri de list circular. O variant alternativ de implementare a unei liste de tip coad dinamic este obinut prin considerarea unei liste circulare, cu memorarea adresei ultimului element. n continuare sunt prezentate operaiile de inserare i extragere a unei informaii dintr-o list liniar de tip coad. #include<stdio.h> #include<conio.h> #include<alloc.h> typedef struct nod{ int inf; struct nod *leg; } list, *lista; int inserare(lista *,lista *,int); int extragere(lista *,lista *,int *); void parc(lista);

Programarea calculatoarelor

36

void main() { clrscr(); int n,info; lista cap=NULL,ultim=NULL; printf("Numarul de noduri:");scanf("%i",&n); printf("Introduceti informatiile\n"); for(int i=0;i<n;i++){ scanf("%i",&info); if(inserare(&cap,&ultim,info)); else {printf("\n Spatiu insuficient \n");return;} } printf("\nCoada rezultata\n");parc(cap); printf("\n\nCoada dupa o extragere:\n"); if(extragere(&cap,&ultim,&info)) parc(cap); else printf("\nEroare: Coada vida"); getch(); } void parc(lista cap) { if(cap){ printf("%i ",cap->inf);parc(cap->leg);} } int extragere(lista *cap,lista *ultim,int *info) { if(*cap){ lista aux=*cap;*info=aux->inf; if((*ultim)==(*cap)) *cap=*ultim=NULL; else *cap=(*cap)->leg; free(aux);return 1;} return 0; } int inserare(lista *cap,lista *ultim,int info) { lista nou; if(nou=(lista)malloc(sizeof(list))){ nou->inf=info;nou->leg=NULL; if(*cap==NULL) *cap=*ultim=nou; else{ (*ultim)->leg=nou;(*ultim)=nou;} return 1; } return 0; } Teste de autoevaluare. Probleme n exerciiile propuse se va folosi o stiv de elemente cu tip real. Pentru implementarea ei se va folosi o list simplu nlnuit. Capul acestei liste, mpreun cu capacitatea maxim a stivei i numrul de poziii ocupate constituie cmpuri ale structurii STIVA: typedef struct TNOD{float x; struct TNOD* next;}; typedef struct STIVA{TNOD* vf; capacitate c,max;};

* Material didactic pentru ID *

37

2.4.1.Scriei funcia care verific dac o stiv este vid. 2.4.2. Scriei funcia care verific dac o stiv este plin. 2.4.3. Scriei funcia pentru adugarea unui element de tip real ntr-o stiv. 2.4.4. Scriei funcia pentru extragerea unui element dintr-o stiv. 2.4.5. S se scrie funcia pentru golirea unei stive.

2.5 Rspunsuri la testele de autoevaluare. Comentarii


2.1.1
typedef struct nod{ float inf; struct nod *leg; } list, *lista;

2.1.2 n cazul static, lista este reprezentat prin intermediul unui vector. Presupunem n continuare c n<100.
char lista l[100]; int n;

2.2.1
#include<stdio.h> #include<alloc.h> typedef struct TNOD{float x; struct TNOD* next;}; void main() {TNOD *cap,*p,*q; int n,i; float a; //creare lista printf("primul nod="); scanf("%f",&a); cap=(TNOD*)malloc(sizeof(TNOD)); cap->x=a; cap->next=NULL; p=cap; printf("nod (CTRL-Z)="); scanf("%f",&a); while(!feof(stdin)) {q=(TNOD*)malloc(sizeof(TNOD)); q->x=a; q->next=NULL; p->next=q; p=q; printf("nod (CTRL-Z)="); scanf("%f",&a);} //afisare continut printf("\n"); p=cap; while(p) {printf("\t%5.2f",p->x); p=p->next;} //stergere lista while(cap) {p=cap; cap=cap->next; free(p);} }

2.2.2 Funcia are ca parametri capul listei n care se insereaz, valoarea care se insereaz

i valoarea dup care se insereaz. Prin numele funciei se ntoarce noul cap al listei.
TNOD* ins2(TNOD* cap, float a,float x) {TNOD *p,*q; p=NEW; p->x=a; if(!cap) {cap=p;p->next=NULL;} else{q=cap; while((q->next)&&(q->x!=x))

Programarea calculatoarelor

38

q=q->next; p->next=q->next; q->next=p;} return cap;}

2.2.3 Funcia are ca parametri capul listei n care se insereaz, valoarea care se insereaz i valoarea naintea crei se insereaz. Prin numele funciei se ntoarce noul cap al listei.
TNOD* ins3(TNOD* cap, float a,float x) {TNOD *p,*q; p=NEW; p->x=a; if(!cap) {cap=p;p->next=NULL;} else{q=cap; while((q->next)&&(q->next->x!=x)) q=q->next; if(!q->next){p->next=cap;cap=p;} else {p->next=q->next; q->next=p;} } return cap;}

2.2.4 Funcia are ca parametru capul listei care trebuie tears i ntoarce, prin numele ei, noul cap al listei (valoarea NULL).
TNOD* sterg(TNOD* cap) {TNOD* p; while(cap) {p=cap; cap=cap->next; free(p);} return NULL;}

2.2.5. Funcia are ca parametri capul listei, valoarea din nodul cutat (naintea cruia se face tergerea) i adresa unde se nscrie parametrul de eroare (0 dac se face tergerea, 1 dac nodul cutat este primul, 2 dac nodul cutat nu a fost gsit). Funcia ntoarce noul cap al listei.
TNOD* sterg5(TNOD* cap,float a, int *er) {TNOD *p, *q; if(!cap) *er=2; else{p=cap; if(cap->x==a)*er=1; else if(cap->next) if(cap->next->x==a) {*er=0; p=cap; cap=cap->next; free(p);} else if(cap->next->next) {p=cap; while((p->next->next)&&(p->next->next->x!=a)) p=p->next; if(p->next->next) {q=p->next; p->next=p->next->next; free(q); * er=0;} else *er=2; } } return cap;}

2.2.6.
#include<stdio.h> #include<alloc.h> #define NEW (TNOD*)malloc(sizeof(TNOD));

* Material didactic pentru ID *

39

typedef struct TNOD{float x; struct TNOD *next,*pred;}; void main() {TNOD *cap, *p, *q; float x; //creare lista printf("\nprimul nod: ");scanf("%f",&x); cap=NEW; cap->pred=NULL; cap->next=NULL; cap->x=x; p=cap; printf("\nnod: ");scanf("%f",&x); while(!feof(stdin)) {q=NEW; q->next=NULL; q->x=x; q->pred=p; p->next=q; p=q; printf("\nnod: ");scanf("%f",&x);} //afisare lista printf("\n"); p=cap; while(p) {printf("\t%5.2f",p->x); p=p->next;} //stergere lista while(cap) {p=cap; cap=cap->next; if(cap)cap->pred=NULL; free(p);} printf("\ngata");}

2.2.7. Funcia are ca parametri capul listei i valoarea care trebuie inserat. Prin numele funciei se ntoarce noul cap al listei.
TNOD* ins1(TNOD* cap, float a) {TNOD *p; p=NEW; p->x=a; p->pred=NULL; p->next=cap; if(cap)cap->pred=p; return p;}

2.2.8. Funcia are ca parametri capul listei, valoarea care se insereaz i valoarea dup care se insereaz. Dac valoarea cutat nu este gsit, se face inserare la sfritul listei.
TNOD* ins2(TNOD* cap, float a, float x) {TNOD *p,*q; p=NEW; p->x=a; if(!cap){cap=p;p->next=NULL;p->pred=NULL;} else{q=cap; while((q->next)&&(q->x!=x)) q=q->next; p->next=q->next; p->pred=q; q->next=p; if(p->next)p->next->pred=p;} return cap;}

2.2.9 Funcia are ca parametri capul listei i valoarea de identificare a nodului care trebuie ters. Prin numele funciei se ntoarce noul cap al listei.
TNOD* sterg2(TNOD* cap, float a) {TNOD *p, *q;

Programarea calculatoarelor

40

if(cap) {p=cap; while((p->next)&&(p->x!=a)) p=p->next; if(p->x==a) {if(p->next)p->next->pred=p->pred; if(p->pred)p->pred->next=p->next; else cap=p->next; free(p);} } return cap;}

2.2.10 Funcia are ca parametru capul listei i ntoarce, prin numele ei, noul cap al listei.
TNOD* sterg3(TNOD* cap) {TNOD *p,*q; if(cap) {p=cap; while(p->next) p=p->next; if(!p->pred){free(cap);cap=NULL;} else {q=p->pred;free(p);q->next=NULL;} } return cap;}

2.3.1. Funcia are ca parametri capul listei, valoarea care trebuie inserat i valoarea dup care se insereaz. Dac valoarea cutat nu este gsit, atunci inserarea se face la sfrit.
TNOD* ins2(TNOD* cap, float a,float x) {TNOD *p,*q; p=NEW; p->x=a; if(!cap) {cap=p;p->next=cap;} else{q=cap; while((q->next!=cap)&&(q->x!=x)) q=q->next; p->next=q->next; q->next=p;} return cap;}

2.3.2. Funcia are ca parametri capul listei, valoarea care se adaug i valoarea naintea creia se adaug. Prin numele funciei se ntoarce noul cap al listei.
TNOD* ins3(TNOD* cap, float a,float x) {TNOD *p,*q; p=NEW; p->x=a; if(!cap) {cap=p;p->next=cap;} else{q=cap; while((q->next!=cap)&&(q->next->x!=x)) q=q->next; if(q->next==cap)cap=p; p->next=q->next; q->next=p;} return cap;}

2.3.3. Funcia are ca parametru capul listei i ntoarce, prin numele ei, noul cap al listei (NULL).
TNOD* sterg(TNOD* cap) {TNOD *p,*q; if(cap) {p=cap; do {q=cap; cap=cap->next; free(q);}

* Material didactic pentru ID *

41

while(cap!=p);} return NULL;}

2.3.4 Funcia are ca parametri capul listei, valoarea cutat i adresa unde va scrie parametrul de eroare (1 dac valoarea cutat nu este gsit sau lista are numai un nod, 0 dac se face tergerea).
TNOD* sterg4(TNOD* cap,float a,int *er) {TNOD *p, *q; if(!cap) *er=1; else if(cap==cap->next)*er=1; else{p=cap; if(cap->x!=a) {p=cap->next; while((p!=cap)&&(p->x!=a)) p=p->next;} if(p->x!=a)*er=1; else {if(p->next==cap)cap=sterg1(cap); else{q=p->next; p->next=q->next; free(q);} *er=0;} } return cap;}

2.3.5 Funcia are ca parametri capul listei, valoarea cutat i adresa unde se va nscrie parametrul de eroare (0 dac tergerea s-a efectuat, 1 dac nodul cutat nu este gsit sau lista are un singur nod, 2 dac lista este vid).
TNOD* sterg5(TNOD* cap,float a, int *er) {TNOD *p, *q; if(!cap) *er=2; else if(cap==cap->next)*er=1; else if(cap->x==a){*er=0; cap=sterg3(cap);} else if(cap->next->x==a){cap=sterg1(cap);*er=0;} else{p=cap->next; while((p->next->next!=cap)&&(p->next->next->x!=a)) p=p->next; if(p->next->next->x==a) {q=p->next; p->next=p->next->next; free(q); *er=0;} else *er=1;} return cap;}

2.4.1. Funcia are ca parametru stiva i ntoarce valoarea 1 dac stiva este vid sau 0 n caz contrar.
int e_vida(STIVA s) {return s.vf==NULL;}

2.4.2. Funcia are ca parametru stiva i ntoarce valoarea 1 dac stiva este plin sau 0 n caz contrar.
int e_plina(STIVA s) {return s.c==s.max;}

2.4.3. Funcia are ca parametri adresa stivei i valoarea care trebuie adugat n stiv. Prin numele funciei se ntoarce valoarea 1 dac stiva era plin (i nu se poate face adugare) sau 0 dac adugarea a decurs normal.

Programarea calculatoarelor

42

int push(STIVA *s, float a) {TNOD *p; int er; if(!e_plina(*s)) {p=NEW; p->x=a; p->next=s->vf; s->vf=p; (s->c)++; er=0;} else er=1; return er;}

2.4.4 Funcia are ca parametri adresa stivei i adresa unde se va depune valoarea extras din stiv. Prin numele funciei se ntoarce valoarea 1, dac stiva este vid, sau 0 n caz contrar.
int pop(STIVA *s, float *a) {int er; TNOD *p; if(e_vida(*s))er=1; else{p=s->vf; s->vf=s->vf->next; *a=p->x; free(p); er=0; (s->c)--;} return er;}

2.4.5. Funcia are ca parametru adresa stivei i nu ntoarce nimic prin numele ei. Valorile aflate n stiv se pierd.
void golire(STIVA *s) {TNOD *p; while(s->vf) {p=s->vf; s->vf=s->vf->next; free(p);} s->c=0;}

Bibliografie 1. I. Gh. Roca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C. Uscatu, M. Mircea - Programarea calculatoarelor. Algoritmi n programare, Ed. ASE Bucureti, 2007 2. I. Gh. Roca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C. Uscatu - Programarea calculatoarelor. tiina nvrii unui limbaj de programare. Teorie i aplicaii, Ed. ASE Bucureti, 2003 3. Liviu Negrescu - Limbajele C i C++ pentru nceptori, vol. I, II, Ed. Microinformatica, Cluj-Napoca, 1994

* Material didactic pentru ID *

43

3. Structuri arborescente
Obiective Principalele obiective vizeaz nsuirea i aprofundarea de ctre studeni a noiunilor fundamentale cu care se opereaz n studiul grafurilor arbore i a arborilor binari de sortare, respectiv a arborilor binari de structur: definiii, caracterizri, reprezentri i parcurgeri ale grafurilor arbore, arbori pariali de cost minim, arbori direcionai, construcia i utilitatea arborilor de sortare, construcia, modul de operare i utilitatea arborilor de structur asociai expresiilor. Cuprins 3.1. Definiii i caracterizri ale grafurilor arbori ...............................................................43 3.2. Reprezentri i parcurgeri ale arborilor orientai........................................................45 3.3. Arbori pariali; algoritmul kruskal................................................................................48 3.4. Arbori binari. Arbori binari de sortare.........................................................................50 3.5. Arbori de structur..........................................................................................................54 3.6. Rspunsuri la testele de autoevaluare. Comentarii ......................................................59
Timpul mediu de nvare pentru studiul individual: 8 ore.

3.1. Definiii i caracterizri ale grafurilor arbori


Structurile cele mai simple i care apar cel mai frecvent n aplicaii sunt cele arborescente (arbori). Grafurile arbori constituie o subclas a grafurilor conexe. Definiia 3.1.1 Graful G este arbore dac G este aciclic i conex. Definiia 3.1.2. Fie G=(V,E) graf arbore. Subgraful H=(V1,E1) al lui G este subarbore al lui G dac H este graf arbore. Exemple 3.1.1. Graful

1 2 3 4
este arbore, deoarece, orice (i,j) E , ij, exist un i-j drum i graful nu conine cicluri. 3.1.2. Graful

1 4 2

3 7 5 6

nu este arbore, deoarece drumul :1,4,6,2,1 este un ciclu. Verificarea proprietii unui graf de a fi arbore poate fi realizat prin intermediul unor algoritmi care s verifice calitile de conexitate i respectiv aciclicitate. De asemenea, verificarea proprietii unui graf de a fi arbore poate fi realizat astfel.

Programarea calculatoarelor

44

Proprietatea 3.1.1. Un graf G=(V,E), cu V = n , E = m este graf arbore dac i numai dac G este aciclic i n=m+1. Exemple 3.1.3. Graful din 9.1.1 este arbore, pentru c este aciclic i n=7, m=6. 3.1.4. Graful din 9.1.2. nu este arbore pentru c este ciclic. Proprietatea 3.1.2 Un graf G=(V,E), cu V = n , E = m este graf arbore dac i numai dac G este conex i n=m+1. Exemple 3.1.5. Graful din 3.1.1. este arbore deoarece este conex i n=m+1. 3.1.6. Graful conex din exemplul 3.1.2. nu este arbore pentru c n=6 i m=8.

Observaie
Fie G=(V,E) un graf. Urmtoarele afirmaii sunt echivalente, G este graf arbore; G este graf conex minimal: oricare ar fi eE, prin eliminarea muchiei e din E, graful rezultat nu este conex; G este graf aciclic maximal: prin adugarea unei noi muchii n graf rezult cel puin un ciclu. Definiia 3.1.3. Se numete graf asimetric un digraf D=(V,E) cu proprietatea c pentru orice u ,v E dac uvE, atunci vu E. Digraful D este simetric dac u ,v E , uvE, dac i numai dac vuE. Definiia 3.1.4. Fie D=(V,E) digraf netrivial. Graful G=(V,E), unde E={uv/ uvE sau vuE} se numete graf suport al digrafului D. Definiia 3.1.5. Un arbore direcionat este un graf orientat asimetric i astfel nct graful suport corespunztor lui este graf arbore. Definiia 3.1.6. Arborele direcionat T=(V,E) este arbore cu rdcin dac exist rV astfel nct, pentru orice uV, u r, exist r-u drum n T. Vrful r se numete rdcina arborelui direcionat T. Definiia 3.1.7. Fie T=(V,E) arbore direcionat. Arborele T1=(V1,E1) este subarbore al lui T dac V1V, E1E i T1 este arbore direcionat. Observaie Graful suport al unui arbore direcionat este aciclic, deci, pentru orice uV, u r, ru drumul din T este unic. De asemenea, un arbore direcionat are cel mult o rdcin. Rezult c, pentru orice uV, u r, distana de la rdcin la vrful u este egal cu numrul de muchii ale r-u drumului n T. Exemple 3.1.7. Arborele direcionat

1
3 2 5 6 4

10

este arbore cu rdcin 1.

* Material didactic pentru ID *

45

3.1.8. Arborele

1
4 2 5 6

10

este un subarbore cu rdcin 1 al arborelui din 3.1.7.

Teste de autoevaluare
3.1.1. Graful

1 4

3 2

5
6 8

este graf arbore? Justificai rspunsul. 3.1.2. Utiliznd proprietatea 3.1.1, verificai dac garful de la ntrebarea 3.1.1 este arbore. 3.1.3. Utiliznd proprietatea 3.1.1, verificai dac garful de la ntrebarea 3.1.1 este arbore.

3.2. Reprezentri i parcurgeri ale arborilor orientai


Definiia 3.2.1 Un arbore orientat este un arbore direcionat cu rdcin. Definiia 3.2.2. Fie T=(V,E) arbore orientat cu rdcin r. Un vrf v V este situat pe nivelul i al arborelui T, dac distana de la vrf la rdcin este egal cu i. Rdcina arborelui este considerat de nivel 0. Deoarece orice arbore orientat este, n particular, digraf, reprezentarea arborilor orientai poate fi realizat prin utilizarea oricreia dintre modalitile prezentate n unitatea de instruire 1. Datorit caracteristicilor arborilor orientai pot fi ns obinute reprezentri mai eficiente din punct de vedere al spaiului de memorie solicitat. Una dintre modaliti este reprezentarea de tip FIU-FRATE, care const n numerotarea convenional a vrfurilor grafului i memorarea, pentru fiecare vrf i al arborelui, a urmtoarelor informaii, FIU(i): numrul ataat primului descendent al vrfului i; FRATE(i): numrul ataat vrfului descendent al tatlui vrfului i i care urmeaz imediat lui i; INF(i): informaia ataat vrfului i (de obicei valoarea i).

Programarea calculatoarelor

46

Pentru reprezentarea arborelui sunt reinute rdcina i numrul nodurilor. Absena fiului, respectiv a fratelui unui vrf este marcat printr-o valoare din afara mulimii de numere ataate vrfurilor (de obicei valoarea 0). Exemplu 3.2.1. Arborele orientat

1 2 3 4

9 10

11

12 13

14

15

16

este reprezentat astfel, N=16, R=1 (rdcina), FIU=(2,5,0,8,0,9,0,14,0,0,0,0,0,0,0,0) FRATE=(0,3,4,0,6,7,0,0,10,11,12,13,0,15,16,0) O alternativ a reprezentrii FIU-FRATE poate fi obinut prin utilizarea structurile de date dinamice, astfel. Presupunnd c fiecare vrf al arborelui are cel mult n descendeni, fiecrui vrf i este ataat structura, identificator vrf vector de legturi ctre descendenii vrfului adres fiu 1 adres fiu n

Parcurgerea unui arbore orientat revine la aplicarea sistematic a unei reguli de vizitare a vrfurilor arborelui. Cele mai utilizate reguli de parcurgere a arborilor orientai sunt A-preordine, Apostordine i parcurgerea pe nivele. Parcurgerea n A-preordine Modalitatea de vizitare a vrfurilor n parcurgerea n A-preordine poate fi descris astfel. Iniial, rdcina arborelui este selectat drept vrf curent. Este vizitat vrful curent i sunt identificai descendenii lui. Se aplic aceeai regul de vizitare pentru arborii avnd ca rdcini descendenii vrfului curent, arborii fiind vizitai n ordinea precizat prin numerele ataate vrfurilor rdcin corespunztoare. n reprezentarea FIU-FRATE, implementarea parcurgerii n A-preordine este realizat prin urmtoarea funcie recursiv, cu parametru de intrare rdcina arborelui curent. void A_preordine (nod R) { if (R){ vizit (R); A_preordine(FIU[R]); A_preordine(FRATE[R]); } }

* Material didactic pentru ID *

47

Parcurgerea A-postordine Regula de parcurgerea n A-postordine este asemntoare traversrii A-preordine, singura diferen fiind aceea c, n acest tip de traversare, rdcina fiecrui arbore este vizitat dup ce au fost vizitate toate celelalte vrfuri ale arborelui. Exemplu 3.2.2. Pentru arbori reprezentai prin structuri dinamice de date, implementarea parcurgerii n Apostordine poate fi obinut pe baza urmtoarei funcii recursive. Parametrul de intrare al funciei A_postordine reprezint rdcina arborelui curent n momentul apelului. void A_postordine (nod R) { if (R) { for(i=0;i<n;i++) A_postordine(R->leg[i]); vizit (R); } } Observaie Parcurgerile n A-preordine i A-postordine sunt variante de parcurgeri n adncime (variante ale metodei DF). Ambele metode consider prioritare vrfurile aflate la distan maxim fa de rdcina arborelui iniial. Parcurgerea pe niveluri Parcurgerea unui arbore orientat pe nivele const n vizitarea vrfurilor sale n ordinea cresctoare a distanelor fa de rdcin. Ca i n cazul metodei BF, implementarea parcurgerii pe nivele este bazat pe utilizarea unei structuri de coad C. La momentul iniial rdcina arborelui este inserat n C. Atta timp ct timp coada este nevid, este preluat cu tergere un vrf din C, este vizitat i sunt introdui n coad descendenii si. Calculul este ncheiat cnd C=. Exemplu 3.2.3. Pentru arborele de la exemplul 3.2.1, evoluia algoritmului este, C T t=1 t=2 t=3 t=4 t=5 t=6 t=7 t=8 t=9 t=10 t=11 t=12 t=13 t=14 t=15 t=16 t=17 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 15 3 4 5 6 7 8 9 10 11 12 13 14 15 16 4 5 6 7 8 9 10 11 12 13 14 15 16 6 7 8 10 11 12 13 14 15 16 7

11 12 13 14 15 16

12 13 13 14 15 15 16 16

16

deci vrfurile sunt vizitate n ordinea, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16.

Programarea calculatoarelor

48

Observaie Metoda BF pentru parcurgerea grafurilor este o generalizare a tehnicii de parcurgere pe nivele a arborilor orientai. O alternativ de implementare a parcurgerii pe nivele poate fi descris prin intermediul funciilor recursive frai i parc. Coada C este o variabil global i este iniializat cu rdcina arborelui. Parcurgerea este obinut prin apelul parc(C). void frai(v) { if (v){ push(C,v); frai(FRATE[v]); } } void parc() { if (C){ pop(C,v);VIZIT(v); frai(FIU[v]); parc(); } }

Teste de autoevaluare. Probleme


3.2.1. Scriei o surs C pentru construcia unui arbore orientat, reprezentat prin intermediul unei structuri dinamice arborescente. Numrul maxim de descendeni ai unui nod este 4. 3.2.2. Care este ordinea de vizitare a nodurilor arborelui din exemplul 3.2.1 n parcurgerea Apreordine? 3.2.3. Care este ordinea de vizitare a nodurilor arborelui din exemplul 3.2.1 n parcurgerea Apostordine? 3.2.4. Care este ordinea de vizitare a nodurilor arborelui din exemplul 3.2.1 n parcurgerea pe niveluri? 3.2.5. Scriei o funcie C pentru implementarea parcurgerii pe niveluri n cazul reprezentrii FIU-FRATE a arborelui de traversat.

3.3. Arbori pariali; algoritmul Kruskal


Definiia 3.3.1. Fie G un graf. Subgraful parial H este un arbore parial al lui G dac H este graf arbore. Definiia 3.3.2. Fie G=(V,E,w) un graf ponderat conex.Dac T=(V,E0) este un arbore parial al grafului G=(V,E), ponderea arborelui T, notat W(T), este definit prin W(T)= w( e ) .
eE0

Exemplu 3.3.1. Pentru graful ponderat


1 4 2 2 4 8 6 1 3 9 12 2 3 8 5

* Material didactic pentru ID *

49

T este un arbore parial de pondere 32.


T: 4 2 8 4 3 6 9 1 2 3 5

Definiia 3.3.4. Arborele parial T0T(G) este arbore parial minim pentru G dac W(T0)=min{W(T); TT(G)}, unde T(G) este mulimea arborilor pariali corespunztori grafului G. Observaie Dac G este graf finit, atunci T(G) este mulime finit, deci orice graf finit ponderat i conex are cel puin un arbore parial minim. n continuare este prezentat algoritmul Kruskal, pentru determinarea unui arbore parial minim al unui graf ponderat conex G=(V,E,w). Pas 1: i=1; E0= Pas 2: Determin R={e/eE \ Ei-1 astfel nct graful (V,Ei-1 {e}) este aciclic} Dac R=, atunci stop; altfel, selecteaz eiR cu w(ei)=min{w(e), eR}; Ei=Ei-1 {ei} Pas 3: i=i+1 i reia pasul 2. Arborele parial de cost minim al grafului G este (V,Ei-1). Pentru implementarea algoritmului Kruskal, graful conex ponderat este reprezentat sub form tabelar, muchiile fiind ordonate cresctor dup ponderi. Muchiile selectate de algoritm pot fi meninute de asemenea ntr-o structur tabelar, sau doar marcate ca fiind incluse n mulimea de muchii din arborele parial minim a crui construcie este dorit.n varianta prezentat n continuare muchiile selectate sunt afiate. Verificarea condiiei ca muchia selectat s nu formeze nici un ciclu cu muchiile selectate la etapele precedente este realizat prin utilizarea un vector TATA, definit astfel. Pentru fiecare vrf i (vrfurile grafului fiind numerotate de la 1 la n, unde n este numrul de noduri ale grafului), componenta TATA [i] este predecesorul su n arborele care conine vrful i construit pn la momentul curent dac i nu este rdcina acelui arbore, respectiv TATA[i] este egal cu numrul de vrfuri ale arborelui de rdcin i, n caz contrar. Componentele vectorului TATA sunt iniializate cu valoarea -1. Calculul care realizeaz adugarea unei noi muchii poate fi descris astfel.Este determinat o muchie de cost minim e=v1v2 care nu a fost selectat anterior. Dac vrfurile v1 i v2 nu aparin aceluiai arbore, atunci proprietatea de aciclicitate este ndeplinit i muchia e este adugat la structura curent. Adugarea muchiei e selectate este realizat prin reunirea arborilor din care fac parte v1 i v2 de rdcini r1, respectiv r2, astfel: dac TATA[r1]<TATA[r2], atunci arborele rezultat prin reunirea celor doi arbori are ca rdcin vrful r1, iar vrful r2 devine fiu al lui r1. Altfel, rdcina arborelui rezultat prin reunire fiind r2, iar r1 devenind fiu al rdcinii. Calculul se ncheie dup ce a fost adugat i cea de-a (n-1)-a muchie. Algoritmul Kruskall poate fi implementat prin urmtoarea surs C, #include<stdio.h> #include<conio.h> int radacina(int v,int *tata) { int u=v; while(tata[u]>=0) u=tata[u]; return u; }

Programarea calculatoarelor

50

int kruskal(int a[][3],int nm, int nv) { int tata[50],i,j;int c=0; for(i=0;i<nv;i++)tata[i]=-1; for(j=i=0;i<nv-1;j++){ int v1=a[j][0]; int v2=a[j][1]; int k=radacina(v2,tata);int p=radacina(v1,tata); if(k-p){ if(tata[k]<tata[p]){tata[k]+=tata[p];tata[p]=k;} else{tata[p]+=tata[k];tata[k]=p;} c+=a[j][2];printf("%i -> %i cost %i\n",v1+1,v2+1,a[j][2]);i++;} } return c; } void main() { clrscr();int nv,nm, a[100][3]; printf("Numarul de varfuri:");scanf("%i",&nv); printf("Numarul de muchii");scanf("%i",&nm); printf("Matricea de reprezentare\n"); for(int i=0;i<nm;i++) for(int j=0;j<3;j++)scanf("%i",&a[i][j]); for(i=0;i<nm;i++) for(int j=0;j<2;j++)a[i][j]--; printf("Arborele de cost minim: \n"); int cost=kruskal(a,nm,nv);printf("\ncu costul%i",cost);getch(); } Test de autoevaluare 3.3.1. Fie graful
1 4 2 2 4 8 6 1 3 9 12 2 8 5 3

Care este evoluia determinat de aplicarea algoritmului Kruskall?

3.4. Arbori binari. Arbori binari de sortare


Definiia 3.4.1. Un arbore binar este un arbore orientat cu proprietatea c pentru orice vrf v, od(v)2. Dac od(v)=2, cei doi descendeni sunt desemnai ca descendent stng (fiu stnga) respectiv descendent drept (fiu dreapta). Pentru vrfurile cu od(v)=1, unicul descendent este specificat fie ca fiu stnga, fie ca fiu dreapta. Definiia 3.4.2. Se numete nod terminal orice vrf v al arborelui cu od(v)=0. n caz contrar nodul v este neterminal. Reprezentarea unui arbore binar este realizat printr-o structur arborescent. Pentru fiecare nod N al arborelui binar, sunt memorate informaia asociat lui N i legturile ctre descendenii lui. Absena unui descendent este reprezentat prin NULL.

* Material didactic pentru ID *

51

identificator nod

legtur fiu stng

legtur fiu drept

Definiia 3.4.3. Fie T=(V,E) un arbore binar cu rdcina R. Subarborele stng al lui T este ST=(V\{R},E\{RS}), unde S este fiul stnga al rdcinii. Subarborele drept al lui T este DT=(V\{R},E\{RD}), unde D este fiul dreapta al rdcinii. Exemplu 3.4.1. Pentru arborele binar,

1 2 4 5 6 3 7

8 2 4 5

10 3 6 7

8 subarbore stng

10 subarbore drept

n plus fa de metodele A-preordine, A-postordine i pe nivele, parcurgerile n preordine(RSD), inordine(SRD) i respectiv postordine(SDR) sunt special considerate pentru arbori binari i au multiple aplicaii. Regula de vizitare pentru aceste tipuri de parcurgere revine la parcurgerea subarborelui stng i parcurgerea subarborelui drept corespunztori vrfului curent, la momentul iniial vrf curent fiind rdcina arborelui. Diferena ntre aceste trei tipuri de parcurgere este dat de momentul n care devine vizitat fiecare vrf al arborelui. n parcurgerea RSD (rdcin-subarbore stng-subarbore drept), fiecare vrf al arborelui este vizitat n momentul n care devine vrf curent; n parcurgerea SRD, vizitarea vrfului curent R este efectuat dup ce a fost parcurs subarborele stng al lui R, respectiv n parcurgerea SDR vizitarea fiecrui vrf este efectuat dup ce au fost parcuri subarborii afereni lui. Exemplu 3.4.2. Pentru arborele de la exemplul 3.4.1., secvenele de vrfuri rezultate prin aplicarea parcurgerilor RSD, SRD, SDR sunt: - preordine: 1,2,4,8,5,3,6,9,10,7 - inordine: 4,8,2,5,1,9,6,10,3,7 - postordine: 8,4,5,2,9,10,6,7,3,1.

Programarea calculatoarelor

52

Definiia 3.4.4. Un arbore de sortare este un arbore binar cu urmtoarele proprieti, - fiecrui nod i al arborelui i este ataat o informaie INF(i) dintr-o mulime ordonat de valori; - pentru fiecare nod i, INF(i) este mai mare dect INF(j), pentru toate nodurile j din subarborele stng al arborelui cu rdcin i; - pentru fiecare nod i, INF(i) este mai mic dect INF(j), pentru toate nodurile j din subarborele drept al arborelui cu rdcin i; - pentru orice vrfuri i i j daca ij atunci INF(i)INF(j). Exemplu 3.4.3. Arborele binar

50

30

70

10

40 20 80

90

este arbore de sortare. Operaiile primitive asupra arborilor de sortare sunt inserarea unui nou nod, tergerea unui nod i parcurgerea arborelui (n preordine, inordine sau postordine). Inserarea i tergerea de noduri aplicate unui arbore de sortare trebuie realizate astfel nct arborele rezultat s fie de asemenea arbore de sortare. Observaie Parcurgerea n inordine a unui arbore de sortare determin obinerea secvenei informaiilor asociate vrfurilor arborelui n ordine cresctoare. Inserarea unui nod ntr-un arbore de sortare Algoritmul de inserare a unei informaii nr n arborele de sortare de rdcin rad este recursiv i const n efectuarea urmtoarelor operaii. Vrful curent v la momentul iniial este rdcina arborelui. 1. dac arborele de rdcin v este vid, este generat arborele cu un singur nod, cu informaia ataat nr; 2. altfel - dac informaia ataat nodului v este mai mare dect nr, atunci vrf curent devine fiul stnga al lui v; - dac informaia ataat nodului v este egal cu nr, atunci stop; - dac informaia ataat nodului v este mai mic dect nr, atunci vrf curent devine fiul dreapta al lui v. Exemplu 3.4.4. Aplicarea algoritmul descris pentru inserarea informaiei 55 n arborele de sortare din exemplul 3.4.3. determin urmtoarele operaii, INF(v)=50; 50<55, insereaz n subarborele cu rdcina avnd informaia ataat 70 INF(v)=70; 70>55, insereaz n subarborele stng cu rdcina NULL. Este creat nodul cu informaie 55, fiu stng al nodului de informaie 70.

* Material didactic pentru ID *

53

Arborele rezultat este

50

30

70

10

40

55

90

20

80

tergerea unei informaii dintr-un arbore de sortare Algoritmul pentru tergerea unei informaii nr din arborele de sortare de rdcin rad este recursiv i poate fi descris astfel. Vrful curent v la momentul iniial este rdcina arborelui. - 1. dac arborele este vid atunci stop; - 2. altfel a) dac informaia ataat nodului v este mai mare dect nr, atunci vrf curent devine fiul stnga al lui v ; b) dac informaia ataat nodului v este mai mic dect nr, vrf curent devine fiul dreapta al lui v ; c) dac INF(v)=nr atunci: - c1) dac subarborele stng este vid, atunci adresa vrfului v este memorat ntr-o celul suplimentar aux, v devine fiul dreapta al lui v, iar celula aux este eliberat din memorie; - c2)dac subarborele stng este nevid atunci se determin cel mai mare element din subarborele stng; c2.1.) dac fiul stnga al lui v nu are subarbore drept, atunci informaia ataat fiului stnga se transfer n vrful curent, iar fiul stnga este nlocuit cu fiul su stnga i este eliberat memoria corespunztoare celulei v->fius; c2.2) altfel, se transfer n rdcin informaia ataat ultimului nod p determinat la c2), nodul p este nlocuit cu fiul su stng i celula corespunztoare lui p este eliberat din memorie. Exemplu 3.4.5. tergerea informaiei 70 n arborele de sortare din exemplul 3.4.4. este realizat astfel: 70>50, decide tergerea din subarborele drept 70=70, decide tergerea din arborele curent: rdcina etichetat cu 70; exist subarbore stng iar acesta nu are subarbore drept- nodul cu informaie 70 este etichetat cu 55, iar p este nlocuit cu subarborele su stng (vid). Arborele rezultat

Programarea calculatoarelor

54

50

30

55

10

40

90

20
este arbore de sortare.

80

Observaie Punctul c) de la pasul 2 al algoritmului de eliminare a unei informaii dintr-un arbore de sortare poate fi nlocuit cu. c) dac INF(v)=nr atunci, c1) dac subarborele drept este vid, atunci adresa vrfului v este memorat ntr-o celul suplimentar aux, v devine fiul stnga al lui v, iar celula aux este eliberat din memorie; c2) dac subarborele drept este nevid atunci se determin cel mai mic element din subarborele drept c2.1.) dac fiul dreapta al lui v nu are subarbore stng, atunci informaia ataat fiului dreapta este transferat n vrful curent, iar fiul dreapta este nlocuit cu fiul su dreapta i este eliberat memoria corespunztoare celulei v->fiud. c2.2) altfel, se transfer n rdcin informaia ataat ultimului nod p determinat la c2), nodul p este nlocuit cu fiul su dreapta i celula corespunztoare lui p este eliberat din memorie.

Teste de autoevaluare. Probleme


3.4.1. Scriei o funcie C pentru implementarea operaiei parcurgere SRD a unui arbore de sortare. 3.4.2. Scriei o funcie C pentru implementarea operaiei de adugare a unei informaii ntr-un arbore de sortare. 3.4.3. Scriei o funcie C pentru implementarea operaiei de extragere a unei informaii dintr-un arbore de sortare. 3.4.4. Scriei o funcie C pentru implementarea operaiei parcurgere RSD a unui arbore binare.

3.5. Arbori de structur


Expresiile aritmetice n care intervin numai operatori binari pot fi reprezentate prin intermediul arborilor binari n care fiecare nod neterminal are doi fii. Definiia 3.5.1. Un arbore de structur are vrfurile etichetate astfel: - fiecare nod neterminal este etichetat cu un simbol corespunztor unuia dintre operatori; - fiecare nod terminal este etichetat cu un operand. Construcia arborelui de structur corespunztor unei expresii aritmetice date se realizeaz pe baza parantezrii existente n expresie i a prioritilor convenional asociate operatorilor (ordinea operaiilor) astfel nct rdcina fiecrui subarbore este etichetat cu operatorul care se execut ultimul n evaluarea subexpresiei corespunztoare acelui subarbore.

* Material didactic pentru ID *

55

Exemplu 4.5.1. Pentru expresia matematic (a+b)*(c-d)+e/g, arborele de structur corespunztor este,

Construcia arborelui de structur pentru o expresie s este realizat n dou etape, i anume, 1. ataarea de prioriti operatorilor i operanzilor. Prioritile ataate permit eliminarea parantezelor fr ca semnificaia expresiei s se modifice; 2. construcia propriu-zis. Prima etap este realizat astfel, - prioritatea iniial a operatorilor +,- este 1 (dac expresia nu conine paranteze atunci n construcie aceti operatori vor fi primii luai n considerare n ordinea de la dreapta la stnga); - prioritatea iniial a operatorilor /,* este 10 (dac expresia nu conine paranteze, acetia sunt considerai dup operatorii de prioritate 1 n ordinea de la dreapta la stnga); - prioritatea fiecrui operator este incrementat cu valoarea 10 pentru fiecare pereche de paranteze n interiorul crora se afl; - prioritatea ataat fiecrui operand este MAXINT. Dup stabilirea sistemului de prioriti, sunt eliminate parantezele din expresie, ordinea de efectuare a operaiilor n cadrul expresiei fiind indicat de vectorul de prioriti ataat. Construcia arborelui de structur pe baza expresiei s din care au fost eliminate parantezele i a vectorului de prioriti, poate fi realizeaz recursiv n modul urmtor. La momentul iniial expresia curent este expresia dat. - pentru expresia curent se determin operatorul/operandul de prioritate minim care se ataeaz ca etichet a rdcinii r a subarborelui de structur corespunztor ei; fie i poziia acestuia n cadrul expresiei; - dac expresia are un singur simbol atunci r->fius=r->fiud=NULL; - altfel, se consider subexpresiile s1 i s2, constnd din simbolurile de pe poziiile 0 pn la i-1 i respectiv i+1 pn la lungimea irului s. Arborii de structur corespunztori subexpresiilor s1 i s2 se ataeaz ca subarbore stng, respectiv subarbore drept vrfului r.

Programarea calculatoarelor

56

Exemplu 3.5.2. Etapele calculului sistemului de prioriti i al arborelui de structur pentru expresia de la exemplul 3.5.1. pot fi descrise astfel, dim
1 2 3 3 4 4 5 6 7 7 8 9 10 11

vectorul prioritate
(MAXINT) (MAXINT,11) (MAXINT,11,MAXINT) (MAXINT,11,MAXINT) (MAXINT,11,MAXINT,10) (MAXINT,11,MAXINT,10) (MAXINT,11,MAXINT,10,MAXINT) (MAXINT,11,MAXINT,10,MAXINT,11) (MAXINT,11,MAXINT,10,MAXINT,11,MAXINT) (MAXINT,11,MAXINT,10,MAXINT,11,MAXINT) (MAXINT,11,MAXINT,10,MAXINT,11,MAXINT,1) (MAXINT,11,MAXINT,10,MAXINT,11,MAXINT,1,MAXINT) (MAXINT,11,MAXINT,10,MAXINT,11,MAXINT,1,MAXINT,10) (MAXINT,11,MAXINT,10,MAXINT,11,MAXINT,1,MAXINT,10,MAXINT)

Dup eliminarea parantezelor, expresia rezultat este s=a+b*c-d+e/g. Arborele de structur este,

Observaie Construcia arborelui de structur poate fi realizat n ipoteza n care expresia este corect. Definiia 3.5.2. Se numete forma polonez direct a unei expresii, expresia rezultat n urma parcurgerii RSD a arborelui de structur . Se numete forma polonez invers a unei expresii, expresia rezultat n urma parcurgerii SDR a arborelui de structur Exemplu 3.5.3. Pentru expresia considerat la exemplul 3.5.1., forma polonez direct este +*+ab-cd/eg. Forma polonez invers a expresiei date este ab+cd-*eg/+. Observaie Parcurgerea arborelui n inordine determin secvena de simboluri rezultat prin eliminarea parantezelor din expresia dat. Restaurarea unei forme parantezate poate fi realizat printr-o parcurgere SRD i anume n modul urmtor. La momentul iniial vrf curent este rdcina arborelui de structur. Dac vrful curent v nu este vrf terminal atunci se genereaz (s1) eticheta(v)(s2), unde eticheta(v) este operatorul etichet a vrfului, s1 este secvena rezultat prin traversarea SRD a subarborelui stng, s2 este secvena rezultat prin traversarea SRD a subarborelui drept. Dac v este vrf terminal atunci este generat secvena eticheta(v).

* Material didactic pentru ID *

57

Evaluarea expresiilor aritmetice pe baza arborilor de structur Traversarea SRD a arborelui de structur ataat unei expresii aritmetice permite evaluarea expresiei pentru valorile curente corespunztoare variabilelor. Evaluarea poate fi efectuat n mod recursiv astfel. La momentul iniial vrf curent este rdcina arborelui. Dac v este vrf curent atunci noua informaie asociat lui v este: val(eticheta(v)), dac v este vrf terminal; val(s1)eticheta(v)val(s2), dac v este neterminal, unde val(s1), val(s2) sunt valorile rezultate prin evalurile subarborilor stng i respectiv drept ai lui v, val(eticheta(v)) este valoarea curent a variabilei, dac eticheta lui v este variabil, respectiv valoarea constantei, dac eticheta lui v este o constant. Exemplu 3.5.4. Prin aplicarea metodei de evaluare descrise pentru a=3, b=2, c=5, d=2, e=6 i g=2, obinem

18

15

Construcia arborelui de structur asociat unei expresii i evaluarea expresiei pentru valori date ale operanzilor pot fi implementate prin intermediul urmtoarei surse C. #include<stdio.h> #include<conio.h> #include<alloc.h> #include<values.h> #include<string.h> #include<math.h> typedef struct nod{ char inf; float v; struct nod *l,*r; } arb, *arbore; void prioritati(char *s, int *prioritate) { int i,j,dim; //stabilirea prioritatilor for(i=j=dim=0;i<strlen(s);i++) switch(s[i]){ case ')':j-=10;break; case '(':j+=10;break; case '+':{prioritate[dim]=j+1;dim++;break;} case '-':{prioritate[dim]=j+1;dim++;break;} case '*':{prioritate[dim]=j+10;dim++;break;}

Programarea calculatoarelor

58

case '/':{prioritate[dim]=j+10;dim++;break;} default:{prioritate[dim]=MAXINT;dim++;break;} } //eliminarea parantezelor for(i=0;i<strlen(s);) if((s[i]==')')||(s[i]=='(')){ for(j=i+1;j<strlen(s);j++)s[j-1]=s[j]; s[strlen(s)-1]='\0';} else i++; } void cr_arb_str(arbore *rad, unsigned p, unsigned u, char *s,int *pri) { int min=pri[p]; int poz=p; for(int i=p+1;i<=u;i++) if(min>=pri[i]){min=pri[i];poz=i;} (*rad)=(arbore)malloc(sizeof(arb)); (*rad)->inf=s[poz]; if(p==u) (*rad)->l=(*rad)->r=NULL; else{ cr_arb_str(&((*rad)->l),p,poz-1,s,pri); cr_arb_str(&((*rad)->r),poz+1,u,s,pri); } } void forma_poloneza(arbore rad) { if(rad){ printf("%c",rad->inf); forma_poloneza(rad->l); forma_poloneza(rad->r); } } float eval(arbore rad) { char s[1]; if(rad){ if((rad->r==rad->l)&&(rad->l==NULL))return rad->v; else{ switch (rad->inf){ case '+':rad->v=eval(rad->l)+eval(rad->r);break; case '-':rad->v=eval(rad->l)-eval(rad->r);break; case '*':rad->v=eval(rad->l)*eval(rad->r);break; case '/':rad->v=eval(rad->l)/eval(rad->r);break; } return rad->v; }}} void atribuie_arbore(arbore rad) { if(rad){ if((rad->r==rad->l)&&(rad->l==NULL)){ printf("%c =",rad->inf); float t; scanf("%f",&t); rad->v=t; } else {atribuie_arbore(rad->l);

* Material didactic pentru ID *

59

atribuie_arbore(rad->r); }}} void main() { clrscr(); char s[100];int p[100];arbore radacina=NULL; printf("Expresia:");scanf("%s",&s); prioritati(s,p); int n=strlen(s); cr_arb_str(&radacina,0,n-1,s,p); printf("\nForma poloneza inversa "); forma_poloneza(radacina); printf("\n Valori pentru varabile\n"); atribuie_arbore(radacina); printf("\nEvaluarea: %7.3f",eval(radacina)); getch(); } Teste de autoevaluare Fie expresia Expr=(a-b-c)/e+f*g 3.5.1. Care este arborele de structur asociat expresiei Expr? 3.5.2. Care este forma polonez invers asociat expresiei Expr? 3.5.3. Care este forma polonez direct asociat expresiei Expr?

3.6. Rspunsuri la testele de autoevaluare. Comentarii


3.1.1. Graful nu este arbore, deoarece conine trei componente conexe: {1,2,3,4,6}, {3} i {7,8}. 3.1.2. Graful din problema 3.1.1. nu este arbore deoarece este aciclic, dar n=9, m=6. 3.1.3. Graful din problema 3.1.1. nu este conex, deci nu este graf arbore. 3.2.1. Urmtoarea surs C implementeaz problema construciei unui arbore orientat, reprezentat prin intermediul unei structuri dinamice arborescente. Numrul maxim de descendeni ai unui nod este 4. n cazul unui numr mai mare de descendeni este preferat n general reprezentarea FIU-FRATE, datorit dimensiunii spaiului de memorie ocupat. Afiarea informaiilor arborelui creat este realizat prin traversarea n A-preordine (a se vedea paragraful urmtor).
#include<stdio.h> #include<conio.h> #include<alloc.h> typedef struct nod{ int inf; struct nod *fiu[4]; } arb, *arbore; void inserare_tata(arbore *ptata,int k,int info) { arbore nou=(arbore)malloc(sizeof(arb)); nou->inf=info; for(int i=0;i<4;i++)nou->fiu[i]=NULL; (*ptata)->fiu[k]=nou; } void inserare(arbore *ppred) { int j,info; arbore *pred; for(int nr=0;(*ppred)->fiu[nr];nr++){ (*pred)=(*ppred)->fiu[nr]; printf("Numarul de fii ai nodului %i:",(*pred)->inf); scanf("%i",&j); for(int k=0;k<j;k++){

Programarea calculatoarelor

60

scanf("%i",&info); inserare_tata(pred,k,info); } } for(nr=0;(*ppred)->fiu[nr];nr++) inserare(&((*ppred)->fiu[nr])); } void A_preordine(arbore r) { if(r){ printf("%i ",r->inf); for(int i=0;i<4;i++) A_preordine(r->fiu[i]); } } void main(){ clrscr(); int n,j,info; arbore radacina=NULL; printf("Introduceti informatiile pe niveluri\n"); printf("Introduceti radacina\n"); scanf("%i",&info); radacina=(arbore)malloc(sizeof(arb)); radacina->inf=info; for(int i=0;i<4;i++)radacina->fiu[i]=NULL; printf("Numarul de fii ai nodului %i",radacina->inf); scanf("%i",&j); for(int k=0;k<j;k++){ scanf("%i",&info); inserare_tata(&radacina,k,info); } arbore ppred=radacina; inserare(&ppred); printf("Parcurgerea A-preordine a arborelui : \n"); A_preordine(radacina); getch(); }

3.2.2. Pentru arborele orientat din exemplul 3.2.1., prin aplicarea parcurgerii n A-preordine, rezult: 1,2,5,6,9,10,11,12,13,7,3,4,8,14,15,16. 3.2.3. Pentru arborele orientat din exemplul 3.2.1. ordinea de vizitare a vrfurilor n parcurgerea A-postordine este: 5,9,10,11,12,13,6,7,2,3,14,15,16,8,4,1. 3.2.4. Pentru arborele definit n exemplul 3.2.1., prin aplicarea parcurgerii pe niveluri, rezult urmtoarea ordine de vizitare a nodurilor, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16. 3.2.5. n cazul reprezentrii FIU-FRATE a arborelui de traversat, parcurgerea pe niveluri poate fi implementat prin urmtoarea funcie.
void parcurgere_pe_nivele(nod R,int FIU[],int FRATE[],int n) { ptcoada C=NULL;push(C,R); while (C) { pop(C,v); VIZIT(v); v=FIU[v]; while (v){ push(C,v); v=FRATE[v]; } } }

Funciile push i pop implementeaz inserarea unuei celule n coad, respectiv extragerea unui element al cozii. 3.3.1. Evoluia determinat de program pentru graful

* Material didactic pentru ID *

61

1 4 2 6 2 8 5, 3

2 4

8 4

1 3

2 2 1 1 A = 3 1 4 5 3

3 4 6 5 4 2 6 6 6

1 2 2 3 4 4 8 8 9

este, i, j dup cea demuchia TATA a t-a iteraie selectat t=0 (-1,-1,-1,-1,-1,-1) t=1,i=0,j=0 (2,3) (-1,-2,2,-1,-1,-1) t=2,i=1,j=1 (2,4) (-1,-3,2,2,-1,-1) t=3,i=2,j=2 (1,6) (-2,-3,2,2,-1,1) t=4,i=3,j=3 (1,5) (-3,-3,2,2,1,1) t=5,i=4,j=4 (-3,-3,2,2,1,1) t=6,i=4,j=5 (1,2) (-5,1,1,2,1,1) MUCHIILE ARBORELUI MINIM: {(2,3),(2,4),(1,6),(1,5),(1,2)} Costul 1 2 2 3 4 COSTUL: 12

3.4.1, 3.4.2, 3.4.3 n urmtoarea surs C sunt implementai algoritmii de adugare, tergere i parcurgere SRD n arbori de sortare.
#include<stdio.h> #include<conio.h> #include<alloc.h> typedef struct nod{ int inf; struct nod *l,*r; } arb, *arbore; void inserare(arbore *radacina,int info) { if(*radacina==NULL){

Programarea calculatoarelor

62

arbore nou; nou=(arbore)malloc(sizeof(arb)); nou->inf=info; nou->l=nou->r=NULL; *radacina=nou; } else if((*radacina)->inf>info) inserare(&((*radacina)->l),info); else if((*radacina)->inf<info) inserare(&((*radacina)->r),info); } int extragere(arbore *radacina,int info) { if(*radacina==NULL) return 0; else if((*radacina)->inf>info) return extragere(&((*radacina)->l),info); else if((*radacina)->inf<info) return extragere(&((*radacina)->r),info); else{ if((*radacina)->l==NULL){ arbore aux=*radacina; *radacina=(*radacina)->r; free(aux); } else{ arbore p,p1; for(p=(*radacina)->l;p->r;p1=p,p=p->r); if(((*radacina)->l)->r==NULL){ (*radacina)->inf=p->inf; (*radacina)->l=p->l; free(p); } else{ (*radacina)->inf=p->inf; arbore aux=p; p1->r=p->l; free(aux); } } return 1; } } void srd(arbore radacina) { if(radacina){ srd(radacina->l); printf("%i ",radacina->inf); srd(radacina->r); } } void main() { clrscr(); int n,info; arbore radacina=NULL; printf("Numarul de noduri:"); scanf("%i",&n); printf("Introduceti informatiile\n"); for(int i=0;i<n;i++){ scanf("%i",&info); inserare(&radacina,info); } printf("Parcurgerea SRD a arborelui de sortare: \n"); srd(radacina); printf("\nInformatia nodului de extras:");

* Material didactic pentru ID *

63

scanf("%i",&info); if(extragere(&radacina,info)){ printf("\nArborele rezultat in parcurgere SRD\n"); srd(radacina); } else printf("\nInformatia nu este in arbore"); getch(); }

3.4.4. Utiliznd structura de arbore de la problemele 3.4.1-3.4.3, poate fi scris funcia,


void rsd(arbore radacina) { if(radacina){ printf("%i ",radacina->inf); srd(radacina->l); srd(radacina->r); }

3.5.1. Arborele de structur este,


+ / e f * g

3.5.2. Forma polonez invers: ab-c-e/fg*+ 3.5.3. Forma polonez direct: +/--abce*fg

Bibliografie 1. I. Gh. Roca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C. Uscatu, M. Mircea Programarea calculatoarelor. Algoritmi n programare, Ed. ASE Bucureti, 2007 2. I. Gh. Roca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C. Uscatu Programarea calculatoarelor. tiina nvrii unui limbaj de programare. Teorie i aplicaii, Ed. ASE Bucureti, 2003 3. Liviu Negrescu Limbajele C i C++ pentru nceptori, vol. I, II, Ed. Microinformatica, Cluj-Napoca, 1994

Programarea calculatoarelor

64

4. Elemente de programare orientat obiect


Obiective Obiectivele acestei uniti de nvare constau n familiarizarea studenilor cu noiunile teoretice de programare orientat obiect i dobndirea abilitilor de baz n acest domeniu, pentru a pregti trecerea la etapa urmtoare din studiul programrii calculatoarelor. Limbajul folosit pentru exemplificare este C++, o dezvoltare a limbajului C standard utilizat anterior. Cuprins 4.1. Modelul de date orientat obiect ...................................................................................... 64 4.2. Definirea claselor ............................................................................................................. 70 4.3. Constructori ..................................................................................................................... 73 4.4. Destructori........................................................................................................................ 76 4.5. Funcii prieten.................................................................................................................. 77 4.6. Derivarea claselor ............................................................................................................ 78 4.7. Rspunsuri i comentarii la testele de autoevaluare .................................................... 93
Timpul mediu de nvare pentru studiul individual: 5 ore. Programarea orientat obiect (Object Oriented Programming - OOP) reprezint o tehnic ce s-a impus n anii 90, dovedindu-se benefic pentru realizarea sistemelor software de mare complexitate. Noiunea de obiect dateaz din anii 60, odat cu apariia limbajului Simula. Exist limbaje ca Smalltalk i Eiffel care corespund natural cerinelor programrii orientate obiect, fiind concepute n acest spirit. Recent au fost dezvoltate i alte limbaje orientate obiect, fie pentru programare general, fie pentru realizarea de scripturi Java, Delphi, C++, Visual Basic .NET, C#, Python, Ruby. Unele dintre ele ofer n continuare i posibilitatea programri procedurale (Delphi, C++). Toate limbajele folosite n prezent ofer i faciliti de programare orientat obiect ADA, Fortran, Cobol, PHP etc. n prezent exist n funciune sisteme software de mare anvergur realizate n tehnica programrii orientat obiect, principiile ei fiind suficient de bine clarificate, astfel nct s se treac din domeniul cercetrii n cel al produciei curente de programe. Acest capitol prezint o introducere n lucrul orientat obiect n limbajul C++, fr a acoperi toat problematica specific.

4.1. Modelul de date orientat obiect


OOP reprezint o abordare cu totul diferit fa de programarea procedural, devenit deja clasic. Dac n programarea clasic programatorul era preocupat s rspund la ntrebarea ce trebuie fcut cu datele?, adic s defineasc proceduri care s transforme datele n rezultate, n OOP accentul cade asupra datelor i legturilor ntre acestea, ca elemente prin care se modeleaz obiectele lumii reale. Se poate afirma, ntr-o prim analiz, c OOP organizeaz un program ca o colecie de obiecte, modelate prin date i legturi specifice, care interacioneaz dinamic, adic manifest un anumit comportament, producnd rezultatul scontat. n general, pentru modelul de date orientat pe obiect, se consider definitorii urmtoarele concepte: abstractizare, obiect, atribut, metod, clas, spaiu propriu, spaiu extins, ncapsulare, motenire i polimorfism. Abstractizarea constituie procesul de simplificare a realitii prin reinerea caracteristicilor i comportamentelor eseniale i constituirea lor ntr-un model adecvat rezolvrii problemelor.

* Material didactic pentru ID *

65

Obiectul este un model informaional al unei entiti reale, care posed, la un anumit nivel, o mulime de proprieti i care are, n timp, un anumit comportament, adic manifest reacii specifice n relaiile cu alte entiti din mediul su de existen. Ca model, un obiect este o unitate individualizabil prin nume, care conine o mulime de date i funcii. Datele descriu proprietile i nivelul acestora, iar funciile definesc comportamentul. Avnd n vedere proprietile comune i comportamentul similar al entitilor pe care le modeleaz, obiectele pot fi clasificate n mulimi. O mulime de obiecte de acelai fel constituie o clas de obiecte, descris prin modelul comun al obiectelor sale. Exemplu n figura 10.1, numerele complexe, ca perechi de numere reale de forma (parte real, parte imaginar) pot fi descrise printr-un model comun, denumit ClasaComplex. Modelul arat c orice obiect de acest fel se caracterizeaz printr-o pereche de numere ntregi i c pe aceast mulime snt definite operaii unare i binare care arat cum interacioneaz obiectele n interiorul mulimii: un numr complex poate da natere modulului i opusului su, dou numere complexe pot produce un alt numr complex ca sum, produs etc. Generaliznd, se poate afirma c o clas de obiecte se manifest ca un tip obiect, iar modelul comun al obiectelor este modelul de definire a tipului obiect. Astfel, obiectele individuale apar ca manifestri, realizri sau instanieri ale clasei, adic exemplare particulare generate dup modelul dat de tipul obiect. Altfel spus, o clas poate fi considerat ca un tip special de dat, iar obiectele sale ca date de acest tip.

Fig.4.1. Clas i obiecte mulimea numerelor complexe Acceptarea acestei semnificaii pentru clase de obiecte este de natur s simplifice descrierea obiectelor i s asigure un tratament al acestora similar tipurilor structurate de date din limbajele de programare: este suficient o descriere a tipului obiect i apoi se pot declara constante i variabile de acest tip. Datele care reprezint proprietile obiectelor se numesc atribute i snt de un anumit tip (de exemplu ntregi, reale, caractere etc.). Setul de valori ale atributelor unui obiect la un moment dat formeaz starea curent a obiectului respectiv. Funciile care definesc comportamentul obiectelor snt cunoscute ca metode ale clasei. mpreun, atributele i metodele snt membrii clasei, identificabili prin nume. Pentru a pune n eviden faptul c un membru aparine unui obiect se utilizeaz calificarea, astfel: nume_obiect.nume_membru. n figura 4.1, a.P_real refer valoarea 1.0, iar a.Modul refer metoda Modul a obiectului a pentru a produce obiectul rezultat. Aa cum sugereaz figura 4.1, fiecare obiect trebuie s conin valorile atributelor sale, deoarece ele definesc starea obiectului respectiv. Spaiul de memorie ocupat de atributele unui obiect se numete spaiu propriu al obiectului. n multe cazuri, ntre atribute se afl pointeri care indic anumite zone de memorie alocate dinamic pentru obiect (de exemplu clasa list are ca membru atributul cap care conine adresa primului nod al unei liste dinamice simplu nlnuite). Acest spaiu alocat dinamic aparine tot obiectului, dar el se numete spaiu extins al obiectului. Gestiunea acestui spaiu extins trebuie asigurat de metodele clasei. Metodele, care descriu aciuni identice pentru toate obiectele clasei, snt memorate o singur dat, ntr-o zon comun tuturor obiectelor clasei. ntruct metodele descriu comportamentele obiectelor, ele nu pot fi apelate independent, ci numai n legtur cu un anumit obiect.

Programarea calculatoarelor

66

Despre o metod apelat pentru un anumit obiect se spune c se execut n contextul obiectului respectiv, iar acesta este numit obiect curent. Apelarea metodei este considerat ca trimitere de mesaj ctre obiectul curent, iar execuia metodei reprezint rspunsul (reacia) obiectului curent la mesajul primit. Faptul c o metod se execut n contextul obiectului curent nseamn c are, n mod implicit, acces la toate atributele i metodele obiectului. Acestea nu trebuie s apar ca parametri ai metodei. Pentru a utiliza alte obiecte, din aceeai clas sau din clase diferite, metoda trebuie s aib parametri corespunztori. De asemenea, pentru a simplifica scrierea, n interiorul unei metode referirea la membrii obiectului curent se face fr calificare. Exemplu Pe baza acestor convenii, n funciile Conjugat, Suma i Modul, scrise n pseudocod, s-a specificat cu un parametru mai puin dect numrul de operanzi pe care i presupune operaia respectiv, deoarece un operand este obiectul curent. Referirea la atributele obiectului curent se distinge de celelalte prin lipsa calificrii. Descrierea n pseudocod a metodelor Conjugat, Suma i Modul din clasa CComplex (figura 4.1) poate fi fcut astfel: void Conjugat(b) { b.p_reala=p_reala; b.p_imaginara=-p_imaginara; } void Suma(b,c) { c.p_reala=p_reala+b.p_reala; c.p_imaginara=-p_imaginara+b.p_imaginara; } float Modul() { Modul=sqrt(p_reala*p_reala+p_imaginara*p_imaginara); } Deoarece o clas este un tip de dat, n definirea unei clase B se pot declara atribute de tip A, unde A este la rndul ei o clas. Mai mult, o clas A poate defini atribute de tip A. De exemplu clasa Carte, din figura 4.2 are atributul Autor de tipul Persoana care este, de asemenea, o clas. Mai mult, Persoana are atributul Sef care este de acelai tip (Persoana).

Fig.4.2. Atribute de tip clas

* Material didactic pentru ID *

67

Definirea atributelor unei clase ca tipuri ale altei clase pune n eviden o relaie ntre clase i deci ntre obiectele acestora. Din punct de vedere funcional, metodele unei clase au destinaii diverse. n multe cazuri i depinznd de limbaj, unei clase i se poate defini o metod (sau mai multe) constructor i o metod destructor. Un constructor este o metod care creeaz un obiect, n sensul c i aloc spaiu i/sau iniializeaz atributele acestuia. Destructorul este o metod care ncheie ciclul de via al unui obiect, elibernd spaiul pe care acesta l-a ocupat. ncapsularea exprim proprietatea de opacitate a obiectelor cu privire la structura lor intern i la modul de implementare a metodelor. Ea este legat de securitatea programrii, furniznd un mecanism care asigur accesul controlat la starea i funcionalitatea obiectelor. Se evit astfel modificri ale atributelor obiectelor i transformri ale acestora care pot s le deterioreze. Potrivit acestui mecanism, o clas trebuie s aib membrii mprii n dou seciuni: partea public i partea privat. Partea public este constituit din membri (atribute i metode) pe care obiectele le ofer spre utilizare altor obiecte. Ea este interfaa obiectelor clasei respective cu lumea exterioar i depinde de proiectantul clasei. Modalitatea extrem de constituire a interfeei este aceea a unei interfee compus numai din metode. Dac se dorete ca utilizatorii obiectelor clasei s poat prelua i/sau stabili valorile unor atribute ale acestora, interfaa trebuie s prevad metode speciale, numite accesorii, care au ca unic rol accesul la atribute. Partea privat cuprinde membri (atribute i/sau metode) care servesc exclusiv obiectelor clasei respective. De regul, n aceast parte se includ atribute i metode care faciliteaz implementarea interfeei i a funcionalitii interne a obiectului. Exemplu De exemplu, o stiv, ca tip de dat poate fi descris de o clas Stiva n care interfaa este constituit din metodele Push, Pop, Top, Empty, n timp ce pointerul la capul stivei, Cap i numrtorul de noduri, Contor, ca atribute, snt ascunse n partea privat. Ea se servete de obiectele altei clase, denumit Nod, ale crei obiecte le nlnuiete n stiv (figura 4.3)

Fig.4.3. Interfaa obiectelor Trebuie remarcat c ncapsularea nseamn i faptul c utilizatorul metodelor nu trebuie s cunoasc codul metodelor i nici nu trebuie s fie dependent de eventuala schimbare a acestuia, interfaa fiind aceea care i ofer funcionalitatea obiectelor n condiii neschimbate de apelare. Motenirea reprezint o relaie ntre clase i este, probabil, elementul definitoriu al OOP. Relaia permite constituirea unei noi clase, numit derivat (sau fiu) pornind de la clase existente, denumite de baz (sau printe). Dac n procesul de construire particip o singur clas de baz, motenirea este simpl, altfel este multipl. Se spune c o clas D motenete o clas A, dac obiectele din clasa D conin toate atributele clasei A i au acces la toate metodele acestei clase. Din aceast definiie, dac D motenete A, atunci obiectele din D vor avea toate atributele i acces la toate metodele lui A, dar n plus: - D poate defini noi atribute i metode; - D poate redefini metode ale clasei de baz; - metodele noi i cele redefinite au acces la toate atributele dobndite sau nou definite.

Programarea calculatoarelor

68

Exemplu n figura 4.4, clasa Cerc motenete clasa Punct, deci un obiect de tipul Cerc va avea ca membri coordonatele x,y motenite i ca atribut propriu Raza. Funcia Distana, definit pentru calculul distanei dintre punctul curent i punctul p, dat ca parametru, este accesibil i pentru obiectele Cerc i va calcula distana dintre centrul cercului i un alt punct, primit ca parametru. Funcia Arie calculeaz aria din interiorul cercului, fiind nou definit. Funcia Deseneaz este redeclarat de clasa Cerc, lucru impus de codul diferit pe care trebuie s-l aib pentru desenarea obiectelor din aceast clas (cerc sau alt figur).
Punct X: int Y:int Deseneaz() Distana(p: punct): float Cerc Raza: int Arie():float Deseneaz() Distana(p: punct): float X= 200 Y= 200 Raza= 50 X= 100 Y= 100

Fig.4.4. Motenirea simpl. Dac se au n vedere mulimi de clase, atunci se observ c relaia de motenire simpl induce un arbore ierarhic de motenire pe aceast mulime. Exist o singur clas iniial, rdcina arborelui, fiecare clas are un singur ascendent (printe) i orice clas care nu este frunz poate avea unul sau mai muli descendeni (fii). n fine, cu privire la motenirea simpl se pot face urmtoarele observaii: dac se aduc modificri n clasa de baz, prin adugarea de atribute i/sau metode, nu este necesar s se modifice i clasa derivat; motenirea permite specializarea i mbogirea claselor, ceea ce nseamn c, prin redefinire i adugare de noi membri, clasa derivat are, n parte, funcionalitatea clasei de baz, la care se adaug elemente funcionale noi; motenirea este mecanismul prin care se asigur reutilizarea codului sporind productivitatea muncii de programare. Cobornd (de obicei, n reprezentrile grafice ale arborilor de clase, rdcina se afl n partea superioar) n arborele ierarhic al claselor de la rdcin ctre frunze, se poate spune c ntlnim clase din ce n ce mai specializate. Prin motenire se realizeaz o specializare a claselor. n sens invers, de la frunz ctre rdcin, clasele snt din ce n ce mai generale, avem o relaie de generalizare. Clasa aflat la baza ierarhiei este cea mai general. Limbajele de programare orientate obiect au implementate ierarhii standard extinse de clase, care corespund necesitilor generale ale programrii. Utilizatorii pot deriva clase noi din cele standard. Polimorfismul este un concept mai vechi al programrii, cu diferite implementri n limbajele de programare care se bazeaz pe tipuri de date (limbaje cu tip). El i-a gsit extensia natural i n modelul orientat pe date, implementat prin limbaje cu tip, n care clasa reprezint tipul de date obiect. Polimorfismul n limbajele de programare cu tip. Noiunea de polimorfism exprim capacitatea unui limbaj de programare cu tip de a exprima comportamentul unei proceduri independent de natura (tipul) parametrilor si. De exemplu, o funcie care determin cea mai mare valoare dintr-un ir de valori este polimorfic dac poate fi scris independent de tipul acestor valori. n funcie de modul de implementare, se disting mai multe tipuri de polimorfism. Polimorfismul ad-hoc se materializeaz sub forma unor funcii care au toate acelai nume, dar se disting prin numrul i/sau tipul parametrilor. Acest polimorfism este denumit i suprancrcare, avnd n vedere semantica specific fiecrei funcii n parte.

* Material didactic pentru ID *

69

Polimorfismul de incluziune se bazeaz pe o relaie de ordine parial ntre tipurile de date, denumit relaie de incluziune sau inferioritate. Dac un tip A este inclus (inferior) ntr-un tip B, atunci se poate trimite un parametru de tip A unei funcii care ateapt un parametru de tip B. Astfel, un singur subprogram definete funcional o familie de funcii pentru toate tipurile inferioare celor declarate ca parametri. Un exemplu clasic este cazul tipului int, inferior tipului float n toate operaiile de calcul. Polimorfism parametric const n definirea unui model de procedur pentru care chiar tipurile snt parametri. Acest polimorfism, denumit i genericitate, presupune c procedura se genereaz pentru fiecare tip transmis la apel ca parametru. Cele trei tipuri de polimorfism exist (toate sau numai o parte din ele) n limbajele clasice de programare, dar unele pot s nu fie accesibile programatorului. Polimorfismul n limbajele orientate obiect. Limbajele orientate obiect sau extensiile obiect ale unor limbaje cu tip ofer, n mod natural, polimorfismul ad-hoc i de incluziune. Polimorfismul ad-hoc intrinsec reprezint posibilitatea de a defini n dou clase independente metode cu acelai nume, cu parametri identici sau diferii. Acest polimorfism nu necesit mecanisme speciale i decurge simplu, din faptul c fiecare obiect este responsabil de tratarea mesajelor pe care le primete. Polimorfismul este de aceeai natur i n cazul n care ntre clase exist o relaie de motenire, cu precizarea c, n cazul n care o metod din clasa derivat are parametrii identici cu ai metodei cu acelai nume din clasa de baz, nu mai este suprancrcare, ci redefinire, dup cum s-a precizat mai sus. Polimorfimsul de incluziune este legat de relaia de motenire i de aceea se numete polimorfism de motenire. ntr-adevr, relaia de motenire este o relaie de ordine parial, astfel nct dac clasa D motenete direct sau indirect clasa A, atunci D este inferior lui A. n aceste condiii, orice metod a lui A este aplicabil la obiectele de clas D i orice metod, indiferent de context, care are definit un parametru de tip A (printe) poate primi ca argument corespunztor (parametru actual) un obiect de clas D (fiu). Observaie: un obiect de clas A nu poate lua locul unui obiect de clas D, deoarece A acoper numai parial pe D, care este o extensie i o specializare a lui A. Legare static respectiv dinamic a metodelor. Legarea static a metodelor se regsete att n limbajele orientate obiect ct i n cele clasice. Compilatorul poate determina care metod i din care clas este efectiv apelat ntr-un anumit context i poate genera codul de apel corespunztor. Exemplu Fie o clas A i o clas D, unde D este derivat din A. Fie o metod din clasa A, numit calculeaz, care este redefinit n clasa derivat, D. Atunci cnd metoda este apelat n contextul unui obiect static, compilatorul poate determina tipul acelui obiect (ca fiind parte a clasei A sau D). Astfel, el va ti ce metod s apeleze (a clasei de baz sau cea redefinit, a clasei derivate). n acest caz are loc o legare static a metodelor (decizia este luat n momentul compilrii). Fie un pointer p, definit ca pointer spre clasa A. Datorit polimorfismului, n limbajele orientate obiect unui obiect din clasa printe, desemnat indirect prin referin (pointer) i nu prin nume, i se poate atribui un obiect fiu. n acest context, p poate primi ca valoare, n timpul execuiei programului, adresa unui obiect din clasa A sau din clasa D. Nu se poate ti la momentul compilrii ce se va ntmpla n timpul execuiei programului, ca urmare nu se poate determina dac, n contextul dat, trebuie apelat metoda clasei de baz sau metoda clasei derivate. De aceea, n locul din program n care este apelat metoda, compilatorul adaug o secven de cod care, la momentul execuiei, va verifica tipul efectiv al obiectului i, dup caz, va realiza apelarea metodei adecvate. n acest caz are loc legarea dinamic a metodelor (sau la momentul execuiei). Legarea dinamic este evident mai costisitoare dect cea static, dar reprezint o necesitate pentru a asigura elasticitatea necesar n realizarea programelor OOP, obiectele putnd avea caracter de variabile dinamice.

Programarea calculatoarelor

70

Teste de autoevaluare 1. Explicai noiunea de obiect din POO. 2. Explicai noiiunea de clas de obiecte din POO. 3. Care snt conceptele de baz ale POO? 4. Explicai noiunea de ncapsulare din POO. 5. Care snt formele de polimorfism din limbaje procedurale? Dai exemple. 6. Care snt formele de polimorfism din POO? 7. Explicai noiunea de polimorfism de motenire. 8. Explicai noiunea de legare static a metodelor. 9. Explicai noiunea de legare dinamic a metodelor.

4.2. Definirea claselor


Definiia unei clase este asemntoare cu definiia unui articol, ns n locul cuvntului rezervat struct se folosete cuvntul class: class nume { descriere membri; }; Membrii unei clase pot fi atribute sau metode. Atributele snt descrise asemntor declaraiilor de variabile independente (i asemntor cmpurilor unui articol struct), specificnd tipul i numele atributului respectiv. Membrii unei clase pot fi de orice tip, mai puin de acelai tip cu clasa descris (dar pot fi pointeri ctre clasa descris). Metodele snt descrise asemntor funciilor independente. Ele pot fi descrise integral n interiorul clasei (descriind antetul i corpul lor) sau specificnd n interiorul clasei doar prototipul funciei, corpul urmnd s fie descris ulterior, n afara clasei. Este preferat a doua variant, deoarece descrierea clasei este mai compact dect n primul caz. Atunci cnd se descrie ulterior corpul unei metode, pentru a specifica apartenena sa la clasa respectiv, numele metodei este prefixat cu numele clasei din care face parte, folosind operatorul de rezoluie (::), astfel: tip_rezultat nume_clas::nume_metod(lista parametrilor) corp metod Mai mult, funciile care snt integral descrise n interiorul clasei snt considerate funcii inline , de aceea ele trebuie s fie simple. Pentru funciile mai complexe, ntotdeauna se recomand s fie descrise folosind a doua variant. ntruct o clas este un tip de dat, declararea unui obiect se face asemntor oricrei declaraii de dat: nume_clas nume_obiect; Atunci cnd se dorete lucrul cu obiecte dinamice, se poate declara o variabil de tip pointer ctre clas: nume_clas* p_obiect; Declararea unui obiect mai este numit i instanierea clasei, n sensul c se creeaz o instan a acelei clase, o entitate concret din mulimea descris de clasa respectiv.. Exemplu Definirea clasei Complex, care implementeaz entitatea matematic numr complex. Clasa are atributele p_reala i p_imaginara i o metod pentru afiarea valorii obiectului afiseaza.
1

Apelul funciilor inline nu produce un salt n segmentul de cod ctre codul executabil al funciei, aa cum se ntmpl n cazul funciilor obinuite. Pentru aceste funcii, compilatorul insereaz n program, n locul apelului, secvena de cod corespunztoare corpului funciei, nlocuind parametrii formali cu valorile actuale. Funciile inline au un comportament asemntor macrodefiniiilor.

* Material didactic pentru ID *

71

class Complex { float p_reala,p_imaginara; void Afiseaza(); }; void Complex::Afiseaza() { printf("\n%5.2f%ci*%5.2f\n",p_reala,p_imaginara>=0?'+':'-', p_imaginara>=0?p_imaginara:-p_imaginara); } Complex tc; Metoda afieaz ine cont de semnul prii imaginare. Dac aceasta este negativ, semnul minus este afiat naintea simbolului i al prii imaginare. Se declar obiectul tc de tipul Complex. Accesul la membrii obiectului se face folosind operatorul de calificare: nume_obiect.nume_membru unde numele obiectului specific din ce obiect este accesat atributul respectiv sau n contextul crui obiect se execut metoda respectiv. Acest mod de accesare este folosit atunci cnd se lucreaz cu obiecte statice. n cazul n care nu avem un obiect ci un pointer ctre un obiect, este necesar i dereferenierea pointerului, nainte de accesul la membri. Acest lucru este realizat folosind operatorul -> n locul operatorului de calificare: p_obiect -> nume_membru Pentru implementarea conceptului de ncapsulare, n interiorul unei definiii de clas pot fi folosii modificatori de acces. Acetia snt private, protected i public (urmai de caracterul : dou puncte). Domeniul de aciune al unul modificator de acces ncepe n locul unde apare el i se ncheie la apariia altui modificator sau la terminarea descrierii clasei. Implicit, toi membri snt considerai sub influena modificatorului private, deci orice membru aflat n afara domeniului de aciune al unui modificator este considerat privat. Modificatorul public face ca toi membri aflai n domeniul su de aciune s poat fi accesai att de ctre metodele clasei ct i de ctre orice entitate din afara clasei (membri publici). Modificatorul private face ca membrii aflai n domeniul su de aciune s poat fi accesai numai de ctre metodele clasei respective (membri privai). Modificatorul protected este similar cu modificatorul private, dar membrii respectivi pot fi accesai i de ctre metodele claselor derivate, dar numai n obiecte aparinnd claselor derivate. De obicei atributele unei clase snt declarate ca fiind private iar metodele snt mprite, unele fiind publice (interfaa clasei) i unele private (detalii i mecanisme interne de implementare a clasei. Dei este tehnic posibil ca toi membrii unei clase s fie privai, un obiect de acest tip nu poate fi folosit, neavnd o interfa cu mediul exterior lui. De asemenea, toi membrii unei clase pot fi publici, dar nu este recomandat aceast tehnic din motive de protecie i securitate. Exemplu n acest context, clasa Complex definit n exemplul anterior nu poate fi folosit, toi membrii ei fiind privai. Pentru a putea folosi obiecte de tipul Complex, metoda afieaz trebuie s fie public. Descrierea clasei devine: class Complex { float p_reala,p_imaginara; public: void Afiseaza(); };

Programarea calculatoarelor

72

void Complex::Afiseaza() { printf("\n%5.2f%ci*%5.2f\n",p_reala,p_imaginara>=0?'+':'-', p_imaginara>=0?p_imaginara:-p_imaginara); } Prin adugarea modificatorului de acces public, metoda afieaz este pus la dispoziia mediului extern, ca interfa a obiectului. Pentru accesul controlat la atributele private, clasele pot pune la dispoziia mediului extern metode publice de acces, numite uzual metode accesorii. Acestea au rolul de a prezenta mediului extern valorile unora dintre atribute (acelea care pot prezenta interes) sau de a modifica valorile unora dintre atribute, n mod controlat (numai acele atribute pentru care modificarea la iniiativa mediului extern are sens). Controlul depinde n fiecare caz de scopul clasei respective. Exemplu Adugnd metode accesorii clasei Complex, descrierea acesteia devine: class Complex { float p_reala,p_imaginara; public: void Afiseaza(); float GetR(); float GetI(); void SetR(float r); void SetI(float i); }; void Complex::Afiseaza() { printf("\n%5.2f%ci*%5.2f\n",p_reala,p_imaginara>=0?'+':'-', p_imaginara>=0?p_imaginara:-p_imaginara); } float Complex::GetR() { return p_reala; } float Complex::GetI() { return p_imaginara; } void Complex::SetR(float r) { p_reala=r; } void Complex::SetI(float i) { p_imaginara=i; } Metodele accesorii definite mai sus (GetR, GetI, SetR, SetI) au rolul de a prezenta valorile atributelor i respectiv de a stabili noi valori pentru ele. n acest exemplu nu se face nici un fel de control asupra modului n care snt stabilite noile valori. Exemplu Folosind descrierile de mai sus, urmtoarea secven de program: void main() { Complex tc; Complex *pc; tc.SetR(5); tc.SetI(-4);

* Material didactic pentru ID *

73

tc.Afiseaza(); pc=&tc; pc->Afiseaza(); pc->SetR(-2); pc->SetI(3); pc->Afiseaza(); } produce pe ecran urmtorul rezultat: 5.00-i* 4.00 5.00-i* 4.00 -2.00+i* 3.00 Pentru a evita eventualele confuzii, n interiorul corpului metodelor poate fi folosit pointerul implicit this atunci cnd se refer membrii obiectului curent. Acesta este gestionat automat i are ca valoare adresa obiectului curent. Ca urmare, metoda SetR ar putea fi rescris astfel: void Complex::SetR(float r) { this -> p_reala = r; } Pointerul this poate fi folosit i pentru accesarea metodelor obiectului curent, n acelai mod. innd cont de domeniul de valabilitate al declaraiilor de tipuri de date, descrierea unei clase se face de obicei la nceputul fiierului surs n care urmeaz a fi folosit. Pentru o mai mare generalitate i reutilizare mai uoar, se prefer ca fiecare clas nou s fie descris ntr-un fiier surs separat, care s fie inclus folosind directiva #include n programe. Teste de autoevaluare 10. Care snt modificatorii de acces din descrierea claselor i ce efect are fiecare? 11. Care este modificatorul de acces implicit? 12. n ce ordine trebuie sa apar modificatorii de acces n cadrul unei clase? 13. Este posibil s ca unul dintre modificatorii de acces s nu apar n descrierea unei clase? Pot lipsi doi modificator de acces? Pot lipsi toi modificatorii de acces?

4.3. Constructori
Declararea obiectelor are ca efect alocarea de spaiu n memorie, la fel ca n cazul declarrii oricrei variabile. Acest spaiu nu este iniializat ns. Mai mult, n cazul n care obiectele clasei au i spaiu extins de memorie, acesta nu este alocat automat, obiectul declarat fiind astfel incomplet. Atributele unui obiect nu pot fi iniializate la declarare ntr-o manier asemntoare datelor de tip articol (struct), deoarece de obicei atributele snt private, deci inaccesibile din exteriorul obiectului. Pentru rezolvarea problemei iniializrii obiectelor exist posibilitatea utilizrii unor metode speciale, numite constructori. La terminarea ciclului de via al obiectelor, este necesar dezalocarea lor. n general aceasta se realizeaz automat, dar n cazul lucrului cu spaiu extins, ea trebuie gestionat n mod explicit. Problema ncheierii ciclului de via al obiectelor este rezolvat prin utilizarea unor metode speciale numite destructori. Constructorii i destructorii nu ntorc nici un rezultat prin numele lor i antetele lor nu precizeaz nici un tip pentru rezultat (nici mcar void). Constructorii snt metode care au acelai nume cu clasa creia i aparin. O clas poate avea mai muli constructori, cu liste diferite de parametri (ca tip i/sau numr) metode suprancrcate. Dac nu este definit nici un constructor pentru o clas, compilatorul va genera un constructor implicit,

Programarea calculatoarelor

74

care nu face dect alocarea spaiului propriu al obiectului, n momentul n care acesta a fost declarat. Ca urmare, n acest caz vom avea obiecte neiniializate, urmnd ca iniializarea atributelor s se fac ulterior, prin intermediul metodelor accesorii. n exemplul anterior, pentru clasa Complex s-a generat un constructor implicit care aloc spaiu pentru atributele p_reala i p_imaginara. Iniializarea s-a fcut prin intermediul metodelor SetR i SetI. n cazul n care clasa prezint cel puin un constructor explicit, compilatorul nu mai genereaz constructorul implicit. Ca urmare nu se vor putea declara obiecte neiniializate dac parametrii constructorului nu au valori implicite. Constructorii nu pot fi apelai explicit, precum metodele obinuite. Apelul lor se realizeaz numai la declararea obiectelor. De asemenea, nu se poate determina adresa constructorilor, aa cum se poate face n cazul funciilor obinuite. Am vzut mai sus c declaraia unui obiect care nu are constructor explicit este identic cu declaraia unei variabile simple. n cazul n care clasa prezint constructori explicii valorile pentru iniializare snt transmise acestuia la declararea obiectului, asemntor listei de parametri reali la apelul unei funcii: nume_clas nume_obiect(lista_valori); Exemplu Pentru clasa Complex se poate defini un constructor care s iniializeze cei doi membri astfel: class Complex { float p_reala,p_imaginara; public: Complex(float a,float b); }; Complex::Complex(float a,float b) { p_reala=a; p_imaginara=b; } Avnd acest constructor n cadrul clasei, nu putem declara obiecte neiniializate (ca n exemplele anterioare) ci doar obiecte iniializate: Complex a(3,4); Complex b(-1,3.2); Complex c; //a reprezint numarul 3+i*4 //b reprezint numarul -1+i*3.2 //incorect

Atunci cnd exist un singur constructor explicit, se aplic regulile transferului parametrilor similar ca la funciile obinuite (se realizeaz conversia parametrilor reali ctre tipurile formale). Dac snt mai muli constructori, cu liste diferite de parametri, se alege acela a crui list de parametri corespunde ca tip i numr cu lista valorilor (expresiilor) precizate la declararea obiectului. Pentru constructori, ca i pentru orice alt funcie, pot fi precizate valori implicite ale parametrilor. Acolo unde la apel lipsesc parametrii actuali, se folosesc valorile implicite. Ca urmare, la declararea obiectelor pot s nu fie precizate valori pentru toate atributele. Exemplu Se definete clasa Complex astfel: class Complex { float p_reala,p_imaginara; public: Complex(float a=0,float b=0); }; Complex::Complex(float a,float b) { p_reala=a; p_imaginara=b; }

* Material didactic pentru ID *

75

Putem declara urmtoarele obiecte: Complex a; Complex b(-1); Complex c(2,3); //a reprezint numarul 0+i*0 //b reprezint numarul -1+i*0 //c reprezint numarul 2+i*3

Dac prototipul constructorului ar fi fost Complex(float a,float b=0); atunci la declaraia obiectului trebuie precizat obligatoriu valoarea pentru primul parametru (cel care va deveni valoarea atributului p_reala); ca urmare, dintre declaraiile anterioare ar fi fost corecte numai cele ale obiectelor b i c. Acolo unde se poate folosi un singur parametru la declararea obiectului, se poate face iniializarea asemntor iniializrii variabilelor simple. Folosind oricare dintre cei doi constructori din exemplul anterior, putem declara un obiect i n modul urmtor: Complex a = 1; //numarul 1+i*0

Valoarea declarat este atribuit primului parametru al constructorului. Pentru situaiile n care avem nevoie att de obiecte iniializate ct i de obiecte neiniializate, se adaug n descrierea clasei att un constructor care realizeaz iniializarea obiectului ct i un constructor vid, care simuleaz constructorul implicit, adugat de compilator atunci cnd nu se definesc constructori implicii. Constructorul vid are urmtoarea form: Complex::Complex() { } n acest context, declaraia Complex a; are ca efect crearea obiectului a neiniializat (se utilizeaz constructorul vid, nu constructorul cu valori implicite pentru parametri). Parametrii unui constructor pot fi de orice tip, mai puin de tipul clasei respective (n exemplele anterioare, constructorul clasei Complex nu poate avea un parametru de tipul Complex. Este posibil ns s avem un parametru de tip pointer ctre clasa respectiv sau referin2 ctre clasa respectiv. Un constructor care primete ca parametru o referin ctre un obiect din acea clas se numete constructor de copiere. Prin utilizarea unui constructor de copiere se poate iniializa un obiect nou cu atributele unui obiect existent (se realizeaz o copie a acelui obiect). Constructorii de copiere pot avea i ali parametri, dar acetia trebuie s aib valori implicite. Compilatorul genereaz automat un constructor de copiere pentru toate clasele care nu au un constructor de copiere definit explicit. Exemplu Clasa Complex conine un constructor de copiere: class Complex { float p_reala,p_imaginara; public: Complex(float a=0,float b=0); Complex(Complex &x); }; Complex::Complex(float a,float b) { p_reala=a;
2

n C++ este implementat transferul parametrilor prin adres. Pentru a transmite un parametru prin adres, n lista de parametri se pune naintea numelui su operatorul de refereniere &

Programarea calculatoarelor

76

p_imaginara=b; } Complex::Complex(Complex &x) { p_reala=x.p_reala; p_imaginara=x.p_imaginara; } Putem declara urmtoarele obiecte: Complex a(3,4); //numarul 3+i*4 Complex b = a; //numarul 3+i*4 Complex c(b); //numarul 3+i*4 Obiectul b este o copie a obiectului a i va avea aceeai stare, imediat dup declarare (aceleai valori ale atributelor). De asemenea, obiectul c este o copie a obiectului b. Pentru ambele obiecte s-a apelat constructorul de copiere. Constructorul implicit de copiere realizeaz o copie binar a spaiului propriu de memorie al obiectului surs. Ca urmare, dac obiectele au spaiu extins de memorie, n urma copierii ambele obiecte refer acelai spaiu extins de memorie. Pentru a evita aceast situaie anormal, atunci cnd clasele descriu obiecte care lucreaz i cu spaiu extins este necesar folosirea unui constructor de copiere explicit, care s realizeze, pe lng copierea atributelor, alocarea de spaiu extins propriu pentru obiectul nou i copierea spaiului extins al obiectului surs n acest spaiu nou. Constructorii nu pot fi apelai n mod explicit n program. Singura situaie n care poate s apar un astfel de apel este la declararea unui obiect, n modul urmtor: Complex a = Complex(2,5); n cazul n care o clas are ca membri obiecte, la declararea unui obiect al clasei se apeleaz nti constructorii pentru obiectele membru i apoi constructorul noului obiect.

4.4. Destructori
Destructorii snt metode speciale, asemntoare constructorilor, care au rol invers: ncheierea ciclului de via al obiectelor. Aa cum pentru fiecare clas se genereaz un constructor implicit (dac nu a fost prevzut unul explicit), compilatorul genereaz i un destructor implicit, dac nu a fost prevzut unul explicit. Spre deosebire de constructori, o clas poate avea numai un destructor explicit. Ca i constructorii, destructorii nu ntorc nici un rezultat. Numele destructorului este numele clasei precedat de caracterul ~ (tilda). Destructorii nu au parametri. Exemplu class Complex { float p_reala, p_imaginara; public ~Complex(); } Complex::~Complex() { //descrierea corpului destructorului } Pentru clasa Complex destructorul nu are nimic de fcut i nu e necesar descrierea unui destructor explicit. Acest exemplu urmrete doar s arate cum se declar un destructor explicit.

* Material didactic pentru ID *

77

Spre deosebire de constructori, destructorii pot fi apelai explicit, atunci cnd este necesar tergerea unui obiect. Apelul se face la fel ca pentru orice alt metod, n contextul obiectului care trebuie ters: a.~Complex(); Utilizarea destructorilor este obligatorie atunci cnd se lucreaz cu date dinamice, deoarece destructorii implicii nu pot elibera spaiul alocat dinamic. n cazul n care la crearea unui obiect au fost apelai mai muli constructori, la tergerea lui se apeleaz destructorii corespunztori, n ordine invers. Teste de autoevaluare 14. Explicai noiunea de constructor. 15. Explicai noiunea de destructor. 16. Alegei atributele i metodele pentru o clas de obiecte care s implementeze entitatea masiv unidimensional (vector). 17. Alegei atributele i metodele pentru o clas de obiecte care s implementeze entitatea masiv bidimensional (matrice). 18. Implementai o clas de obiecte care s descrie noiunea de stiv, folosind o structur de tip list dinamic.

4.5. Funcii prieten


n unele situaii este nevoie ca funcii care nu snt membri ai unei clase s poat accesa atributele protejate ale clasei. n acest scop a fost introdus n C++ conceptul de funcie prieten. O funcie prieten nu face parte din clas, dar poate accesa atributele protejate ale obiectului pe care l-a primit ca parametru. Pentru a specifica o funcie prieten, n interiorul descrierii clasei se scrie prototipul funciei prieten, prefixat cu cuvntul rezervat friend. ntruct funcia prieten nu este membru al clasei, n interiorul su nu este definit pointerul this, ceea ce face ca funcia prieten s nu poat accesa direct atributele obiectului. De aceea este necesar ca obiectul s fie parametru al funciei prieten. Ca urmare, o funcie prieten are un parametru n plus fa de o metod. Modificatorii de acces nu au nici o influen asupra funciilor prieten, de aceea ele pot fi specificate oriunde n cadrul descrierii clasei. Exemplu n acest exemplu funcia Afieaz va fi scoas n afara clasei Complex i va fi declarat ca funcie prieten. class Complex { float p_reala,p_imaginara; public: friend void Afiseaza(Complex x); Complex(float a=0,float b=0); Complex(Complex &x); }; void Afiseaza(Complex x) { printf("\n%5.2f%ci*%5.2f\n",x.p_reala,x.p_imaginara>=0?'+':'-', x.p_imaginara>=0?x.p_imaginara:-x.p_imaginara); } void main()

Programarea calculatoarelor

78

{ Complex a(1,2); Afiseaza(a); }

4.6. Derivarea claselor


Derivarea claselor este legat de implementarea conceptului de motenire. n limbajul C++ este permis motenirea multipl. Pentru a defini o clas fiu ca fiind derivat dintr-o clas printe (sau mai multe clase printe), se procedeaz astfel: class nume_clasa_fiu : lista_clase_printe { descriere membri noi ai clasei fiu}; n lista claselor printe se specific numele claselor printe, separate prin virgul i, eventual, precedate de modificatori de acces se pot folosi modificatorii public sau private. Aceti modificatori de acces definesc nivelul de protecie a membrilor clasei printe n clasa fiu, conform tabelului urmtor: Nivel acces n clasa printe private protected public Modificator de acces n lista claselor printe public private public private public private Nivel acces n clasa fiu inaccesibil inaccesibil protected private public private

Pentru fiecare clas printe se poate specifica un modificator de acces. Dac nu se specific nici un modificator de acces atunci, implicit, se consider modificatorul public. Se observ c o clas derivat are acces la membrii clasei printe care au fost definii ca fiind publici sau protejai i nu are acces la membrii privai. Prin derivare se construiesc ierarhii de clase, deci din clasa fiu se pot deriva alte clase noi. Dac la derivare s-a folosit modificatorul de acces private, atunci toi membrii clasei printe vor deveni privai n clasa fiu i o derivare n continuare nu mai este posibil, ei fiind inaccesibili pentru orice alt clas derivat din clasa fiu. ntruct ierarhiile de clase nu snt definitive ci ofer posibilitatea extinderii prin adugarea de noi clase derivate, se prefer ca la derivare s se foloseasc modificatorul de acces public. De aceea aceste este modificatorul explicit. Exemplu Fie clasa Punct care implementeaz entitatea punct geometric. Aceasta are atributele x i y, care reprezint coordonatele punctului n plan. Este inclus o singur metod, care deseneaz punctul pe ecran (aceast metod nu va implementat n acest exemplu). class punct { int x,y; public: void deseneaza(); }; void punct::deseneaza() { //corpul nu este descris in acest exemplu }

* Material didactic pentru ID *

79

Fie clasa Cerc care implementeaz entitatea geometric cerc. Aceasta este descris prin coordonatele centrului cercului i raza sa. Ca urmare clasa Cerc poate fi derivat din clasa Punct, adugnd un nou atribut (raza) i o nou metod, pentru desenarea cercului. class cerc: punct { float raza; public: void deseneaza(); }; void cerc::deseneaza() { //corpul nu este descris in acest exemplu } Clasa cerc o s aib ca membri atributele x, y i raza i metodele deseneaz (motenit de la clasa Punct, care va fi folosit pentru desenarea centrului cercului) i deseneaz (nou definit, care va fi folosit pentru desenarea cercului). n lucrul cu ierarhii de clase se pune problema compatibilitii tipurilor de date (clase) n cadrul atribuirilor i a conversiilor tipurilor de date. Ca principiu, un obiect al unei clase printe poate primi ca valoare un obiect al unei clase derivate. Acelai principiu este valabil i n cazul pointerilor ctre obiecte. Utiliznd exemplul de mai sus i declaraiile punct a, *pp; cerc b, *pc; snt corecte atribuirile pp=&a; pc=&b; a=b; pp=pc; pp=&b; Nu snt corecte urmtoarele atribuiri: pc=&a; b=a; pc=pp; Pot fi realizate atribuiri folosind conversia explicit a tipurilor de date, astfel: pc=(cerc *)pp; pc=(cerc *)&a; 4.6.1.Redefinirea atributelor Este posibil ca o clas fiu s redefineasc atribute motenite de la clasa printe (atribute publice sau protejate, ntruct cele private snt oricum inaccesibile n clasa fiu). n acest caz, clasa fiu va avea dou atribute cu acelai nume. Implicit, utilizarea numelui atributului respectiv refer atributul redefinit. Pentru a accesa atributul motenit, trebuie folosit operatorul de rezoluie, prefixnd numele atributului cu numele clasei printe.

Programarea calculatoarelor

80

Exemplu Fie o clas Clasa_parinte care are un atribut a de tip float, atribut protejat, i o clas Clasa_fiu care redefinete atributul a, de tip double. x este un obiect de tipul Clasa_fiu. class Clasa_parinte { protected: float a; //descrierea restului clasei }; Class Clasa_fiu: public Clasa_parinte { protected: double a; //descrierea restului clasei } Clasa_fiu x; Expresia x.a refer atributul a al clasei derivate, de tip double. Pentru a accesa atributul a motenit de la clasa printe, n cadrul unei metode a obiectului x trebuie folosit expresia
Clasa_parinte::a

4.6.2.Redefinirea metodelor La fel ca n cazul atributelor, o clas fiu poate s redefineasc metodele motenite de la clasa printe, n cazul n care metoda motenit nu corespunde necesitilor. n exemplul anterior, clasa Cerc redefinete metoda deseneaz. Dac a este un obiect de tipul Cerc, atunci apelul a.deseneaz(); sau deseneaz(); efectuat din interiorul clasei Cerc va lansa n execuie metoda redefinit, cea descris de clasa Cerc, care va desena conturul cercului. Atunci cnd e nevoie s se apeleze metoda motenit numele acesteia se prefixeaz cu numele clasei printe, folosind operatorul de rezoluie: punct::deseneaza(); Un astfel de apel poate s apar n interiorul metodei deseneaz a clasei Cerc pentru a desena centrul cercului. 4.6.3.Constructori i destructori n relaia de motenire Constructorii i destructorii nu se motenesc precum alte metode. La crearea unui obiect al unei clase fiu se apeleaz nti constructorul clasei printe i apoi constructorul clasei fiu. Dac snt mai multe clase printe (motenire multipl) se apeleaz constructorii claselor printe, n ordinea n care acestea apar n lista claselor printe. La tergerea unui obiect al unei clase fiu se apeleaz destructorii n ordine invers fa de constructori: nti destructorul clasei fiu i apoi destructorii claselor printe, n ordine invers celei n care acestea apar n lista claselor printe. Pentru a preciza parametrii reali utilizai pentru fiecare din constructorii claselor printe, antetul constructorului clasei derivate are o form special: class Clasa_fiu: clasa_p1, clasa_p2, clasa_p3 { //attribute public: Clasa_fiu(); //constructorul clasei fiu }

* Material didactic pentru ID *

81

Clasa_fiu::clasa_fiu():clasa_p1(),clasa_p2(),clasa_p3() { //descrierea corpului constructorului } Se observ c n descrierea clasei, constructorul se descrie n mod obinuit, dar ulterior, n antetul constructorului apar apeluri ale constructorilor claselor printe. Ordinea n care apar aceste apeluri nu are nici o importan, deoarece ordinea de apelare este dat de ordinea n care snt specificate clasele printe. Dac una din clasele printe nu are constructor, atunci nu o s apar un apel corespunztor n antetul constructorului clasei fiu, pentru ea apelndu-se automat constructorul implicit. Dac nici una dintre clase nu are constructor explicit, atunci se folosesc constructorii implicii pentru toate clasele. O situaie deosebit este aceea n care clasa fiu nu are constructor explicit, dar cel puin una din clasele printe are un constructor explicit. Deoarece n aceast situaie nu se pot descrie explicit parametrii pentru apelarea constructorilor claselor printe, aceti constructori trebuie s aib valori implicite pentru toi parametrii. 4.6.4.Clase virtuale n cazul motenirii multiple pot s apar situaii n care o clas derivate motenete un atribut (sau mai multe) de mai multe ori, prin intermediul mai multor linii de motenire. Aceste situaii produc ambiguiti legate de referirea atributului respectiv. Limbajul C++ ofer un mecanism simplu prin care s se revin astfel de situaii, prin utilizarea claselor virtuale. Fie urmtoare ierarhie, n care clasa cf este derivat din clasele cp1, cp2 i cp3, toate acestea fiind la rndul lor derivate din clasa cb. Clasa de la baza ierarhiei, cb are un atribut x. Acest atribut va fi motenit n clasele cp1, cp2, cp3. Clasa cf va moteni 3 exemplare ale atributului x.

Fig.4.5. Exemplu de motenire multipl Pentru a evita aceast situaie, clasa cb poate fi declarat ca virtual la descrierea claselor cp1, cp2 i cp3, astfel: class cp1: virtual public cb { }; class cp2: virtual public cb { }; class cp3: virtual public cb {

Programarea calculatoarelor

82

}; class cf: public cp1, public cp2, public cp3, { }; Considernd aceste declaraii, clasa cf motenete o singur dat atributul x, prin intermediul clasei cp1. Ca principiu, atributul este motenit prin intermediul clasei care apare prima n lista claselor printe. n cazul n care n lista claselor printe apar i clase virtuale, se apeleaz nti constructorii claselor virtuale, n ordinea n care au fost specificate, apoi constructorii claselor nevirtuale, n ordinea n care snt acestea specificate. 4.6.5.Funcii virtuale Exist situaii n care nu se poate decide n momentul compilrii care este contextul curent n care se apeleaz o metod, care a fost redefinit ntr-o clas fiu. Astfel de situaii apar atunci cnd se lucreaz cu pointeri. Fie o clas cp care conine metoda executa, i o clas derivat din ea, numit cf, care redefinete metoda executa, cu aceeai list de parametri. Fie urmtoarele declaraii: cp a; cf b; cp* po; //a este obiect de tipul cp //b este obiect de tipul cf // po este pointer catre clasa cp

Pointerul po poate lua ca valoare att adresa unui obiect de tipul cp ct i adresa unui obiect de tipul cf. Fie apelul po->executa(); n momentul compilrii nu se poate stabili ce metod s se apeleze, a clasei printe sau a clasei fiu. n astfel de situaii compilatorul genereaz un apel ctre metoda clasei printe. Limbajul C++ ofer posibilitatea de a ntrzia decizia pn la momentul execuiei. n acest scop metoda clasei printe se declar ca fiind virtual prin scrierea cuvntului rezervat virtual naintea antetului su. Este suficient ca metoda clasei de baz s fie declarat ca fiind virtual, n mod automat i metodele claselor derivate vor fi virtuale. Constructorii, destructorii i funciile inline nu pot fi virtuale. 4.6.6.Clase abstracte n limbajul C++ este definit conceptul de funcie virtual pur. Acest concept este necesar n cazul ierarhiilor de clase. Exist situaii n care toate clasele ierarhiei trebuie s conin o anumit metod, implementarea ei fiind diferit n fiecare clas derivat. Clasa de la baza ierarhiei este prea general pentru a putea implementa metodele. n aceast situaie n clasa de baz se includ metode virtuale pure. Prezena unei astfel de metode oblig toate clasele derivate s o conin, fie c o redefinesc fie c nu. O metod virtual pur se declar asemntor cu o metod virtual, adugnd la sfritul antetului =0: virtual tip_rezultat nume_metoda(lista parametri) =0; Metodele virtuale pure nu au un corp, nefiind implementate. O clas care conine o metod virtual pur se numete clas abstract. O clas abstract nu poate fi instaniat, ea coninnd metode care nu snt implementate.

* Material didactic pentru ID *

83

Clasele derivate din clase abstracte pot s redefineasc metodele virtuale pure sau nu. Dac metoda virtual pur nu este implementat, atunci i clasa respectiv este clas abstract i nu poate fi instaniat. De exemplu, ntr-o ierarhie de clase care descriu figuri geometrice, fiecare clas are nevoie de metode pentru desenarea figurii respective, calculul ariei sau perimetrului. La baza ierarhiei se afl o clas abstract care include metode virtuale pure pentru aceste operaii. Clasele derivate, vor implementa metodele conform specificului fiecrei figuri geometrice.

Exemplu Clasa TVector este un exemplu de implementare a entitii masiv unidimensional (vector) printr-o clas de obiecte. Exemplul este realizat conform urmtoarelor specificaii:
elementele masivului sunt de tip float, spaiul pentru elementele masivului este alocat dinamic i poate fi redimensionat la nevoie (numai mrirea spaiului), snt implementate operaiile de baz necesare funcionrii corecte a clasei i cteva operaii demonstrative; pot fi adugate i alte operaii de prelucrare a vectorilor.
//tipul elementelor din vector

#define TIP float

class TVector { int max, n; //dimensiune maxima si curenta TIP* x; //adresa zonei in care se afla elementele public: //metode de acces int Get_max() {return max;} int Get_n() {return n;} TIP Get_elem(int i) {return x[i];} //constructori TVector(){x=NULL; max=n=0;} TVector(int max) {this->max=max; n=0; x=new TIP[max];} TVector(int n, TIP* a); //constructor de copiere TVector(TVector &b); //destructor ~TVector(); //metode TIP minim(); void realocare(int newmax); void sortare(); void interclasare(TVector &a, TVector &b); TVector* MinPoz(TIP* min); void adauga(TIP a); void insereaza(int p, TIP a); TIP extrage(int p, int *er); }; //constructor cu initializare, //valorile elementelor se afla deja in memorie TVector::TVector(int n, TIP* a) { int i; x=new TIP[n]; this->n=max=n; for(i=0;i<n;i++) x[i]=a[i]; } //destructor TVector::~TVector()

Programarea calculatoarelor

84

{ if(x) delete x; x=NULL; max=n=0; } //constructor de copiere TVector::TVector(TVector &b) { int i; max=b.Get_max(); n=b.Get_n(); if(max>0) x=new TIP[max]; else x=NULL; for(i=0;i<n;i++) x[i]=b.Get_elem(i); } //metoda pentru determinarea elementului minim din obiect TIP TVector::minim() { int i; TIP min; min=x[0]; for(i=1;i<n;i++) if(min>x[i]) min=x[i]; return min; } //metoda pentru marirea spatiului disponibil //pentru elementele obiectului void TVector::realocare(int newmax) { int i; TIP* a; if(newmax>max) { a=new TIP[newmax]; max=newmax; if(x) { for(i=0;i<n;i++) a[i]=x[i]; delete x; } this->x=a; } } //metoda pentru sortarea elementelor obiectului void TVector::sortare() { int i,m; m=0; TIP aux; while(!m) { m=1; for(i=0;i<n-1;i++) if(x[i]>x[i+1]) { aux=x[i]; x[i]=x[i+1]; x[i+1]=aux; m=0; } } }

* Material didactic pentru ID *

85

void TVector::interclasare(TVector &a, TVector &b) { int i,j, l1, l2; l1=a.Get_n(); l2=b.Get_n(); this->~TVector(); if(l1+l2>0) { this->realocare(l1+l2); i=0; j=0; n=0; while((i<l1)&&(j<l2)) if(a.Get_elem(i)<b.Get_elem(j)) x[n++]=a.Get_elem(i++); else x[n++]=b.Get_elem(j++); while(i<l1) x[n++]=a.Get_elem(i++); while(j<l2) x[n++]=b.Get_elem(j++); } } //metoda pentru determinarea minimului //si a tuturor pozitiilor pe care apare //pozitiile minimului vor forma un nou obiect de tip TVector, //creat dinamic in metoda. TVector* TVector::MinPoz(TIP* min) { TVector *a; int i,l; TIP *b; if(n>0) { b=new TIP[max]; *min=x[0]; l=1; b[0]=0; for(i=1;i<n;i++) if(*min>x[i]) { *min=x[i]; l=1; b[0]=(TIP)i; } else if(*min==x[i]) { b[l]=(TIP)i; l++; } a=new TVector(l,b); } else a=NULL; return a; }

//metoda pentru adaugarea unui element nou la sfirsitul vectorului void TVector::adauga(TIP a) { if(n==max) realocare(max+1); x[n++]=a; } //metoda pentru inserarea unui element pe o anumita pozitie void TVector::insereaza(int p, TIP a) { int i; if(n==max) realocare(max+1); for(i=n-1;i>=p;i--)

Programarea calculatoarelor

86

x[i+1]=x[i]; x[p]=a; n++; } //metoda pentru extragerea unui element de pe o pozitie data TIP TVector::extrage(int p, int *er) { int i; TIP rez; *er=1; if((p<n)&&(p>=0)) { rez=x[p]; for(i=p;i<n-1;i++) x[i]=x[i+1]; n--; *er=0; } return rez; }

O mbuntire a acestei clase const n eliminarea dependenei de tipul elementelor masivului (float n exemplul de mai sus). Descriei o metod prin care s se realizeze acest lucru.
Exemplu Clasa TFisR reprezint un exemplu de implementare a entitii fiier binar organizat relativ printr-o clas de obiecte. Aceast clas poate fi utilizat pentru prelucrarea oricrui fiier organizat relativ deoarece nu depinde de structura articolelor fiierului. Structura logic a articolelor este descris n aplicaia care utilizeaz clasa pentru gestiunea unui fiier. Obiectul care gestioneaz fiierul are nevoie de dimensiunea articolului logic (n octei). Pentru operaiile de citire/scriere din memoria intern. adaug automat indicatorul de stare la nceputul articolului logic pentru a forma articolul fizic.
class TFisR { FILE* f; //adresa fisierului char nume[50]; //numele extern al fisierului unsigned int dim; //dimensiunea unui articol logic int NrSpatii(); //numar total spatii in fis. crt. int Preformare(int nr); //preformare/extindere fis.crt. public: //constructori TFisR() { f=NULL;nume[0]='\0';dim=0;} //obiect nul TFisR(char* n, unsigned int dim, char m = 'N'); ~TFisR(void); //destructor, reseteaza obiectul //metode de acces char* Nume(){return nume;} unsigned int Dim(){return dim;} int Deschis() {return f==NULL?0:1;} int Deschide(char* n, unsigned int dim, char m = 'N'); int Inchide(); int Pozitia(); int Sfirsit(){return Pozitia()<NrSpatii()?0:1;} int Pozitionare(int p); int CitesteUrmatorul(void* adresa); int CitestePozitia(int poz, void* adresa); int ScriePozitia(void* adresa, int poz);

* Material didactic pentru ID *

87

int Sterge(int poz); }; //constructor, deschide un fisier //I: nume fisier, dimensiune articol, mod deschidere (implicit N - fisier nou) TFisR::TFisR(char* n, unsigned int dim, char m) { if(toupper(m)=='N') f=fopen(n,"wb+"); else f=fopen(n,"rb+"); if(!f) nume[0]='\0'; else { strcpy(nume, n); this->dim=dim; //Pozitionare(0); } } //destructor TFisR::~TFisR() { if(f) { fclose(f); f=NULL; nume[0]='\0'; dim=0; } } //deschidere fisier nou, dupa ce obiectul a fost deja creat //I: nume fisier, dimensiune articol, mod deschidere (implicit N - fisier nou) //E: cod succes 0 - fisier inchis, 1 - fisier deschis int TFisR::Deschide(char* n, unsigned int dim, char m) { if(f) Inchide(); if(toupper(m)=='N') f=fopen(n,"wb+"); else f=fopen(n,"rb+"); if(!f) nume[0]='\0'; else { strcpy(nume, n); this->dim=dim; //Pozitionare(0); } return Deschis(); } //inchidere fisier curent //I: //E: cod eroare 0 - fisier inchis, 1 - fisier deschis int TFisR::Inchide() { if(f) { fclose(f); f=NULL; nume[0]='\0'; dim=0; } return Deschis(); } //numar total spatii in fisier (nr. articole fizice)

Programarea calculatoarelor

88

//I: //E: nr. articole, 0 daca fisierul e inchis int TFisR::NrSpatii() { long p; int nr; nr=0; if(f) { p=ftell(f); fseek(f,0,SEEK_END); nr=ftell(f)/(dim+1); fseek(f,p,SEEK_SET); } return nr; } //pozitia curenta in fisier //I: //E: pozitia curenta, in nr. de articole, 0 daca fisierul e inchis int TFisR::Pozitia() { int nr; nr=0; if(f) nr=ftell(f)/(dim+1); return nr; } //preformare fisier //I: numar de articole pentru preformare/extindere //E: cod succes, 1 - succes, 0 - fisierul era inchis int TFisR::Preformare(int nr) { int i,er; char *art; er=1; if(f) { fseek(f,0,SEEK_END); art=(char*)malloc(dim+1); art[0]=0; for(i=0;i<nr;i++) fwrite(art,dim+1,1,f); er=0; free(art); } return er; } //pozitionare //I: pozitia dorita, in nr. relativ articol //E: cod eroare, 0 - succes, 1 - pozitia ceruta e prea mare, 2 - fisierul era inchis int TFisR::Pozitionare(int p) { int er; er=2; if(f) if(p<NrSpatii()) { fseek(f,p*(dim+1),SEEK_SET); er=0; } else er=1; return er; }

* Material didactic pentru ID *

89

//citire in acces secvential, urmatorul articol //I: adresa la care se depune articolul citit //E: cod eroare, 0 - articolul a fost citit, 1 - fisierul era inchis, 2 sfirsit fisier int TFisR::CitesteUrmatorul(void* adresa) { char* art; int er=1; if(f) { art=(char*)malloc(dim+1); while(!Sfirsit() && er) { fread(art,dim+1,1,f); if(art[0]==1) { er=0; memcpy(adresa,art+1,dim); } } if(er) er=2; free(art); } return er; } //citire in acces direct //I: cheia articolului (nr. relativ), adresa unde se depune articolul //E: cod eroare, 0 - articolul a fost citit, 1 - fisierul era inchis sau pozitie prea mare, 2 - cheie invalida int TFisR::CitestePozitia(int poz, void* adresa) { char* art; int er; er=Pozitionare(poz); if(!er) { art=(char*)malloc(dim+1); fread(art,dim+1,1,f); if(art[0]==0) er=2; else { er=0; memcpy(adresa, art+1, dim); } free(art); } return er; } //scriere articol in acces direct //I: adresa articolului care trebuie scris, cheia articolului //E: cod eroare, 0 - scriere reusita, 1 - fisierul era inchis, 2 - cheie invalida (deja e un articol acolo) int TFisR::ScriePozitia(void* adresa, int poz) { char* art; int n,er=1; if(f) { n=NrSpatii(); if(poz>=n) Preformare(poz-n+1); art=(char*)malloc(dim+1); Pozitionare(poz); fread(art,dim+1,1,f); if(art[0]==1) er=2; else { er=0;

Programarea calculatoarelor

90

memcpy(art+1,adresa,dim); art[0]=1; Pozitionare(poz); fwrite(art,dim+1,1,f); } free(art); } return er; } //sterge articolul cu cheia data //I: cheia articolului de sters //E: cod eroare, 0 - stergere reusita, 1 - fisierul era inchis sau pozitie prea mare, 2 - cheie invalida (spatiu gol), int TFisR::Sterge(int poz) { char* art; int er; er=Pozitionare(poz); if(!er) { art=(char*)malloc(dim+1); fread(art,dim+1,1,f); if(art[0]==0) er=2; else { er=0; art[0]=0; Pozitionare(poz); fwrite(art,dim+1,1,f); } free(art); } return er; }

Exemplul urmtor demonstreaz utilizarea clasei TFisR pentru gestionarea unui fiier organizat relativ. Fiierul ales pentru exemplificare conine date despre studeni, cheia relativ fiind numrul matricol asociat fiecrui student. Snt prezentate cteva din operaiile uzuale asupra unui astfel de fiier.
#include <stdio.h> #include <conio.h> #include <malloc.h> #include <ctype.h> #include "TFisR.h" #define MAXNOTE 15 typedef struct char nume[30]; int grupa; int an; char note[MAXNOTE]; int nrm; }STUDENT; {

void main() { TFisR f; STUDENT s; char numef[30],r; int i,nr,er;

* Material didactic pentru ID *

91

//creare si populare fisier nou printf("\n\nCreare si populare fisier nou"); printf("\nNume fisier:"); gets(numef); f.Deschide(numef,sizeof(STUDENT)); printf("\nNumar matricol:"); scanf("%d",&nr); while(!feof(stdin)) { er=f.CitestePozitia(nr,&s); if(!er) printf("\nNr. matricol invalid, alocat deja altui student"); else { printf("Nume:"); fflush(stdin); gets(s.nume); printf("Grupa:"); scanf("%d",&s.grupa); printf("An:"); scanf("%d",&s.an); printf("Note:\n"); for(i=0;i<MAXNOTE;i++) { printf("Nota %2d: ",i+1); scanf("%d",&s.note[i]); } s.nrm=nr; er=f.ScriePozitia(&s,nr); } printf("\nNumar matricol(^Z):"); scanf("%d",&nr); } f.Inchide();

//vizualizare fisier in acces secvential (lista) printf("\n\nVizualizare fisier in acces secvential"); printf("\nNume fisier:"); gets(numef); f.Deschide(numef,sizeof(STUDENT),'e'); printf("\n%3s %30s An Grupa Note","Nrm","Nume si prenume"); while(!f.Sfirsit()) { er=f.CitesteUrmatorul(&s); if(!er) { printf("\n%3d %30s %2d %5d ",s.nrm,s.nume,s.an,s.grupa); for(i=0;i<MAXNOTE;i++) printf("%2d",s.note[i]); } } printf("\n\n"); f.Inchide();

//vizualizare in acces direct printf("\n\nVizualizare date in acces direct"); printf("\nNume fisier:");

Programarea calculatoarelor

92

gets(numef); f.Deschide(numef,sizeof(STUDENT),'e'); printf("\nNumar matricol:"); scanf("%d",&nr); while(!feof(stdin)) { er=f.CitestePozitia(nr,&s); if(er) printf("\nNr. matricol invalid (nealocat)%s",er==1?" - mai mare decit dim. fis.":""); else { printf("\n%3s %30s An Grupa Note","Nrm","Nume si prenume"); printf("\n%3d %30s %2d %5d ",s.nrm,s.nume,s.an,s.grupa); for(i=0;i<MAXNOTE;i++) printf("%2d",s.note[i]); } printf("\nNumar matricol(^Z):"); scanf("%d",&nr); } f.Inchide();

//stergere articole din fisier printf("\n\Stergere date in acces direct"); printf("\nNume fisier:"); gets(numef); f.Deschide(numef,sizeof(STUDENT),'e'); printf("\nNumar matricol:"); scanf("%d",&nr); while(!feof(stdin)) { er=f.CitestePozitia(nr,&s); if(er) printf("\nNr. matricol invalid (nealocat)%s",er==1?" - mai mare decit dim. fis.":""); else { printf("\n%3s %30s An Grupa Note","Nrm","Nume si prenume"); printf("\n%3d %30s %2d %5d ",s.nrm,s.nume,s.an,s.grupa); for(i=0;i<MAXNOTE;i++) printf("%2d",s.note[i]); printf("\nConfirmare stergere (D/N - orice): "); r=getche(); if(toupper(r)=='D') f.Sterge(nr); } printf("\nNumar matricol(^Z):"); scanf("%d",&nr); }

f.Inchide(); //eliminare obiect fisier f.~TFisR();

//vizualizare fisier in acces secvential (lista) printf("\n\nVizualizare fisier in acces secvential, folosind un obiect dinamic"); printf("\nNume fisier:"); gets(numef);

* Material didactic pentru ID *

93

TFisR* g; g=new TFisR(numef,sizeof(STUDENT),'e'); printf("\n%3s %30s An Grupa Note","Nrm","Nume si prenume"); while(!(g->Sfirsit())) { er=g->CitesteUrmatorul(&s); if(!er) { printf("\n%3d %30s %2d %5d ",s.nrm,s.nume,s.an,s.grupa); for(i=0;i<MAXNOTE;i++) printf("%2d",s.note[i]); } } printf("\n\n"); g->Inchide(); delete g; }

1. Extindei clasa TFisR prin adugarea de metode care implementeaz i alte operaii cu fiiere organizate relativ. 2. Definii o problem de gestiune a datelor care se bazeaz pe un fiier organizat relativ. Utiliznd clasa TFisR, scriei un program multifuncional pentru gestiunea acestui fiier.

Teme: 1. Implementai o clas de obiecte care s descrie entitatea masiv bidimensional (matrice). Scriei un program care s demonstreze modul de lucru cu aceast clas. 2. Implementai o clas de obiecte care s descrie entitatea fiier organizat indexat. Scriei un program care s demonstreze modul de lucru cu aceast clas. Rezumat n aceast unitate de nvare au fost studiate elemente de baz ale programrii orientate obiect. n urma studiului, studenii au dobndit cunotinele teoretice de baz ale programrii orientate obiecte: obiect, clas, atribut, metod, ncapsulare, polimorfism, motenire. De asemenea, au fost studiate elemente de programare orientat obiect specifice limbajului C++: definirea claselor, modificatori de acces, constructor, destructor, funcii prieten, derivarea claselor. Exemplele prezentate i temele propuse urmresc dezvoltarea abilitilor practice necesare rezolvrii problemelor de programare orientat obiect, pregtind studenii pentru trecerea la urmtoarea etap de instruire pentru rezolvarea problemelor de informatic economic.

4.7. Rspunsuri i comentarii la testele de autoevaluare


18. S se implementeze clasa Stiv dinamic. O list dinamic este format din noduri, deci putem defini nti clasa Nod, urmnd a folosi tipul Nod pentru a descrie clasa Stiv. Pentru acest exemplu, datele memorate n nodurile stivei snt de tip float. Exempul de mai jos conine i o funcie main, cu rolu lde a demonstra modul de utilizare a clasei.

Programarea calculatoarelor

94

#include <stdio.h> typedef float TIP_INFO; class Nod { TIP_INFO info; Nod* next; public: float GetInfo(); Nod* GetNext(); Nod(float a, Nod* n); }; float Nod::GetInfo() { return info; } Nod* Nod::GetNext() { return next; } Nod::Nod(float a, Nod* n) { info=a; next=n; } class Stiva { Nod* Cap; public: Stiva(); Stiva(float a); ~Stiva(); void Push(float a); float Pop(); int Empty(); void Afiseaza(); }; void Stiva::Afiseaza() { Nod* x; x=Cap; while(x) { printf("%5.2f ",x->GetInfo()); x=x->GetNext(); } } Stiva::~Stiva() { Nod* x; while(Cap) { x=Cap; Cap=Cap->GetNext(); delete x; } } float Stiva::Pop() { float x; Nod* y; y=Cap; x=Cap->GetInfo(); Cap=Cap->GetNext(); delete y; return x; } void Stiva::Push(float a) { Cap=new Nod(a,Cap); } int Stiva::Empty()

* Material didactic pentru ID *

95

{ return Cap?0:1; } Stiva::Stiva(float a) { Cap= new Nod(a,NULL); } Stiva::Stiva() { Cap=NULL; } void main() { Stiva s; int i; float x; if(s.Empty()) printf("\nStiva este goala"); else printf("\nStiva contine date"); for(i=0;i<10;i++) s.Push((float)i); s.Afiseaza(); x=s.Pop(); s.Afiseaza(); if(s.Empty()) printf("\nStiva este goala"); else printf("\nStiva contine date");

Clasa Nod conine atributele info (informaia util din nod) i next iar ca metode un constructor care iniializeaz atributele obiectului i metode accesorii pentru accesarea valorilor atributelor. Clasa Stiva conine un singur atribut, Cap care are ca valoare adresa primului nod al stivei (vrful stivei). Constructorii clasei asigur crearea unei stive vide sau a unei stive cu un element. Metodele asigur adugarea unei informaii n stiv, respectiv extragerea unei informaii. Metoda Afiseaza asigur afiarea pe ecran a informaiilor din stiv.

Bibliografie 1. I. Gh. Roca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C. Uscatu, M. Mircea Programarea calculatoarelor. Algoritmi n programare, Ed. ASE Bucureti, 2007 2. I. Gh. Roca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C. Uscatu Programarea calculatoarelor. tiina nvrii unui limbaj de programare. Teorie i aplicaii, Ed. ASE Bucureti, 2003 3. Liviu Negrescu Limbajele C i C++ pentru nceptori, vol. I, II, Ed. Microinformatica, Cluj-Napoca, 1994

Programarea calculatoarelor

96

Bibliografie
1. I. Gh. Roca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C. Uscatu, M. Mircea Programarea calculatoarelor. Algoritmi n programare, Ed. ASE Bucureti, 2007 2. I. Gh. Roca, B. Ghilic-Micu, C. Cocianu, M. Stoica, C. Uscatu Programarea calculatoarelor. tiina nvrii unui limbaj de programare. Teorie i aplicaii, Ed. ASE Bucureti, 2003 3. Liviu Negrescu Limbajele C i C++ pentru nceptori, vol. I, II, Ed. Microinformatica, Cluj-Napoca, 1994