bazele informaticii-c++

Embed Size (px)

Citation preview

  • 7/25/2019 bazele informaticii-c++

    1/311

    http://vega.unitbv.ro/~andonie/http://vega.unitbv.ro/~andonie/Cartea%20de%20algoritmi/cap8.htm

    In clipa cand exprimam un lucru, reusim, in mod biar, sa!l si depreciem.Maeterlinck

    PrefataCartea noastra isi propune in primul rand sa fie un curs si nu o "enciclopedie" de algoritmi. Pornind de la structurile de date cele maiuzuale si de la analiza eficientei algoritmilor, cartea se concentreaza pe principiile fundamentale de elaborare a algoritmilor: greedy,divide et impera, programare dinamica, backtracking. Interesul nostru pentru inteligenta artificiala a facut ca penultimul capitol sa fie, defapt, o introducere - din punct de vedere al algoritmilor - in acest domeniu.

    Maoritatea algoritmilor selectati au o conotatie estetica. !fortul necesar pentru intelegerea elementelor mai subtile este uneoriconsiderabil. Ce este insa un algoritm "estetic" Putem raspunde foarte simplu: un algoritm este estetic daca e#prima mult in cuvinteputine. $n algoritm estetic este oare in mod necesar si eficient Cartea raspunde si acestor intrebari.

    In al doilea rand, cartea prezinta mecanismele interne esentiale ale limbaului C%% &mosteniri, legaturi dinamice, clase parametrice,e#ceptii' si trateaza implementarea algoritmilor in conceptul programarii orientate pe obiect. (otusi, aceasta carte nu este un curs completde C%%.

    )lgoritmii nu sunt pur si simplu "transcrisi" din pseudo-cod in limbaul C%%, ci sunt reganditi din punct de vedere al programariiorientate pe obiect. *peram ca, dupa citirea cartii, veti dezvolta aplicatii de programare orientata pe obiect si veti elabora implementari alealtor structuri de date. Programele au fost scrise pentru limbaul C%% descris de !llis si *troustrup in "(+e )nnotated C%% eferenceManual". )cest limba se caracterizeaza, in principal, prin introducerea claselor parametrice si a unui mecanism de tratare a e#ceptiilorfoarte avansat, facilitati deosebit de importante pentru dezvoltarea de biblioteci C%%. Compilatoarele $ C%% /.0.1 &$I234inu#' si5orland C%% 6.7 &89*' suporta destul de bine clasele parametrice. Pentru tratarea e#ceptiilor se pot utiliza compilatoarele 5orland C%%.; si, in viitorul apropiat, $ C%% /.

  • 7/25/2019 bazele informaticii-c++

    2/311

    functie, procedura si pseudo-cod. !#ista mai multe modalitati de parcurgere a cartii. In functie de interesul si pregatirea cititorului, acestapoate alege oricare din partile referitoare la elaborarea, analiza, sau implementarea algoritmilor. Cu e#ceptia partilor de analiza aeficientei algoritmilor &unde sunt necesare elemente de matematici superioare', cartea poate fi parcursa si de catre un elev de liceu. Pentruparcurgerea sectiunilor de implementare, este recomandabila cunoasterea limbaului C.Cartea noastra se bazeaza pe cursurile pe care le tinem, incepand cu 7>>7, la *ectia de electronica si calculatoare a $niversitatii

    (ransilvania din 5rasov. *-a dovedit utila si e#perienta noastra de peste zece ani in dezvoltarea produselor soft?are. Colectivul deprocesare a imaginilor din I(C 5rasov a fost un e#celent mediu in care am putut sa ne dezvoltam profesional. 4e multumim pentruaceasta celor care au facut parte, alaturi de noi, din acest grup: *orin Cismas, *tefan @ozsa, !ugen Carai. u putem sa nu ne amintim cunostalgie de compilatorul C al firmei 8!C &pentru minicalculatoarele din seria P8P-77' pe care l-am "descoperit" impreuna, cu zece aniin urma.

    Ca de obicei in astfel de situatii, numarul celor care au contribuit intr-un fel sau altul la realizarea acestei carti este foarte mare,cuprinzand profesorii nostri, colegii de catedra, studentii pe care am "testat" cursurile, prietenii. 4e multumim tuturor. 8e asemenea,apreciem rabdarea celor care ne-au suportat in cei peste doi ani de elaborare a cartii.

    *peram sa cititi aceasta carte cu aceeasi placere cu care ea a fost scrisa.

    "rasov, ianuarie #$$

    1. Preliminarii1.1 Ce este un algoritm?

    )bu @aAfar Mo+ammed ibn Musa al-B+o?arizmi &autor persan, sec. III-I2', a scris o carte de matematica cunoscuta in traducere latina

    ca D)lgorit+mi de numero indorumE, iar apoi ca D4iber algorit+miE, unde Dalgorit+mE provine de la Dal-B+o?arizmiE, ceea ce literalinseamna Ddin orasul B+o?arizmE. In prezent, acest oras se numeste B+iva si se afla in $zbec+istan. )tat al-B+o?arizmi, cat si altimatematicieni din !vul Mediu, intelegeau prin algoritmo regula pe baza careia se efectuau calcule aritmetice. )stfel, in timpul lui )damiese &sec. 2I', algoritmii foloseau la: dublari, inumatatiri, inmultiri de numere. )lti algoritmi apar in lucrarile lui *tifer &D)rit+meticaintegraE, Frnberg, 70' si Cardano &D)rs magna sive de reguli algebraicisE, Frnberg, 700'. C+iar si 4eibniz vorbeste de Dalgoritmide inmultireE. (ermenul a ramas totusi multa vreme cu o intrebuintare destul de restransa, c+iar si in domeniul matematicii.

    Bronecker &in 711G' si 8edekind &in 7111' semneaza actul de nastere al teoriei functiilor recursive. Conceptul de recursivitate devineindisolubil legat de cel de algoritm. 8ar abia in deceniile al treilea si al patrulea ale secolului nostru, teoria recursivitatii si algoritmilorincepe sa se constituie ca atare, prin lucrarile lui *kolem, )ckermann, *udan, Hdel, C+urc+, Bleene, (uring, Peter si altii.

  • 7/25/2019 bazele informaticii-c++

    3/311

    !ste surprinzatoare transformarea gandirii algoritmice, dintr-un instrument matematic particular, intr-o modalitate fundamentala deabordare a problemelor in domenii care aparent nu au nimic comun cu matematica. )ceasta universalitate a gandirii algoritmice esterezultatul cone#iunii dintre algoritm si calculator. )stazi, intelegem prin algoritmo metoda generala de rezolvare a unui anumit tip deproblema, metoda care se poate implementa pe calculator. In acest conte#t, un algoritm este esenta absoluta a unei rutine.

    Cel mai faimos algoritm este desigur algoritmul lui !uclid pentru aflarea celui mai mare divizor comun a doua numere intregi. )ltee#emple de algoritmi sunt metodele invatate in scoala pentru a inmulti3imparti doua numere. Ceea ce da insa generalitate notiunii dealgoritm este faptul ca el poate opera nu numai cu numere. !#ista astfel algoritmi algebrici si algoritmi logici. Pana si o reteta culinaraeste in esenta un algoritm. Practic, s-a constatat ca nu e#ista nici un domeniu, oricat ar parea el de imprecis si de fluctuant, in care sa nuputem descoperi sectoare functionand algoritmic.

    $n algoritm este compus dintr-o multime finita de pasi, fiecare necesitand una sau mai multe operatii. Pentru a fi implementabile pecalculator, aceste operatii trebuie sa fie in primul rand de&inite, adica sa fie foarte clar ce anume trebuie e#ecutat. In al doilea rand,operatiile trebuie sa fie e&ective, ceea ce inseamna ca in principiu, cel putin o persoana dotata cu creion si +artie trebuie sa poataefectua orice pas intr-un timp finit. 8e e#emplu, aritmetica cu numere intregi este efectiva. )ritmetica cu numere reale nu este insaefectiva, deoarece unele numere sunt e#primabile prin secvente infinite. om considera ca un algoritm trebuie sa se termine dupa unnumar finit de operatii, intr-un timp rezonabil de lung.

    'rogramuleste e#primarea unui algoritm intr-un limba de programare. !ste bine ca inainte de a invata concepte generale, sa fi acumulatdea o anumita e#perienta practica in domeniul respectiv. Presupunand ca ati scris dea programe intr-un limba de nivel inalt, probabil caati avut uneori dificultati in a formula solutia pentru o problema. )lteori, poate ca nu ati putut decide care dintre algoritmii care rezolvauaceeasi problema este mai bun. )ceasta carte va va invata cum sa evitati aceste situatii nedorite.

    *tudiul algoritmilor cuprinde mai multe aspecte:

    i( )laborarea algoritmilor. )ctul de creare a unui algoritm este o arta care nu va putea fi niciodata pe deplin automatizata. !ste in fondvorba de mecanismul universal al creativitatii umane, care produce noul printr-o sinteza e#trem de comple#a de tipul:

    tehnici de elaborare *reguli( %creativitate *intuitie( + solutie.

    $n obiectiv maor al acestei carti este de a prezenta diverse te+nici fundamentale de elaborare a algoritmilor. $tilizand aceste te+nici,acumuland si o anumita e#perienta, veti fi capabili sa concepeti algoritmi eficienti.

    ii( )xprimarea algoritmilor.=orma pe care o ia un algoritm intr-un program trebuie sa fie clara si concisa, ceea ce implica utilizareaunui anumit stil de programare. )cest stil nu este in mod obligatoriu legat de un anumit limba de programare, ci, mai curand, de tipul

  • 7/25/2019 bazele informaticii-c++

    4/311

    limbaului si de modul de abordare. )stfel, incepand cu anii J1;, standardul unanim acceptat este cel de programare structurata. Inprezent, se impune standardul programarii orientate pe obiect.

    iii( alidarea algoritmilor. $n algoritm, dupa elaborare, nu trebuie in mod necesar sa fie programat pentru a demonstra ca functioneazacorect in orice situatie. !l poate fi scris initial intr-o forma precisa oarecare. In aceasta forma, algoritmul va fi validat, pentru a ne asigura

    ca algoritmul este corect, independent de limbaul in care va fi apoi programat.iv( -nalia algoritmilor. Pentru a putea decide care dintre algoritmii ce rezolva aceeasi problema este mai bun, este nevoie sa definim uncriteriu de apreciere a valorii unui algoritm. In general, acest criteriu se refera la timpul de calcul si la memoria necesara unui algoritm.om analiza din acest punct de vedere toti algoritmii prezentati.

    v( estarea programelor. )ceasta consta din doua faze: depanare &debugging' si trasare &profiling'.epanareaeste procesul e#ecutariiunui program pe date de test si corectarea eventualelor erori. 8upa cum afirma insa !. K. 8ikstra, prin depanare putem evidentiaprezenta erorilor, dar nu si absenta lor. 9 demonstrare a faptului ca un program este corect este mai valoroasa decat o mie de teste,deoarece garanteaza ca programul va functiona corect in oricesituatie. rasareaeste procesul e#ecutarii unui program corect pe diferitedate de test, pentru a-i determina timpul de calcul si memoria necesara. ezultatele obtinute pot fi apoi comparate cu analiza anterioara aalgoritmului.

    )ceasta enumerare serveste fi#arii cadrului general pentru problemele abordate in carte: ne vom concentra pe domeniile i(, ii(si iv(.

    om incepe cu un e#emplu de algoritm. !ste vorba de o metoda, cam ciudata la prima vedere, de inmultire a doua numere. *e numesteDinmultirea a la russeE.

    0 7> 7>

    // 61 -

    77

  • 7/25/2019 bazele informaticii-c++

    5/311

    7 G;1 G;1

    855

    om scrie deinmultitul si inmultitorul &de e#emplu 0 si 7>' unul langa altul, formand sub fiecare cate o coloana, conform urmatoareireguli: se imparte numarul de sub deinmultit la /, ignorand fractiile, apoi se inmulteste cu / numarul de sub inmultitor. *e aplica regula,pana cand numarul de sub deinmultit este 7. In final, adunam toate numerele din coloana inmultitorului care corespund, pe linie, unornumere impare in coloana deinmultitului. In cazul nostru, obtinem: 7> %

  • 7/25/2019 bazele informaticii-c++

    6/311

    8e multe ori, vom atribui unor elemente ale unui tablou valorile , intelegand prin acestea doua valori numerice e#treme, astfel incatpentru oricare alt element NiO avem -Q NiO Q %.

    Pentru simplitate, vom considera uneori ca anumite variabile sunt globale, astfel incat sa le putem folosi in mod direct in proceduri.

    Iata acum si primul nostru algoritm, cel al inmultirii Da la russeE:

    functionrusse&-,"' arrays, 1 RinitializareS N7O -T 1N7O " i7 Rse construiesc cele doua coloaneS whileNiO U 7 do Ni%7O NiO div/ Rdivreprezinta impartirea intreagaS 1Ni%7O 1NiO%1NiO

    ii%7 Raduna numerele 1NiO corespunzatoare numerelorNiO impareS prod; whileiU ; do ifNiO este impar thenprodprod%1NiO i i-7 returnprod

    $n programator cu e#perienta va observa desigur ca tablourile si 1nu sunt de fapt necesare si ca programul poate fi simplificat cu

    usurinta. )cest algoritm poate fi programat deci in mai multe feluri, c+iar folosind acelasi limba de programare.

    Pe langa algoritmul de inmultire invatat in scoala, iata ca mai avem un algoritm care face acelasi lucru. !#ista mai multi algoritmi carerezolva o problema, dar si mai multe programe care pot descrie un algoritm.

    )cest algoritm poate fi folosit nu doar pentru a inmulti pe 0 cu 7>, dar si pentru a inmulti orice numere intregi pozitive. om numi&0, 7>' un ca&instance'. Pentru fiecare algoritm e#ista un domeniu de de&initieal cazurilor pentru care algoritmul functioneaza corect.9rice calculator limiteaza marimea cazurilor cu care poate opera. )ceasta limitare nu poate fi insa atribuita algoritmului respectiv. Inca odata, observam ca e#ista o diferenta esentiala intre programe si algoritmi.

  • 7/25/2019 bazele informaticii-c++

    7/311

    1.2 Eficienta algoritmilor

    Ideal este ca, pentru o problema data, sa gasim mai multi algoritmi, iar apoi sa-l alegem dintre acestia pe cel optim. Care este insa criteriul

    de comparatie !ficienta unui algoritm poate fi e#primata in mai multe moduri. Putem analiza a posteriori&empiric' comportareaalgoritmului dupa implementare, prin rularea pe calculator a unor cazuri diferite. *au, putem analiza a priori&teoretic' algoritmul,inaintea programarii lui, prin determinarea cantitativa a resurselor &timp, memorie etc' necesare ca o &unctie de marimea cauluiconsiderat.

    arimea unui cazx, notata cu Vx V, corespunde formal numarului de biti necesari pentru reprezentarea lui x, folosind o codificare precisdefinita si rezonabil de compacta. )stfel, cand vom vorbi despre sortare, Vx V va fi numarul de elemente de sortat. 4a un algoritm numeric,VxV poate fi c+iar valoarea numerica a cazului x.

    )vantaul analizei teoretice este faptul ca ea nu depinde de calculatorul folosit, de limbaul de programare ales, sau de indemanareaprogramatorului. !a salveaza timpul pierdut cu programarea si rularea unui algoritm care se dovedeste in final ineficient. 8in motivepractice, un algoritm nu poate fi testat pe calculator pentru cazuri oricat de mari. )naliza teoretica ne permite insa studiul eficienteialgoritmului pentru cazuri de orice marime.

    !ste posibil sa analizam un algoritm si printr-o metoda hibrida. In acest caz, forma functiei care descrie eficienta algoritmului estedeterminata teoretic, iar valorile numerice ale parametrilor sunt apoi determinate empiric. )ceasta metoda permite o predictie asupracomportarii algoritmului pentru cazuri foarte mari, care nu pot fi testate. 9 e#trapolare doar pe baza testelor empirice este foarteimprecisa.

    !ste natural sa intrebam ce unitate trebuie folosita pentru a e#prima eficienta teoretica a unui algoritm. $n raspuns la aceasta problemaeste dat deprincipiul invariantei, potrivit caruia doua implementari diferite ale aceluiasi algoritm nu difera in eficienta cu mai mult de oconstanta multiplicativa. )dica, presupunand ca avem doua implementari care necesita t7&n' si, respectiv, t/&n' secunde pentru a rezolvaun caz de marime n, atunci e#ista intotdeauna o constanta pozitiva c, astfel incat t7&n' ct/&n' pentru orice nsuficient de mare. )cestprincipiu este valabil indiferent de calculatorul &de constructie conventionala' folosit, indiferent de limbaul de programare ales siindiferent de indemanarea programatorului &presupunand ca acesta nu modifica algoritmulW'. 8eci, sc+imbarea calculatorului ne poatepermite sa rezolvam o problema de 7;; de ori mai repede, dar numai modificarea algoritmului ne poate aduce o imbunatatire care sadevina din ce in ce mai marcanta pe masura ce marimea cazului solutionat creste.

    evenind la problema unitatii de masura a eficientei teoretice a unui algoritm, aungem la concluzia ca nici nu avem nevoie de o astfel deunitate: vom e#prima eficienta in limitele unei constante multiplicative. om spune ca un algoritm necesita timp in ordinulluit, pentru o

  • 7/25/2019 bazele informaticii-c++

    8/311

    functie tdata, daca e#ista o constanta pozitiva csi o implementare a algoritmului capabila sa rezolve fiecare caz al problemei intr-un timpde cel mult ct&n' secunde, unde n este marimea cazului considerat. $tilizarea secundelor in aceasta definitie este arbitrara, deoarecetrebuie sa modificam doar constanta pentru a margini timpul la at&n' ore, sau bt&n' microsecunde. 8atorita principiului invariantei, oricealta implementare a algoritmului va avea aceeasi proprietate, cu toate ca de la o implementare la alta se poate modifica constantamultiplicativa. In Capitolul 0 vom reveni mai riguros asupra acestui important concept, numit notatie asimptotica.

    8aca un algoritm necesita timp in ordinul lui n, vom spune ca necesita timp liniar, iar algoritmul respectiv putem sa-l numim algoritmliniar. *imilar, un algoritm estepatratic, cubic,polinomial, sau exponentialdaca necesita timp in ordinul lui n/, n6, n3, respectiv cn, unde 3si csunt constante.

    $n obiectiv maor al acestei carti este analiza teoretica a eficientei algoritmilor. e vom concentra asupra criteriului timpului de e#ecutie.)lte resurse necesare &cum ar fi memoria' pot fi estimate teoretic intr-un mod similar. *e pot pune si probleme de compromis memorie -timp de e#ecutie.

    1.3 Cazul mediu si cazul cel mai nefavorabil

    (impul de e#ecutie al unui algoritm poate varia considerabil c+iar si pentru cazuri de marime identica. Pentru a ilustra aceasta, vomconsidera doi algoritmi elementari de sortare a unui tablou de nelemente:

    procedureinsert&N7 .. nO' fori / ton do x NiOT4i-7 while4U ;andxQ N4O do

    N4%7O N4O 44-7 N4%7O x

    procedureselect &N7 .. nO' fori 7 ton-7 do min4iTminxNiO for4i%7 tondo ifN4O Q minxthen min4 4

  • 7/25/2019 bazele informaticii-c++

    9/311

    minx N4O Nmin4O NiO NiO minx

    Ideea generala a sortariiprin insertieeste sa consideram pe rand fiecare element al sirului si sa il inseram in subsirul ordonat creat

    anterior din elementele precedente. 9peratia de inserare implica deplasarea spre dreapta a unei secvente. *ortarea prinselectielucreazaaltfel, plasand la fiecare pas cate un element direct pe pozitia lui finala.

    =ie 5si doua tablouri de nelemente, unde 5este dea sortat crescator, iar este sortat descrescator. 8in punct de vedere al timpului dee#ecutie, reprezinta cazul cel mai nefavorabil iar5cazul cel mai favorabil.

    om vedea mai tarziu ca timpul de e#ecutie pentru sortarea prin selectie este patratic, independent de ordonarea initiala a elementelor.(estul DifN4O QminxE este e#ecutat de tot atatea ori pentru oricare dintre cazuri. elativ micile variatii ale timpului de e#ecutie sedatoreaza doar numarului de e#ecutari ale atribuirilor din ramura thena testului.

    4a sortarea prin insertie, situatia este diferita. Pe de o parte, insert&5' este foarte rapid, deoarece conditia care controleaza bucla whileeste mereu falsa. (impul necesar este liniar. Pe de alta parte, insert&' necesita timp patratic, deoarece bucla whileeste e#ecutata de i-7ori pentru fiecare valoare a lui i. &om analiza acest lucru in Capitolul 0'.

    8aca apar astfel de variatii mari, atunci cum putem vorbi de un timp de e#ecutie care sa depinda doar de marimea cazului considerat 8eobicei consideram analiza pentru cel mai nefavorabil caz. )cest tip de analiza este bun atunci cand timpul de e#ecutie al unui algoritmeste critic &de e#emplu, la controlul unei centrale nucleare'. Pe de alta parte insa, este bine uneori sa cunoastem timpul mediude e#ecutieal unui algoritm, atunci cand el este folosit foarte des pentru cazuri diferite. om vedea ca timpul mediu pentru sortarea prin insertie estetot patratic. In anumite cazuri insa, acest algoritm poate fi mai rapid. !#ista un algoritm de sortare &6uic3sort' cu timp patratic pentru celmai nefavorabil caz, dar cu timpul mediu in ordinul lui n log n. &Prin log notam logaritmul intr-o baza oarecare, lg este logaritmul in baza

    /, iar ln este logaritmul natural'. 8eci, pentru cazul mediu, 6uic3sorteste foarte rapid.

    )naliza comportarii in medie a unui algoritm presupune cunoasterea a priori a distributiei probabiliste a cazurilor considerate. 8in aceastacauza, analiza pentru cazul mediu este, in general, mai greu de efecuat decat pentru cazul cel mai nefavorabil.

    )tunci cand nu vom specifica pentru ce caz analizam un algoritm, inseamna ca eficienta algoritmului nu depinde de acest aspect &ci doarde marimea cazului'.

  • 7/25/2019 bazele informaticii-c++

    10/311

    1.4 Operatie elementara

    9operatie elementaraeste o operatie al carei timp de e#ecutie poate fi marginit superior de o constanta depinzand doar de particularitateaimplementarii &calculator, limba de programare etc'. 8eoarece ne intereseaza timpul de e#ecutie in limita unei constante multiplicative,vom considera doar numarul operatiilor elementare e#ecutate intr-un algoritm, nu si timpul e#act de e#ecutie al operatiilor respective.

    $rmatorul e#emplu este testul lui Kilson de primalitate &teorema care sta la baza acestui test a fost formulata initial de 4eibniz in 7G1/,reluata de Kilson in 7

  • 7/25/2019 bazele informaticii-c++

    11/311

    Figura 1.1)lgoritmi sau +ard?are

    *a presupunem ca pentru rezolvarea unei anumite probleme avem un algoritm e#ponential si un calculator pe care, pentru cazuri demarime n, timpul de rulare este de 7;-X /nsecunde. Pentru nL 7;, este nevoie de 737; secunde. Pentru nL /;, sunt necesare aproape /minute. Pentru nL 6;, o zi nu este de auns, iar pentru nL 61, c+iar si un an ar fi insuficient. Cumparam un calculator de 7;; de ori mairapid, cu timpul de rulare de 7;-GX /nsecunde. 8ar si acum, pentru nL 0, este nevoie de mai mult de un anW In general, daca in cazul

    masinii vec+i intr-un timp anumit se putea rezolva problema pentru cazul n, pe noul calculator, in acest timp, se poate rezolva cazul n%

  • 7/25/2019 bazele informaticii-c++

    12/311

    Pentru cazuri de marime mica, uneori este totusi mai rentabil sa investim intr-o noua masina, nu si intr-un nou algoritm. )stfel, pentrunL 7;, pe masina vec+e, algoritmul nou necesita 7; secunde, adica de o suta de ori mai mult decat algoritmul vec+i. Pe vec+iul calculator,algoritmul nou devine mai performant doar pentru cazuri mai mari sau egale cu /;.

    1. E!emple

    Poate ca va intrebati daca este intr-adevar posibil sa acceleram atat de spectaculos un algoritm. aspunsul este afirmativ si vom da catevae#emple.

    1.6.1 Sortare

    )lgoritmii de sortare prin insertie si prin selectie necesita timp patratic, atat in cazul mediu, cat si in cazul cel mai nefavorabil. Cu toate caacesti algoritmi sunt e#celenti pentru cazuri mici, pentru cazuri mari avem algoritmi mai eficienti. In capitolele urmatoare vom analiza si

    alti algoritmi de sortare: heapsort, mergesort, 6uic3sort. (oti acestia necesita un timp mediu in ordinul lui n log n, iar heapsortsimergesortnecesita timp in ordinul lui n log nsi in cazul cel mai nefavorabil.

    Pentru a ne face o idee asupra diferentei dintre un timp patratic si un timp in ordinul lui n log n, vom mentiona ca, pe un anumitcalculator, 6uic3sorta reusit sa sorteze in 6; de secunde 7;;.;;; de elemente, in timp ce sortarea prin insertie ar fi durat, pentru acelasicaz, peste noua ore. Pentru un numar mic de elemente insa, eficienta celor doua sortari este asemanatoare.

    1.6.2 Calculul determinantilor

    =ie det& ' determinantul matricii

    L &ai4'i,4L 7, ..., n

    si fiei4submatricea de &n-7' X &n-7' elemente, obtinuta dinprin stergerea celei de-a i!a linii si celei de-a4!a coloane. )vembinecunoscuta definitie recursiva

  • 7/25/2019 bazele informaticii-c++

    13/311

    8aca folosim aceasta relatie pentru a evalua determinantul, obtinem un algoritm cu timp in ordinul lui nW, ceea ce este mai rau decate#ponential. 9 alta metoda clasica, eliminarea auss-@ordan, necesita timp cubic. Pentru o anumita implementare s-a estimat ca, in cazulunei matrici de /; X /; elemente, in timp ce algoritmul auss-@ordan dureaza 73/; secunde, algoritmul recursiv ar dura mai mult de 7;milioane de aniW

    u trebuie trasa de aici concluzia ca algoritmii recursivi sunt in mod necesar neperformanti. Cu autorul algoritmului recursiv al lui*trassen, pe care il vom studia si noi in *ectiunea

  • 7/25/2019 bazele informaticii-c++

    14/311

    1.6.4 Numerele lui Fibonacci

    *irul lui =ibonacci este definit prin urmatoarea recurenta:

    )cest celebru sir a fost descoperit in 7/;/ de catre 4eonardo Pisano &4eonardo din Pisa', cunoscut sub numele de 4eonardo =ibonacci.Cel de-al n!lea termen al sirului se poate obtine direct din definitie:

    function&ib7&n' ifn Q / then returnn else return&ib7&n-7' %&ib7&n-/'

    )ceasta metoda este foarte ineficienta, deoarece recalculeaza de mai multe ori aceleasi valori. om arata in *ectiunea 0.6.7 ca timpul este

    in ordinul lui n, unde L &7% '3/ estesectiunea de aur, deci este un timp e#ponential.

    Iata acum o alta metoda, mai performanta, care rezolva aceeasi problema intr-un timp liniar.

    function&ib/&n' i7T4 ; for3 7 tondo 4i %4 i4 - i return4

    Mai mult, e#ista si un algoritm cu timp in ordinul lui log n, algoritm pe care il vom argumenta insa abia in Capitolul < :

    function&ib6&n' i7T4 ;T 3;T h7 whilen U ; do ifneste impar then t4h 4ih%43%t ii3%t t h/

  • 7/25/2019 bazele informaticii-c++

    15/311

    h /3h%t 3 3/%t nndiv/ return4

    a recomandam sa comparati acesti trei algoritmi, pe calculator, pentru diferite valori ale lui n.

    1." E!ercitii

    1.1 )plicati algoritmii insertsiselectpentru cazurile L N7, /, 6, , 0, GO si 5 L NG, 0, , 6, /, 7O. )sigurati-va ca ati inteles cumfunctioneaza.

    1.2 Inmultirea Da la russeE este cunoscuta inca din timpul !giptului antic, fiind probabil un algoritm mai vec+i decat cel al lui !uclid.Incercati sa intelegeti rationamentul care sta la baza acestui algoritm de inmultire.

    Indicatie:=aceti legatura cu reprezentarea binara.

    1.3 In algoritmul)uclid, este important ca nm

    1. !laborati un algoritm care sa returneze cel mai mare divizor comun a trei intregi nenuli.

    !olutie:

    function)uclid!trei&m, n,p' return)uclid&m,)uclid&n,p''

    1.5 Programati algoritmul&ib7 in doua limbae diferite si rulati comparativ cele doua programe, pe mai multe cazuri. erificati dacaeste valabil principiul invariantei.

  • 7/25/2019 bazele informaticii-c++

    16/311

    1." !laborati un algoritm care returneaza cel mai mare divizor comun a doi termeni de rang oarecare din sirul lui =ibonacci.

    Indicatie:$n algoritm eficient se obtine folosind urmatoarea proprietateNYO , valabila pentru oricare doi termeni ai sirului lui =ibonacci:

    cmmdc&&m,&n' L&cmmdc&m, n'

    1.# =ie matricea . Calculati produsul vectorului &&n-7, &n' cu matriceam, unde&n-7si&nsunt doi termeni consecutivioarecare ai sirului lui =ibonacci.

    NYO )ceasta surprinzatoare proprietate a fost descoperita in 71

  • 7/25/2019 bazele informaticii-c++

    17/311

    In fata comple#itatii crescande a problemelor care se cereau solutionate, structurarea programelor a devenit indispensabila. *coala deprogramare )lgol a propus la inceputul anilor JG; o abordare devenita intre timp clasica. Conform celebrei ecuatii a lui iklaus Kirt+:

    algoritmi%structuri de dateLprograme

    un program este format din doua parti total separate: un ansamblu de proceduri si un ansamblu de date asupra carora actioneazaprocedurile. Procedurile sunt privite ca si cutii negre, fiecare avand de rezolvat o anumita sarcina &de facut anumite prelucrari'. )ceastamodalitate de programare se numesteprogramare diri4ata de prelucrari. !volutia calculatoarelor si a problemelor de programare a facutca in apro#imativ zece ani programarea diriata de prelucrari sa devina ineficienta. )stfel, c+iar daca un limba ca Pascal-ul permite obuna structurare a programului in proceduri, este posibil ca o sc+imbare relativ minora in structura datelor sa provoace o dezorganizaremaora a procedurilor.

    Inconvenientele programarii diriate de prelucrari sunt eliminate prin incapsulareadatelor si a procedurilor care le manipuleaza intr-osingura entitate numita obiect. 4umea e#terioara obiectului are acces la datele sau procedurile lui doar prin intermediul unor operatii careconstituie inter&ataobiectului. Programatorul nu este obligat sa cunoasca reprezentarea fizica a datelor si procedurilor utilizate, motivpentru care poate trata obiectul ca pe o cutie neagra cu un comportament bine precizat. )ceasta caracteristica permite realizarea unortipuri abstracte de date. !ste vorba de obiecte inzestrate cu o interfata prin care se specifica interactiunile cu e#teriorul, singuramodalitate de a comunica cu un astfel de obiect fiind invocarea interfetei sale. In terminologia specifica programarii orientate pe obiect,procedurile care formeaza interfata unui obiect se numesc metode. 9biectul este singurul responsabil de maniera in care se efectueazaoperatiile asupra lui. )pelul unei metode este doar o cerere, un mesa4al apelantului care solicita e#ecutarea unei anumite actiuni. 9biectulpoate refuza sa o e#ecute, sau, la fel de bine, o poate transmite unui alt obiect. In acest conte#t, programarea devine diri4ata de date, si nude prelucrarile care trebuie realizate.

    $tilizarea consecventa a obiectelor confera programarii urmatoarele calitati:

    -bstractiarea datelor. u este nevoie de a cunoaste implementarea si reprezentarea interna a unui obiect pentru a-i adresamesae. 9biectul decide singur maniera de e#ecutie a operatiei cerute in functie de implementarea fizica. !ste posibila supraincarcareametodelor, in sensul ca la aceleasi mesae, obiecte diferite raspund in mod diferit. 8e e#emplu, este foarte comod de a desemna printr-unsimbol unic, %, adunarea intregilor, concatenarea sirurilor de caractere, reuniunea multimilor etc.odularitate. *tructura programului este determinata in mare masura de obiectele utilizate. *c+imbarea definitiilor unor obiectese poate face cu un minim de implicatii asupra celorlalte obiecte utilizate in program.lexibilitate. $n obiect este definit prin comportamentul sau gratie e#istentei unei interfete e#plicite. !l poate fi foarte usorintrodus intr-o biblioteca pentru a fi utilizat ca atare, sau pentru a construi noi tipuri prin mostenire, adica prin specializare si compunerecu obiecte e#istente. Claritate. Incapsularea, posibilitatea de supraincarcare si modularitatea intaresc claritatea programelor. 8etaliile de implementare

  • 7/25/2019 bazele informaticii-c++

    18/311

    sunt izolate de lumea e#terioara, numele metodelor pot fi alese cat mai natural posibil, iar interfetele specifica precis si detaliat modul deutilizare al obiectului.

    2.2 #imba$ul C%%

    (oate limbaele de nivel inalt, de la =9() la 4I*P, permit adaptarea unui stil de programare orientat pe obiect, dar numai catevaofera mecanismele pentru utilizarea directa a obiectelor. 8in acest punct de vedere, mentionam doua mari categorii de limbae:

    4imbae care ofera doar facilitati de abstractizarea datelor si incapsulare, cum sunt )da si Modula-/. 8e e#emplu, in )da, datele siprocedurile care le manipuleaza pot fi grupate intr-un pac+et &package'. 4imbae orientate pe obiect, care adauga abstractizarii datelor notiunea de mostenire.

    8esi definitiile de mai sus restrang mult multimea limbaelor calificabile ca Dorientate pe obiectE, aceste limbae raman totusi foarte

    diverse, atat din punct de vedere al conceptelor folosite, cat si datorita modului de implementare. *-au conturat trei mari familii, fiecareaccentuand un anumit aspect al notiunii de obiect: limbae de clase, limbae de cadre &frames' si limbae de tip actor.

    4imbaul C%%NYO apartine familiei limbaelor de clase. 9 clasaeste un tip de date care descrie un ansamblu de obiecte cu aceeasi structurasi acelasi comportament. Clasele pot fi imbogatite si completate pentru a defini alte familii de obiecte. In acest mod se obtin ierar+ii declase din ce in ce mai specializate, care mostenescdatele si metodele claselor din care au fost create. 8in punct de vedere istoric primelelimbae de clase au fost *imula &7>

  • 7/25/2019 bazele informaticii-c++

    19/311

    float maxim( float, float );float x = maxim( 3, 2.5 );

    In acest e#emplu, functia maxim()este declarata ca o functie de tip floatcu doi parametri tot de tip float, motiv pentru careconstanta intreaga 3este convertita in momentul apelului la tipulfloat. 8eclaratia unei functii consta inprototipul &unctiei, carecontine tipul valorii returnate, numele functiei, numarul si tipul parametrilor. 8iferenta dintre de&initiesi declaratie notiuni valabile sipentru variabile consta in faptul ca definitia este o declaratie care provoaca si rezervare de spatiu sau generare de cod. 8eclararea uneivariabile se face prin precedarea obligatorie a definitiei de cuvantul c+eieextern. *i o declaratie de functie poate fi precedata decuvantul c+eie extern, accentuand astfel ca functia este definita altundeva.

    8efinirea unor functii foarte mici, pentru care procedura de apel tinde sa dureze mai mult decat e#ecutarea propriu-zisa, se realizeaza inlimbaul C%% prin functiile inline.

    inline float maxim( float x, float y ) {putchar( 'r' ); return x > y? x: y;

    *pecificarea inlineeste doar orientativa si indica compilatorului ca este preferabil de a inlocui fiecare apel cu corpul functiei apelate.!#pandarea unei functii inlinenu este o simpla substitutie de te#t in progamul sursa, deoarece se realizeaza prin pastrarea semanticiiapelului, deci inclusiv a verificarii corespondentei tipurilor parametrilor efectivi.

    Mecanismul de verificare a tipului lucreaza intr-un mod foarte fle#ibil, permitand atat e#istenta functiilor cu un numar variabil deargumente, cat si a celorsupraincarcate. *upraincarcarea permite e#istenta mai multor functii cu acelasi nume, dar cu paremetri diferiti.!liminarea ambiguitatii care apare in momentul apelului se rezolva pe baza numarului si tipului parametrilor efectivi. Iata, de e#emplu, oalta functie maxim():

    inline int maxim( int x, int y ) {putchar( 'i' ); return x > y? x: y;

    &Prin apelarea functiei putchar(), putem afla care din cele doua functii maxim()este efectiv invocata'.

    In limbaul C%% nu este obligatorie definirea variabilelor locale strict la inceputul blocului de instructiuni. In e#emplul de mai os, tabloul!uf si intregul ipot fi utilizate din momentul definirii si pana la sfarsitul blocului in care au fost definite.

  • 7/25/2019 bazele informaticii-c++

    20/311

    "#efine $%& 5

    oi# f( ) { int !uf $%& ;

    for ( int i = *; i + $%&; )

    !uf i = maxim( i, $%& - i ); hile ( --i ) printf( /03# /, !uf i );

    In legatura cu acest e#emplu, sa mai notam si faptul ca instructiunea forpermite c+iar definirea unor variabile &variabila iin cazulnostru'. ariabilele definite in instructiunea forpot fi utilizate la nivelul blocului acestei instructiuni si dupa terminarea e#ecutarii ei.

    8esi transmiterea parametrilor in limbaul C se face numai prin valoare, limbaul C%% autorizeaza in egala masura si transmiterea prinre&erinta. eferintele, indicate prin caracterul 1, permit accesarea in scriere a parametrilor efectivi, fara transmiterea lor prin adrese. Iataun e#emplu in care o procedura intersc+imba &s?ap' valorile argumentelor.

    oi# ap( float1 a, float1 ! ) { float tmp = a; a = !; ! = tmp;

    eferintele evita duplicarea provocata de transmiterea parametrilor prin valoare si sunt utile mai ales in cazul transmiterii unor structuri.8e e#emplu, presupunand e#istenta unei structuri de tip truct punct,

    truct punct { float x; 4 coor#onatele unui 4 float y; 4 punct #in plan 4;

    urmatoarea functie transforma un punct in simetricul lui fata de cea de a doua bisectoare.

    oi# im2( truct punct1 p ) { ap( p.x, p.y ); p.x i p.y e tranmit prin referinta i nu prin aloare

  • 7/25/2019 bazele informaticii-c++

    21/311

    p.x = -p.x; p.y = -p.y;

    Parametrii de tip referinta pot fi proteati de modificari accidentale prin declararea lor cont.

    oi# print( cont truct punct1 p ) { compilatorul interice orice tentatia #e a mo#ifica aria!ila p printf( /(06.7f, 06.7f) /, p.x, p.y );

    Caracterele indica faptul ca restul liniei curente este un comentariu. Pe langa aceasta modalitate noua de a introduce comentarii,limbaul C%% a preluat din limbaul C si posibiliatea incadrarii lor intre 3Y si Y3.

    )tributul contpoate fi asociat nu numai parametrilor formali, ci si unor definitii de variabile, a caror valoare este specificata inmomentul compilarii. )ceste variabile sunt variabile read-only &constante', deoarece nu mai pot fi modificate ulterior. In limbaul C,constantele pot fi definite doar prin intermediul directivei "#efine, care este o sursa foarte puternica de erori. )stfel, in e#emplul demai os, constanta intreaga dim este o variabila propriu-zisa accesibila doar in functia 8(). 8aca ar fi fost definita prin "#efine&vezisimbolul $%&utilizat in functia f()de mai sus' atunci orice identificator #im, care apare dupa directiva de definire si pana la sfarsitulfisierului sursa, este inlocuit cu valoarea respectiva, fara nici un fel de verificari sintactice.

    oi# 8( ) { cont int #im = 5; truct punct !uf #im ;

    for ( int i = *; i + #im; i ) { !uf i .x = i; !uf i .y = #im 2. - i;

    im2( !uf i ); print( !uf i );

    Pentru a obtine un prim program in C%%, nu avem decat sa adaugam obisnuitul

  • 7/25/2019 bazele informaticii-c++

    22/311

    "inclu#e +t#io.h>

    precum si functia main()

    int main( ) { put( /9n main./ );

    put( /9n f( )/ ); f( ); put( /9n 8( )/ ); 8( );

    put( /9n ---9n/ ); return *;

    ezultatele obtinute in urma rularii acestui program:

    rmain.

    f( )iiiii 6 3 3 6 8( )(-2.5, -*.*) (-7.5, -7.*) (-*.5, -2.*)( *.5, -3.*) ( 7.5, -6.*)---

    surprind prin faptul ca functia float maxim( float, float ) este invocata inaintea functiei main(). )cest lucru este normal,deoarece variabila x trebuie initializata inaintea lansarii in e#ecutie a functiei main().

    2.2.2 ntrari!iesiri in limbajul C++

    4imbaul C%% permite definirea tipurilor abstracte de date prin intermediul claselor. Clasele nu sunt altceva decat generalizari alestructurilor din limbaul C. !le contin date membre, adica variabile de tipuri predefinite sau definite de utilizator prin intermediul altorclase, precum si functii membre, reprezentand metodele clasei.

  • 7/25/2019 bazele informaticii-c++

    23/311

    Cele mai utilizate clase C%% sunt cele prin care se realizeaza intrarile si iesirile. eamintim ca in limbaul C, intrarile si iesirile se fac prinintermediul unor functii de biblioteca cum sunt canf()si printf(), functii care permit citirea sau scrierea numai a datelor&variabilelor' de tipuri predefinite &char, int, float etc.'. 5iblioteca standard asociata oricarui compilator C%%, contine ca suportpentru operatiile de intrare si iesire nu simple functii, ci un set de clase adaptabile c+iar si unor tipuri noi, definite de utilizator. )ceastabiblioteca este un e#emplu tipic pentru avantaele oferite de programarea orientata pe obiect. Pentru fi#area ideilor, vom folosi unprogram care determina termenul de rang nal sirului lui =ibonacci prin algoritmul&ib/ din *ectiunea 7.G. .

    "inclu#e +iotream.h>

    lon8 fi!2( int n ) { lon8 i = 7, = *;

    for ( int = *; + n; = i , i = - i ); return ;

    int main( ) { cout ++ /9n> n;

    cout ++ / ete / ++ fi!2( n ); cout ++ '9n';

    return *;

    5iblioteca standard C++contine definitiile unor clase care reprezinta diferite tipuri de &luxuri de comunicatie&tream-uri'. =iecare flu#poate fi de intrare, de iesire, sau de intrare/iesire. 9peratia primara pentru flu#ul de iesire este inserareade date, iar pentru cel de iesireeste extragereade date. =isierul prefi# &+eader' iotream.hcontine declaratiile flu#ului de intrare &clasaitream', ale flu#ului deiesire &clasa otream', precum si declaratiile obiectelorcinsi cout:

    extern itream cin;extern otream cout;

  • 7/25/2019 bazele informaticii-c++

    24/311

    9peratiile de inserare si e#tragere sunt realizate prin functiile membre ale claselor otreamsi itream. 8eoarece limbaul C%%permite e#istenta unor functii care supraincarca o parte din operatorii predefiniti, s-a convenit ca inserarea sa se faca prin supraincarcareaoperatorului de decalare la stanga QQ, iar e#tragerea prin supraincarcarea celui de decalare la dreapta UU. *emnificatia secventei deinstructiuni

    cin >> n; cout ++ / ete / ++ fi!2( n );

    este deci urmatoarea: se citeste valoarea lui n, apoi se afiseaza sirul / ete /urmat de valoarea returnata de functia fi!2().

    =lu#urile de comunicatie cinsicoutlucreaza in mod implicit cu terminalul utilizatorului. Ca si pentru programele scrise in C, esteposibila redirectarea lor spre alte dispozitive sau in diferite fisiere, in functie de dorinta utilizatorului. Pentru sistemele de operare $I2si 89*, redirectarile se indica adaugand comenzii de lansare in e#ecutie a programului, argumente de forma >nume-fiier-ieire,sau +nume-fiier-intrare. In iotream.hmai este definit inca un flu# de iesire numit cerr, utilizabil pentru semnalarea unorconditii de e#ceptie. =lu#ul cerreste legat de terminalul utilizatorului si nu poate fi redirectat.

    9peratorii de inserare &QQ' si e#tragere &UU' sunt, la randul lor, supraincarcati astfel incat operandul drept sa poata fi de orice tippredefinit. 8e e#emplu, in instructiunea

    cout ++ / ete / ++ fi!2( n );

    se va apela operatorul de inserare cu argumentul drept de tip char4. )cest operator, ca si toti operatorii de inserare si e#tragere,returneaza operandul stang, adica tream-ul. )stfel, invocarea a doua oara a operatorului de inserare are sens, de acesata dataalegandu-se cel cu argumentul drept de tiplon8. In prezent, biblioteca standard de intrare3iesire are in ur de ;;; de linii de cod, sicontine 70 alternative pentru fiecare din operatorii QQ si >>. Programatorul poate supraincarca in continuare acesti operatori pentru

    propriile tipuri.

    2.3 Clase in limba$ul C%%

    uland programul pentru determinarea termenilor din sirul lui =ibonacci cu valori din ce in ce mai mari ale lui n, se observa ca rezultatelenu mai pot fi reprezentate intr-un int, lon8sau uni8ne# lon8. *olutia care se impune este de a limita ranguln la valorirezonabile reprezentarii alese. Cu alte cuvinte,n nu mai este de tipint, ci de un tip care limiteaza valorile intregi la un anumit

  • 7/25/2019 bazele informaticii-c++

    25/311

    interval. om elabora o clasa corespunzatoare acestui tip de intregi, clasa utila multor programe in care se cere mentinerea unei valoriintre anumite limite.

    Clasa se numeste intral, si va fi implementata in doua variante. Prima varianta este realizata in limbaul C. u este o clasa propriu-zisa, ci o structura care confirma faptul ca orice limba permite adaptarea unui stil de programare orientat pe obiect si scoate in evidentainconvenientele generate de lipsa mecanismelor de manipulare a obiectelor. ) doua varianta este scrisa in limbaul C%%. !ste un adevarattip abstract ale carui calitati sunt si mai bine conturate prin comparatia cu &pseudo' tipul elaborat in C.

    2.3.1 "i#ul intErvalin limbajul C

    eprezentarea interna a tipului contine trei membri de tip intreg: marginile intervalului si valoarea propriu-zisa. 4e vom grupa intr-ostructura care, prin intermediul instructiunii typedef, devine sinonima cu intral.

    type#ef truct { int min; 4 mar8inea inferioara a interalului 4 int max; 4 mar8inea uperioara a interalului 4 int ; 4 aloarea, min += , + max 4 intral;

    ariabilele &obiectele' de tip intralse definesc folosind sinta#a uzuala din limbaul C.

    intral numar = { *, 32, @6 ;intral in#ice, limita;

    !fectul acestor definitii consta in rezervarea de spatiu pentru fiecare din datele membre ale obiectelor numar, in#icesilimita. Inplus, datele membre din

    numarsunt initializate cu valorile 1; &

    min', 6/ &

    max' si G &

    '. Initializarea, desi corecta din punct de vedere

    sintactic, face imposibla functionarea tipului intral, deoarece marginea inferioara nu este mai mica decat cea superioara.8eocamdata nu avem nici un mecanism pentru a evita astfel de situatii.

    Pentru manipularea obiectelor de tip intral, putem folosi atribuiri la nivel de structura:

    limita = numar;

    )stfel de atribuiri se numesc atribuiri membru cu membru, deoarece sunt realizate intre datele membre corespunzatoare celor douaobiecte implicate in atribuire.

  • 7/25/2019 bazele informaticii-c++

    26/311

    9 alta posibilitate este accesul direct la membri:

    in#ice.min = 32; in#ice.max = @6;in#ice. = numar. 7;

    *electarea directa a membrilor incalca proprietatile fundamentale ale obiectelor. eamintim ca un obiect este manipulat e#clusiv prin

    interfata sa, structura lui interna fiind in general inaccesibila.

    Comportamentul obiectelor este realizat printr-un set de metode implementate in limbaul C ca functii. Pentru intral, acestea trebuiesa permita in primul rand selectarea, atat in scriere cat si in citire, a valorii propriu-zise date de membrul . =unctia de scriere atr()verifica incadrarea noii valori in domeniul admisibil, iar functia de citire al()pur si simplu returneaza valoarea . Practic, aceste douafunctii implementeaza o forma de incapsulare, izoland reprezentarea interna a obiectului de restul programului.

    int atr( intral 4pn, int i ) { return pn-> = er$om( 4pn, i );

    int al( intral n ) { return n.;

    =unctia er$om()verifica incadrarea in domeniul admisibil:

    int er$om( intral n, int i ) { if ( i + n.min AA i >= n.max ) { fput( /9n9nintral -- aloare exterioara.9n9n/, t#err); exit( 7 ); return i;

    $tilizand consecvent cele doua metode ale tipului intral, obtinem obiecte ale caror valori sunt cu certitudine intre limiteleadmisibile. 8e e#emplu, utilizand metodele atr()si al(), instructiunea

    in#ice. = numar. 7;

  • 7/25/2019 bazele informaticii-c++

    27/311

    devine

    atr( 1in#ice, al( numar ) 7 );

    8eoarece numarare valoarea G, iar domeniul in#ice-lui este 6/, ..., G, instructiunea de mai sus semnaleaza depasirea domeniuluivariabileiin#icesi provoaca terminarea e#ecutarii programului.

    )ceasta implementare este departe de a fi completa si comod de utilizat. u ne referim acum la aspecte cum ar fi citirea &sau scrierea'obiectelor de tip intral, operatie rezolvabila printr-o functie de genul

    oi# cit( intral 4pn ) { int i; canf( /0#/, 1i ); atr( pn, i );

    ci la altele, mult mai delicate, cum ar fi:

    I1 !vitarea unor initializari eronate din punct de vedere semantic si interzicerea utilizarii obiectelor neinitializate:

    intral numar = {*,32,@6; o!iect incorect initialiatintral in#ice, limita; o!iecte neinitialiate

    I2 Interzicerea modificarii necontrolate a datelor membre:

    in#ice. = numar. 7;

    I3 *inta#a foarte incarcata, diferita de sinta#a obisnuita in manipularea tipurilor intregi predefinite.

    In concluzie, aceasta implementare, in loc sa ne simplifice activitatea de programare, mai mult a complicat-o. Cauza nu este insaconceperea gresita a tipului intral, ci lipsa facilitatilor de manipulare a obiectelor din limbaul C.

  • 7/25/2019 bazele informaticii-c++

    28/311

    2.3.2 "i#ul int$rval in limbajul C++

    Clasele se obtin prin completarea structurilor uzuale din limbaul C cu setul de functii necesar implementarii interfetei obiectului. In plus,pentru realizarea izolarii reprezentarii interne de restul programului, fiecarui membru i se asociaza nivelul de incapsulare pu!licsaupriate. $n membru pu!liccorespunde, din punct de vedere al nivelului de accesibilitate, membrilor structurilor din limbaul C.Membrii

    priatesunt accesibili doar in domeniul clasei, adica in clasa propriu-zisa si in toate functiile membre. In clasa

    intral,

    membrii publici sunt doar functiile atr()sial(), iar membrii er$om(), min, maxsi sunt privati.

    cla intral {pu!lic: int atr( int ); int al( ) { return ;

    priate: int er$om( int );

    int min, max; int ;;

    9biectele de tip intralse definesc ca si in limbaul C.

    intral numar;intral in#ice, limita;

    )ceste obiecte pot fi atribuite intre ele &fiind structuri atribuirea se va face membru cu membru':

    limita = numar;

    si pot fi initializate &tot membru cu membru' cu un obiect de acelasi tip:

    intral co# = numar;

    *electarea membrilor se face prin notatiile utilizate pentru structuri. 8e e#emplu, dupa e#ecutarea instructiunii

  • 7/25/2019 bazele informaticii-c++

    29/311

    in#ice.atr( numar.al( ) 7 );

    valoarea obiectului in#iceva fi valoarea obiectului numar,incrementata cu 7. )ceasta operatie poate fi descrisa si prin intructiunea

    in#ice. = numar. 7;

    care, desi corecta din punct de vedere sintactic, este incorecta semantic, deoareceeste un membru priate, deci inaccesibil prinintermediul obiectelor in#icesi numar.

    8upa cum se observa, au disparut argumentele de tip intral4si intralale functiilor atr(), respectiv al(). Cauza estefaptul ca functiile membre au un argument implicit, concretizat in obiectul invocator, adica obiectul care selecteaza functia. !ste oconventie care intareste si mai mult atributul de functie membra &metoda' deoarece permite invocarea unei astfel de functii numai prinobiectul respectiv.

    8efinirea functiilor membre se poate face fie in corpul clasei, fie in e#teriorul acestuia. =unctiile definite in corpul clasei sunt considerateimplicit

    inline, iar pentru cele definite in e#teriorul corpului se impune precizarea statutului de functie membra. Inainte de a defini

    functiile atr()si er$om(), sa observam ca functia al(), definita in corpul clasei intral, incalca de doua ori cele precizatepana aici. In primul rand, nu selecteaza membrul prin intermediul unui obiect, iar in al doilea rand, este privatW 8aca functia al()ar fi fost o functie obisnuita, atunci observatia ar fi fost cat se poate de corecta. 8ar al()este functie membra si atunci:

    u poate fi apelata decat prin intermediul unui obiect invocator si toti membrii utilizati sunt membrii obiectului invocator.

    Incapsularea unui membru functioneaza doar in e#teriorul domeniului clasei. =unctiile membre f ac parte din acest domeniu si au acces la toti membrii, indiferent de nivelul lor de incapsulare.

    *pecificarea atributului de functie membra se face precedand numele functiei de operatorul domeniu :: si de numele domeniului, care estec+iar numele clasei. Pentru asigurarea consistentei clasei, functiile membre definite in e#terior trebuie obligatoriu declarate in corpul

    clasei.

    int intral::er$om( int i ) { if ( i + min AA i >= max ) { cerr ++ /9n9nintral -- / ++ i ++ /: aloare exterioara #omeniului / ++ min ++ /, / ++ (max - 7) ++ / .9n9n/; exit( 7 );

  • 7/25/2019 bazele informaticii-c++

    30/311

    return i;

    int intral::atr( int i ) { return = er$om( i ); er$om(), fiin# mem!ru ca i , e a inoca pentru

    o!iectul inocator al functiei atr()

    8in cele trei inconveniente mentionate in finalul *ectiunii /.6.7 am rezolvat, pana in acest moment, doar inconvenientul I /, cel care serefera la incapsularea datelor. In continuare ne vom ocupa de I6, adica de simplificarea sinta#ei.

    4imbaul C%% permite nu numai supraincarcarea functiilor, ci si a maoritatii operatorilor predefiniti. In general, sunt posibile douamodalitati de supraincarcare:

    Ca functii membre, caz in care operandul stang este implicit obiect invocator.

    Ca functii nemembre, dar cu conditia ca cel putin un argument &operand' sa fie de tip clasa.

    Pentru clasa intral, ne intereseaza in primul rand operatorul de atribuire &implementat deocamdata prin functia atr()' si unoperator care sa corespunda functiei al(). 8esi pare surprinzator, functia al()nu face altceva decat sa converteasca tipulintralla tipul int. In consecinta, vom implementa aceasta functie ca operator de conversie la int. In noua sa forma, clasaintralarata astfel:

    cla intral {pu!lic:

    operatorul #e atri!uire corepunator functiei atr() int operator =( int i ) { return = er$om( i );

    operatorul #e conerie corepunator functiei al() operator int( ) { return ;

    priate: int er$om( int );

  • 7/25/2019 bazele informaticii-c++

    31/311

    int min, max; int ;;

    evenind la obiectele in#icesi numar, putem scrie acum

    in#ice = (int)numar 7;

    sau direct

    in#ice = numar 7;

    conversia numar-ului la intfiind invocata automat de catre compilator. u este nimic miraculos in aceasta invocare DautomataE,deoarece operatorul % nu este definit pentru argumente de tip intralsi int, dar este definit pentru intsi int. )ltfel spus, e#presianumar 7poate fi evaluata printr-o simpla conversie a primului operand de la intralla int.

    9 alta functie utila tipului intraleste cea de citire a valorii , functie denumita in paragraful precedent cit(). e propunem sa oinlocuim cu operatorul de e#tragere UU, pentru a putea scrie direct cin >> numar. *upraincarcarea operatorului UU ca functie membranu este posibila, deoarece argumentul stang este obiectul invocator si atunci ar trebui sa scriem nUU cin.

    9peratorul de e#tragere necesar pentru citirea valorii obiectelor de tip intralse poate defini astfel:

    itream1 operator >>( itream1 i, intral1 n ) { int i; if ( i >> i ) e citete aloarea

    n = i; e inoca operatorul #e atri!uire return i;

    *unt doua intrebari la care trebuie sa raspundem referitor la functia de mai sus:

    Care este semnificatia testului if ( i >> i ) 8e ce se returneazaitream-ul

  • 7/25/2019 bazele informaticii-c++

    32/311

    In testul if ( i >> i )se invoca de fapt operatorul de conversie de la itreamla int, rezultatul fiind valoarea logica true&valoare diferita de zero' sau&alse&valoarea zero', dupa cum operatia a decurs normal sau nu.

    eturnarea itream-ului este o modalitate de a aplica operatorului UU sinta#a de concatenare, sinta#a utilizata in e#presii de formai = = *. 8e e#emplu, obiectele numarsi in#icede tip intral, pot fi citite printr-o singura instructiune

    cin >> numar >> in#ice;

    8e asemenea, remarcam si utilizarea absolut ustificata a argumentelor de tip referinta. In lipsa lor, obiectul numarar fi putut sa fiemodificat doar daca i-am fi transmis adresa. In plus, utilizarea sinta#ei de concatenare provoaca, in lipsa referintelor, multiplicareaargumentului de tip itreamde doua ori pentru fiecare apel: prima data ca argument efectiv, iar a doua oara ca valoare returnata.

    Clasa intrala devenit o clasa comod de utilizat, foarte bine incapsulata si cu un comportament similar intregilor. Incapsularea esteinsa atat de buna, incat, practic, nu avem nici o modalitate de a initializa limitele superioara si inferioara ale domeniului admisibil. 8efapt, am revenit la inconvenientul I7mentionat in finalul *ectiunii /.6.7 . Problema initializarii datelor membre in momentul definirii

    obiectelor nu este specifica doar clasei intral. Pentru rezolvarea ei, limbaul C%% ofera o categorie speciala de functii membre,numite constructori. Constructorii nu au tip, au numele identic cu numele clasei si sunt invocati automat de catre compilator, duparezervarea spatiului pentru datele obiectului definit.

    Constructorul necesar clasei intralare ca argumente limitele domeniului admisibil. (ransmiterea lor se poate face implicit, prinnotatia

    intral numar( *, 32 );

    sau e#plicit, prin specificarea constructorului

    intral numar = intral( *, 32 );

    8efinitia acestui constructor este

    intral::intral( int up, int inf ) { if ( inf >= up ) { cerr ++ /9n9nintral -- #omeniu incorect pecificat / ++ inf ++ /, / ++ (up - 7) ++ / .9n9n/; exit( 7 );

  • 7/25/2019 bazele informaticii-c++

    33/311

    min = = inf; max = up;

    8atorita lipsei unui constructor fara argumente, compilatorul va interzice orice declaratii in care nu se specifica domeniul. 8e e#emplu,

    intral in#ice;

    este o definitie incompleta, semnalata la compilare. Mai mult, definitiile incorecte semantic cum este

    intral limita( 32, * );

    sunt si ele detectate, dar nu de catre compilator, ci de catre constructor. )cesta, dupa cum se observa, verifica daca limita inferioara adomeniului este mai mica decat cea superioara, semnaland corespunzator domeniile incorect specificate.

    In declaratiile functiilor, limbaul C%% permite specificarea valorilor implicite ale argumentelor, valori utilizabile in situatiile in care nu sespecifica toti parametrii efectivi. )ceasta facilitate este utila si in cazul constructorului clasei intral. Prin declaratia

    intral( int = 7, int = * );

    definitia

    intral in#ice;

    nu va mai fi respinsa, ci va provoca invocarea constructorului cu argumentele implicite 7si *. Corespondenta dintre argumentele actuale

    si cele formale se realizeaza pozitional, ceea ce inseamna ca primul argument este asociat limitei superioare, iar cel de-al doilea celeiinferioare. =recvent, limita inferioara are valoarea implicita zero. 8eci la transmiterea argumentelor constructorului, ne putem limita doarla precizarea limitei superioare.

    Constructorul apelabil fara nici un argument se numeste constructor implicit. )ltfel spus, constructorul implicit este constructorul care, fienu are argumente, fie are toate argumentele implicite. 4imbaul C%% nu impune prezenta unui constructor implicit in fiecare clasa, darsunt anumite situatii in care acest constructor este absolut necesar.

    8upa aceste ultime precizari, definitia clasei intraleste:

  • 7/25/2019 bazele informaticii-c++

    34/311

    cla intral {pu!lic: intral( int = 7, int = * ); Bintral( ) {

    int operator =( int i ) { return = er$om( i );

    operator int( ) { return ;

    priate: int er$om( int );

    int min, max; int ;;

    *e observa aparitia unei noi functii membre, numita Bintral(), al carui corp este vid. !a se numeste destructor, nu are tip si niciargumente, iar numele ei este obtinut prin precedarea numelui clasei de caracterul Z. olul destructorului este opus celui alconstructorului, in sensul ca realizeaza operatiile necesare distrugerii corecte a obiectului. 8estructorul este invocat automat, inainte de aelibera spatiul alocat datelor membre ale obiectului care inceteaza sa mai e#iste. $n obiect inceteaza sa mai e#iste in urmatoarele situatii:

    9biectele definite intr-o functie sau bloc de instructiuni &obiecte cu existenta locala' inceteaza sa mai e#iste la terminareae#ecutarii functiei sau blocului respectiv. 9biectele definite global, in e#teriorul oricarei functii, sau cele definite tatic&obiecte cu existenta statica' inceteaza sa maie#iste la terminarea programului. 9biectele alocate dinamic prin operatorul ne? &obiecte cu existenta dinamica' inceteaza sa mai e#iste la invocarea operatorului#elete.

    Ca si in cazul constructorilor, prezenta destructorului intr-o clasa este optionala, fiind lasata la latitudinea proiectantului clasei.

    Pentru a putea fi inclusa in toate fisierele sursa in care este utilizata, definitia unei clase se introduce intr-un fisier +eader &prefi#'. Inscopul evitarii includerii de mai multe ori a aceluiasi fisier &includeri multiple', se recomanda ca fisierele +eader sa aiba structura

    "ifn#ef im!ol"#efine im!ol

  • 7/25/2019 bazele informaticii-c++

    35/311

    continutul fiierului

    "en#if

    unde im!oleste un identificator unic in program. 8aca fisierul a fost dea inclus, atunci identificatorul im!oleste dea definit, sideci, toate liniile situate intre "ifn#efsi "en#ifvor fi ignorate. 8e e#emplu, in fisierul intral.h, care contine definitia claseiintral, identificatorul im!olar putea fi CC%D

  • 7/25/2019 bazele informaticii-c++

    36/311

    "inclu#e /intral.h/"inclu#e +t#li!.h>

    intral::intral( int up, int inf ) { if ( inf >= up ) { cerr ++ /9n9nintral -- #omeniu incorect pecificat /

    ++ inf ++ /, / ++ (up - 7) ++ / .9n9n/; exit( 7 ); min = = inf; max = up;

    int intral::er$om( int i ) { if ( i + min AA i >= max ) { cerr ++ /9n9nintral -- /

    ++ i ++ /: aloare exterioara #omeniului / ++ min ++ /, / ++ (max - 7) ++ / .9n9n/; exit( 7 ); return i;

    itream1 operator >>( itream1 i, intral1 n ) { int i; if ( i >> i ) e citete aloarea

    n = i; e inoca operatorul #e atri!uire return i;

    )daptarea programului pentru determinarea termenilor sirului lui =ibonacci necesita doar includerea fisierului intral.h, precum sisc+imbarea definitiei rangului ndinintin intral.

    "inclu#e +iotream.h>"inclu#e /intral.h/

  • 7/25/2019 bazele informaticii-c++

    37/311

    lon8 fi!2( int n ) { lon8 i = 7, = *;

    for ( int = *; + n; = i , i = - i ); return ;

    int main( ) { cout ++ /9n> n; cout ++ / ete / ++ fi!2( n ); cout ++ '9n';

    return *;

    8esigur ca, la programul e#ecutabil, se va lega si fisierul rezultat in urma compilarii definitiilor functiilor membre din clasa intral.

    econcordanta dintre argumentul formal de tip intdin fi!2()si argumentul efectiv &actual' de tip intralse rezolva, de catrecompilator, prin invocarea operatorului de conversie de la intralla int.

    Programarea orientata pe obiect este deosebit de avantaoasa in cazul aplicatiilor mari, dezvoltate de ec+ipe intregi de programatori peparcursul catorva luni, sau c+iar ani. )plicatia prezentata aici este mult prea mica pentru a putea fi folosita ca un argument in favoareaacestei te+nici de programare. Cu toate acestea, comparand cele doua implementari ale clasei intral&in limbaele C, respectiv C%%',sunt dea evidente doua avantae ale programarii orientate pe obiect:

    In primul rand, este posibilitatea dezvoltarii unor tipuri noi, definite e#clusiv prin comportament si nu prin structura. Codul sursaeste mai compact, dar in nici un caz mai rapid decat in situatia in care nu am fi folosit obiecte. *a retinem ca programarea orientata peobiect nu este o modalitate de a micsora timpul de e#ecutie, ci de a spori eficienta activitatii de programare. In al doilea rand, se remarca posibilitatile de a supraincarca operatori, inclusiv pe cei de conversie. !fectul este foarte spectaculos,deoarece utilizarea noilor tipuri este la fel de comoda ca si utilizarea tipurilor predefinite. Pentru tipul intral, aceste avantae seconcretizeaza in faptul ca obiectele de tip intralse comporta e#act ca si cele de tip int, incadrarea lor in limitele domeniuluiadmisibil fiind absolut garantata.

  • 7/25/2019 bazele informaticii-c++

    38/311

    2.4 E!ercitii&''(

    2.1 *crieti un program care determina termenul de rang nal sirului lui =ibonacci prin algoritmii&ib7 si&ib6.

    2.2 Care sunt valorile ma#ime ale lui npentru care algoritmii&ib7,&ib/ si&ib6 returneaza valori corecte Cum pot fi marite acestevalori

    !olutie:Presupunand ca unlon8este reprezentat pe octeti, atunci cel mai mare numar =ibonacci reprezentabil pe lon8este cel curangul G. 4ucrand pe uni8ne# lon8, se poate aunge pana la termenul de rang

  • 7/25/2019 bazele informaticii-c++

    39/311

    !olutie:$tilizarea definitiei pentru calculul combinarilor este o idee total neinspirata, nu numai in ceea ce priveste eficienta, ci si pentrufaptul ca nu poate fi aplicata decat pentru valori foarte mici ale lui n. 8e e#emplu, intr-unlon8de octeti, valoarea 76W nu mai poate ficalculata. =unctia recursiva este simpla:

    int K( int n, int m) { return m == * AA

    m == n? 7: K( n - 7, m - 7 ) K( n - 7, m );

    dar si ineficienta, deoarece numarul apelurilor recursive este foarte mare &vezi !#ercitiul 1.7'. Programul complet este:

    "inclu#e +iotream.h>

    cont int D = 7@, & = 7J;

    int rD&; contorieaa numarul #e apeluri ale

    functiei K( int, int ) eparat, pentru toate alorile ar8umentelor

    lon8 tr; numarul total #e apeluri ale functiei K( int, int )

    int K( int n, int m ) { rnm; tr; return m == * AA m == n? 7: K( n - 7, m - 7 ) K( n - 7, m );

    oi# main( ) { int n, m; for ( n = *; n + D; n ) for ( m = *; m + &; m ) rnm = *; tr = *;

    cout ++ /9nKom!inari #e (maxim / ++ D ++ /) ... /; cin >> n;

  • 7/25/2019 bazele informaticii-c++

    40/311

    cout ++ / luate cate ... /; cin >> m; cout ++ /unt / ++ K( n, m ) ++ '9n';

    cout ++ /9n9nK( int, int ) a fot inocata #e / ++ tr ++ / ori atfel:9n/;

    for ( int i = 7; i += n; i, cout ++ '9n' ) for ( int = *; += i; ) { cout.i#th( 6 ); cout ++ ri ++ ' ';

    ezultatele obtinute in urma rularii sunt urmatoarele:

    Kom!inari #e (maxim 7@) ...72

    luate cate ...Junt JL2

    K( int, int ) a fot inocata #e 753 ori atfel:27* 27* 6 27* 72@ 2 6 72@ J* J 2 5@ J* 35 7 J 27 35 35 75 * 7 @ 75 2* 75 5

    * * 7 5 7* 7* 5 7 * * * 7 6 @ 6 7 * * * * * 7 3 3 7 * * * * * * * 7 2 7 * * * * * * * * * 7 7 * * * * * * * * * * * 7 * * * ...

    *e observa ca K(7,7)a fost invocata de /7; ori, iar K(2,2)de 7/G de oriW

  • 7/25/2019 bazele informaticii-c++

    41/311

    NYO 4imba dezvoltat de 5arne *troustrup la inceputul anilor J1;, in cadrul laboratoarelor 5ell de la )([(, ca o e#tindere orientata peobiect a limbaului C.

    NYYO C+iar daca nu se precizeaza e#plicit, toate implementarile se vor realiza in limbaul C%%.

    3. !tructuri elementare de date

    Inainte de a elabora un algoritm, trebuie sa ne gandim la modul in care reprezentam datele. In acest capitol vom trece in revista structurilefundamentale de date cu care vom opera. Presupunem in continuare ca sunteti dea familiarizati cu notiunile de fisier, tablou, lista, graf,arbore si ne vom concentra mai ales pe prezentarea unor concepte mai particulare: +eap-uri si structuri de multimi disuncte.

    3.1 #iste

    9listaeste o colectie de elemente de informatie &noduri' aranate intr-o anumita ordine. 9ungimeaunei liste este numarul de noduri dinlista. *tructura corespunzatoare de date trebuie sa ne permita sa determinam eficient care este primul3ultimul nod in structura si care estepredecesorul3succesorul &daca e#ista' unui nod dat. Iata cum arata cea mai simpla lista, lista liniara:

    9 lista circulara este o lista in care, dupa ultimul nod, urmeaza primul, deci fiecare nod are succesor si predecesor.

    9peratii curente care se fac in liste sunt: inserarea unui nod, stergerea &e#tragerea' unui nod, concatenarea unor liste, numarareaelementelor unei liste etc. Implementarea unei liste se poate face in principal in doua moduri:

    Implementarea secventiala, in locatii succesive de memorie, conform ordinii nodurilor in lista. )vantaele acestei te+nici sunt accesul rapid la predecesorul3succesorul unui nod si gasirea rapida a primului3ultimului nod.8ezavantaele sunt inserarea3stergerea relativ complicata a unui nod si faptul ca, in general, nu se foloseste intreaga memorie alocata listei.

    http://vega.unitbv.ro/~andonie/Cartea%20de%20algoritmi/cap2.htm#_ftnref1%23_ftnref1http://vega.unitbv.ro/~andonie/Cartea%20de%20algoritmi/cap2.htm#_ftnref2%23_ftnref2http://vega.unitbv.ro/~andonie/Cartea%20de%20algoritmi/cap2.htm#_ftnref1%23_ftnref1http://vega.unitbv.ro/~andonie/Cartea%20de%20algoritmi/cap2.htm#_ftnref2%23_ftnref2
  • 7/25/2019 bazele informaticii-c++

    42/311

    Implementarea inlantuita. In acest caz, fiecare nod contine doua parti: informatia propriu-zisa si adresa nodului succesor. )locarea memoriei fiecarui nod se poate face in mod dinamic, in timpul rularii programului. )ccesul laun nod necesita parcurgerea tuturor predecesorilor sai, ceea ce poate lua ceva mai mult timp. Inserarea3stergerea unui nod este in sc+imb foarte rapida. *e pot folosi doua adrese in loc de una, astfel incat un nod sa contina pe

    langa adresa nodului succesor si adresa nodului predecesor. 9btinem astfel o lista dublu inlantuita, care poate fi traversata in ambele directii.

    4istele implementate inlantuit pot fi reprezentate cel mai simplu prin tablouri. In acest caz, adresele sunt de fapt indici de tablou. 9alternativa este sa folosim tablouri paralele: sa memoram informatia fiecarui nod &valoarea' intr-o locatie -9NiO a tabloului -9N7 .. nO,

    iar adresa &indicele' nodului sau succesor intr-o locatie9I;NiO a tabloului9I;N7 .. nO. Indicele de tablou al locatiei primului nod estememorat in variabila head. om conveni ca, pentru cazul listei vide, sa avem head + ;. Convenim de asemenea ca9I;Nultimul nod dinlistaO L ;. )tunci, -9NheadO va contine informatia primului nod al listei,9I;NheadO adresa celui de-al doilea nod, -9N9I;NheadOOinformatia din al doilea nod,9I;N9I;NheadOO adresa celui de-al treilea nod etc.

    )cest mod de reprezentare este simplu dar, la o analiza mai atenta, apare o problema esentiala: cea a gestionarii locatiilor libere. 9 solutieeleganta este sa reprezentam locatiile libere tot sub forma unei liste inlantuite. )tunci, stergerea unui nod din lista initiala implicainserarea sa in lista cu locatii libere, iar inserarea unui nod in lista initiala implica stergerea sa din lista cu locatii libere. )spectul cel maiinteresant este ca, pentru implementarea listei de locatii libere, putem folosi aceleasi tablouri. )vem nevoie de o alta variabila,&reehead,care va contine indicele primei locatii libere din -9si9I;. =olosim aceleasi conventii: daca&reehead L ; inseamna ca nu mai avem

    locatii libere, iar9I;Nultima locatie liberaO L ;.

    om descrie in continuare doua tipuri de liste particulare foarte des folosite.

    3.1.1 Stive

    9stiva&stac3' este o lista liniara cu proprietatea ca operatiile de inserare3e#tragere a nodurilor se fac in3din coada listei. 8aca nodurile ),5, C, 8 sunt inserate intr-o stiva in aceasta ordine, atunci primul nod care poate fi e#tras este 8. In mod ec+ivalent, spunem ca ultimulnod inserat va fi si primul sters. 8in acest motiv, stivele se mai numesc si liste9I

  • 7/25/2019 bazele informaticii-c++

    43/311

    >NtopO x returnDsuccesE

    functionpop&>N7 .. nO' Rsterge ultimul nod inserat din stiva si il returneazaS iftop; then returnDstiva vidaE x>NtopO toptop-7 returnx

    Cei doi algoritmi necesita timp constant, deci nu depind de marimea stivei.

    om da un e#emplu elementar de utilizare a unei stive. 8aca avem de calculat e#presia aritmetica

    0&&&>%1'&G''%'Tpush&1'Tpush&pop %pop'Tpush&'Tpush&G'Tpush&pop pop'Tpush&pop pop'Tpush&

  • 7/25/2019 bazele informaticii-c++

    44/311

    9 reprezentare secventiala interesanta pentru o coada se obtine prin utilizarea unui tablou CN; .. n-7O, pe care il tratam ca si cum ar ficircular: dupa locatia CNn-7O urmeaza locatia CN;O. =ie tailvariabila care contine indicele locatiei predecesoare primei locatii ocupate sifie headvariabila care contine indicele locatiei ocupate ultima oara. ariabilele headsi tailau aceeasi valoare atunci si numai atunci candcoada este vida. Initial, avem head L tailL ;. Inserarea si stergerea &e#tragerea' unui nod necesita timp constant.

    functioninsert!6ueue&x, CN; .. n-7O'

    Radauga nodulxin capul coziiS head&head%7' modn ifheadL tail then returnDcoada plinaE CNheadO x returnDsuccesE

    functiondelete!6ueue&CN; .. n-7O' Rsterge nodul din coada listei si il returneazaS ifheadL tailthen returnDcoada vidaE tail&tail%7' modn xCNtailO returnx

    !ste surprinzator faptul ca testul de coada vida este acelasi cu testul de coada plina. 8aca am folosi toate cele nlocatii, atunci nu am puteadistinge intre situatia de Dcoada plinaE si cea de Dcoada vidaE, deoarece in ambele situatii am avea head L tail. In consecinta, se folosescefectiv numai n-7 locatii din cele nale tabloului C, deci se pot implementa astfel cozi cu cel mult n-7 noduri.

    3.2 )rafuri

    $ngra&este o perec+e ?L Q,U, unde este o multime de varfuri, iareste o multime de muc+ii. 9 muc+ie de la varful ala varful beste notata cu perec+ea ordonata &a, b', daca graful este orientat, si cu multimea Ra, bS, daca graful este neorientat. In cele ceurmeaza vom presupune ca varfurile asi bsunt diferite. 8oua varfuri unite printr-o muc+ie se numesc adiacente. $n drum este osuccesiune de muc+ii de forma

    &a7, a/', &a/, a6', ..., &an-7, an'sau de forma Ra7, a/S, Ra/, a6S, ..., Ran-7, anS

  • 7/25/2019 bazele informaticii-c++

    45/311

    dupa cum graful este orientat sau neorientat.9ungimeadrumului este egala cu numarul muc+iilor care il constituie. $n drumsimpluesteun drum in care nici un varf nu se repeta. $n ciclueste un drum care este simplu, cu e#ceptia primului si ultimului varf, care coincid. $ngra&acicliceste un graf fara cicluri. $nsubgra&al lui?este un graf Q@,@U, unde @, iar@este formata din muc+iile dincareunesc varfuri din @. $ngra& partialeste un graf Q,AU, undeA.

    $n graf neorientat este conex, daca intre oricare doua varfuri e#ista un drum. Pentru grafuri orientate, aceasta notiune este intarita: un graforientat este tareconex, daca intre oricare doua varfuri isi4e#ista un drum de la ila4si un drum de la4la i.

    In cazul unui graf necone#, se pune problema determinarii componentelor sale cone#e. 9 componenta conexaeste un subgraf cone#ma#imal, adica un subgraf cone# in care nici un varf din subgraf nu este unit cu unul din afara printr-o muc+ie a grafului initial.Impartirea unui graf ?L Q,U in componentele sale cone#e determina o partitie a lui si una a lui.

    $n arboreeste un graf neorientat aciclic cone#. *au, ec+ivalent, un arbore este un graf neorientat in care e#ista e#act un drum intreoricare doua varfuriNO .$n graf partial care este arbore se numeste arbore partial.

    arfurilor unui graf li se pot atasa informatii numite uneori valori, iar muc+iilor li se pot atasa informatii numite uneori lungimisaucosturi.

    !#ista cel putin trei moduri evidente de reprezentare ale unui graf:

    Printr-omatrice de adiacenta -, in care-Ni,4O L truedaca varfurile isi4sunt adiacente, iar-Ni,4O L&alsein caz contrar. 9 varianta alternativa este sa-i dam lui-Ni,4O valoarea lungimii muc+iei dintre varfurile isi4,considerand-Ni,4O L %atunci cand cele doua varfuri nu sunt adiacente. Memoria necesara este in ordinul lui n/. Cu aceasta reprezentare, putem verifica usor daca doua varfuri sunt adiacente. Pe de alta parte, daca dorim sa

    aflam toate varfurile adiacente unui varf dat, trebuie sa analizam o intreaga linie din matrice. )ceasta necesita noperatii &unde neste numarul de varfuri in graf', independent de numarul de muc+ii care conecteaza varful

    respectiv.

    Prinliste de adiacenta, adica prin atasarea la fiecare varf ia listei de varfuri adiacente lui &pentru grafuri orientate, este necesar ca muc+ia sa plece din i'. Intr-un graf cu mmuc+ii, suma lungimilor listelor de adiacenta este /m,

    daca graful este neorientat, respectiv m, daca graful este orientat. 8aca numarul muc+iilor in graf este mic, aceasta reprezentare este preferabila din punct de vedere al memoriei necesare. !ste posibil sa e#aminam toti vecinii

    unui varf dat, in medie, in mai putin de noperatii. Pe de alta parte, pentru a determina daca doua varfuri isi4 sunt adiacente, trebuie sa analizam lista de adiacenta a lui i&si, posibil, lista de adiacenta a lui4', ceea ce este mai

    putin eficient decat consultarea unei valori logice in matricea de adiacenta.

    Printr-o lista de muchii. )ceasta reprezentare este eficienta atunci cand avem de e#aminat toate muc+iile grafului.

    http://vega.unitbv.ro/~andonie/Cartea%20de%20algoritmi/cap3.htm#_ftn1%23_ftn1http://vega.unitbv.ro/~andonie/Cartea%20de%20algoritmi/cap3.htm#_ftn1%23_ftn1http://vega.unitbv.ro/~andonie/Cartea%20de%20algoritmi/cap3.htm#_ftn1%23_ftn1http://vega.unitbv.ro/~andonie/Cartea%20de%20algoritmi/cap3.htm#_ftn1%23_ftn1http://vega.unitbv.ro/~andonie/Cartea%20de%20algoritmi/cap3.htm#_ftn1%23_ftn1
  • 7/25/2019 bazele informaticii-c++

    46/311

    Figura 3.1 $n arbore cu radacina.

    3.3 *rbori cu radacina

    =ie ?un graf orientat. ?este un arbore cu radacina r, daca e#ista in ?un varf rdin care oricare alt varf poate fi auns printr-un drumunic.

    8efinitia este valabila si pentru cazul unui graf neorientat, alegerea unei radacini fiind insa in acest caz arbitrara: orice arbore este unarbore cu radacina, iar radacina poate fi fi#ata in oricare varf al sau. )ceasta, deoarece dintr-un varf oarecare se poate aunge in oricare altvarf printr-un drum unic.

    Cand nu va fi pericol de confuzie, vom folosi termenul DarboreE, in loc de termenul corect Darbore cu radacinaE. Cel mai intuitiv este sareprezentam un arbore cu radacina, ca pe un arbore propriu-zis. In =igura 6.7, vom spune ca betaeste tatallui deltasi&iullui alpha, cabetasigammasunt&rati, ca deltaeste un descendental lui alpha, iar alphaeste un ascendental lui delta. $n varf terminaleste un varffara descendenti. arfurile care nu sunt terminale sunt neterminale. 8e multe ori, vom considera ca e#ista o ordonare a descendentiloraceluiasi parinte: betaeste situat la stanga luigamma, adica betaeste fratele mai varstnic al luigamma.

    9rice varf al unui arbore cu radacina este radacina unui subarboreconstand din varful respectiv si toti descendentii sai. 9 multime dearbori disuncti formeaza opadure.

    Intr-un arbore cu radacina vom adopta urmatoarele notatii.-dancimeaunui varf este lungimea drumului dintre radacina si acest varfTinaltimeaunui varf este lungimea celui mai lung drum dintre acest varf si un varf terminalT inaltimeaarboreluieste inaltimea radaciniiTnivelulunui varf este inaltimea arborelui, minus adancimea acestui varf.

  • 7/25/2019 bazele informaticii-c++

    47/311

    eprezentarea unui arbore cu radacina se poate face prin adrese, ca si in cazul listelor inlantuite. =iecare varf va fi memorat in trei locatiidiferite, reprezentand informatia propriu-zisa a varfului &valoarea varfului', adresa celui mai varstnic fiu si adresa urmatorului frate.Pastrand analogia cu listele inlantuite, daca se cunoaste de la inceput numarul ma#im de varfuri, atunci implementarea arborilor curadacina se poate face prin tablouri paralele.

    8aca fiecare varf al unui arbore cu radacina are pana la nfii, arborele respectiv este n!ar. $n arbore binar poate fi reprezentat prin adrese,

    ca in =igura 6./. 9bservam ca pozitiile pe care le ocupa cei doi fii ai unui varf sunt semnificative: lui aii lipseste&iul drept, iar beste&iulstangal lui a.

    Figura 3.2 eprezentarea prin adrese a unui arbore binar.

    Intr-un arbore binar, numarul ma#im de varfuri de adancime 3este /3. $n arbore binar de inaltime iare cel mult /i%7-7 varfuri, iar daca aree#act /i%7-7 varfuri, se numeste arbore plin. arfurile unui arbore plin se numeroteaza in ordinea adancimii. Pentru aceeasi adancime,

    numerotarea se face in arbore de la stanga la dreapta &=igura 6.6'.

  • 7/25/2019 bazele informaticii-c++

    48/311

    Figura 3.3umerotarea varfurilor intr-un arbore binar de inaltime 6.

    $n arbore binar cu nvarfuri si de inaltime ieste complet, daca se obtine din arborele binar plin de inaltime i, prin eliminarea, daca estecazul, a varfurilor numerotate cu n%7, n%/, \, /i%7-7. )cest tip de arbore se poate reprezenta secvential folosind un tablou , punandvarfurile de adancime 3, de la stanga la dreapta, in pozitiile N/3O, N/3%7O, \, N/3%7-7O &cu posibila e#ceptie a nivelului ;, care poate fiincomplet'. 8e e#emplu, =igura 6. e#emplifica cum poate fi reprezentat un arbore binar complet cu zece varfuri, obtinut din arboreleplin din =igura 6.6, prin eliminarea varfurilor 77, 7/, 76, 7 si 70. (atal unui varf reprezentat in NiO, iU 7, se afla in Nidiv/O. =iii unuivarf reprezentat in NiO se afla, daca e#ista, in N/iO si N/i%7O.

    Figura 3.$n arbore binar complet.

  • 7/25/2019 bazele informaticii-c++

    49/311

    =acem acum o scurta incursiune in matematica elementara, pentru a stabili cateva rezultate de care vom avea nevoie in capitoleleurmatoare. Pentru un numar real oarecarex, definim

    xL ma#Rn nx, neste intregS si xL minRn nx, neste intregS

    Puteti demonstra cu usurinta urmatoarele proprietati:

    i( x-7 Q xxxQx%7pentru oricexreal

    ii( n3/% n3/L npentru orice nintreg

    iii( n3a3bL n3ab si n3a3bL n3ab

    pentru orice n, a, bintregi &a, b

    ;'

    iv( n3mL &n-m%7'3m si n3mL &n%m-7'3mpentru orice numere intregi pozitive nsi m

    In fine, aratati ca un arbore binar complet cu nvarfuri are inaltimea lg n.

    3.4 +eap,uri

    $n heap&in traducere apro#imativa, Dgramada ordonataE' este un arbore binar complet, cu urmatoarea proprietate, numita proprietatedeheap: valoarea fiecarui varf este mai mare sau egala cu valoarea fiecarui fiu al sau. =igura 6.0 prezinta un e#emplu de +eap.

  • 7/25/2019 bazele informaticii-c++

    50/311

    Figura 3.5$n +eap.

    )celasi +eap poate fi reprezentat secvential prin urmatorul tablou:

    7; < > < 0 / / 7 G

    N7O N/O N6O NO N0O NGO NO N7;O

    Caracteristica de baza a acestei structuri de data este ca modificarea valorii unui varf se face foarte eficient, pastrandu-se proprietatea de+eap. 8aca valoarea unui varf creste, astfel incat depaseste valoarea tatalui, este suficient sa sc+imbam intre ele aceste doua valori si sacontinuam procedeul in mod ascendent, pana cand proprietatea de +eap este restabilita. om spune ca valoarea modificata a fost &iltrata&percolated ' catre noua sa pozitie. 8aca, dimpotriva, valoarea varfului scade, astfel incat devine mai mica decat valoarea cel putin a unuifiu, este suficient sa sc+imbam intre ele valoarea modificata cu cea mai mare valoare a fiiilor, apoi sa continuam procesul in moddescendent, pana cand proprietatea de +eap este restabilita. om spune ca valoarea modificata a fost cernuta&si&ted do=n' catre noua sapozitie. $rmatoarele proceduri descriu formal operatiunea de modificare a valorii unui varf intr-un +eap.

    procedurealter!heap&N7 .. nO, i, v' RN7 .. nO este un +eapT lui NiO, 7 i n, i se atribuie valoarea vsi proprietatea de +eap este restabilitaS xNiO NiO v ifvQx then si&t-do=n&, i'

    else percolate&, i'

  • 7/25/2019 bazele informaticii-c++

    51/311

    proceduresi&t-do=n&N7 .. nO, i' Rse cerne valoarea din NiOS 3i repeat

    43 Rgaseste fiul cu valoarea cea mai mareS

    if/4nandN/4O U N3O then3/4 if/4Q nandN/4%7O U N3O then3/4%7 intersc+imba N4O si N3O until4L 3

    procedurepercolate&N7 .. nO, i' Rse filtreaza valoarea din NiOS 3i repeat

    4 3 if4U 7 andN4 div/O Q N3O then34 div/ intersc+imbaN4O si N3O until4L 3

    ]eap-ul este structura de date ideala pentru determinarea si e#tragerea ma#imului dintr-o multime, pentru inserarea unui varf, pentrumodificarea valorii unui varf. *unt e#act operatiile de care avem nevoie pentru a implementa o lista dinamica de prioritati: valoarea unuivarf va da prioritatea evenimentului corespunzator. !venimentul cu prioritatea cea mai mare se va afla mereu la radacina +eap-ului, iarprioritatea unui eveniment poate fi modificata in mod dinamic. )lgoritmii care efectueaza aceste operatii sunt:

    function&ind!max&N7 .. nO' Rreturneaza elementul cel mai mare din +eap-ul S returnN7O

    proceduredelete!max&N7 .. nO' Rsterge elementul cel mai mare din +eap-ul S N7O NnO si&t-do=n&N7 .. n-7O, 7'

  • 7/25/2019 bazele informaticii-c++

    52/311

    procedureinsert&N7 .. nO, v' Rinsereaza un element cu valoarea vin +eap-ul si restabileste proprietatea de +eapS Nn%7O v percolate&N7 .. n%7O, n%7'

    amane de vazut cum putem forma un +eap pornind de la tabloul neordonat N7 .. nO. 9 solutie evidenta este de a porni cu un +eap vid sisa adaugam elementele unul cate unul.

    procedureslo=!ma3e!heap&N7 .. nO' Rformeaza, in mod ineficient, din un +eapS fori/ tondopercolate&N7 .. iO, i'

    *olutia nu este eficienta si, in Capitolul 0 , vom reveni asupra acestui lucru. !#ista din fericire un algoritm mai inteligent, care lucreaza intimp liniar, dupa cum vom demonstra tot in Capitolul 0 .

    procedurema3e-heap&N7 .. nO' Rformeaza din un +eapS fori &ndiv/' downto7 dosi&t-do=nN, iO

    e reamintim ca in Nndiv/O se afla tatal varfului din NnO. Pentru a intelege cum lucreaza aceasta procedura, sa presupunem ca pornimde la tabloul:

    7 G > / < 0 / < 7;

    care corespunde arborelui:

  • 7/25/2019 bazele informaticii-c++

    53/311

    Mai intai formam +eap-uri din subarborii cu radacina la nivelul 7, aplicand procedura si&t!do=nradacinilor respective:

    8upa acest pas, tabloul devine:

    7 G > < 7; 0 / / < < 0 / / G

    $rmeaza apoi sa repetam procedeul si pentru nivelul 6, obtinand in final +eap-ul din =igura 6.0.

    $n min!heapeste un +eap in care proprietatea de +eap este inversata: valoarea fiecarui varf este mai mica sau egala cu valoarea fiecaruifiu al sau. !vident, radacina unui min-+eap va contine in acest caz cel mai mic element al +eap-ului. In mod corespunzator, se modifica sicelelalte proceduri de manipulare a +eap-ului.

    C+iar daca +eap-ul este o structura de date foarte atractiva, e#ista totusi si operatii care nu pot fi efectuate eficient intr-un +eap. 9 astfel deoperatie este, de e#emplu, gasirea unui varf avand o anumita valoare data.

    Conceptul de +eap poate fi imbunatatit in mai multe feluri. )stfel, pentru aplicatii in care se foloseste mai des procedura percolatedecatprocedurasi&t!do=n, renteaza ca un varf neterminal sa aiba mai mult de doi fii. )ceasta accelereaza procedura percolate. *i un astfel de+eap poate fi implementat secvential.

    ]eap-ul este o structura de date cu numeroase aplicatii, inclusiv o remarcabila te+nica de sortare, numita heapsort.

    procedureheapsort&N7 .. nO' Rsorteaza tabloul S ma3e-heap&'

  • 7/25/2019 bazele informaticii-c++

    55/311

    fori ndownto/ do intersc+imba N7O si NiO si&t-do=n&N7 ..i-7O, 7'

    *tructura de +eap a fost introdusa &Killiams, 7>G' tocmai ca instrument pentru acest algoritm de sortare.

    3.5 -tructuri de multimi dis$uncte

    *a presupunem ca avemelemente, numerotate de la 7 la. umerele care identifica elementele pot fi, de e#emplu, indici intr-un tablouunde sunt memorate numele elementelor. =ie o partitie a acestorelemente, formata din submultimi doua cate doua disuncte: >7, >/, \ .e intereseaza sa rezolvam doua probleme:

    i( Cum sa obtinem reuniunea a doua submultimi, >i>4.

    ii( Cum sa gasim submultimea care contine un element dat.

    )vem nevoie de o structura de date care sa permita rezolvarea eficienta a acestor probleme.

    8eoarece submultimile sunt doua cate doua disuncte, putem alege ca etic+eta pentru o submultime oricare element al ei. om convenipentru inceput ca elementul minim al unei multimi sa fie etic+eta multimii respective. )stfel, multimea R6, 0, /, 1S va fi numitaDmultimea /E.

    om aloca tabloulsetN7 ..O, in care fiecarei locatiisetNiO i se atribuie etic+eta submultimii care contine elementul i. )vem atunciproprietatea:setNiO i, pentru #i.

    Presupunem ca, initial, fiecare element formeaza o submultime, adica setNiO L i, pentru 7 i. Problemele i(si ii(se pot rezolva prinurmatorii algoritmi:

    function&ind7&x' Rreturneaza etic+eta multimii care il contine pexS returnsetNxO

  • 7/25/2019 bazele informaticii-c++

    56/311

    proceduremerge7&a, b' Rfuzioneaza multimile etic+etate cu asi bS i aT4b ifiU4thenintersc+imba isi4 for34todo ifsetN3O L4 thensetN3O i

    8aca consultarea sau modificarea unui element dintr-un tablou conteaza ca o operatie elementara, atunci se poate demonstra &!#ercitiul6.

  • 7/25/2019 bazele informaticii-c++

    57/311

    ix whilesetNiO idoisetNiO returni

    proceduremerge/&a, b' Rfuzioneaza multimile etic+etate cu asi bS

    ifaQ b then setNbO a else setNaO b

    9 serie de noperatii&ind/ si merge/ necesita, pentru cazul cel mai nefavorabil si