holdsoft.free.frholdsoft.free.fr/mnsoft/facultate/cursjava.pdf · cuprins 1 instalarea mediului...

182
Daniel DANCIU Silviu DUMITRESCU ALGORITMIC ˘ AS ¸I PROGRAMARE Curs ¸ si probleme de seminar JAVA Bra¸ sov 2002

Upload: nguyenque

Post on 05-Feb-2018

247 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

Daniel DANCIU Silviu DUMITRESCU

ALGORITMICA SI PROGRAMARECurs si probleme de seminar

JAVA

Brasov 2002

Page 2: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

Cuprins

1 Instalarea mediului Java 61.1 Obtinerea mediului Java pentru platforma dumneavoastra . . . . . . . . . . 6

1.1.1 Medii de dezvoltare integrata . . . . . . . . . . . . . . . . . . . . . . 61.2 Instalarea mediului Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

1.2.1 Instructiuni de instalare pentru Windows . . . . . . . . . . . . . . . 71.2.2 Instructiuni de instalare pentru Linux/Unix . . . . . . . . . . . . . . 7

2 Notiuni fundamentale de programare in Java 92.1 Mediul de lucru Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92.2 Primul program Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.2.1 Comentarii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102.2.2 Functia main . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102.2.3 Scrierea pe ecran . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10

2.3 Tipuri de date primitive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102.3.1 Tipurile primitive . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102.3.2 Constante . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112.3.3 Declararea si initializarea tipurilor primitive ın Java . . . . . . . . . 122.3.4 Citire/scriere de la terminal . . . . . . . . . . . . . . . . . . . . . . . 12

2.4 Operatori de baza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122.4.1 Operatori de atribuire . . . . . . . . . . . . . . . . . . . . . . . . . . 132.4.2 Operatori aritmetici binari . . . . . . . . . . . . . . . . . . . . . . . 142.4.3 Operatori aritmetici unari . . . . . . . . . . . . . . . . . . . . . . . . 142.4.4 Conversii de tip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

2.5 Instructiuni conditionale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152.5.1 Operatori relationali . . . . . . . . . . . . . . . . . . . . . . . . . . . 152.5.2 Operatori logici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152.5.3 Instructiunea if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162.5.4 Instructiunea while . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182.5.5 Instructiunea for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182.5.6 Instructiunea do . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192.5.7 Instructiunile break si continue . . . . . . . . . . . . . . . . . . . . . 202.5.8 Instructiunea switch . . . . . . . . . . . . . . . . . . . . . . . . . . . 212.5.9 Operatorul conditional . . . . . . . . . . . . . . . . . . . . . . . . . . 22

2.6 Metode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232.6.1 Supraıncarcarea numelor la metode . . . . . . . . . . . . . . . . . . . 23

2

Page 3: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

CUPRINS 3

2.7 Probleme propuse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

3 Referinte 253.1 Ce este o referinta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253.2 Fundamente despre obiecte si referinte . . . . . . . . . . . . . . . . . . . . . 27

3.2.1 Operatorul punct (.) . . . . . . . . . . . . . . . . . . . . . . . . . . . 273.2.2 Declararea obiectelor . . . . . . . . . . . . . . . . . . . . . . . . . . . 273.2.3 Colectarea de gunoaie (garbage collection) . . . . . . . . . . . . . . . 283.2.4 Semnificatia lui = . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283.2.5 Transmiterea de parametri . . . . . . . . . . . . . . . . . . . . . . . 293.2.6 Semnificatia lui == . . . . . . . . . . . . . . . . . . . . . . . . . . . 303.2.7 Supraıncarcarea operatorilor pentru obiecte . . . . . . . . . . . . . . 30

3.3 Siruri de caractere (stringuri) . . . . . . . . . . . . . . . . . . . . . . . . . . 303.3.1 Fundamentele utilizarii stringurilor . . . . . . . . . . . . . . . . . . . 303.3.2 Concatenarea stringurilor . . . . . . . . . . . . . . . . . . . . . . . . 313.3.3 Comparatia stringurilor . . . . . . . . . . . . . . . . . . . . . . . . . 323.3.4 Alte metode pentru stringuri . . . . . . . . . . . . . . . . . . . . . . 323.3.5 Conversia de la string la tipurile primitive . . . . . . . . . . . . . . . 32

3.4 Siruri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333.4.1 Declaratie, Atribuire si Metode . . . . . . . . . . . . . . . . . . . . . 333.4.2 Expansiunea dinamica a sirurilor . . . . . . . . . . . . . . . . . . . . 353.4.3 Siruri cu mai multe dimensiuni . . . . . . . . . . . . . . . . . . . . . 383.4.4 Argumente ın linie de comanda . . . . . . . . . . . . . . . . . . . . 38

3.5 Tratarea exceptiilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393.5.1 Procesarea exceptiilor . . . . . . . . . . . . . . . . . . . . . . . . . . 393.5.2 Exceptii uzuale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

3.6 Intrare si iesire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423.6.1 Operatii de baza pe fluxuri (stream-uri) . . . . . . . . . . . . . . . . 423.6.2 Obiectul StringTokenizer . . . . . . . . . . . . . . . . . . . . . . . . 433.6.3 Fisiere secventiale . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

3.7 Probleme propuse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

4 Obiecte si clase 474.1 Ce este programarea orientata pe obiecte? . . . . . . . . . . . . . . . . . . . 474.2 Un exemplu simplu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 484.3 Metode uzuale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

4.3.1 Constructori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 504.3.2 Modificatori si Accesori . . . . . . . . . . . . . . . . . . . . . . . . . 524.3.3 Afisare si toString . . . . . . . . . . . . . . . . . . . . . . . . . . . . 524.3.4 Metoda equals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 534.3.5 Variabile si metode statice . . . . . . . . . . . . . . . . . . . . . . . . 534.3.6 Metoda main . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

4.4 Pachete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 534.4.1 Directiva import . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 544.4.2 Instructiunea package . . . . . . . . . . . . . . . . . . . . . . . . . . 554.4.3 Variabila sistem CLASSPATH . . . . . . . . . . . . . . . . . . . . . 55

Page 4: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

4 CUPRINS

4.4.4 Reguli de vizibilitate Package-Friendly . . . . . . . . . . . . . . . . . 564.4.5 Compilarea separata . . . . . . . . . . . . . . . . . . . . . . . . . . . 56

4.5 Alte operatii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 564.5.1 Referinta this . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 564.5.2 Prescurtarea this pentru constructori . . . . . . . . . . . . . . . . . . 574.5.3 Operatorul instanceof . . . . . . . . . . . . . . . . . . . . . . . . . . 584.5.4 Atribute statice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 584.5.5 Initializatori statici . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

4.6 Probleme propuse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

5 Mostenire 685.1 Ce este mostenirea? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 685.2 Sintaxa de baza Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

5.2.1 Reguli de vizibilitate . . . . . . . . . . . . . . . . . . . . . . . . . . . 715.2.2 Constructor si super . . . . . . . . . . . . . . . . . . . . . . . . . . . 725.2.3 Metode si clase final . . . . . . . . . . . . . . . . . . . . . . . . . . . 725.2.4 Redefinirea unei metode . . . . . . . . . . . . . . . . . . . . . . . . . 735.2.5 Metode si clase abstracte . . . . . . . . . . . . . . . . . . . . . . . . 74

5.3 Exemplu: Extinderea clasei Shape . . . . . . . . . . . . . . . . . . . . . . . 765.4 Mostenire multipla . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 815.5 Interfete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

5.5.1 Definirea unei interfete . . . . . . . . . . . . . . . . . . . . . . . . . . 825.5.2 Implementarea unei interfete . . . . . . . . . . . . . . . . . . . . . . 825.5.3 Interfete multiple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

5.6 Implementarea de componente generice . . . . . . . . . . . . . . . . . . . . 845.7 Anexa - clasa Reader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 875.8 Probleme propuse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88

6 Analiza eficientei algoritmilor 906.1 Ce este analiza algoritmilor? . . . . . . . . . . . . . . . . . . . . . . . . . . 906.2 Notatia asimptotica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

6.2.1 O notatie pentru ordinul de marime al timpului de executie al unuialgoritm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

6.3 Tehnici de analiza algoritmilor . . . . . . . . . . . . . . . . . . . . . . . . . 946.3.1 Sortarea prin selectie . . . . . . . . . . . . . . . . . . . . . . . . . . . 946.3.2 Sortarea prin insertie . . . . . . . . . . . . . . . . . . . . . . . . . . . 956.3.3 Turnurile din Hanoi . . . . . . . . . . . . . . . . . . . . . . . . . . . 96

6.4 Analiza algoritmilor recursivi . . . . . . . . . . . . . . . . . . . . . . . . . . 976.4.1 Metoda iteratiei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 976.4.2 Inductia constructiva . . . . . . . . . . . . . . . . . . . . . . . . . . 976.4.3 Recurente liniare omogene . . . . . . . . . . . . . . . . . . . . . . . . 986.4.4 Recurente liniare neomogene . . . . . . . . . . . . . . . . . . . . . . 1006.4.5 Schimbarea variabilei . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

6.5 Probleme propuse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103

Page 5: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

CUPRINS 5

7 Structuri de date 1077.1 De ce avem nevoie de structuri de date? . . . . . . . . . . . . . . . . . . . . 1077.2 Stive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1097.3 Cozi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1117.4 Liste ınlantuite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1137.5 Arbori binari de cautare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1177.6 Tabele de repartizare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1217.7 Cozi de prioritate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1237.8 Aplicatie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124

8 Metoda Backtracking 1298.1 Prezentare generala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1298.2 Prezentarea metodei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130

8.2.1 Atribuie si avanseaza . . . . . . . . . . . . . . . . . . . . . . . . . . . 1338.2.2 Incercare esuata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1338.2.3 Revenire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1348.2.4 Revenire dupa construirea unei solutii . . . . . . . . . . . . . . . . . 134

8.3 Implementarea metodei backtracking . . . . . . . . . . . . . . . . . . . . . . 1358.4 Probleme clasice care admit rezolvare prin metoda backtracking . . . . . . . 137

8.4.1 Problema generarii permutarilor . . . . . . . . . . . . . . . . . . . . 1378.4.2 Generarea aranjamentelor si a combinarilor . . . . . . . . . . . . . . 1388.4.3 Problema damelor . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1408.4.4 Problema colorarii hartilor . . . . . . . . . . . . . . . . . . . . . . . 141

8.5 Probleme propuse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143

9 Divide et Impera 1539.1 Notiuni elementare referitoare la recursivitate . . . . . . . . . . . . . . . . . 153

9.1.1 Functii recursive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1539.1.2 Recursivitatea nu ınseamna recurenta . . . . . . . . . . . . . . . . . 157

9.2 Prezentarea metodei Divide et Impera . . . . . . . . . . . . . . . . . . . . . 1599.3 Cautare binara . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1609.4 Sortarea prin interclasare (MergeSort) . . . . . . . . . . . . . . . . . . . . . 1619.5 Sortarea rapida (QuickSort) . . . . . . . . . . . . . . . . . . . . . . . . . . . 1629.6 Expresii aritmetice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1649.7 Probleme propuse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167

10 Algoritmi Greedy 17210.1 Problema spectacolelor (selectarea activitatilor) . . . . . . . . . . . . . . . . 172

10.1.1 Demonstrarea corectitudinii algoritmului . . . . . . . . . . . . . . . . 17310.2 Elemente ale strategiei Greedy . . . . . . . . . . . . . . . . . . . . . . . . . 174

10.2.1 Proprietatea de alegere Greedy . . . . . . . . . . . . . . . . . . . . . 17610.2.2 Substructura optima . . . . . . . . . . . . . . . . . . . . . . . . . . . 177

10.3 Minimizarea timpului mediu de asteptare . . . . . . . . . . . . . . . . . . . 17710.4 Interclasarea optima a mai multor siruri ordonate . . . . . . . . . . . . . . 17810.5 Probleme propuse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181

Page 6: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

Capitolul 1

Instalarea mediului Java

In acest capitol, vom da cateva posibilitati de descarcare a softului, necesar pentru rulareaunor aplicatii Java, de pe Internet precum si instalarea acestuia.

1.1 Obtinerea mediului Java pentru platforma dumneav-oastra

Mediul de baza Java consta dintr-un browser web unde puteti vizualiza applet-urile Java,un compilator Java ce transforma codul sursa Java ın cod binar si un interpretor pentrurularea programelor Java. Aveti nevoie de asemenea de un editor de texte ca emacs,TextPad sau BBEdit. Alte unelte ca debugger, un mediu vizual de dezvoltare etc. nu suntabsolut necesare.Nu este necesar sa luati toate partile de la aceeasi sursa. De obicei, browser-ul vostrude web va fi Internet Explorer sau Netscape. Celelalte le puteti obtine de la Sun’s JavaDeveloper Kit (JDK). Sun publica versiuni pentru toate platformele (Windows, Solaris,X86 Linux).JDK nu include un browser web dar contine un applet viewer pentru testarea applet-urilor.JDK include de asemenea compilatorul javac, interpretorul java, profiler-ul javaprof, gene-ratotul fisierelor C de tip header (pentru integrarea metodelor scrise ın C ıntr-o clasa Java)javah precum si depanatorul Java si generatorul de documentatie. Mai multa documentatiedespre JDK puteti gasi ın pagina de web a firmei Sun.Sun furnizeaza masina virtuala Java pentru Solaris, X86 Linux si Wndows 95, 98, NT.Pentru aceasta lucrare aveti nevoie de Java 2 Software Development Kit, versiunea 1.2(JDK 1.2) sau urmatoarele. Versiunea JDK 1.3, utilizata si ea destul de des, nu estedisponibila pentru toate platformele. Totusi, diferentele ıntre JDK1.2 si JDK1.3 nu suntfoarte importante.

1.1.1 Medii de dezvoltare integrata

Posibilitatile de dezvoltare integrata ale aplicatiilor Java sunt ınca primitive ın comparatiecu ceea ce este disponibil pentru C++. Se pare ca cel putin deocamdata mediile dedezvoltare integrata (IDE-Integrated Development Environments) nu sunt foarte perfor-mante. Acestea includ Metrowerks Code Warrior, Borland JBuilder, WinGate Visual Cafe

6

Page 7: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

1.2. INSTALAREA MEDIULUI JAVA 7

sau Microsoft Visual J++.

1.2 Instalarea mediului Java

Pentru ınceput trebuie sa va descarcati JDK-ul pe care urmeaza apoi sa ıl instalati.

1.2.1 Instructiuni de instalare pentru Windows

Stergeti mai ıntai toate variantele de JDK pe care le aveti deja instalate, mai ales dacadoriti sa puneti noul JDK ıntr-un alt director. De asemenea, trebuie sa folositi regeditpentru a sterge toate cheile anterior instalate.Aveti nevoie de aproximativ 60 MB de spatiu de memorie liber pentru instalarea JDK-ului. Executati un dublu clic pe icoana din File Manager sau selectand Run... din meniulProgram Manager File editati calea catre fisier. Aceasta va poduce dezarhivarea incluzandtoate directoarele si subdirectoarele necesare. Vom presupune ca instalarea s-a facut ınC:\jdk.Este necesar sa adaugati directotul C:\jdk\bin variabilei de mediu PATH. De exemplu:

C:\>set PATH="c:\jdk\bin;$PATH"

Acest lucru poate fi realizat permanent prin introducerea comenzii anterioare ın fisierulautoexec.bat.Pentru a va asigura ca mediul vostru Java este corect configurat, deschideti o fereastraDOS si editati ”javac nofile.java”. Astfel:

C:\>javac nofile.java

Daca primiti raspunsul:

error: Can’t read: nofile.java

atunci instalarea a fost facuta cu succes. Daca primiti raspunsul:

The name specified is not recognized as aninternal or external command, operable program or batch file.

sau ceva similar atunci mediul Java nu a fost bine instalat sau variabila PATH nu are ovaloare corecta. Trebuie rezolvate aceste probleme ınainte de a continua.

1.2.2 Instructiuni de instalare pentru Linux/Unix

Aveti nevoie de aproximativ 60 MB de spatiu de memorie liber pentru instalarea JDK-ului dar dublu ar fi de mare ajutor. Modul de dezarhivare dintr-un fisier gzipped tar esteurmatorul:

% gunzip jk1_2_2-linux-i386.tar.gz% tar xvf jdk1_2_2-linux-i386.tar

Page 8: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

8 CAPITOLUL 1. INSTALAREA MEDIULUI JAVA

Numele exact al fisierului poate fi un pic modificat daca folositi o platforma diferita ca siIrix sau o versiune diferita.Puteti face dezarhivarea ın directorul curent sau daca aveti drepturi de root ıntr-un altloc ca de exemplu /usr/local/java unde toti utilizatorii pot avea acces la fisiere. Oricumdrepturile de root nu sunt necesare pentru a instala Java.Dezarhivarea creaza toate directoarele si subdirectoarele necesare. Calea exacta nu esteimportanta, dar pentru simplitate vom presupune ın continuare ca instalarea s-a facut ın/usr/local/java. Veti gasi fisierele ın /usr/local/java/jdk1.2.2. Daca dezarhivati o versiunediferita, atunci fisierele vor fi ıntr-o cale usor modificata ca de exemplu /usr/local/java/jdk1.3.Este posibil ca mai multe versiuni de JDK sa coexiste armonios ın acelasi sistem. Dacadezarhivati altundeva decat /usr/local/java trebuie sa ınlocuiti /usr/local/java cu caleacompleta pana la directorul java. Daca instalati ın directorul curent puteti folosi /javaın loc de calea completa.Acum trebuie sa adaugati directorul /usr/local/java/jdk1.2.2/bin variabilei de mediu PATH.Acest lucru se poate face astfel dependent de shell-ul vostru:

csh,tcsh:

% set PATH=($PATH/usr/local/java/jdk1.2.2/bin)

sh:

% PATH=($PATH/usr/local/java/bin); export $PATH

Puteti sa adaugati liniile anterioare la sfarsitul fisierelor .profile sau .cshrc pentru a nu lemai scrie la fiecare login-are.Pentru a va asigura ca mediul vostru Java este corect configurat, editati ”javac nofile.java”la prompt-ul vostru shell:

% javac nofile.java

Daca primiti raspunsul:

error: Can’t read: nofile.java

atunci instalarea a fost facuta cu succes. Daca primiti raspunsul:

javac: Command not found

sau ceva similar atunci mediul Java nu a fost bine instalat sau variabila PATH nu are ovaloare corecta. Trebuie rezolvate aceste probleme ınainte de a continua.

Page 9: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

Capitolul 2

Notiuni fundamentale deprogramare in Java

2.1 Mediul de lucru Java

Codul sursa Java este continut ın fisiere text care au extensia .java. Compilatorul local,care este de obicei javac sau jikes1, compileaza programul si genereaza fisiere .class carecontin byte-code. Byte-code este un limbaj intermediar portabil care este interpretat decatre interpretorul de Java, numit java.

2.2 Primul program Java

Sa ıncepem prin a examina programul simplu din Figura 2.1. Acest program tiparesteun scurt mesaj pe ecran. Numerele din stanga fiecarei linii nu fac parte din program. Elesunt furnizate doar pentru o mai usoara referire a secventelor de cod.Transpuneti programul ıntr-un fisier cu numele FirstProgram.java2 dupa care compilati-lsi rulati-l. Java este case-sensitive, ceea ce ınseamna ca face deosebirile ıntre literele marisi mici.

1. //Primul program2. public class FirstProgram3. 4. public static void main(String[] args)5. 6. System.out.println("Primul meu program Java") ;7. 8.

Figura 2.1 Un prim program simplu

1javac este compilatorul de la Sun, jikes este compilatorul de la IBM si este preferat de multi program-atori deoarece este mult mai rapid.

2Atentie la literele mari si mici!

9

Page 10: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

10 CAPITOLUL 2. NOTIUNI FUNDAMENTALE DE PROGRAMARE IN JAVA

2.2.1 Comentarii

In Java exista trei tipuri de comentarii. Prima forma, care este mostenita de la C ıncepecu /* si se termina cu */. Iata un exemplu:

1. /* Acesta este un comentariu2. pe doua linii */

Comentariile nu pot fi imbricate, deci nu putem avea un comentariu ın interiorul altuicomentariu.Cea de-a doua forma de comentarii este mostenita de la limbajul C++ si ıncepe cu //.Nu exista simbol pentru ıncheiere, deoarece un astfel de comentariu se extinde automatpana la sfarsitul liniei curente. Acest comentariu este folosit ın linia 1 din Figura 2.1.Cea de-a treia forma este asemanatoare cu prima doar ca ıncepe cu /** ın loc de /*.Acesta forma de comentariu este utilizata pentru a furniza informatii utilitarului javadoc.Comentariile au fost introduse pentru a face codul mai lizibil pentru programatori. Unprogram bine comentat reprezinta un semn al unui bun programator.

2.2.2 Functia main

Un program Java consta dintr-o colectie de clase care interactioneaza ıntre ele prin inter-mediul metodelor. Echivalentul Java al unei proceduri sau functii din Pascal sau C estemetoda statica, pe care o vom descrie mai pe larg ın acest capitol. Atunci cand se executaun program Java, va fi invocata automat metoda statica main. Linia 4 din Figura 2.1arata ca metoda main poate fi eventual invocata cu anumiti parametri ın linia de comanda.Tipul parametrilor functiei main cat si tipul functiei, void, sunt obligatorii.

2.2.3 Scrierea pe ecran

Programul din Figura 2.1 consta dintr-o singura instructiune, aflata la linia 6. Functiaprintln reprezinta principalul mecanism de scriere ın Java, fiind echivalent ıntr-o anumitamasura cu functia writeln din Pascal sau printf din C. In aceasta situatie se scrie un sirde caractere la fluxul de iesire standard System.out. Vom discuta despre citire/scriere maitarziu. Deocamdata ne multumim doar sa amintim ca aceeasi sintaxa este folosita pentrua scrie orice fel de entitate, fie ca este vorba despre un ıntreg, real, sir de caractere sau alttip.

2.3 Tipuri de date primitive

Java defineste opt tipuri primitive de date, oferind de asemenea, o foarte mare flexibili-tate ın a defini noi tipuri de date, numite clase. Totusi ın Java, exista cateva diferenteestentiale ıntre tipurile de date primitive si cele definite de utilizator. In aceasta sectiunevom examina tipurile primitive si operatiile fundamentale care pot fi realizate asupra lor.

2.3.1 Tipurile primitive

Java are opt tipuri de date primitive prezentate ın Figura 2.2.

Page 11: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

2.3. TIPURI DE DATE PRIMITIVE 11

Tip de data Ce retine Valoribyte ıntreg pe 8 biti -128 la 127short ıntreg pe 16 biti -32768 la 32767int ıntreg pe 32 biti -2.147.483.648 la 2.147.483.647long ıntreg pe 64 biti −263 la 263 − 1float virgula mobila pe 32 biti 6 cifre semnificative, (10−46 la 1038)

double virgula mobila pe 64 biti 15 cifre semnificative, (10−324 la 10308)char caracter unicode

boolean variabila booleana false si true

Figura 2.2 Cele opt tipuri de date primitive ın Java

Cel mai des utilizat este tipul ıntreg specificat prin cuvantul cheie int. Spre deosebirede majoritatea altor limbaje de programare, marja de valori a tipurilor ıntregi nu estedependenta de masina. Java accepta si tipurile ıntregi byte, short si long. Numerele reale(virgula mobila) sunt reprezentate de tipurile float si double. Tipul double are mai multecifre semnficative, de aceea utilizarea lui este recomandata ın locul tipului float. Tipul chareste folosit pentru a reprezenta caractere. Un char ocupa 16 biti pentru a putea reprezentastandardul Unicode. Standardul Unicode contine peste 30.000 de caractere distincte careacopera principalele limbi scrise (inclusiv Japoneza, Chineza etc.). Prima parte a tabeleiUnicode este identica cu tabela ASCII. Ultimul tip primitiv este boolean; o variabila de tipboolean poate lua una din valorile true sau false.

2.3.2 Constante

Constantele ıntregi pot fi reprezentate ın bazele 10, 8 sau 16. Notatia octala este indi-cata printr-un 0 nesemnificativ la ınceput; notatia hexa este indicata printr-un 0x sau 0Xla ınceput. Iata cateva moduri echivalente de a reprezenta ıntregul 37: 37, 045, 0x25.Notatiile octale si hexazecimale nu vor fi utilizate ın acest curs. Totusi trebuie sa fimconstienti de ele pentru a folosi 0-uri la ınceput doar acolo unde chiar vrem aceasta.O constanta caracter este cuprinsa ıntre apostrofuri, cum ar fi ’a’. Intern, Java inter-preteaza aceasta constanta ca pe un numar (codul Unicode). Ulterior, functiile de scrierevor transforma acest numar ın caracterul corespunzator. Constantele caracter mai pot fireprezentate si ın forma:

’\uxxxx’.

unde xxxx este un numar ın baza 16 reprezentand codul Unicode al caracterului.Constantele de tip sir de caractere sunt cuprinse ıntre ghilimele, ca ın ”Primul meu programJava”. Exista anumite secvente speciale, numite secvente escape, care sunt folosite pentruanumite caractere speciale. Noi vom folosi mai ales

’\n’, ’\\’, ’\’’ si ’\"’,

care ınseamna respectiv linie noua, backslash, apostrof si ghilimele.

Page 12: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

12 CAPITOLUL 2. NOTIUNI FUNDAMENTALE DE PROGRAMARE IN JAVA

2.3.3 Declararea si initializarea tipurilor primitive ın Java

Orice variabila Java, inclusiv cele primitive, sunt declarate prin descrierea numelui a ti-pului si, optional, a valorii initiale. Numele variabilei trebuie sa fie un identificator. Unidentificator poate sa contina orice combinatie de litere, cifre si caracterul underscore(liniuta de subliniere). Identificatorii nu pot ıncepe cu o cifra. Cuvintele rezervate, cum arfi int nu pot fi identificatori. Nu pot fi utilizati nici identificatorii care deja sunt declaratisi sunt vizibili.Java este case-sensitive, ceea ce ınseamna ca sum si Sum reprezinta identificatori diferiti.In acest text vom folosi urmatoarea conventie pentru numele variabilelor:

• toate numele de variabila ıncep cu litera mica, iar cuvintele noi din cadrul numeluiıncep cu litera mare. De exemplu: sumaMaxima, nodVizitat etc.

• numele claselor ıncepe cu litera mare. De exemplu: FirstProgram, ArithmeticExcep-tion, BinaryTree etc.

Alte conventii vor mai fi prezentate pe parcurs.Iata cateva exemple de declaratii de variabile:

int numarElemente ;double mediaGenerala ;int produs = 1, suma = 0 ;int produs1 = produs ;

O variabila este bine sa fie declarata imediat ınainte de a fi folosita. Asa cum vom vedeamai tarziu, locul unde este declarata determina domeniul de vizibilitate si semnificatia ei.

2.3.4 Citire/scriere de la terminal

Scrierea la terminal ın Java se realizeaza cu functia println si nu pune probleme ma-jore. Lucrurile nu stau deloc la fel cu citirea de la tastatura, care se realizeaza mult maianevoios. Acest lucru se datoreaza ın primul rand faptului ca programele Java nu suntconcepute pentru a citi de la tastatura. In imensa majoritate a cazurilor programele Javaısi preiau datele dintr-o interfata grafica (Applet-urile), din forme HTML (Java Servlets,Java Server Pages) sau din fisiere.Citirea si scrierea de la consola sunt realizate prin readLine, respectiv println. Fluxul deintrare standard este System.in iar fluxul de iesire standard este System.out.Mecanismul de baza pentru citirea/scrierea formatata foloseste tipul String, care va fi de-scris ın capitolul urmator. La afisare, operatorul + concateneaza doua String-uri. Pentrutipurile primitive, daca parametrul scris nu este de tip String se face o conversie temporarala String. Aceste conversii pot fi definite si pentru obiecte, asa cum vom arata mai tarziu.Pentru citire se asociaza un obiect de tipul BufferedReader cu System.in. Apoi se citesteun String care va fi ulterior prelucrat.

2.4 Operatori de baza

Aceasta sectiune descrie operatorii de baza care sunt disponibili ın Java. Acesti operatorisunt utilizati pentru a crea expresii. O constanta sau o variabila reprezinta o expresie,

Page 13: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

2.4. OPERATORI DE BAZA 13

la fel ca si combinatiile de constante si variabile cu operatori. O expresie urmata de ”;”reprezinta o instructiune simpla. In paragraful 2.5 vom prezenta alte tipuri de instructiuni,care vor introduce noi tipuri de operatori.

2.4.1 Operatori de atribuire

Programul simplu din Figura 2.3 ilustreaza cativa operatori Java. Operatorul de atribuireeste semnul egal (=). De exemplu, ın linia 16, variabilei a i se atribuie valoarea variabilei c(care ın acel moment are valoarea 6). Modificarile ulterioare ale variabilei c nu vor afectavariabila a. Operatorii de atribuire pot fi ınlantuiti ca ın:

z=y=x=0.

Un alt operator de atribuire este += al carui mod de utilizare este ilustrat ın linia 18.Operatorul += adauga valoarea aflata la dreapta (operatorului) la variabila din stanga.Astfel, valoarea lui c este incrementata de la 6 la 14. Java ofera si alti operatori de atribuirecum ar fi -=, *= si /= care modifica variabila aflata ın partea stanga prin scadere, ınmultiresi respectiv ımpartire.

1. public class OperatorTest2. 3. //program care ilustreaza operatorii de baza4. //programul va afisa:5. //12 8 66. //6 8 67. //6 8 148. //22 8 149. //24 10 3310.11. public static void main(String[] args)12. 13. int a = 12, b = 8, c = 6 ;14.15. System.out.println(a + " " + b + " " + c) ;16. a = c ;17. System.out.println(a + " " + b + " " + c) ;18. c += b ;19. System.out.println(a + " " + b + " " + c) ;20. a = b + c ;21. System.out.println(a + " " + b + " " + c) ;22. a++ ;23. ++b ;24. c = a++ + ++b ;25. System.out.println(a + " " + b + " " + c) ;26. 27.

Figura 2.3 Program care ilustreaza anumiti operatori simpli

Page 14: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

14 CAPITOLUL 2. NOTIUNI FUNDAMENTALE DE PROGRAMARE IN JAVA

2.4.2 Operatori aritmetici binari

Linia 20 din Figura 2.3 ilustreaza unul dintre operatorii binari care sunt tipici pentrulimbajele de programare: operatorul de adunare (+). Operatorul + are ca efect adunareacontinutului variabilelor b si c; valorile lui b si c raman neschimbate. Valoarea rezultataeste atribuita lui a. Alti operatori aritmetici folositi ın Java sunt: -, *, / si % utilizatirespectiv pentru scadere, ınmultire, ımpartire si rest.Impartirea a doua valori ıntregi are ca valoare doar partea ıntrega a rezultatului. De e-xemplu 3/2 are valoarea 1, dar 3.0/2 are valoarea 1.5.Asa cum este si normal, adunarea si scaderea au aceeasi prioritate. Aceasta prioritateeste mai mica decat cea a grupului format din ınmultire, ımpartire si rest; astfel 1 + 2*3are valoarea 7. Toti acesti operatori sunt evaluati de la stanga la dreapta (astfel 3-2-2 arevaloarea -1). Toti operatorii aritmetici au o anumita prioritate si o anumita asociere.

2.4.3 Operatori aritmetici unari

In plus fata de operatorii aritmetici binari care necesita doi operanzi, Java dispune si deoperatori unari care necesita doar un singur operand. Cel mai cunoscut operator unareste operatorul minus (-) care returneaza operandul cu semn opus. Astfel, -x, este opusullui x.Java ofera de asemenea operatorul de autoincrementare care adauga 1 la valoarea uneivariabile, notat prin ++, si operatorul de autodecrementare care scade 1 din valoareavariabilei, notat cu - -. Un caz banal de utilizare a acestor operatori este exemplificatın liniile 22 si 23 din Figura 2.3. In ambele cazuri operatorul ++ adauga 1 la valoareavariabilei. In Java, ca si ın C, orice expresie are o valoare. Astfel, un operator aplicatunei variabile genereaza o expresie cu o anumita valoare. Desi faptul ca variabila esteincrementata ınainte de executia urmatoarei instructiuni este garantat, se pune ıntrebarea:”Care este valoarea expresiei de autoincrementare daca ea este utilizata ın cadrul unei alteexpresii?”In acest caz, locul unde se plaseaza operatorul ++ este esential. Semnficatia lui ++xeste ca valoarea expresiei este egala cu noua valoare a lui x. Acest operator este numitincrementare prefixata. In mod analog, x++ ınseamna ca valoarea expresiei este egala cuvaloarea originala a lui x. Acesta este numit incrementare postfixata. Aceste trasaturisunt exemplificate ın linia 24 din Figura 2.3. Atat a, cat si b sunt incrementate cu 1, iarc este obtinut prin adunarea valorii initiale a lui a (care este 23) cu valoarea incrementataa lui b (care este 10).

2.4.4 Conversii de tip

Operatorul conversie de tip, numit adeseori si operatorul de cast, este utilizat pentru agenera o variabila temporara de un nou tip. Sa consideram, de exemplu, secventa de cod:

double rest ;int x = 6 ;int y = 10 ;rest = x / y ; //mai mult ca sigur gresit!

Page 15: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

2.5. INSTRUCTIUNI CONDITIONALE 15

La efectuarea operatiei de ımpartire, atat x cat si y fiind numere ıntregi, se va realiza oımpartire ıntreaga si se obtine 0. Intregul 0 este apoi convertit implicit la double astfelıncat sa poata fi atribuit lui rest. Probabil ca intentia noastra era aceea de a atribui luirest valoarea 0.6. Solutia este de a converti temporar pe x sau pe y la double, pentru caımpartirea sa se realizeze ın virgula mobila. Acest lucru se poate obtine astfel:

rest = ( double ) x / y ;

De remarcat ca nici x si nici y nu se schimba. Se creaza o variabila temporara fara nume,avand valoarea 6.0, iar valoarea ei este utilizata pentru a efectua ımpartirea. Operatorulde conversie de tip are o prioritate mai mare decat operatorul de ımpartire, de aceeaconversia de tip se efectueaza ınainte de a se efectua ımpartirea.

2.5 Instructiuni conditionale

Aceasta sectiune este dedicata instructiunilor care controleaza fluxul de executie al pro-gramului: instructiunile conditionale si iteratia.

2.5.1 Operatori relationali

Testul fundamental care poate fi realizat asupra tipurilor primitive este comparatia. Com-paratia se realizeaza utilizand operatorii de egalitate/inegalitate si operatorii de comparatie(<,> etc.). In Java, operatorii de egalitate/inegalitate sunt == respectiv !=. De exemplu,

exprStanga == exprDreapta

are valoarea true daca exprStanga si exprDreapta sunt egale; altfel are valoarea false.Analog, expresia:

exprStanga != exprDreapta

are valoarea true daca exprStanga si exprDreapta sunt diferite; altfel are valoarea false.Operatorii de comparatie sunt <,<=, >, >= iar semnficatia lor este cea naturala pentrutipurile fundamentale. Operatorii de comparatie au prioritate mai mare decat operatoriide egalitate. Totusi, ambele categorii au prioritate mai mica decat operatorii aritmetici,dar mai mare decat operatorii de atribuire. Astfel, veti constata adeseori ca folosireaparantezelor nu va fi necesara. Toti acesti operatori se asociaza de la stanga la dreapta,dar cunoasterea acestui lucru nu ne foloseste prea mult. De exemplu, ın expresia a < b < 6,prima comparatie genereaza o valoare booleana, iar a doua expresie este gresita, deoareceoperatorul < nu este definit pentru valori booleene. Paragraful urmator descrie cum sepoate realiza acest test ın mod corect.

2.5.2 Operatori logici

Java dispune de operatori logici care sunt utilizati pentru a simula operatorii and, orsi not din algebra Booleana. Acesti operatori mai sunt referiti uneori si sub numele deconjunctie, disjunctie si, respectiv, negare, simbolurile corespunzatoare fiind &&, || si !.Implementarea corecta a testului din paragraful anterior este:

Page 16: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

16 CAPITOLUL 2. NOTIUNI FUNDAMENTALE DE PROGRAMARE IN JAVA

a<b && b<6

Prioritatea conjunctiei si a disjunctiei este suficient de mica fata de prioritatea celorlaltioperatori din expresie pentru ca parantezele sa nu fie necesare. && are prioritate maimare decat ||, iar ! are aceeasi prioritate cu alti operatori unari (++, - -, vezi Figura2.4).

Categorie Exemple AsociereOperatori pe referinte . [] Stanga la dreapta

Unari + + −− ! − (tip) Dreapta la stangaMultiplicativi ∗ / % Stanga la dreapta

Aditivi + − Stanga la dreaptaShiftare (pe biti) << >> >>> Stanga la dreapta

Relationali < <= > >= instanceof Stanga la dreaptaEgalitate == ! = Stanga la dreapta

AND pe biti & Stanga la dreaptaXOR pe biti ˆ Stanga la dreaptaOR pe biti | Stanga la dreaptaAND logic && Stanga la dreaptaOR logic || Stanga la dreapta

Conditional ? : Dreapta la stangaAtribuire = ∗ = / = % = + = − = Dreapta la stanga

Figura 2.4 Operatori Java listati ın ordinea prioritatii3

O regula importanta este ca operatorii && si || folosesc evaluarea booleana scurtcircuitata4.Aceasta ınseamna ca daca rezultatul poate fi determinat evaluand prima expresie, a douanu mai este evaluata. De exemplu, ın expresia:

x !=0 && 1/x != 3

daca x este 0, atunci prima jumatate este false. Aceasta ınsemna ca rezultatul conjunctieiva fi fals, deci a doua expresie nu mai este evaluata. Acesta este un detaliu important,deoarece ımpartirea la 0 ar genera un comportament eronat.

2.5.3 Instructiunea if

Instructiunea if este instructiunea fundamentala de decizie. Forma sa simpla este:

if( expresie )instructiuneurmatoarea instructiune

Daca expresie are valoarea true atunci se executa instructiune; ın caz contrar instructiunenu se executa. Dupa ce instructiunea if se ıncheie (fara o exceptie netratata), controluleste preluat de urmatoarea instructiune.Optional, putem folosi instructiunea if-else dupa cum urmeaza:

3Prin asociere ıntelegem odinea de evaluare ıntr-o expresie care contine operatori de acelasi tip si nuare paranteze.

4Numita uneori si evaluare booleana partiala

Page 17: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

2.5. INSTRUCTIUNI CONDITIONALE 17

if( expresie )instructiune1elseinstructiune2urmatoarea instructiune

In acest caz, daca expresie are valoarea true, atunci se executa instructiune1; altfel seexecuta instructiune2. In ambele cazuri controlul este apoi preluat de urmatoarea instruc-tiune. Iata un exemplu:

System.out.println("1/x este") ;if( x != 0 )System.out.print( 1/x ) ;elseSystem.out.print( "Nedefinit" ) ;System.out.println() ;

De retinut ca doar o singura instructiune poate exista pe ramura de if sau de else indiferentde cum indentati codul. Iata doua erori frecvente la ıncepatori:

if( x == 0 ) ; //instructiune vida!!!System.out.println( "x este 0" ) ;elseSystem.out.print( "x este" ) ;System.out.println(x) ; // a doua instructiune nu

// face parte din else

Prima gresala consta ın a pune ; dupa if. Simbolul ; reprezinta ın sine instructiunea vida;ca o consecinta, acest fragment de cod nu va fi compilabil (else nu va fi asociat cu niciun if). Dupa ce am corectat aceasta eroare, ramanem cu o eroare de logica: ultima liniede cod nu face parte din if, desi acest lucru este sugerat de indentare. Pentru a rezolvaaceasta problema vom utiliza un bloc ın care grupam o secventa de instructiuni printr-opereche de acolade:

if( x == 0 )System.out.println( "x este 0" ) ;elseSystem.out.print( "x este" ) ;System.out.println( x ) ;

Observati ca am folosit acolade si pe ramura de if desi acestea nu sunt absolut necesare.Aceasta practica ımbunatateste foarte mult lizibilitatea codului si va invitam si pe dum-neavoastra sa o adoptati.Instructiunea if poate sa faca parte dintr-o alta instructiune if sau else, la fel ca si celelalteinstructiuni de control prezentate ın continuare ın aceasta sectiune.

Page 18: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

18 CAPITOLUL 2. NOTIUNI FUNDAMENTALE DE PROGRAMARE IN JAVA

2.5.4 Instructiunea while

Java, ca si Pascal sau C, dispune de trei instructiuni de ciclare: instructiunea while,instructiunea do si instructiunea for. Sintaxa instructiunii while este:

while( expresie )instructiune

urmatoarea instructiune

Observati ca, la fel ca si la instructiunea if, nu exista ; ın sintaxa. Daca apare un ; , dupawhile, va fi considerat ca instructiune vida.Cat timp expresie este true se executa instructiune; apoi expresie este evaluata din nou.Daca expresie este false de la bun ınceput, atunci instructiune nu va fi executata niciodata.In general, instructiune face o actiune care ar putea modifica valoarea lui expresie; ıncaz contrar, ciclarea s-ar putea produce la infinit. Cand instructiunea while se ıncheie,controlul este preluat de urmatoarea instructiune.

2.5.5 Instructiunea for

Instructiunea while ar fi suficienta pentru a exprima orice fel de ciclare. Totusi, Javamai ofera ınca doua forme de a realiza ciclarea: instructiunea for si instructiunea do.Instructiunea for este utilizata ın primul rand pentru a realiza iteratia. Sintaxa ei este:

for( initializare; test; actualizare)instructiune

urmatoarea instructiune

In aceasta situatie, initializare, test si actualizare sunt toate expresii, si toate trei suntoptionale. Daca test lipseste, valoarea sa implicita este true. Dupa paranteza de ınchiderenu se pune ;.Instructiunea for se executa realizand mai ıntai initializare. Apoi, cat timp test estetrue, au loc urmatoarele doua instructiuni: se executa instructiune, iar apoi se executaactualizare. Daca initializare si actualizare sunt omise, instructiunea for se va comportaexact ca si instructiunea while.Avantajul instructiunii for consta ın faptul ca se poate vedea clar marja pe care itereazavariabilele contor.Urmatoarea secventa de cod afiseaza primele 100 numere ıntregi pozitive:

for( int i=1; i<100; ++i)System.out.println( i );

Acest fragment ilustreaza si practica obisnuita pentru programatorii Java (si C++) de adeclara un contor ıntreg ın secventa de initializare a ciclului. Durata de viata a acestuicontor se extinde doar ın interiorul ciclului.Atat initializare cat si actualizare pot folosi operatorul virgula pentru a permite expresiimultiple. Urmatorul fragment ilustreaza aceasta tehnica frecvent folosita:

Page 19: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

2.5. INSTRUCTIUNI CONDITIONALE 19

for( i = 0, sum = 0; i <= n; i++, sum += n)System.out.println( i + "\t" + sum) ;

Ciclurile pot fi imbricate la fel ca si instructiunile if. De exemplu, putem gasi toateperechile de numere mici a caror suma este egala cu produsul lor (cum ar fi 2 si 2, a carorsuma si produs este 4) folosind secventa de cod de mai jos:

for( int i = 1; i <= 10; i++)for(int j = 1; j <= 10; j++)if( i+j == i*j )System.out.println( i + ", " + j) ;

2.5.6 Instructiunea do

Instructiunea while realizeaza un test repetat. Daca testul este true atunci se executainstructiunea din cadrul ei. Totusi, daca testul initial este false, instructiunea din cadrulciclului nu este executata niciodata. In anumite situatii avem nevoie ca instructiunile dinciclu sa se execute cel putin o data. Acest lucru se poate realiza utilizand instructiuneado. Instructiunea do este asemanatoare cu instructiunea while, cu deosebirea ca testul esterealizat dupa ce instructiunile din corpul ciclului se executa. Sintaxa sa este:

doinstructiunewhile( expresie ) ;urmatoarea instructiune ;

Remarcati faptul ca instructiunea do se termina cu ;. Un exemplu tipic ın care se utilizeazainstructiunea do este dat de fragmentul de (pseudo-) cod de mai jos:

doafiseaza mesaj ;citeste data ;while( data nu este corecta ) ;

Instructiunea do este instructiunea de ciclare cel mai putin utilizata. Totusi, atunci candvrem sa executam ceva cel putin o data ciclul, si for nu poate fi utilizat, atunci do estealegerea potrivita.

Page 20: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

20 CAPITOLUL 2. NOTIUNI FUNDAMENTALE DE PROGRAMARE IN JAVA

2.5.7 Instructiunile break si continue

Instructiunile for si while au conditia de terminare ınaintea instructiunilor care se repeta.Instructiunea do are conditia de terminare dupa instructiunile care se repeta. Totusi, ınanumite situatii, s-ar putea sa fie nevoie sa ıntrerupem ciclul ın mijlocul instructiunilorcare se repeta. In acest scop, se poate folosi instructiunea break. De obicei, instructiuneabreak apare ın cadrul unei instructiuni if, ca ın exemplul de mai jos:

while(...)...if( conditie )break ;...

In cazul ın care sunt doua cicluri imbricate, instructiunea break paraseste doar ciclul celmai din interior. Daca exista mai mult de un ciclu care trebuie terminat, break nu vafunctiona corect, si mai mult ca sigur ca ati proiectat prost algoritmul. Totusi, Java oferaasa numitul break etichetat. In acest caz, o anumita instructiune de ciclare este etichetatasi instructiunea break poate fi aplicata acelei instructiuni de ciclare, indiferent de numarulde cicluri imbricate. Iata un exemplu:

eticheta:while(...)while(...)...if( conditie )break eticheta;

//controlul programului trece aici dupa executia lui break

In anumite situatii dorim sa renuntam la executia iteratiei curente din ciclu si sa trecem laurmatoarea iteratie a ciclului. Acest lucru poate fi realizat cu instructiunea continue. Casi break, instructiunea continue este urmata de ; si se aplica doar ciclului cel mai interiorın cazul ciclurilor imbricate. Urmatorul fragment tipareste primele 100 de numere ıntregi,cu exceptia celor divizibile cu 10:

for( int i = 1 ; i <= 100; i++)if( i % 10 == 0)

Page 21: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

2.5. INSTRUCTIUNI CONDITIONALE 21

continue ;System.out.println( i ) ;

Desigur, ca exemplul de mai sus poate fi implementat si utilizand un if simplu. Totusiinstructiunea continue este adeseori folosita pentru a evita imbricari complicate de tipif-else ın cadrul ciclurilor.

2.5.8 Instructiunea switch

Instructiunea switch este numita uneori si instructiune de selectie. Instructiunea switchselecteaza dintre mai multe secvente de cod, una care va fi executata, functie de valoareaunei expresii ıntregi. Forma sa este:

switch( expresie-selectare )case valoare-intreaga1: instructiune ; break ;case valoare-intreaga2: instructiune ; break ;//...case valoare-intreagan: instructiune ; break ;default: instructiune ;

expresie-selectare este o expresie care produce o valoare ıntreaga. Instructiunea switchcompara valoarea lui expresie-selectare cu fiecare valoare-intreaga. Daca are loc egalitatease executa instructiunea corespunzatoare (simpla sau compusa). Daca nu are loc nici oegalitate se executa instructiunea din default.Observati ca ın exemplul de mai sus, fiecare case se ıncheie cu un break care are ca efectsaltul la sfarsitul instructiunii switch. Acesta este modul obisnuit de a scrie o instructiunede tip switch, dar prezenta instructiunii break este optionala. Daca instructiunea breaklipseste, atunci se va executa si codul corespunzator instructiunilor case urmatoare panacand se ıntalneste un break. Desi de obicei nu ne dorim un astfel de comportament, elpoate fi uneori util pentru programatorii experimentati.Programul de mai jos creaza litere aleator si determina daca acestea sunt vocale sauconsoane (ın limba engleza):

1. //VowelsAndConsonants.java2. // Program demonstrativ pentru instructiunea switch3. public class VowelsAndConsonants4. 5. public static void main(String[] args)6. 7. for(int i = 0; i < 100; i++)8. 9. char c = (char)(Math.random() * 26 + ’a’);10. System.out.print(c + ": ");

Page 22: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

22 CAPITOLUL 2. NOTIUNI FUNDAMENTALE DE PROGRAMARE IN JAVA

11. switch(c)12. 13. case ’a’:14. case ’e’:15. case ’i’:16. case ’o’:17. case ’u’:18. System.out.println("vocala");19. break;20. case ’y’:21. case ’w’:22. System.out.println(23. "Uneori vocale "); //doar in limba engleza!!!24. break;25. default:26. System.out.println("consoana");27. //switch28. //for29. //main30. //class

Figura 2.5 Instructiunea switch

Functia Math.random() genereaza o valoare ın intervalul [0,1). Prin ınmultirea valoriireturnate de aceasta functie cu numarul de litere din alfabet (26 litere) se obtine un numarın intervalul [0,26). Adunarea cu prima litera (’a’, care are de fapt valoarea 97) are caefect transpunerea ın intervalul [97,123). In final se foloseste operatorul de conversie detip pentru a trunchia numarul la o valoare din multimea 97,98,...,122, adica un cod ASCIIal unui caracter din alfabetul englez .

2.5.9 Operatorul conditional

Operatorul coditional este folosit ca o prescurtare pentru instructiuni simple de tipul if-else.Forma sa generala este:

exprTest ? expresieDa : expresieNu ;

Mai ıntai se evalueaza exprTest urmata fie de expresieDa fie de expresieNu, rezultandastfel valoarea ıntregii expresii. expresieDa este evaluata daca exprTest are valoarea true;ın caz contrar se evalueaza expresieNu. Prioritatea operatorului conditional este chiardeasupra operatorilor de atribuire. Acest lucru permite omiterea parantezelor atunci candasignam rezultatul operatorului conditional unei variabile. Ca un exemplu, minimul adoua variabile poate fi calculat dupa cum urmeaza:

valMin = x < y ? x : y ;

Page 23: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

2.6. METODE 23

2.6 Metode

Ceea ce ın alte limbaje de programare numeam procedura sau functie, ın Java este numitmetoda. O definitie mai exacta si completa a notiunii de metoda o vom da mai tarziu. Inacest paragraf prezentam doar cateva notiuni elementare pentru a putea scrie functii degenul celor din C sau Pascal pe care sa le folosim ın cateva programe simple.Antetul unei metode consta dintr-un nume, o lista (eventual vida) de parametri si un tippentru valoarea returnata. Codul efectiv al metodei numit adeseori corpul metodei esteun bloc (o secventa de instructiuni cuprinsa ıntre acolade). Definirea unei metode constaın antet si corp. Un exemplu de metoda si de o functie main care o utilizeaza este dat ınFigura 2.6.Prin prefixarea metodelor cu ajutorul cuvintelor cheie public static putem mima ıntr-o oarecare masura functiile din Pascal si C. Desi aceasta tehnica este utila ın anumitesituatii, ea nu trebuie utilizata ın mod abuziv. Numele metodei este un identificator.Lista de parametri consta din 0 sau mai multi parametri formali, fiecare avand un tipprecizat. Cand o metoda este apelata, parametrii actuali sunt trecuti ın parametrii for-mali utilizand atribuirea obisnuita. Aceasta ınsemna ca tipurile primitive sunt transmiseutilizand exclusiv transmiterea prin valoare. Parametrii actuali nu vor putea fi modificatide catre functie. Definirile metodelor pot aparea ın orice ordine.Instructiunea return este utilizata pentru a ıntoarce o valoare catre codul apelant. Dacatipul functiei este void atunci nu se ıntoarce nici o valoare si se foloseste

return ;

fara nici un parametru.

1. public class Minim2. 3. public static void main( String[] args )4. 5. int a = 3 ;6. int b = 7 ;7. System.out.println( min(a,b) ) ;8. 9. //declaratia metodei min10. public static int min( int x, int y )11. 12. return x < y ? x : y ;13. 14.

Figura 2.6 Declararea si apelul unei metode

2.6.1 Supraıncarcarea numelor la metode

Sa presupunem ca dorim sa scriem o metoda care calculeaza maximul a trei numere ıntregi.Un antet pentru aceasta metoda ar fi:

int max(int a, int b, int c)

Page 24: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

24 CAPITOLUL 2. NOTIUNI FUNDAMENTALE DE PROGRAMARE IN JAVA

In unele limbaje de programare (Pascal, C), acest lucru nu ar fi permis daca exista dejao functie max cu doi parametri. De exemplu, se poate sa avem deja declarata o metodamax cu antetul:

int max(int a, int b)

Java permite supraıncarcarea (engl. overloading) numelui metodelor. Aceasta ınsemna camai multe metode pot fi declarate ın cadrul aceleiasi clase atata timp cat semnaturile lor(adica lista de parametri) difera. Atunci cand se face un apel al metodei max compilatorulpoate usor sa deduca despre care metoda este vorba examinand lista parametrilor de apel.Se poate sa existe metode supraıncarcate cu acelasi numar de parametri formali atatatimp cat cel putin unul din tipurile din lista de parametri este diferit.De retinut faptul ca tipul functiei nu face parte din semnatura ei. Aceasta ınseamna canu putem avea doua metode ın cadrul aceleiasi clase care sa difere doar prin tipul valoriireturnate. Metode din clase diferite pot avea acelasi nume, parametri si chiar tip returnat;despre aceasta vom discuta pe larg mai tarziu.

2.7 Probleme propuse

1. Scrieti o instuctiune while echivalenta cu ciclul for de mai jos. La ce ar putea fiutilizat un astfel de ciclu?

for( ; ; )instructiune

2. Scrieti un program care genereaza tabelele pentru ınmultirea si adunarea numerelorcu o singura cifra.

3. Scrieti doua metode statice. Prima sa returneze maximul a trei numere ıntregi, adoua maximul a patru numere ıntregi .

4. Scrieti o metoda statica care primeste ca parametru un an si returneaza true dacaanul este bisect si false ın caz contrar.

Page 25: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

Capitolul 3

Referinte

In capitolul 1 am prezentat tipurile primitive din Java. Toate tipurile care nu sunt ıntrecele opt primitive, inclusiv tipurile importante cum ar fi stringurile, sirurile si fisierele sunttipuri referinta.In acest capitol vom ınvata:

• Ce este un tip referinta si ce este o variabila referinta

• Prin ce difera un tip referinta de un tip primitiv

• Exemple de tipuri referinta incluzand stringuri, siruri si fluxuri

• Cum sunt utilizate exceptiile pentru a semnala comportamentul gresit alunei secvente de cod

3.1 Ce este o referinta

In capitolul 1 am examinat cele opt tipuri primitive, ımpreuna cu cateva operatii care potfi realizate pe aceste tipuri. Toate celelalte tipuri de date din Java sunt referinte. Ce estedeci o referinta? O variabila referinta ın Java (numita adeseori simplu referinta) este ovariabila care retine adresa de memorie la care se afla un anumit obiect.

point3*

point1HHj

point2*

1000 (0,0)1024 (5,12)3200 point2 = 10243600 point1 = 10005124 point3 = 1000

Figura 3.1 Ilustrarea unei referinte: Obiectul de tip Point stocat la adresa de memorie1000 este referit atat de catre point1 cat si de catre point3. Obiectul de tip Point stocat laadresa 1024 este referit de catre point2. Locatiile de memorie unde sunt retinute variabileleau fost alese arbitrar.

Ca un exemplu, ın Figura 3.1 exista doua obiecte de tipul Point1. Presupunem ca acesteobiecte au fost stocate la adresele de memorie 1000 si respectiv 1024. Pentru aceste doua

1Care contin coordonatele unui punct din plan.

25

Page 26: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

26 CAPITOLUL 3. REFERINTE

obiecte am definit trei referinte, point1, point2 si point3. Atat point1 cat si point3 referaobiectul stocat la adresa 1000; point2 refera obiectul stocat la adresa 1024; aceasta ınsemnaca atat point1 cat si point3 au valoarea 1000, iar point2 va avea valoarea 1024. Retinetica locatiile efective, cum ar fi 1000 si 1024, sunt atribuite de compilator la discretia sa(unde gaseste memorie libera). In consecinta, aceste valori nu sunt utile efectiv ca valorinumerice. Totusi, faptul ca point1 si point3 au aceeasi valoare este folositor: ınseamnaca ele refera acelasi obiect. O referinta stocheaza ıntotdeauna adresa la care un anumitobiect se afla, cu exceptia situatiei cand nu refera nici un obiect. In aceasta situatie vastoca referinta nula, notata cu null. Java nu permite referinte catre tipurile primitive.Exista doua mari tipuri de operatii care se pot aplica variabilelor referinta.

1. Prima categorie permite examinarea si manipularea valorii referinta. De exemplu,daca modificam valoarea stocata ın point1 (care este 1000), putem sa facem ca point1sa refere un alt obiect. Putem de asemenea compara point1 si point3 pentru a vedeadaca refera acelasi obiect.

2. A doua categorie de operatii se aplica obiectului care este referit; am putea deexemplu examina sau modifica starea unuia dintre obiectele de tipul Point (amputea examina coordonatele x si y ale unui obiect de tipul Point).

Inainte de a descrie ce se poate face cu ajutorul referintelor, sa descriem ce nu se poateface. Sa consideram expresia point1*point2. Deoarece valorile retinute de point1 si point2sunt respectiv 1000 si 1024, produsul lor ar fi 1024000. Totusi acest calcul este lipsit desens si nu ar putea avea nici o valoarea practica. Variabilele referinta retin adrese, si nupoate fi asociata nici o semnificatie logica ınmultirii adreselor.Analog, point1++ nu are nici un sens ın Java; ar sugera ca point1 - care are valoarea1000 - sa fie crescut la 1001, dar ın acest caz nu ar mai referi un obiect valid. Multe altelimbaje de programare definesc notiunea de pointer care are un comportament similar cucel al unei variabile referinta. Totusi, pointerii ın C sunt mult mai periculosi, deoareceeste permisa aritmetica pe adresele stocate. In plus, deoarece C permite pointeri si catretipurile fundamentale trebuie avut grija pentru a distinge ıntre aritmetica pe adrese siaritmetica pe variabilele care sunt referite. Acest lucru se face prin dereferentierea explicitaa pointerului. In practica, pointerii limbajului C tind sa provoace numeroase erori greudetectabile, care uneori ıi pot face si pe programatorii experimentati sa planga de ciuda!In Java, singurele operatii care sunt permise asupra referintelor (cu o singura exceptiepentru Stringuri) sunt atribuirea via = si comparatia via == sau !=. De exemplu, prinatribuirea lui point3 a valorii lui point2, vom face ca point3 sa refere acelasi obiect pe care ılrefera point2. Acum expresia point2 == point3 este adevarata, deoarece ambele referintestocheaza valoarea 1024 si refera deci acelasi obiect. point1 != point2 este de asemeneaadevarata, deoarece point1 si point2 refera acum obiecte distincte.Cealalta categorie de operatii se refera la obiectul care este referit. Exista doar trei actiunifundamentale care pot fi realizate:

1. Aplicarea unei conversii de tip

2. Accesul la un camp al obiectului sau apelul unei metode prin operatorul punct (.)

3. Utilizarea operatorului instanceof pentru a verifica daca obiectul retinut are un an-umit tip.

Page 27: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

3.2. FUNDAMENTE DESPRE OBIECTE SI REFERINTE 27

Sectiunea urmatoare ilustreaza mai detaliat operatiile pe referinte.

3.2 Fundamente despre obiecte si referinte

In Java un obiect este orice variabila de un tip ne-primitiv. Obiectele sunt tratate diferitfata de tipurile primitive. Variabilele de tipuri primitive sunt manipulate prin valoare,ceea ce ınsemna ca valorile lor sunt retinute ın acele variabile si sunt copiate dintr-o vari-abila primitiva ın alta variabila primitiva ın timpul instructiunii de atribuire. Dupa cumam aratat ın sectiunea anterioara, variabilele referinta stocheaza referinte catre obiecte.Obiectul ın sine este stocat undeva ın memorie, iar variabila referinta stocheaza adresade memorie a obiectului. Astfel, variabila referinta nu este decat un nume pentru aceazona de memorie. Aceasta ınsemna ca variabilele primitive si cele referinta vor avea uncomportament diferit. Aceasta sectiune examineaza mai ın detaliu aceste diferente si ilus-treaza operatiile care sunt permise pe tipurile referinta.

3.2.1 Operatorul punct (.)

Operatorul punct (.) este folosit pentru a selecta o metoda care se aplica unui obiect. Deexemplu, sa presupunem ca avem un obiect de tip Cerc care defineste metoda arie. Dacavariabila unCerc este o referinta catre un Cerc, atunci putem calcula aria (si salva aceastaarie ıntr-o variabila de tip double) cercului referit astfel:

double arieCerc = unCerc.arie() ;

Este posibil ca variabila unCerc sa retina referinta null. In acest caz, aplicarea operatoruluipunct va genera o exceptie de tipul NullPointerException la executia programului. Deobicei aceasta exceptie va determina terminarea anormala a programului.Operatorul punct poate fi folosit si pentru a accesa componentele individuale ale unuiobiect, daca cel care a proiectat obiectul permite acest lucru. Capitolul urmator descriecum se poate face acest lucru; tot acolo vom explica de ce ın general este preferabil ca sanu se permita accesul direct la componentele individuale ale unui obiect.

3.2.2 Declararea obiectelor

Am vazut deja care este sintaxa pentru declararea variabilelor primitive. Pentru obiecteexista o diferenta importanta. Atunci cand declaram o referinta, nu facem decat safurnizam un nume care poate fi utilizat pentru a referi un obiect stocat ın prealabil ınmemorie. Totusi, declaratia ın sine nu furnizeaza si acel obiect. Sa presupunem, de exem-plu, ca avem un obiect de tip Cerc caruia dorim sa ıi calculam aria folosind metoda arie().Sa consideram secventa de instructiuni de mai jos:

Cerc unCerc ; //unCerc poate referi un obiect de tip Cercdouble arieCerc = unCerc.arie() ; //calcul arie pentru cerc

Totul pare ın regula cu aceste instructiuni, pana cand ne aducem aminte ca unCerc estenumele unui obiect oarecare de tip Cerc, dar nu am creat nici un cerc efectiv. In consecinta,dupa ce se declara variabila unCerc, aceasta va contine valoarea null, ceea ce ınseamna ca

Page 28: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

28 CAPITOLUL 3. REFERINTE

unCerc ınca nu refera un obiect Cerc valid. Aceasta ınseamna ca a doua linie de programeste invalida, deoarece ıncercam sa calculam aria unui cerc care ınca nu exista. In exemplulde fata chiar compilatorul va detecta eroarea, afirmand ca unCerc ”nu este initializat”.In alte situatii mai complexe compilatorul nu va putea detecta eroarea si se va genera oNullPointerException ın timpul executiei programului.Singura posibilitate (normala) de a aloca memorie unui obiect este folosirea cuvantuluicheie new. new este folosit pentru a construi un nou obiect. O posibilitate de a face acestlucru este:

Cerc unCerc ; //unCerc poate referi un obiect de tip CercunCerc = new Cerc() ; //acum unCerc refera un obiect alocatdouble arieCerc = unCerc.arie() ; //calcul arie pentru cerc

Remarcati parantezele care se pun dupa numele obiectului.Adeseori programatorii combina declararea si initializarea obiectului ca ın exemplul demai jos:

Cerc unCerc = new Cerc() ; //acum unCerc refera un obiect alocatdouble arieCerc = unCerc.arie() ; //calcul arie pentru cerc

Multe obiecte pot fi de asemenea construite cu anumite valori initiale. De exemplu, obiec-tul de tip Cerc ar putea fi construit cu trei parametri, doi pentru coordonatele centruluisi unul pentru lungimea razei.

Cerc unCerc = new Cerc(0,0,10) ; //cerc cu centru in origine si de raza 10double arieCerc = unCerc.arie() ; //calcul arie pentru cerc

3.2.3 Colectarea de gunoaie (garbage collection)

Deoarece toate obiectele trebuie construite, ne-am putea astepta ca atunci cand nu maieste nevoie de ele sa trebuiasca sa le distrugem. Totusi, ın Java, cand un obiect dinmemorie nu mai este referit de nici o variabila, memoria pe care o consuma va fi eliberataautomat. Aceasta tehnica se numeste colectare de gunoaie.

3.2.4 Semnificatia lui =

Sa presupunem ca avem doua variabile de tipuri primitive x si y. Atunci, semnificatiainstructiunii de atribuire

x = y ;

este simpla: valoarea stocata ın y este stocata ın variabila primitiva x. Modificarile ulte-rioare ale lui x sau y nu au efecte asupra celeilalte.Pentru obiecte, semnificatia lui = este aceeasi: se copiaza valorile stocate. Daca x si ysunt referinte (de tipuri compatibile), atunci, dupa operatia de atribuire, x va referi acelasiobiect ca si y. Ceea ce se copiaza ın acest caz sunt adrese. Obiectul pe care x ıl refereaınainte nu mai este referit de x. Daca x a fost singura referinta catre acel obiect, atunciobiectul nu mai este referit acum de nici o variabila si este disponibil pentru colectarea degunoaie. Retineti faptul ca obiectele nu se copiaza.

Page 29: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

3.2. FUNDAMENTE DESPRE OBIECTE SI REFERINTE 29

Iata cateva exemple. Sa presupunem ca dorim sa cream doua obiecte de tip Cerc pentrua calcula suma ariilor lor. Cream mai ıntai obiectul cerc1, dupa care ıncercam sa creamobiectul cerc2 prin modificarea lui cerc1 dupa cum urmeaza (vezi si Figura 3.2):

Cerc cerc1 = new Cerc(0,0,10) ; //un cerc de raza 10Cerc cerc2 = cerc1 ;cerc2.setRaza(20) ; // modificam raza la 20 ;double arieCercuri = cerc1.arie() + cerc2.arie() ; //calcul arie

Cerc(0,0,10)

cerc1HHj

Cerc(0,0,20)cerc1*

cerc2HHj Cerc(0,0,10) cerc1

HHY

cerc2

Figura 3.2 cerc1 si cerc2 indica acelasi obiect. Modificarea razei lui cerc2 implica simodificarea razei lui cerc1.

Acest cod nu va functiona corect, deoarece nu s-a construit decat un singur obiect de tipCerc. Astfel, cea de-a doua instructiune nu face decat sa spuna ca cerc2 este un alt numepentru cerc1, construit anterior. Cercul construit ın prima linie are acum doua nume. Atreia instructiune modifica raza cercului la 20, dar de fapt se modifica raza unicului cerccreat, deci ultima linie aduna aria aceluiasi cerc de raza 20.Secventa de cod corecta ar fi:

Cerc cerc1 = new Cerc(0,0,10) ; //un cerc de raza 10Cerc cerc2 = new Cerc() ;cerc2.setRaza(20) ; // modificam raza la 20 ;double arieCercuri = cerc1.arie() + cerc2.arie() ; //calcul arie

La o prima vedere, faptul ca obiectele nu pot fi copiate, pare sa fie o limitare severa. Inrealitate nu este deloc asa, desi ne trebuie un pic de timp pentru a ne obisnui cu acestlucru. Exista totusi anumite situatii cand chiar trebuie sa copiem obiecte; ın aceste situatiise va folosi metoda clone(). clone() foloseste new pentru a crea un nou obiect duplicat.Totusi, ın acesta lucrare metoda clone() nu este folosita.

3.2.5 Transmiterea de parametri

Din cauza ca apelul se face prin valoare, parametri actuali (de apel) se transpun ınparametri formali folosind atribuirea obisnuita. Daca parametrul trimis este un tip referinta,atunci stim deja ca prin atribuire atat parametrul formal, cat si parametrul de apel vorreferi acelasi obiect. Orice metoda aplicata parametrului formal este astfel implicit aplicatasi parametrului de apel. In alte limbaje de programare acest tip de apel se numeste apelareprin referinta. Utilizarea acestei notiuni ın Java ar fi oarecum nepotrivita, deoarece ne-arputea face sa credem ca transmiterea referintelor s-ar face ın mod diferit. In realitate,transmiterea parametrilor nu s-a modificat; ceea ce s-a modificat sunt parametrii ın sine,care nu mai sunt tipuri primitive, ci tipuri referinta.

Page 30: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

30 CAPITOLUL 3. REFERINTE

3.2.6 Semnificatia lui ==

Pentru tipurile primitive operatia == are valoarea true daca au valori identice. Pentrutipuri referinta semnificatia lui == este diferita, dar perfect consistenta cu discutia dinparagraful anterior. Doua variabile referinta sunt egale via == daca ele refera acelasiobiect (s-au ambele sunt null). Sa consideram urmatorul exemplu:

Cerc cerc1 = new Cerc(0,0,10) ; //un cerc de raza 10Cerc cerc2 = new Cerc(0,0,10) ; //un alt cerc tot de raza 10Cerc cerc3 = cerc2 ;

In acest caz avem doua obiecte. Primul este cunoscut sub numele de cerc1, al doilea estecunoscut sub doua nume: cerc2 si cerc3. Expresia cerc2 == cerc3 este adevarata. Totusi,desi cerc1 si cerc2 par sa refere obiecte care au aceeasi valoare, expresia cerc1 == cerc2este falsa, deoarece ele refera obiecte diferite. Aceleasi reguli se aplica si pentru operatorul!=.Cum facem ınsa pentru a vedea daca obiectele referite sunt identice? De exemplu, cumputem sa verificam faptul ca cerc1 si cerc2 refera obiecte Cerc care sunt egale? Obiectelepot fi comparate folosind metoda equals. Vom vedea ın curand un exemplu de folosire alui equals, ın care vom discuta despre tipul String (paragraful 3.3). Fiecare obiect are ometoda equals, care, ın mod implicit, nu face altceva decat testul ==. Pentru ca equals safunctioneze corect, programatorul trebuie sa redefineasca aceasta metoda pentru obiectelepe care le creaza.

3.2.7 Supraıncarcarea operatorilor pentru obiecte

In afara unei singure exceptii pe care o vom discuta ın paragraful urmator, operatorii nupot fi definiti pentru a lucra cu obiecte2. Astfel, nu exista operatorul < pentru nici unfel de obiect. Pentru acest scop, va trebui definita o metoda, cum ar fi lessThan, care varealiza comparatia.

3.3 Siruri de caractere (stringuri)

Sirurile de caractere ın Java sunt definite folosind obiectul String. Limbajul Java face sapara ca String este un tip primitiv, deoarece pentru el sunt definiti operatorii + si +=pentru concatenare. Totusi, acesta este singurul tip referinta pentru care Java a permissupraıncarcarea operatorilor. In rest, String se comporta ca orice alt obiect.

3.3.1 Fundamentele utilizarii stringurilor

Exista doua reguli fundamentale referitoare la obiectele de tip String. Prima este aceea ca,exceptand operatorul de concatenare, obiectele de tip String se comporta ca toate celelalteobiecte. A doua regula este aceea ca stringurile sunt ne-modificabile. Aceasta ınseamnaca, odata construit, un obiect de tip String nu mai poate fi modificat. Deoarece obiectele

2Aceasta este o diferenta notabila ıntre Java si C++, care permite supraıncarcarea operatorilor pentruobiecte. Inginerii de la Sun au considerat ca supraıncarcarea operatorilor pentru obiecte aduce mai multeprobleme decat beneficii si au decis ca Java sa nu permita acest lucru

Page 31: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

3.3. SIRURI DE CARACTERE (STRINGURI) 31

de tip String nu se pot modifica, putem folosi linistiti operatorul = pentru ele. Iata unexemplu:

String vid = "" ;String mesaj = "Salutare!" ;String repetat = mesaj ;

Dupa aceste declaratii, exista doua obiecte de tip String. Primul este un sir vid, si estereferit de variabila vid. Al doilea este sirul ”Salutare!”, care este referit de variabilele mesajsi repetat. Pentru majoritatea obiectelor, faptul ca obiectul este referit de doua variabilear putea genera probleme. Totusi, deoarece stringurile nu pot fi modificate, partajarea lornu pune nici un fel de probleme. Singura posibilitate de a modifica valoarea catre carerefera variabila repetat este aceea de a construi un nou obiect de tip String si a-l atribuilui repetat. Aceasta operatie nu va avea nici un efect asupra valorii pe care o refera mesaj.

3.3.2 Concatenarea stringurilor

Java nu permite supraıncarcarea operatorilor pentru tipurile referinta. Totusi, pentrucomoditate, se acorda o exceptie speciala pentru concatenarea de obiecte de tipul String.Atunci cand cel putin unul dintre operanzi este de tip String, operatorul + realizeazaconcatenarea. Rezultatul este o referinta catre un obiect nou construit de tip String. Iatacateva exemple:

"Sunt" + " curajos!" //rezulta "Sunt curajos!"2 + " mere" //rezulta "2 mere""mere " + 2 //rezulta "mere 2""a" + "b" + "c" //rezulta "abc"

Sirurile de caractere formate dintr-un singur caracter NU trebuie ınlocuite cu constantede tip caracter (constantele caracter sunt de fapt numere).Java dispune si de operatorul += pentru siruri de caractere. Efectul instructiunii str+=expr este str = str + expr. Cu alte cuvinte str va referi un nou String generat de str +expr.Este important sa observam ca ıntre atribuirea:

i=i+5 //i este un intreg

si atribuirea:

str=str+"hello" //str este un String

exista o diferenta esentiala. In primul caz, variabila i este incrementata cu 5; locatia dememorie a lui i nu se modifica. In al doilea caz, se creaza un nou string avand valoareastr+”hello”. Dupa atribuire, str va referi acest nou string. Fostul string referit va fi supuslui garbage-collection daca nu a existat o alta referinta catre el.

Page 32: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

32 CAPITOLUL 3. REFERINTE

3.3.3 Comparatia stringurilor

Deoarece operatorul de atribuire functioneaza pe siruri de caractere, am fi tentati sacredem ca functioneaza si operatorii relationali. Acest lucru nu este ınsa adevarat.Conform regulii privind supraıncarcarea operatorilor, operatorii relationali ( <, <=, >,>=) nu sunt definiti pentru obiecte de tip String. Mai mult, operatorii == si != ausemnificatia clasica pentru obiecte de tip referinta. De exemplu, pentru doua obiecte detip String, x si y, expresia x == y este adevarata doar daca x si y refera acelasi obiect detip String. Astfel, daca x si y refera obiecte diferite cu continut identic, expresia x == yeste falsa. Acelasi rationament este valabil si pentru !=.Pentru a testa egalitatea a doua obiecte de tip String, se foloseste metoda equals. Expresiax.equals(y) este adevarata daca sirurile de caractere referite de x si de y sunt identice.Un test mai general poate fi realizat cu metoda compareTo. Expresia x.compareTo(y)compara doua obiecte de tip String x si y. Valoarea returnata este un numar negativ, zerosau un numar pozitiv daca x este mai mic, egal, respectiv mai mare decat y.

3.3.4 Alte metode pentru stringuri

Lungimea unui obiect de tip String (un sir vid are lungimea 0) poate fi obtinuta cu metodalength(). Deoarece, length() este o metoda parantezele sunt necesare.Exista doua metode pentru a accesa caracterele din interiorul unui String. Metoda charAtreturneaza caracterul aflat la pozitia specificata (primul caracter este pe pozitia 0). Metodasubstring returneaza o referinta catre un String nou construit. Metoda are ca parametripozitia de ınceput si pozitia primului caracter neinclus.Iata un exemplu de folosire al acestor metode:

String mesaj = "Hello" ;int lungimeMesaj = mesaj.length() ; //lungimea este 5char ch = mesaj.charAt(1) ; //ch este ’e’String subSir = mesaj.substring( 2 , 4 ) ; //sub este "ll"

3.3.5 Conversia de la string la tipurile primitive

Metoda toString() poate fi utilizata pentru a converti orice tip primitiv la String. Deexemplu, toString(45) returneaza o referinta catre sirul nou construit ”45”. Majoritateaobiectelor furnizeaza o implementare a metodei toString(). De fapt, atunci cand operatorul+ are un operand de tip String, operandul care nu este de tip String este automat convertitla String folosind metoda toString(). Pentru tipurile de date numerice, exista o variantaa metodei toString() care permite precizarea unei anumite baze. Astfel, instructiunea:

System.out.println( Integer.toString( 55 , 2 ) ) ;

are ca efect tiparirea reprezentarii ın baza 2 a numarului 55.Pentru a converti un String la un int se poate folosi metoda Integer.parseInt(). Aceastametoda genereaza o exceptie daca String-ul convertit nu contine o valoare ıntreaga. Despreexceptii vom vorbi pe scurt ın paragraful 3.5. Pentru a obtine un double dintr-un Stringse poate utiliza metoda parseDouble(). Iata doua exemple:

int x = Integer.parseInt( "75" ) ;double y = Double.parseDouble( "3.14" ) ;

Page 33: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

3.4. SIRURI 33

3.4 Siruri

Sirurile sunt structura fundamentala prin care se pot retine mai multe elemente de acelasitip. In Java, sirurile nu sunt tipuri primitive; ele se comporta foarte asemanator cu unobiect. Din acest motiv, multe dintre regulile care sunt valabile pentru obiecte se aplicasi la siruri.Fiecare element dintr-un sir poate fi accesat prin mecanismul de indiciere oferit de opera-torul [ ]. Spre deosebire de limbajele C sau C++, Java verifica validitatea indicilor3.In Java, ca si ın C, sirurile sunt ıntotdeauna indiciate de la 0. Astfel, un sir a cu 3elemente este format din a[0], a[1], a[2]. Numarul de elemente care pot fi stocate ın sirul aeste permanent retinut ın variabila a.length. Observati ca aici (spre deosebire de String-uri)nu se pun paranteze. O parcurgere tipica pentru un sir ar fi:

for( int i = 0 ; i < a.length ; i++ )

3.4.1 Declaratie, Atribuire si Metode

Un sir de elemente ıntregi se declara astfel:

int[] sir1 ;

Deoarece un sir este un obiect, declaratia de mai sus nu aloca memorie pentru sir. Variabilasir1 este doar un nume (referinta) pentru un sir de numere ıntregi, si ın acest momentvaloarea ei este null. Pentru a aloca 100 de numere ıntregi, vom folosi instructiunea:

sir1 = new int[100];

Acum sir1 este o referinta catre un sir de 100 de numere ıntregi.Exista si alte posibilitati de a declara siruri. De exemplu, putem unifica declaratia sialocarea de memorie:

int[] sir1 = new int[100];

Se pot folosi si liste de initializare, ca ın C sau C++. In exemplul urmator se aloca un sircu patru elemente, care va fi referit de catre variabila sir2:

int[] sir2 = 3, 4, 6, 19 ;

Parantezele patrate pot fi puse fie ınainte fie dupa numele sirului. Plasarea parantezelorınainte de nume face mai vizibil faptul ca este vorba de un sir, de aceea vom folosi aceastanotatie.Declararea unui sir de obiecte (deci nu tipuri primitive) foloseste aceeasi sintaxa. Trebuiesa retineti ınsa ca dupa alocarea sirului, fiecare element din sir va avea valoarea null.Pentru fiecare element din sir trebuie alocata memorie separat. De exemplu, un sir cu 5cercuri se construieste astfel:

3Acesta este un lucru foarte important care vine ın ajutorul programatorilor, mai ales a celor ıncepatori.Indicii acaror valoare depasesc numarul de elemente alocat sunt adeseori cauza multor erori obscure ın Csi C++. In Java, accesarea unui sir cu un indice ın afara limitei este imediat semnalata prin exceptiaIndexOutOfBoundsException.

Page 34: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

34 CAPITOLUL 3. REFERINTE

Cerc[] sirDeCercuri ;//declaram un sir de cercurisirDeCercuri = new Cerc[ 5 ] ;

//alocam memorie pentru 5 referinte la Cercfor( int i = 0 ; i < sirDeCercuri.length ; ++i)

sirDeCercuri[ i ] = new Cerc() ;//se aloca un obiect Cerc referintei nr. i

Programul din Figura 3.4 ilustreaza modul de folosire al sirurilor ın Java. In jocul deloterie, sase numere de la 1 la 49 sunt selectate saptamanal. Programul alege aleatornumere pentru 1000 de jocuri. Programul afiseaza apoi de cate ori a aparut fiecare numarın cele 1000 de jocuri. Linia 14 declara un sir de numere ıntregi care retine numarul deaparitii ale fiecarui numar. Deoarece indicierea sirurilor ıncepe de la 0, adunarea cu 1 esteesentiala. Fara aceasta adunare am fi avut un sir cu elemente de la 0 la 48, si orice accesla elementul cu indicele 49 ar fi generat o exceptie IndexOutOfBoundsException. Cicluldin liniile 15-18 initializeaza valorile sirului cu 0. Restul programului este relativ simplu.Se foloseste din nou metoda Math.random() care genereaza un numar ın intervalul [0,1).Rezultatele sunt afisate ın liniile 28-31.Dat fiind faptul ca sirul este un tip referinta, operatorul = nu copiaza siruri. De aceeadaca x si y sunt siruri, efectul secventei de instructiuni:

int[] x = new int[100] ;int[] y = new int[100] ;...x = y ;

este ca x si y refera acum al doilea sir.Sirurile pot fi utilizate ca parametri pentru metode. Regulile de transmitere se deduc logicdin faptul ca sirul este o referinta. Sa presupunem ca avem o metoda f care accepta unsir de int ca parametru. Apelul si definirea arata astfel:

f( sirActual ) ; //apelul metodeivoid f( int[] sirFormal) ; //declaratia metodei

Conform conventiilor de transmitere a parametrilor ın Java pentru tipurile referinta, vari-abilele sirActual si sirFormal refera acelasi obiect. Astfel, accesul la sirFormal[i] este defapt un acces la sirActual[i]. Aceasta ınseamna ca variabilele continute ın sir pot fi mod-ificate de catre metoda. O observatie importanta este aceea ca linia de cod din cadrul luif():

sirFormal = new int [20] ;

nu are nici un efect asupra lui sirActual. Acest lucru se datoreaza faptului ca ın Javatransmiterea parametrilor se face prin valoare, deci sirFormal este o noua referinta catresir. Instructiunea de mai sus nu face decat sa schimbe sirul catre care refera sirFormal(vezi Figura 3.3).

Page 35: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

3.4. SIRURI 35sirActual

HHjsirActual

HHjsirFormal

sirActual

HHjsirFormal

HHj

a. ınainte de apel b. imediat dupa apel c. dupa atribuirea sirFormal=new int[20];

Figura 3.3 Transmiterea parametrilor ın Java

Deoarece numele sirurilor sunt doar niste referinte, o functie poate sa returneze un sir.

1. //clasa demonstrativa pentru siruri2. public class Loterie3. 4. //genereaza numere de loterie intre 1 si 495. //afiseaza numarul de aparitii al fiecarui numar6. //declaratii constante:7. public static final int NUMERE = 49 ;8. public static final int NUMERE_PE_JOC = 6 ;9. public static final int JOCURI = 1000 ;10. //main11. public static final void main( String[] args )12. 13. //genereaza numerele14. int [] numere = new int[NUMERE+1] ;15. for( int i=0 ; i < numere.length; ++i )16. 17. numere[i] = 0 ;18. 19.20. for(int i =0 ; i < JOCURI; ++i)21. 22. for( int j = 0 ; j < NUMERE_PE_JOC; ++j )23. 24. numere[ (int)(Math.random()*49)+1 ] ++ ;25. 26. 27. //afisare rezultate28. for( int k = 1 ; k <= NUMERE; ++k )29. 30. System.out.println( k + ": " + numere[k] ) ;31. 32. 33.

Figura 3.4 Program demonstrativ pentru siruri

3.4.2 Expansiunea dinamica a sirurilor

Sa presupunem ca dorim sa citim o secventa de numere si sa o retinem ıntr-un sir. Unadintre proprietatile fundamentale ale sirurilor este aceea ca ınainte de a fi utilizate, trebuie

Page 36: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

36 CAPITOLUL 3. REFERINTE

sa alocam memorie pentru un numar fix de elemente care vor fi stocate. Daca nu stim dela bun ınceput cate elemente vor fi stocate ın sir, va fi dificil sa alegem o valoare rezonabilapentru dimensiunea sirului. Aceasta sectiune prezinta o metoda prin care putem extindedinamic sirul daca dimensiunea initiala se dovedeste a fi prea mica. Aceasta tehnica poartanumele de expansiune dinamica a sirurilor si permite alocarea de siruri de dimensiunearbitrara pe care le putem redimensiona pe masura ce programul ruleaza.Alocarea obisnuita de memorie pentru siruri se realizeaza astfel:

int[] a = new int[10] ;

Sa presupunem ca dupa ce am facut aceasta declaratie, hotaram ca de fapt avem nevoiede 12 elemente si nu de 10. In aceasta situatie putem folosi urmatoarea manevra:

int original = a ; //salvam referinta lui aa = new int[12] ; //alocam din nou memoriefor(int i=0; i < 10; i++) //copiem elementele in a

a[i] = original[i] ;

Un moment de gandire este suficient pentru a ne convinge ca aceasta operatie este con-sumatoare de resurse, deoarece trebuie copiat originalul ınapoi ın noul sir a. De exemplu,daca extensia dinamica a numarului de elemente ar trebui facuta ca raspuns la citireade date, ar fi ineficient sa expansionam ori de cate ori citim cateva elemente. Din acestmotiv, de cate ori se realizeaza o extensie dinamica, numarul de elemente este crescut cuun coeficient multiplicativ. Am putea de exemplu dubla numarul de elemente la fiecareexpansiune dinamica. Astfel, dintr-un sir cu N elemente, generam un sir cu 2N elemente,iar costul expansiunii este ımpartit ıntre cele N elemente care pot fi inserate ın sir fara arealiza extensia.Pentru a face ca lucrurile sa fie mai concrete, Figura 3.5 prezinta un program care citesteun numar nelimitat de numere ıntregi de la tastatura si le retine ıntr-un sir a carui dimen-siune este extinsa dinamic. Functia resize realizeaza expansiunea (sau contractia!) siruluireturnand o referinta catre un sir nou construit. Similar, metoda getInts returneaza oreferinta catre sirul ın care sunt citite elementele.La ınceputul lui getInts(), nrElemente este initializat cu 0 si ıncepem cu un sir cu 5 e-lemente. In linia 36 citim ın mod repetat cate un element. Daca sirul este ”umplut”,lucru indicat de intrarea ın testul de la linia 36, atunci sirul este expansionat prin apelulmetodei resize. Liniile 51-61 realizeaza expansiunea sirului folosind strategia prezentataanterior. La linia 40, elementul citit este stocat ın tablou, iar numarul de elemente cititeeste incrementat. In final, ın lina 48 contractam sirul la numarul de elemente citite efectiv.

1. import java.io.* ;2. //clasa pentru citirea unui numar nelimitat de valori intregi3. public class ReadInts4. 5. public static void main( String[] args )6. 7. int [] array = getInts( ) ;

Page 37: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

3.4. SIRURI 37

8.9. System.out.println("Elementele citite sunt: " ) ;10. for( int i = 0; i < array.length; i++ )11. 12. System.out.println( array[i] ) ;13. 14. 15.16. /* citeste un numar nelimitat de valori intregi17. * fara a trata erorile */18. public static int[] getInts()19. 20. //BufferedReader este prezentata in sectiunile urmatoare21. BufferedReader in = new BufferedReader(22. new InputStreamReader( System.in ) ) ;23.24. int[] elemente = new int[ 5] ; //se aloca 5 elemente25. int nrElemente = 0 ; //numarul de elemente citite26. String s ; //sir in care se citeste cate o linie27.28. System.out.println("Introduceti numere intregi cate unul pe linie:");29.30. try31. 32. //cat timp linia e nevida33. while( (s = in.readLine()) != null )34. 35. int nr = Integer.parseInt( s ) ;36. if( nrElemente == elemente.length ) //sirul a fost "umplut"37. 38. elemente=resize(elemente,elemente.length*2);//dubleaza dimensiunea sirului39. 40. elemente[ nrElemente++ ] = nr ;41. 42. 43. catch( Exception e )44. 45. //nu se trateaza exceptia46. 47. System.out.println( "Citire incheiata." ) ;48. return resize( elemente, nrElemente ) ;//trunchiaza sirul la numarul de elemente citite49. 50.51. public static int[] resize( int[] sir, int dimensiuneNoua )

Page 38: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

38 CAPITOLUL 3. REFERINTE

52. 53. int[] original = sir ;54. int elementeDeCopiat=Math.min(original.length,dimensiuneNoua);55. sir = new int[ dimensiuneNoua ] ;56. for( int i=0; i<elementeDeCopiat; ++i)57. 58. sir[i] = original[i] ;59. 60. return sir ;61. 62.

Figura 3.5 Program pentru citirea unui numar nelimitat de numere ıntregi urmata deafisarea lor

3.4.3 Siruri cu mai multe dimensiuni

In anumite situatii trebuie sa stocam datele ın siruri cu mai multe dimensiuni. Cel maides folosite sunt matricele, adica sirurile cu doua dimensiuni. Alocarea de memorie pentrusiruri cu mai multe dimensiuni se realizeaza precizand numarul de elemente pentru fiecareindice, iar indicierea se face plasand fiecare indice ıntre paranteze patrate. Ca un exemplu,declaratia:

int[][] x = new int[2][3] ;

defineste matricea x, ın care primul indice poate fi 0 sau 1, iar al doilea este de la 0 la 2.

3.4.4 Argumente ın linie de comanda

Parametrii transmisi ın linie de comanda sunt disponibili prin examinarea parametrilorfunctiei main. Sirul de stringuri numit args din functia main contine parametri transmisiın linie de comanda. De exemplu, daca avem un program numit Echo.java pe care ılexecutam cu comanda:

java Echo buna ziua

parametrul args[0] va fi o referinta catre stringul ”buna”, iar parametrul args[1] va fi oreferinta catre ”ziua”. Astfel, programul de mai jos implementeaza comanda echo exis-tenta atat ın DOS cat si ın Linux (de fapt, implementeaza o versiune simplificata a acesteicomenzi care afiseaza la consola sirurile trimise ca argument):

1. public class Echo2. 3. //afiseaza parametrii primiti in linie de comanda4. public static void main(String[] args)5. 6. if( args.length == 0 )7. 8. System.out.println( "Nu exista argumente" ) ;

Page 39: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

3.5. TRATAREA EXCEPTIILOR 39

9. return ;10. 11. for( int i=0 ; i < args.length; ++i)12. 13. System.out.print( args[i] ) ;14. 15. 16.

3.5 Tratarea exceptiilor

Exceptiile sunt obiecte care retin informatie si care sunt transmise ın afara secventelorreturn. Exceptiile sunt propagate ınapoi prin secventa de functii apelate pana cand o anu-mita metoda prinde exceptia. Exceptiile sunt folosite pentru a semnala situatii exceptie,cum ar fi erorile.De fiecare data cand o exceptie este ıntalnita ıntr-o metoda a unei clase, programul vapermite recuperarea pierderilor cauzate de exceptie si se va ıncheia fara a cauza cadereasistemului. Prin pregatirea tratarii conditiilor de exceptie ın care va ajunge executiaprogramului, se va crea un program mult mai prietenos pentru utilizator. Un program Javapoate detecta erori si apoi indica sistemului de executie ce erori a ıntalnit prin generareaunor conditii de exceptie ce vor duce la oprirea executiei si afisarea unui cod de eroare,sau daca doriti sa tratati unele exceptii ıntr-un mod propriu, puteti folosi o clauza catchpentru a obtine controlul ıntr-o situatie de exceptie.

3.5.1 Procesarea exceptiilor

Secventa de cod din Figura 3.6 prezinta modul de folosire al exceptiilor. Secventa decod care ar putea genera o exceptie care sa fie propagata este inclusa ıntr-un bloc try.Blocul try se extinde de la linia 12 la linia 16. Imediat dupa blocul try trebuie sa aparasecventele de tratare a exceptiilor. Programul ”sare” la secventa de tratare a exceptiilordoar ın situatia ın care se genereaza o exceptie; ın momentul generarii exceptiei, blocultry din care exceptia provine se considera a fi ıncheiat.Fiecare dintre blocurile catch este ıncercat pe rand pana cand se gaseste o secventa detratare adecvata. Deoarece Exception se potriveste cu toate exceptiile generate, ea esteadecvata pentru orice fel de exceptie generata ın blocul try. Exceptiile generate de secventatry din programul nostru pot fi IOException generate de readLine daca apare o eroare lacitire, si NumberFormatException generata de parseInt daca linia citita de la tastatura nupoate fi convertita la int.In cazul unei exceptii se executa codul din blocul catch -ın situatia noastra linia 19. Dupaaceasta blocul catch si blocul continand cuplul try/catch se considera ıncheiate. Un mesajreferitor la eroarea generata este tiparit folosind obiectul de tip Exception numit e. Putemalege sa facem si alte actiuni ın caz de eroare, cum ar fi furnizarea de mesaje de eroaremai detaliate etc.

1. import java.io.* ;2.

Page 40: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

40 CAPITOLUL 3. REFERINTE

3. public class DivideByTwo4. 5. public static void main( String[] args )6. 7. //BufferedReader este prezentata in sectiunile urmatoare8. BufferedReader in = new BufferedReader(9. new InputStreamReader( System.in ) ) ;10.11. System.out.println("Introduceti o valoare intreaga: ") ;12. try13.

//aceasta linie poate genera o exceptie14. int x = Integer.parseInt( in.readLine() ) ;15. System.out.println( "Jumatatea lui "+x+"este"+(x/2)) ;16. 17. catch( Exception e )18. //aceasta instructiune se executa daca linia 14 a generat o exceptie19. System.out.println( e ) ;20. 21. 22.

Figura 3.6 Program simplu pentru ilustrarea exceptiilor

Se pot preciza diferite tipuri de instructiuni pentru fiecare tip de exceptie ıntalnita uti-lizand instructiunea throw. De asemenea, puteti avea propriile obiecte definite ca exceptii,prin care sa tratati anumite evenimente speciale care se pot ıntampla pe timpul executieiprogramului. Pentru a va crea o clasa proprie de exceptii, trebuie ca aceasta sa fie osubclasa4 a clasei Exception.

public class NewException extends Exception...

Acum puteti trata exceptia ın modul dorit:

...try...throw new NewException(e);...

catch(NewException e)

4Despre conceptul de mostenire vom discuta ın capitolul 5.

Page 41: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

3.5. TRATAREA EXCEPTIILOR 41

...//tratarea noii exceptiicatch(Exception e)...//tratarea unor erori generale...

Dupa cum am mai spus dupa tratarea unei exceptii este ignorata orice secventa deinstructiuni. Totusi, daca este neaparata nevoie sa se execute anumite operatii, veti invocaclauza finally. Blocul finally este executat si ın cazul ın care nu are loc nici o exceptie ınblocul try.

try...finally...

3.5.2 Exceptii uzuale

Exista mai multe tipuri de exceptii standard ın Java. O prima categorie de exceptii o con-stituie exceptiile de executie standard (standard runtime exceptions) cum ar fi ımpartireaunui ıntreg la 0 sau accesarea unui element de tablou cu indice ilegal. Avand ın vederefaptul ca aceste exceptii pot aparea practic ın orice secventa de program, ar fi o muncasisifica sa definim secvente de tratare a unor astfel de exceptii. Daca se furnizeaza un bloccatch aceste exceptii se comporta exact la fel ca celelalte exceptii. Daca apare o exceptiestandard pentru care nu exista un bloc catch aceasta se propaga normal, trecand chiar si defunctia main. In aceasta situatie, exceptia produce o terminare anormala a programului,ınsotita de un mesaj de eroare. Figura 3.7 prezinta cateva dintre cele mai uzuale eroristandard de executie.

EXCEPTIE STANDARD DE EXECUTIE SEMNIFICATIE

ArithmeticException Depasire sau ımpartirea unui ıntreg la 0

NumberFormatException Conversie nepermisa a unui String la un tip numeric

IndexOutOfBoundsException Indice ilegal ıntr-un sir sau String

NegativeArraySizeException Tentativa de a crea un sir cu nr. negativ de elemente

NullPointerException Tentativa de a folosi o referinta care are valoarea null

SecurityException Incalcare de securitate ın timpul executiei

Figura 3.7 Cateva exceptii de executie uzuale.

Page 42: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

42 CAPITOLUL 3. REFERINTE

Majoritatea exceptiilor sunt de tipul exceptii standard tratate (standard checked excep-tions). Daca se apeleaza o metoda care poate genera direct sau indirect o astfel deexceptie, atunci programatorul fie trebuie sa o trateze cu un bloc de tip catch sau saindice explicit faptul ca exceptia urmeaza sa fie propagata prin folosirea clauzei throwsın antetul metodei. Retineti faptul ca exceptia tot va trebui tratata la un moment dat,deoarece metoda main (care este la ultimul nivel) nu poate avea o clauza throws.

EXCEPTIE STANDARD TRATATA SEMNIFICATIE

java.io.EOFException Terminare de fisier ınainte de ıncheierea citirii

java.io.FileNotFoundException Fisierul nu a fost gasit pentru a fi deschis

java.io.IOException Cuprinde majoritatea erorilor de intrare/iesire

InterruptedException Aruncata de metoda Thread.Sleep

Figura 3.8 Exceptii standard tratate uzual

Ultima categorie de exceptii sunt erorile care nu sunt prinse de Exception. De obicei acesteexceptii nu pot fi tratate. Cea mai uzuala este OutOfMemoryError. Pentru a prinde oriceexceptie posibila, prindeti un obiect de tip Throwable si utilizati clauza throws. Aceastaclauza apare ın antetul unei metode pentru a specifica tipurile de exceptii ce pot fi lansateın metoda respectiva. Numele acestor tipuri sunt clase derivate din clasa Exception carela randul ei este derivata din clasa Throwable. Clasele Exception si Throwable se afla ınjava.lang.

3.6 Intrare si iesire

Intrarea si iesirea ın Java se realizeaza cu ajutorul claselor din pachetul java.io. Dinacest motiv, orice program care foloseste rutinele de intrare/iesire trebuie sa cuprindainstructiunea:

import java.io.* ;

Biblioteca Java de intrare/iesire este extrem de sofisticata si are un numar foarte marede optiuni. In aceasta faza vom examina doar elementele de baza referitoare la intrare siiesire, concentrandu-ne ın ıntregime asupra operatiilor de intrare-iesire formatate.

3.6.1 Operatii de baza pe fluxuri (stream-uri)

Exista trei fluxuri predefinite pentru operatii I/O de la terminal: System.in, intrareastandard, System.out, iesirea standard si System.err, fluxul de erori.Asa cum am aratat deja, metodele print si println sunt folosite pentru afisare formatata.Orice fel de tip poate fi convertit la o forma tiparibila, folosind metoda toString; ın multecazuri, acest lucru este realizat automat. Spre deosebire de C si C++ care dispun de unnumar enorm de optiuni de formatare, afisarea ın Java se face exclusiv prin concatenarede String-uri, fara nici o formatare.O modalitate simpla de a realiza citirea este aceea de a citi o singura linie ıntr-un obiect

Page 43: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

3.6. INTRARE SI IESIRE 43

de tip String folosind readLine. Metoda readLine preia caractere de la intrare pana candıntalneste un terminator de linie sau sfarsit de fisier. Metoda returneaza caracterele citite(din care extrage terminatorul de linie) ca un String nou construit. Pentru a putea folosireadLine trebuie sa construim un obiect BufferedReader dintr-un obiect InputStreamReadercare la randul sau este construit din System.in. Acest lucru a fost ilustrat ın Figura 3.6,liniile 8 si 9.Daca primul caracter citit este EOF, atunci functia readLine returneaza null. Daca apareo eroare la citire se genereaza o exceptie de tipul IOException. Daca sirul citit nu poate ficonvertit la o valoare ıntreaga se genereaza o NumberFormatException.

3.6.2 Obiectul StringTokenizer

Sa ne reamintim ca pentru a citi o valoare primitiva, cum ar fi int, foloseam readLinepentru a prelua linia ca pe un String si apoi aplicam metoda parseInt pentru a convertistringul la tipul primitiv.In multe situatii avem mai multe elemente pe aceeasi linie. Sa presupunem, de exemplu,ca fiecare linie are doua valori ıntregi. Java furnizeaza clasa StringTokenizer pentru asepara String-ul ın elemente lexicale (engl. token ). Pentru a putea folosi obiecte ale claseiStringTokenizer se foloseste directiva:

import java.util.* ;

Folosirea obiectului StringTolenizer este prezentata ın programul din Figura 3.9. ObiectulStringTokenizer este construit ın linia 19 prin furnizarea obiectului String reprezentandlinia citita. Metoda countTokens din linia 20 ne va da numarul de cuvinte citite (elementelexicale). Metoda nextToken ıntoarce urmatorul cuvant necitit ca pe un String. Aceastaultima metoda genereaza o NoSuchElementException daca nu exista nici un cuvant ramas,dar aceasta este o exceptie de executie standard (vezi 3.5.2) si nu trebuie prinsa. La liniile25 si 26 folosim nextToken urmata de parseInt pentru a obtine un int. Toate erorile suntprinse ın blocul catch.Implicit, elementele lexicale sunt separate de spatii. Obiectul StringTokenizer poate fi ınsaconstruit si ın asa fel ıncat sa recunoasca si alte caractere drept delimitatori.

1. //program pentru exemplificarea clasei2. //StringTokenizer. Programul citeste doua numere aflate pe3. //aceeasi linie si calculeaza maximul lor4.5. import java.io.* ;6. import java.util.* ;7.8. public class TokenizerTest9. 10. public static void main(String[] args)11. 12. BufferedReader in = new BufferedReader(13. new InputStreamReader(System.in) ) ;14.

Page 44: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

44 CAPITOLUL 3. REFERINTE

15. System.out.println("Introduceti doua nr. pe aceeasi linie:");16. try17. 18. String s = in.readLine() ;19. StringTokenizer st = new StringTokenizer( s ) ;20. if( st.countTokens() != 2 )21. 22. System.out.println("Numar invalid de argumente!") ;23. return ;24. 25. int x = Integer.parseInt( st.nextToken() ) ;26. int y = Integer.parseInt( st.nextToken() ) ;27. System.out.println("Maximul este: "+Math.max(x,y)) ;28. 29. catch( Exception e)30. 31. System.out.println( e ) ;32. 33. 34.

Figura 3.9 Program demonstrativ pentru clasa StringTokenizer

3.6.3 Fisiere secventiale

Una dintre regulile fundamentale ın Java este aceea ca ceea ce se poate face pentru operatiiI/O de la terminal se poate face si pentru operatii I/O din fisiere. Pentru a lucra cu fisiereobiectul BufferedReader nu se construieste pe baza unui InputStreamReader. Vom folosiın schimb un obiect de tip FileReader care va fi construit pe baza unui nume de fisier.Un exemplu pentru ilustrarea acestor idei este prezentat ın Figura 3.10. Acest programlisteaza continutul fisierelor text care sunt precizate ca parametri ın linie de comanda.Functia main parcurge pur si simplu argumentele din linia de comanda, apeland metodalistFile pentru fiecare argument. In metoda listFile construim obiectul de tip FileReaderla linia 26 si apoi ıl folosim pe acesta pentru construirea obiectului de tip BufferedReaderın linia urmatoare. Din acest moment, citirea este identica cu cea de la tastatura.Dupa ce am ıncheiat citirea din fisier, trebuie sa ınchidem fisierul, altfel s-ar putea sa numai putem deschide alte stream-uri (sa atingem limita maxima de stream-uri care potfi deschise). Nu putem face acest lucru ın primul bloc try, deoarece o exceptie ar puteagenera parasirea prematura a blocului si fisierul nu ar fi ınchis. Din acest motiv, fisieruleste ınchis dupa secventa try/catch.Scrierea formatata ın fisiere este similara cu citirea formatata.

1. //program pentru afisarea de fisiere2. //al caror nume este precizat in linia de comanda3. import java.io.* ;4.5. public class Lister

Page 45: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

3.6. INTRARE SI IESIRE 45

6. 7. public static void main( String[] args )8. 9. if( args.length == 0 )10. 11. System.out.println( "Nu ati precizat nici un fisier!") ;12. 13.14. for(int i = 0 ; i < args.length; ++i )15. 16. listFile( args[i] ) ;17. 18. 19.20. public static void listFile( String fileName )21. 22. System.out.println("NUME FISIER: " + fileName) ;23.24. try25. 26. FileReader file = new FileReader( fileName ) ;27. BufferedReader in = new BufferedReader( file ) ;28. String s ;29. while( ( s = in.readLine() ) != null )30. 31. System.out.println( s ) ;32. 33. 34. catch( Exception e )35. 36. System.out.println( e ) ;37. 38. try39. 40. if( in != null )41. 42. in.close() ;43. 44. 45. catch( IOException e )46. //ignora exceptia47. 48. 49.

Figura 3.10 Program de listare a continutului unui fisier

Page 46: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

46 CAPITOLUL 3. REFERINTE

3.7 Probleme propuse

1. O suma de verificare este un ıntreg pe 32 de biti (int) care este suma codurilorUnicode ale caracterelor dintr-un fisier (se permite depasirea, desi aceasta este putinprobabila daca toate caracterele sunt ASCII). Doua fisiere identice au aceeasi sumade verificare. Scrieti un program care calculeaza suma de verificare pentru un fisiercare este furnizat ca parametru ın linie de comanda.

2. Modificati programul Echo.java din acest capitol astfel ıncat daca nu se transmitparametri de la linia de comanda sa foloseasca intrarea standard.

3. Scrieti o metoda care returneaza true daca stringul str1 este prefix pentru stringulstr2. Nu folositi nici o alta metoda generala de cautare pe stringuri ın afara decharAt.

Page 47: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

Capitolul 4

Obiecte si clase

In acest capitol vom ıncepe sa discutam despre programarea orientata pe obiecte (objectoriented programming - OOP). O componenta fundamentala a programarii orientate peobiecte este specificarea, implementarea si folosirea obiectelor. In capitolul anterior amvazut deja cateva exemple de obiecte, cum ar fi string-urile si fisierele (stream-urile) carefac parte din bibliotecile limbajului Java. Am putut observa si faptul ca fiecare obiect estecaracterizat de o anumita stare care poate fi modificata prin aplicarea operatorului punct(.). In limbajul Java, starea si functionalitatea unui obiect se definesc prin intermediulunei clase. Un obiect este de fapt o instanta a unei clase.

4.1 Ce este programarea orientata pe obiecte?

Programarea orientata pe obiecte s-a impus ca modelul dominant al anilor ’90. In aceastasectiune vom prezenta modul ın care Java suporta programarea orientata pe obiecte sivom mentiona cateva dintre principiile ei fundamentale.In centrul programarii orientate pe obiecte se afla notiunea de obiect. Obiectul este ovariabila care are o structura si o stare. Fiecare obiect dispune de operatii prin intermediulcarora i se poate manipula starea. Asa cum am vazut deja, ın limbajul Java se facedistinctie ıntre un obiect si o variabila de un tip primitiv, dar aceasta este o specificitate alimbajului Java si nu a programarii orientate pe obiecte. Pe langa operatiile cu un caractergeneral, asupra obiectelor mai putem sa realizam:

• Crearea de noi obiecte, ınsotita eventual de initializarea obiectelor

• Copierea si testarea egalitatii

• Realizarea de operatii de intrare/iesire cu obiectele

Obiectul trebuie privit ca o unitate atomica pe care utilizatorul nu ar trebui sa o disece.In mod normal, nu ne punem problema de a jongla cu bitii din care este format un numarreprezentat ın virgula mobila si ar fi de-a dreptul ridicol sa ıncercam sa incrementam unastfel de numar prin modificarea directa a reprezentarii sale interne.Principiul atomicitatii este cunoscut sub numele de ascunderea informatiei. Utilizatorulnu are acces direct la partile unui obiect sau la implementarea sa. Acestea vor puteafi accesate doar prin intermediul metodelor care au fost furnizate ımpreuna cu obiectul.

47

Page 48: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

48 CAPITOLUL 4. OBIECTE SI CLASE

Putem privi fiecare obiect ca fiind ambalat cu mesajul ”Nu deschideti! Nu contine compo-nente reparabile de catre utilizator!”. In viata de zi cu zi, majoritatea celor care ıncearca sarepare componente cu aceasta inscriptie sfarsesc prin a face mai mult rau decat bine. Dinacest punct de vedere, programarea imita lumea reala. Gruparea datelor si a operatiilorasupra acestor date ın acelasi ıntreg (agregat), avand grija sa ascundem detaliile de im-plementare ale agregatului este cunoscuta sub numele de ıncapsulare.Unul dintre principalele scopuri ale programarii orientate pe obiecte este refolosirea codu-lui. La fel cum inginerii refolosesc din nou si din nou aceleasi componente ın proiectare,programatorii ar trebui sa refoloseasca obiectele ın loc sa le reimplementeze. Atunci candavem deja un obiect care implementeaza exact comportamentul pe care ıl dorim, refolosireanu pune nici un fel de probleme. Adevarata provocare apare atunci cand dorim sa folosimun obiect care deja exista, dar care, desi are un comportament foarte similar cu ceea cevrem, nu corespunde exact cu necesitatile noastre.Limbajele de programare orientate pe obiecte furnizeaza mai multe mecanisme ın acestscop. Unul dintre mecanisme este folosirea codului generic. Daca implementarea esteidentica, si difera doar tipul de baza al obiectului, nu este necesar sa rescriem complet co-dul ei: vom scrie ın schimb un cod generic care functioneaza pentru orice tip. De exemplu,algoritmul de sortare al unui sir de obiecte nu depinde de obiectele care sunt sortate, decise poate implementa un algoritm generic de sortare.Mostenirea este un alt mecanism care permite extinderea functionalitatii unui obiect. Cualte cuvinte, putem crea noi tipuri de date care sa extinda (sau sa restrictioneze) pro-prietatile tipului de date original.Un alt principiu important al programarii orientate pe obiecte este polimorfismul. Un tipreferinta polimorfic poate sa refere obiecte de mai multe tipuri. Atunci cand se apeleaza ometoda a tipului polimorfic, se va selecta automat metoda care corespunde tipului referitın acel moment.Un obiect ın Java este o instanta a unei clase. O clasa este similara cu un tip record dinPascal sau cu o structura din C, doar ca exista doua ımbunatatiri majore. In primul rand,membrii pot fi atat functii cat si date, numite ın acest context metode respectiv atribute. Inal doilea rand, domeniul de vizibilitate al acestor membri poate fi restrictionat. Deoarecemetodele care manipuleaza starea obiectului sunt membri ai clasei, ele sunt accesate prinintermediul operatorului punct, la fel ca si atributele. In terminologia programarii orien-tate pe obiecte, atunci cand apelam o metoda a obiectului spunem ca ”trimitem un mesaj”obiectului.

4.2 Un exemplu simplu

Sa ne amintim ca, atunci cand proiectam o clasa, este important sa ascundem detaliileinterne fata de utilizatorul clasei. Clasa poate sa ısi defineasca functionalitatea prin inter-mediul metodelor. Unele dintre aceste metode vor descrie cum se creeaza si initializeazao instanta a clasei, cum se realizeaza testele de egalitate si cum se scrie starea clasei.Celelalte metode sunt specifice structurii particulare pe care o are clasa. Ideea este cautilizatorul nu trebuie sa aiba dreptul de a modifica direct starea obiectului, el va trebuisa foloseasca metodele clasei pentru a realiza acest lucru. Aceasta idee poate fi impusaprin ascunderea anumitor membri fata de utilizator. Pentru a realiza aceasta vom preciza

Page 49: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

4.2. UN EXEMPLU SIMPLU 49

ca acesti membri sa fie stocati ın sectiunea private. Compilatorul va avea grija ca membridin sectiunea private sa fie inaccesibili utilizatorului acelui obiect. In general, toate datelemembru ar trebui sa fie declarate private.Figura 4.1 prezinta modul de definire al unei clase care modeleaza un cerc. Definireaclasei consta ın doua parti: public si private. Sectiunea public reprezinta portiunea careeste vizibila pentru utilizatorul obiectului. Deoarece datele sunt ascunse fata de utiliza-tor, sectiunea public va contine ın mod normal numai metode si constante. In exemplulnostru avem doua metode pentru a scrie si a citi raza obiectelor de tip Circle. Celelaltedoua metode calculeaza aria respectiv lungimea obiectului de tip Circle. Sectiunea privatecontine datele: acestea sunt invizibile pentru utilizatorul obiectului. Atributul radius vatrebui accesat doar prin intermediul metodelor publice setRadius si getRadius.

1. //clasa simpla Java care modeleaza un Cerc2. import java.util.* ;3. public class Circle4. 5.6. //raza cercului7. //valoarea razei nu poate fi modificata8. //direct de catre utilizator9. private double radius ;10.11. public void setRadius(double r) //modifica raza cercului12. 13. radius = r ;14. 15.16. public double getRadius() //metoda ptr a obt raza cercului17. 18. return radius ;19. 20.21. public double area() //metoda ptr calculul ariei cercului22. 23. return Math.PI*radius*radius ;24. 25.26. public double length() //metoda ptr calculul lungimii27. 28. return 2*Math.PI*radius ;29. 30.

Figura 4.1 Definirea clasei Circle

Figura 4.2 prezinta modul de folosire al unui obiect de tip Circle. Deorece setRadius,getRadius, area si length sunt membri ai clasei, ei sunt accesati folosind operatorul punct.

Page 50: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

50 CAPITOLUL 4. OBIECTE SI CLASE

Atributul radius ar fi putut si el sa fie accesat folosind operatorul punct, daca nu ar fifost declarat de tip private. Accesarea lui radius din linia 15 ar fi fost ilegala daca nu eracomentata.Sa rezumam terminologia ınvatata. Clasa contine membri care pot fi atribute (campuri,date) sau metode (functii). Metodele pot actiona asupra atributelor si pot apela altemetode. Modificatorul de vizibilitate public ınseamna ca membrul respectiv este accesibiloricui prin intermediul operatorului punct. Modificatorul de vizibilitate private ınseamnaca membrul respectiv este accesibil doar metodelor clasei. Daca nu se pune nici un mo-dificator de vizibilitate, atunci accesul la membru este de tip friendly, despre care vomvorbi mai tarziu. Mai exista si un al patrulea modificator, numit protected pe care ıl vomprezenta ın capitolul urmator.

1. //clasa simpla de testare a clasei Circle2. public class TestCircle3. 4. public static void main( String[] args )5. 6. Circle circle = new Circle() ;7.8. circle.setRadius(10) ;9. System.out.println("Raza este:" + circle.getRadius());10. System.out.println("Aria cercului este:"+circle.area());11. System.out.println("Lungimea este:" + circle.length());12.13. //urmatoarea linie ar genera o14. //eroare de compilare15. //circle.radius = 20 ;16. 17.

Figura 4.2 O clasa simpla de testare a clasei Circle

4.3 Metode uzuale

Exista metode care sunt comune pentru toate clasele. Alte metode definesc comportamentespecifice unei anumite clase. In aceasta sectiune vom prezenta metodele care sunt comunetuturor claselor: constructorii, modificatorii, accesorii, toString si equals.

4.3.1 Constructori

Asa cum am mentionat deja, una dintre proprietatile fundamentale ale obiectelor este caacestea pot fi definite si, eventual, initializate. In limbajul Java, metoda care controleazamodul ın care un obiect este creat si initializat este constructorul. Deoarece Java permitesupraıncarcarea metodelor, o clasa poate sa defineasca mai multi constructori.Daca la definirea clasei nu se furnizeaza nici un constructor, cum este cazul clasei Circledin Figura 4.1, compilatorul creeaza un constructor implicit care initializeaza fiecaredata membru cu valorile implicite. Aceasta ınseamna ca atributele de tipuri primitive

Page 51: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

4.3. METODE UZUALE 51

sunt initializate cu 0, iar atributele de tip referinta sunt initializate cu null. Astfel, ıncazul nostru, atributul radius va avea implicit valoarea 0.Pentru a furniza un constructor, vom scrie o metoda care are acelasi nume cu clasa sicare nu returneaza nimic. In Figura 4.3 avem doi constructori: unul ıncepe la linia 8,iar celalalt la linia 16. Folosind acesti doi constructori vom putea construi obiecte de tipDate ın urmatoarele moduri:

Date d1 = new Date( ) ;Date d2 = new Date( 15, 3, 2000) ;

De remarcat faptul ca odata ce ati definit un constructor pentru clasa, compilatorul numai genereaza constructorul implicit fara parametri. Daca aveti nevoie de un astfel deconstructor, va trebui sa ıl scrieti. Astfel constructorul din linia 8 trebuie definit obligatoriupentru a putea construi un obiect de tipul celui referit de catre d1.

1. //clasa Java simpla pentru stocarea unei2. //date calendaristice3. //nu se face validarea datelor4.5. public class Date6. 7. //constructor fara parametri8. public Date( )9. 10. day = 1 ;11. month = 1 ;12. year = 2000 ;13 14.15. //constructor cu trei parametri16. public Date( int theDay, int theMonth, int theYear )17. 18. day = theDay ;19. month = theMonth ;20. year = theYear ;21. 22.23. //test de egalitate24. //intoarce true daca Obiectul x25. //este egal cu obiectul curent26. public boolean equals( Object x )27. 28. if( ! (x instanceof Date) )29. return false ;30.31. Date date = (Date) x ;32. return date.day == day && date.month == month

Page 52: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

52 CAPITOLUL 4. OBIECTE SI CLASE

33. && date.year == year ;34. 35.36. //conversie la String37. public String toString()38. 39. return day + "/" + month + "/" + year ;40. 41.42. //atribute43. private int day ;44. private int month ;45. private int year ;46.

Figura 4.3 O clasa Date minimala care ilustreaza constructorii si metodele equals sitoString

4.3.2 Modificatori si Accesori

Atributele sunt declarate de obicei ca fiind private. Aceasta ınseamna ca ele nu vor puteafi direct accesate de catre rutinele care nu apartin clasei. Exista totusi multe situatii ıncare dorim sa examinam sau chiar sa modificam valoarea unui atribut.O posibilitate este aceea de a declara atributele ca fiind public. Aceasta este ınsa o alegereproasta, deoarece ıncalca principiul ascunderii informatiei. Putem ınsa scrie metode care saexamineze sau sa modifice valoarea fiecarui camp. O metoda care citeste, dar nu modificastarea unui obiect este numita accesor. O metoda care modifica starea unui obiect estenumita modificator (engl. mutator).Cazuri particulare de accesori si modificatori sunt cele care actioneaza asupra unui singurcamp. Accesorii de acest tip au un nume care ıncepe de obicei cu get, cum ar fi getRadius,iar modificatorii au un nume care ıncepe de regula cu set, cum ar fi setRadius.Avantajul folosirii unui modificator este ca acesta poate verifica daca starea obiectului estecorecta. Astfel un modificator care altereaza campul day al unui obiect de tip Date poateverifica corectitudinea datei care rezulta.

4.3.3 Afisare si toString

In general, dorim sa afisam starea unui obiect folosind metoda print. Pentru a putea faceacest lucru trebuie definita o metoda cu numele de toString. Aceasta metoda ıntoarceun String care poate fi afisat. Ca un exemplu, ın Figura 4.3 am prezentat o imple-mentare rudimentara a unei metode toString pentru clasa Date ın liniile 37-40. Definireaacestei metode ne permite sa scriem un obiect d1 de tip Date cu instructiunea sys-tem.out.print(d1).

Page 53: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

4.4. PACHETE 53

4.3.4 Metoda equals

Metoda equals este folosita pentru a testa daca doua referinte indica obiecte care au aceeasivaloare (stare). Antetul acestei metode este ıntodeauna

public boolean equals( Object rhs )

Ati remarcat probabil nedumeriti faptul ca parametrul trimis este de tip Object si nu de tipDate, cum ar fi fost de asteptat. Ratiunea pentru acest lucru o sa o prezentam ın capitoluldespre polimorfism. In general, metoda equals pentru o clasa X este implementata ın asafel ıncat sa returneze true doar daca rhs este o instanta a lui X si, ın plus, dupa conversiala X toate tipurile primitive sunt egale (via ==) si toate tipurile referinta sunt egale (prinaplicarea lui equals pentru fiecare membru).Un exemplu de implementare a lui equals este dat ın Figura 4.3 pentru clasa Date ınliniile 26-34. Operatorul instanceof va fi discutat ın paragraful 4.5.3.

4.3.5 Variabile si metode statice

Exista anumite cuvinte cheie ale limbajului Java care specifica proprietati speciale pen-tru unele variabile sau metode. Un astfel de cuvant cheie este cuvantul rezervat static.Variabilele si metodele declarate static ıntr-o clasa, sunt aceleasi pentru toate obiectele,adica pentru toate variabilele de tipul acelei clase. Variabilele statice pot fi accesate farasa fie nevoie de o instantiere a clasei respective (adica de o variabila de clasa respectiva).Analog, nici metodele statice nu au nevoie de o instantiere a clasei pentru a fi folosite.Metodele statice pot utiliza variabile statice declarate ın interiorul clasei.Cel mai cunoscut exemplu de metoda statica este main. Alte exemple de metode sta-tice pot fi gasite ın clasele String, Integer si Math. Exemple de astfel de metode suntString.valueOf, Integer.parseInt, Math.sin si Math.max. Accesul la metodele statice re-specta aceleasi reguli de vizibilitate ca si metodele normale.

4.3.6 Metoda main

Atunci cand este invocat, interpretorul java cauta metoda main din clasa care i se da caparametru. Astfel, putem scrie cate o metoda main pentru fiecare clasa. Acest lucru nepermite sa testam functionalitatea de baza a claselor individuale. Trebuie sa avem totusiın vedere faptul ca plasarea functiei main ın cadrul clasei ne confera mai multa vizibilitatedecat ne-ar fi permis ın general. Astfel, apeluri ale metodelor private pot fi facute ın test,dar ele vor esua ıntr-un cadru mai general.

4.4 Pachete

Pachetele sunt folosite pentru a organiza clasele similare. Fiecare pachet consta dintr-omultime de clase. Clasele care sunt ın acelasi pachet au restrictii de vizibilitate mai slabeıntre ele decat daca ar fi ın pachete diferite.Java furnizeaza o serie de pachete predefinite, printre care java.io, java.lang, java.util,java.applet, java.awt etc. Pachetul java.lang include, printre altele, clasele Integer, Math,String si System. Clase mai cunoscute din java.util sunt Date, Random, StringTokenizer.

Page 54: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

54 CAPITOLUL 4. OBIECTE SI CLASE

Pachetul java.io cuprinde diferitele clase pentru stream-uri pe care le-am prezentat ıncapitolul anterior.Clasa C din pachetul P este specificata ca P.C. De exemplu, putem declara un obiect detip Date care sa contina data si ora curenta astfel:

java.util.Date today = new java.util.Date() ;

Observati ca prin specificarea numelui pachetului din care face parte clasa evitam con-flictele care pot fi generate de clase cu acelasi nume din pachete diferite1.

4.4.1 Directiva import

Utilizarea permanenta a numelui pachetului din care fac parte clasele poate fi uneorideosebit de anevoioasa. Pentru a evita acest lucru se foloseste directiva import:

import NumePachet.NumeClasa ;

sau

import NumePachet.* ;

Daca recurgem la prima forma a directivei import, vom putea folosi NumeClasa ca oprescurtare pentru numele clasei cu calificare completa. Daca folosim cea de-a doua forma,toate clasele din pachet vor putea fi abreviate cu numele lor.De exemplu, realizand directivele import de mai jos:

import java.util.Date ;import java.io.* ;

putem sa folosim:

Date today = new Date() ;FileReader file = new FileReader( name ) ;

Folosirea directivelor import economiseste timpul de scriere. Avand ın vedere ca cea de-adoua forma este mai generala, ea este cel mai des folosita. Exista doua dezavantaje alefolosirii directivei import. Primul dezavantaj este acela ca la citirea codului va fi mai greude stabilit pachetul din care o anumita clasa face parte. Al doilea este acela ca folosireacelei de-a doua forme poate sa introduca prescurtari neintentionate pentru anumite clase,ceea ce va genera conflicte de denumire care vor trebui rezolvate prin folosirea de numede clase calificate.Sa presupunem ca avem urmatoarele directive:

import java.util.* ; //pachet predefinitimport myutil.* ; //pachet definit de catre noi

cu intentia de a importa clasa java.util.Random si o clasa pe care am definit-o chiar noi.Atunci, daca noi avem propria clasa Date ın pachetul myutil, directiva import va generaun conflict cu java.util.Date si de aceea clasa va trebui sa fie complet calificata. Am fiputut evita aceste probleme daca am fi folosit prima forma

1De exemplu, cu clasa Date definita anterior

Page 55: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

4.4. PACHETE 55

import java.util.Random ;

Directivele import trebuie sa apara ınainte de orice declarare a unei clase. Pachetuljava.lang este automat inclus ın ıntregime. Acesta este motivul pentru care putem folosiprescurtari de genul Math.max, System.out, Integer.parseInt etc.

4.4.2 Instructiunea package

Pentru a indica faptul ca o clasa face parte dintr-un anumit pachet trebuie sa realizamdoua lucruri. In primul rand trebuie sa scriem o instructiune package pe prima linie,ınainte de a declara clasa. Apoi, va trebui sa plasam clasa ın directorul corespunzator.

4.4.3 Variabila sistem CLASSPATH

Java cauta pachetele si clasele ın directoarele care sunt precizate ın variabila sistemCLASSPATH. Ce ınseamna aceasta? Iata doua posibile setari pentru CLASSPATH, maiıntai pentru un sistem Windows ’95/98/2000, iar apoi pentru un sistem Unix/Linux:

SET CLASSPATH = .;C:\JDK1.3\LIB\export CLASSPATH=.:/usr/local/jdk1.3/lib:$HOME/java

In ambele cazuri variabila CLASSPATH contine directoarele (sau arhivele) care continfisierele .class din pachete. De exemplu, daca variabila CLASSPATH nu este setata corect,nu veti putea compila nici macar cel mai banal program Java, deoarece pachetul java.langnu va fi gasit. O clasa care se afla ın pachetul P va trebui sa fie pusa ıntr-un directorcu numele P care sa se afle ıntr-un director din CLASSPATH. Directorul curent (.) esteıntotdeauna ın variabila CLASSPATH, deci daca lucrati ıntr-un singur director principal,puteti crea subdirectoarele chiar ın el. Totusi, ın majoritatea situatiilor, veti dori sa creatiun subdirector Java separat si sa creati directoarele pentru pachete chiar ın el. In aceastasituatie va trebui sa adaugati la variabila CLASSPATH acest director. Acest lucru a fostrealizat ın exemplul de mai sus prin adaugarea directorului $HOME/java la CLASSPATH.In directorul Java veti putea acum crea subdirectorul io. In subdirectorul io vom plasacodul pentru clasa Reader2. O aplicatie va putea ın aceasta situatie sa foloseasca metodareadInt fie prin

int x = io.Reader.readInt() ;

sau pur si simplu prin

int x = Reader.readInt() ;

daca se furnizeaza directiva import corespunzatoare.

2Reader este o clasa ajutatoare care contine metode pentru citirea de la tastatura a tipurilor uzuale.Codul acestei clase este dat ın paragraful 5.7.

Page 56: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

56 CAPITOLUL 4. OBIECTE SI CLASE

4.4.4 Reguli de vizibilitate Package-Friendly

Pachetele au cateva reguli de vizibilitate importante. In primul rand, daca pentru unmembru al unei clase nu se precizeaza nici un modificator de vizibilitate (public, protectedsau private), atunci membrul respectiv devine (package) friendly. Aceasta ınseamna caacel camp este vizibil doar pentru clasele din cadrul aceluiasi pachet. Aceasta este o viz-ibilitate mai putin restrictiva decat private, dar mai restrictiva decat public (care estevizibil si pentru membrii din alte clase).In al doilea rand, doar clasele public din cadrul unui pachet pot fi folosite din afara pa-chetului. Acesta este motivul pentru care am pus ıntotdeauna modificatorul public ın fataunei clase. Clasele nu pot fi declarate private sau protected. Accesul de tip friendly seextinde si pentru clase. Daca o clasa nu este declarata ca fiind de tip public, atunci ea vaputea fi accesata doar de clasele din cadrul aceluiasi pachet.Toate clasele care nu fac parte din nici un pachet, dar sunt accesibile fiind puse ıntr-undirector din CLASSPATH, sunt considerate automat ca facand parte din acelasi pachetimplicit. Ca o consecinta, accesul de tip friendly se aplica pentru toate aceste clase. Acestaeste motivul pentru care vizibilitatea nu este afectata daca omitem sa punem modifica-torul public din clasele care nu fac parte dintr-un pachet. Totusi aceasta modalitate defolosire a accesului friendly nu este recomandata.

4.4.5 Compilarea separata

Atunci cand un program consta din mai multe fisiere .java, fiecare fisier trebuie compilatseparat. In mod normal, fiecare clasa este plasata ıntr-un fisier .java propriu. Ca urmarea compilarii vom obtine o colectie de fisiere .class. Clasele sursa pot fi compilate ın oriceordine.

4.5 Alte operatii

In aceasta sectiune vom prezenta ınca trei cuvinte cheie importante: this, instanceof sistatic. this are mai multe utilizari ın Java. Doua dintre ele le vom prezenta ın aceastasectiune. instanceof are si el mai multe utilizari; ıl vom folosi aici pentru a ne asigura ca oconversie de tip se poate realiza. Si cuvantul cheie static are mai multe semnficatii. Vomvorbi despre metode statice, atribute statice si initializatori statici.

4.5.1 Referinta this

Cea mai cunoscuta utilizare pentru this este ca o referinta la obiectul curent. Imaginati-vaca this va indica ın fiecare moment locul unde va aflati. O utilizare tipica pentru this estecalificarea atributelor unei clase ın cadrul unei metode a clasei care primeste parametri cunume identic cu numele clasei. De exemplu, ın clasa Circle, din Figura 4.1, putem definimetoda setRadius astfel:

public void setRadius(double radius) //modifica raza cercului //radius este parametrul iar this.radius este atributul claseithis.radius = radius ;

Page 57: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

4.5. ALTE OPERATII 57

In codul de mai sus, pentru a face distinctie ıntre parametrul radius si atributul cu acelasinume (care este ”ascuns” de catre parametru) se foloseste sintaxa this.radius pentru areferi atributul clasei.Un alt exemplu de folosire al lui this este pentru a testa ca parametrul pe care o metodaıl primeste nu este chiar obiectul curent. Sa presupunem, de exemplu, ca avem o clasaAccount care are o metoda finalTransfer pentru a transfera toata suma de bani dintr-uncont ın altul. Metoda ar putea fi scrisa astfel:

public void finalTransfer(Account account)dollars += account.dollars ;account.dollars = 0 ;

Sa consideram secventa de cod de mai jos:

Account account1 ;Account account2 ;....account2 = account1 ;account1.finalTransfer(account2) ;

Deoarece transferam bani ın cadrul aceluiasi cont, nu ar trebui sa fie nici o modificare ıncadrul contului. Totusi, ultima linie din finalTransfer are ca efect golirea contului debitor.O modalitate de a evita o astfel de situatie este folosirea unui test pentru pseudonime:

public void finalTransfer(Account account)if( this = account)//se incearca un transfer in acelasi contreturn ;dollars += account.dollars ;account.dollars = 0 ;

4.5.2 Prescurtarea this pentru constructori

Multe clase dispun de mai multi constructori care au un comportament similar. Putemfolosi this ın cadrul unui constructor pentru a apela ceilalti constructori ai clasei. O altaposibilitate de a scrie constructorul fara parametri pentru clasa Date este:

public Date()this(1, 1, 2000) ;//apeleaza constructorul Date(int,int,int)

Se pot realiza si exemple mai complicate, dar ıntotdeauna apelul lui this trebuie sa fieprima instructiune din constructor, celelalte instructiuni fiind ın continuarea acesteia.

Page 58: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

58 CAPITOLUL 4. OBIECTE SI CLASE

4.5.3 Operatorul instanceof

Operatorul instanceof realizeaza o testare de tip ın timpul executiei. Rezultatul expresiei:

exp instanceof NumeClasa

este true daca exp este o instanta a lui NumeClasa si false ın caz contrar. Daca exp estenull rezultatul este ıntotdeauna false. Operatorul instanceof este folosit de obicei ınaintede o conversie de tip, si adeseori este folosit ın legatura cu referintele polimorfice pe carele vom prezenta mai tarziu.

4.5.4 Atribute statice

Atributele statice sunt folosite ın situatia ın care avem variabile care trebuie partajate decatre toate instantele unei clase. De obicei atributele statice sunt constante simbolice, daracest lucru nu este obligatoriu. Atunci cand un atribut al unei clase este declarat de tipstatic, doar o singura instanta a acelei variabile va fi creata. Ea nu face parte din nici oinstanta a clasei. Ea se comporta ca un fel de variabila globala unica, vizibila ın cadrulclasei. Cu alte cuvinte, daca avem declaratia

public class Exempluprivate int x ;private static int y ;

fiecare obiect de tip Exemplu va avea propriul atribut x, dar va fi doar un singur y partajat.O folosire frecventa pentru campurile statice o reprezinta constantele. De exemplu, clasaInteger defineste atributul MAX VALUE astfel

public final static int MAX_VALUE = 2147483647 ;

Analog se defineste si constanta PI din clasa Math pe care am folosit-o ın clasa Circle.Modificatorul final indica faptul ca identificatorul care urmeaza este o constanta. Dacaaceasta constanta nu ar fi fost un atribut static, atunci fiecare instanta a clasei Integer arfi avut un atribut cu numele de MAX VALUE, irosind astfel spatiu ın memorie.Astfel, vom avea o singura variabila cu numele de MAX VALUE. Ea poate fi accesata deoricare dintre metodele clasei Integer prin identificatorul MAX VALUE. Ea va putea fifolosita si de catre un obiect de tip Integer numit x prin sintaxa x.MAX VALUE, ca oricealt camp. Acest lucru este permis doar pentru ca MAX VALUE este public. In sfarsit,MAX VALUE poate fi folosit si prin intermediul numelui clasei ca Integer. MAX VALUE(tot pentru ca este public). Aceasta ultima folosire nu ar fi fost permisa pentru un campcare nu este static.Chiar si fara modificatorul final, atributele statice sunt foarte folositoare. Sa presupunemca vrem sa retinem numarul de obiecte de tip Circle care au fost construite. Pentru aceastaavem nevoie de o variabila statica. In clasa Circle vom face declaratia:

private static int numarInstante = 0 ;

Page 59: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

4.6. PROBLEME PROPUSE 59

Vom putea apoi incrementa numarul de instante ın constructor. Daca acest camp nu ar fifost de tip static am avea un comportament incorect, deoarece fiecare obiect de tip Circlear fi avut propriul atribut numarInstante care ar fi fost incrementat de la 0 la 1.Remarcati faptul ca, deorece un atribut de tip static nu necesita un obiect care sa ılcontroleze, fiind partajat de catre toate instantele clasei, el poate fi folosit de catre ometoda statica (daca regulile de vizibilitate permit acest lucru). Atributele nestatice aleunei clase vor putea fi folosite de catre o metoda statica doar daca se furnizeaza si unobiect care sa le controleze.

4.5.5 Initializatori statici

Atributele statice sunt initializate atunci cand clasa este ıncarcata. Uneori este ınsa nevoiede o initializare mai complexa. Sa presupunem de exemplu ca avem nevoie de un sir staticcare sa contina radacinile patrate ale primelor 100 numere naturale. O posibilitate ar fisa definim o metoda statica si sa cerem programatorului sa apeleze acea metoda ınaintede a folosi sirul.O alternativa la aceasta solutie este folosirea initializatorului static. Un exemplu esteprezentat ın Figura 4.4. Aici initializatorul static se extinde de la linia 5 la linia 11.Initializatorul static trebuie sa urmeze imediat dupa membrul static.

1. public class Squares2. 3. private static double squareRoots[] = new double[100] ;4.5. static6. 7. for( int i =0; i < squareRoots.length; ++i)8. 9. squareRoots[i] = Math.sqrt( (double) i ) ;10. 11. 12. //restul clasei13.

Figura 4.4 Exemplu de initializator static.

4.6 Probleme propuse

1. Scrieti o clasa care suporta numere rationale. Atributele clasei ar trebui sa fie douavariabile de tip long una pentru numitor si una pentru numarator. Stocati numarulsub forma de fractie ireductibila, cu numitorul pozitiv. Formati un numar rezonabilde constructori; metodele add, subtract, multiply, divide; de asemenea, toString,equals si compareTo (care se comporta ca cea din clasa String). Asigurati-va catoString merge ın cazul ın care numitorul este zero.

2. Implementati o clasa pentru numere complexe, Complex. Adaugati aceleasi metodeca la clasa Rational, daca au sens (de exemplu, compareTo nu are sens aici). Adaugatiaccesori pentru partea reala si cea imaginara.

Page 60: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

60 CAPITOLUL 4. OBIECTE SI CLASE

3. Implementati o clasa completa IntType care sa suporte un numar rezonabil de con-structori; metodele add, subtract, multiply, divide; de asemenea, toString, equals sicompareTo. Mentineti IntType sub forma unui sir de cifre suficient de mare.

4. Implementati o clasa simpla numita Date. Clasa va trebui sa reprezinte orice dataıntre 1 Ianuarie 1800 si 31 Decembrie 2500. Definiti metode care sa permita scadereraa doua date, incrementarea unei date cu un numar de zile; compararea a doua date.O data va fi reprezentata intern ca numarul de zile care au trecut de la o anumitadata,(de exemplu prima zi din 1800).

Solutie: Am creat doua siruri care sunt atribute statice. Primul numit pana1Lunva contine numarul de zile pana la prima zi din fiecare luna a anilor care nu suntbisecti. Astfel, el va contine 0,31,59,90 etc. Cel de-al doilea sir, pana1Ian va continenumarul de zile pana la ınceputul fiecarui an, ıncepand cu 1800. Astfel, el va contine0, 365, 730, 1095, 1460, 1826 etc, deoarece 1800 nu este an bisect, dar 1804 este.Programul va initializa acest sir o singura data. Am utilizat acest sir pentru con-versia din reprezentarea interna (numar de zile de la 01.01.1800) ın reprezentareaexterna (zi/luna/an).Clasa este prezentata ın cele ce urmeaza:

import java.io.*;import java.util.*;import java.math.*;

public class Datastatic int p,ziua,luna,an;static int pana1Ian[];static int pana1Lun[]=0,31,59,90,120,151,181,212,243,273,304,334;

public Data()// prin acest constructor care se apeleaze la inceputul executari// programului calculam numarul de zile trecute pana la 1 ianuarie// al anului i+1800 de la 1 ian 1800pana1Ian=new int[701];//aloc memorie pentru vectorpana1Ian[0]=0;for(int i=1;i<701;i++)// acest vector va contine numarul zilelor pana in 1 ianuarie a// anului i+1800pana1Ian[i]=pana1Ian[i-1]+365;if ((((i+1800-1)%4==0) &&((i+1800-1)%100!=0))||((i+1800-1)%400==0))pana1Ian[i]+=1;//verificam daca nu este an bisect

Page 61: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

4.6. PROBLEME PROPUSE 61

public static int readInt()BufferedReader nr=new BufferedReader(new InputStreamReader(System.in));tryp= Integer.parseInt(nr.readLine());//trycatch(IOException e)System.out.println(e.toString());//catchreturn p;//readInt

public static void readData()doSystem.out.print("Anul");an=readInt();// citirea anului se repeta pana se introduce un numar// intre 1800 si 2500while((an>=2500)||(an<1800));doSystem.out.print("Luna");luna=readInt();//citirea lunii se repeta pana se introduce un numar intre 1 si 12while((luna>12)||(luna<1));switch (luna)case 1:case 3:case 5:case 7:case 8:case 10:case 12:doSystem.out.print("Ziua");ziua=readInt();

//se citeste o zi din lunile cu 31 de zilewhile((ziua>31)||(ziua<1));break;

Page 62: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

62 CAPITOLUL 4. OBIECTE SI CLASE

case 11:case 4:case 6:case 9:doSystem.out.print("Ziua");ziua=readInt();//se citeste o zi din lunile cu 30 de zilewhile((ziua>30)||(ziua<1));break;default :if (bisect(an)==1)doSystem.out.print("Ziua");ziua=readInt();//se citeste o zi din luna februarie a unui an bisectwhile((ziua>29)||(ziua<1));elsedoSystem.out.print("Ziua");ziua=readInt();//se citeste o zi din luna februarie a unui an nebisectwhile((ziua>28)||(ziua<1));break;

//readData

public static int conversie(int ziua,int luna,int an)int s=0;s+=pana1Ian[an-1800];if ((bisect(an)==1)&&(luna>2))s+=1;s+=pana1Lun[luna-1];s+=ziua;return s;

//conversia unei date in numar de zile trecute de la 1 ianuarie 1800

public static int bisect(int an)//functie pentru verificarea unui an bisectif ((((an)%4==0) &&((an)%100!=0))||((an)%400==0))return 1;

Page 63: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

4.6. PROBLEME PROPUSE 63

elsereturn 0;

public static String toString(int a)//conversia numarului de zile trecute de la 1 ian 1800 in dataint an=0,lu=0,zi=0;int i=0,j=0;while ((a>pana1Ian[j])&&(j<701))j++;//cautam primul an care are trecute, pina la 1 ianuarie,

//un numar mai mare de zile decat numarul pe care dorim sa-l convertimj--;//scadem unu din acel an si avem anul dateian=j+1800;if (an>1800)a=a-pana1Ian[j];//scoatem din numarul initial numarul zilelor

//trecute pana la 1 ianuarie a anului gasitif (bisect(an)==0)

//verificam daca anul e bisect si apoi cautam luna ce corespunde dateii=0;while ((pana1Lun[i]<a)&&(i<11))i++;if ((pana1Lun[i]<a)&&(i==11))i++;i--;lu=i+1;if (i >=0)a=a-pana1Lun[i];zi=a;else //daca-i bisect problema are trei variante si anume

//daca-i mai mic de 60 se calculeaza ca mai sus, daca nu poate fi//chiar 60 si atunci suntem pe 29 februarie//altfel scadem 1 din numarul zilelor si obtinem o data dintr-un//an obisnuitif (a<60)i=0;while ((pana1Lun[i]<a)&&(i<12))i++;

i--;lu=i+1;

Page 64: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

64 CAPITOLUL 4. OBIECTE SI CLASE

if (i>=0)a=a-pana1Lun[i];zi=a;if (a==60)lu=2;zi=29;if (a>60)a=a-1;i=0;while ((pana1Lun[i]<a)&&(i<12))i++;if ((pana1Lun[i]<a)&&(i==11))i++;i--;lu=i+1;if (i>=0)a=a-pana1Lun[i];zi=a;return zi + "/" + lu + "/" + an ;

public static int incrementare(int a)//incrementam numarul de zile cu un numarint in;doSystem.out.print("Cu cate zile:");in=readInt();while ((in<0)||(in>a)||(in>256000));return (a+in);

public static int decrementare(int a)//decrementam numarul de zile cu un numarint de;do

Page 65: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

4.6. PROBLEME PROPUSE 65

System.out.print("Cu cate zile:");de=readInt();while ((de<0)||(de>a)||(a-de<=0));return (a-de);

public static void compareto(int a,int b)//compararea a doua dateif (a>b)System.out.println("Prima data este mai ’mare’cu "+(a-b)+" zile");if (a==b)System.out.println("Date identice!! De ce le mai compari???");if (a<b)System.out.println("Prima data este mai ’mica’cu "+(b-a)+" zile");

public static boolean equals(int a,int b)if (a==b)return true;elsereturn false;

public static void main(String[]args)int v=0,a,b;String s;Data d=new Data();doSystem.out.println();System.out.println(" Alegeti metoda");System.out.println(" 1 Conversia");System.out.println(" 2 Decrementare");System.out.println(" 3 Incrementare");System.out.println(" 4 Compareto");System.out.println(" 5 Equals");System.out.println(" 6 Diferenta");System.out.println(" 7 Exit");System.out.print ("Alegeti numarul metodei:");dov=readInt();while ((v<0)&&(v>7));switch (v)

Page 66: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

66 CAPITOLUL 4. OBIECTE SI CLASE

case 1:readData();System.out.println(ziua+"/"+luna+"/"+an);a=0;a=conversie(ziua,luna,an);System.out.println("conversia datei este numarul:"+a);break;case 2:readData();System.out.println(ziua+"/"+luna+"/"+an);a=0;a=conversie(ziua,luna,an);a=decrementare(a);s="";s=toString(a);System.out.println("decrementarea datei este data:"+s);break;case 3:readData();System.out.println(ziua+"/"+luna+"/"+an);a=0;a=conversie(ziua,luna,an);a=incrementare(a);s="";s=toString(a);System.out.println("incrementarea datei este data:"+s);break;case 4:readData();a=0;a=conversie(ziua,luna,an);readData();b=0;;b=conversie(ziua,luna,an);compareto(a,b);break;case 5:readData();a=0;a=conversie(ziua,luna,an);readData();b=0;b=conversie(ziua,luna,an);boolean bun;bun=equals(a,b);

Page 67: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

4.6. PROBLEME PROPUSE 67

if (bun)System.out.println("Aceeasi data");elseSystem.out.println("Data diferita");break;

case 7 :System.out.println("La revedere");break;

case 6 :readData();a=0;a=conversie(ziua,luna,an);System.out.println("Scadem data.");readData();b=0;b=conversie(ziua,luna,an);System.out.println("Diferenta este de"+(a-b)+"zile");break;

default :System.out.println("Mai incercati");

while (v!=7);

Page 68: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

Capitolul 5

Mostenire

Asa cum am mentionat ın capitolul anterior, unul dintre principalele scopuri ale pro-gramarii orientate pe obiecte este reutilizarea codului. La fel cum inginerii folosesc aceleasicomponente din nou si din nou ın proiectarea circuitelor, programatorii au posibilitatea sarefoloseasca obiectele, ın loc sa le reimplementeze. In limbajele de programare orientate peobiecte, mecanismul fundamental pentru refolosirea codului este mostenirea. Mostenireane permite sa extindem functionalitatea unui obiect. Cu alte cuvinte, putem crea noiobiecte cu proprietati extinse (sau restranse) ale tipului original, formand astfel o ierarhiede clase. De asemenea, mostenirea este mecanismul pe care Java ıl foloseste pentru aimplementa metode si clase generice. In acest capitol vom prezenta:

• principiile generale ale mostenirii, inclusiv polimorfismul

• cum este mostenirea implementata ın Java

• cum poate fi derivata o colectie de clase dintr-o singura clasa abstracta

• interfata, care este un caz particular de clasa abstracta

• cum se poate realiza programarea generica ın Java folosind interfete

5.1 Ce este mostenirea?

Mostenirea este principiul fundamental al programarii orientate pe obiecte care permiterefolosirea codului ıntre clasele ınrudite. Mostenirea modeleaza relatii de tipul ESTE-UN (sau ESTE-O). Intr-o relatie de tip ESTE-UN, spunem ca clasa derivata ESTE-O(variatiune) a clasei de baza. De exemplu, Cerc ESTE-O Curba, iar Masina ESTE-UNVehicul. In schimb, o Elipsa NU-ESTE-UN Cerc. Relatiile de mostenire formeaza ierarhii.De exemplu, putem extinde clasa Masina la MasinaStraina (pentru care se plateste vama)si MasinaAutohtona (care nu plateste vama) etc.Un alt tip de relatie ıntre obiecte este relatia ARE-UN sau ESTE-COMPUS-DIN. Aceastarelatie nu are proprietatile care ar fi normale ıntr-o ierarhie de mostenire. Un exemplude astfel de relatie este ca Masina ARE-UN Volan. Relatiile de tip ARE-UN nu trebuiemodelate prin mostenire. Ele se vor modela prin agregare, ın care componentele devinsimple campuri de tip private.

68

Page 69: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

5.1. CE ESTE MOSTENIREA? 69

Chiar si limbajul Java foloseste din plin mostenirea pentru a-si implementa propriile bib-lioteci de clase. Un exemplu relativ familiar ıl constituie exceptiile. Java defineste clasaException. Asa cum am vazut deja, exista mai multe tipuri de exceptii, cum ar fi NullPoint-erException si ArrayIndexOutOfBoundsException. Fiecare constituie o clasa separata, ınsatoate dispun de metoda toString pentru a furniza mesaje de eroare utile ın depanare.Mostenirea modeleaza aici o relatie de tip ESTE-UN. NullPointerException ESTE-O Ex-ception. Datorita relatiei de tip ESTE-UN, proprietatea fundamentala a mostenirii garan-teaza ca orice metoda care poate fi aplicata lui Exception poate fi aplicata si lui Null-PointerException. Mai mult decat atat, un obiect de tip NullPointerException poatesa fie referit de catre o referinta de tip Exception (reciproca nu este adevarata!). Ast-fel, deoarece metoda toString este o metoda disponibila ın clasa Exception, vom puteaıntotdeauna scrie:

catch( Exception e )System.out.println( e.toString() ) ;

Daca e refera un obiect de tip NullPointerException, atunci e.toString() este un apel corect.Functie de modul de implementare al ierarhiei de clase, metoda toString ar putea fi invari-anta sau ar putea fi specializata pentru fiecare clasa distincta. Atunci cand o metoda esteinvarianta ın cadrul unei ierarhii, adica are aceeasi functionalitate pentru toate clasele dinierarhie, nu va trebui sa rescriem implementarea acelei metode pentru fiecare clasa.Apelul lui toString mai ilustreaza un alt principiu important al programarii orientate peobiecte, cunoscut sub numele de polimorfism. O variabila referinta care este polimorficapoate sa refere obiecte de tipuri diferite. Atunci cand se aplica o metoda referintei, seselecteaza automat operatia adecvata pentru tipul obiectului care este referit ın acel mo-ment. In Java, toate tipurile referinta sunt polimorfice. In cazul unei referinte de tipException se ia o decizie ın timpul executiei: se va apela metoda toString pentru obiectulpe care e ıl refera ın acel moment ın timpul executiei. Acest proces este cunoscut subnumele de legare tarzie sau legare dinamica.In cazul mostenirii avem o clasa de baza din care sunt derivate alte clase. Clasa de bazaeste clasa pe care se bazeaza mostenirea. O clasa derivata mosteneste toate proprietatileclasei de baza, ınsemnand ca toti membri publici ai clasei de baza devin membri publiciai clasei derivate. Clasa derivata poate sa adauge noi atribute si metode si poate modificasemnificatia metodelor mostenite. Fiecare clasa derivata este o clasa complet noua. Clasade baza nu este ın nici un fel afectata de modificarile aduse ın clasele derivate. Astfel, lacrearea clasei derivate este imposibil sa se strice ceva ın clasa de baza.O clasa derivata este compatibila ca tip cu clasa de baza, ceea ce ınseamna ca o variabilareferinta de tipul clasei de baza poate referi un obiect al clasei derivate, dar nu si invers.Clasele surori (cu alte cuvinte clasele derivate dintr-o clasa comuna) nu sunt compatibileca tip.Asa cum am mentionat anterior, folosirea mostenirii genereaza de obicei o ierarhie de clase.Figura 5.1 prezinta o mica parte din ierarhia de exceptii a limbajului Java. Remarcatifaptul ca NullPointerException este indirect derivata din Exception. Acest lucru nu consti-tuie nici o problema, deoarece relatiile de tipul ESTE-UN sunt tranzitive. Cu alte cuvinte,daca X ESTE-UN Y si Y ESTE-UN Z, atunci X ESTE-UN Z. Ierarhia Exception ilustreaza

Page 70: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

70 CAPITOLUL 5. MOSTENIRE

ın acelasi timp designul clasic ın care se extrag caracteristicile comune ın clasa de baza,urmate de specializari ın clasele derivate. ıntr-o astfel de ierarhie spunem ca clasa derivataeste o subclasa a clasei de baza, iar clasa de baza este o superclasa a clasei derivate. Acesterelatii sunt tranzitive; mai mult, operatorul instanceof functioneaza pentru subclase. Ast-fel, daca obj este de tipul X (si nu e null), atunci expresia obj instanceof Z este adevarata.In urmatoarele sectiuni vom examina urmatoarele probleme:

• Care este sintaxa folosita pentru a deriva o clasa noua dintr-o clasa debaza?

• Cum afecteaza acest lucru statutul membrilor private sau public?

• Cum precizam faptul ca o metoda este invarianta pentru o ierarhie declase?

• Cum specializam o metoda?

• Cum factorizam aspectele comune ıntr-o clasa abstracta, pentru a creaapoi o ierarhie?

• Putem deriva o clasa noua din mai mult de o clasa (mostenire multipla)?

• Cum se foloseste mostenirea pentru a implementa codul generic?

Cateva dintre aceste subiecte sunt ilustrate prin implementarea unei clase Shape dincare vom deriva clasele Circle, Square si Rectangle. Ne vom folosi de acest exemplupentru a vedea cum Java implementeaza polimorfismul cat si pentru a vedea cum poatefi mostenirea folosita pentru a implementa metode generice.

Exception6

RuntimeException

JJ

JJ

IOException

EOFException

JJ

JJ

FileNotFoundException

IndexOutOfBoundsException

HHHjNullPointerException

ArrayIndexOutOfBoundsException

JJ

JJ

StringIndexOutOfBoundsException

Figura 5.1 O parte din ierarhia de exceptii a limbajului Java

Page 71: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

5.2. SINTAXA DE BAZA JAVA 71

5.2 Sintaxa de baza Java

O clasa derivata mosteneste toate proprietatile clasei de baza. Clasa derivata poate apoisa adauge noi atribute, sa redefineasca metode sau sa adauge noi metode. Fiecare clasaderivata este o clasa complet noua. Aspectul general al unei clase derivate este prezentatın Figura 5.2. Clauza extends declara faptul ca o clasa este derivata dintr-o alta clasa.

1. public class ClasaDerivata extends ClasaDeBaza2. 3. //orice membri (public sau protected) care nu sunt listati4. //vor fi mosteniti nemodificati, cu exceptia constructorului5.6. //membri public7. //constructor(i), daca cel implicit nu este suficient8. //met din ClasaDeBaza a caror implementare este modificata9. //metode publice aditionale10.11. //membri private12. //atribute suplimentare (in general private)13. //alte metode private14.

Figura 5.2 Aspectul general al unei clase derivate

Iata o descriere scurta a unei clase derivate:

• In general, toate atributele sunt private, deci atributele suplimentare vor fi adaugateclasei derivate prin precizarea lor ın sectiunea private.

• Orice metode din clasa de baza care nu sunt precizate ın clasa derivata sunt mostenitenemodificat, cu exceptia constructorului. Cazul particular al constructorului ıl vomprezenta ın paragraful 5.2.2.

• Orice metoda din clasa de baza care este definita ın sectiunea public a clasei derivateeste redefinita. Noua metoda va fi aplicata obiectelor din clasa derivata.

• Metodele public din clasa de baza nu pot fi redefinite ın sectiunea private a claseiderivate.

• Clasei derivate ıi putem adauga metode suplimentare.

5.2.1 Reguli de vizibilitate

Stim deja ca orice membru care este declarat a fi private este accesibil doar ın metodeleclasei. Rezulta deci ca nici un membru private din clasa de baza nu va fi accesibil ın clasaderivata.Exista situatii ın care clasa derivata trebuie sa aiba acces la membri clasei de baza. Existadoua optiuni pentru a realiza acest lucru. Prima este aceea de a utiliza accesul de tippublic sau friendly. Totusi, accesul de tip public permite accesul si altor clase, pe langa

Page 72: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

72 CAPITOLUL 5. MOSTENIRE

clasele derivate. Accesul de tip friendly functioneaza doar daca ambele clase sunt ın acelasipachet.Daca dorim sa restrangem accesul unor membri, astfel ıncat ei sa fi vizibili doar pentruclasele derivate, putem sa declaram membri ca fiind de tip protected. Un membru de tipprotected este private pentru toate clasele cu exceptia claselor derivate (si a claselor dinacelasi pachet). Declararea atributelor ca fiind public sau protected ıncalca principiileıncapsularii si ascunderii informatiei si se recurge la ea doar din motive de comoditate. Deobicei, este preferabil sa se scrie modificatori si accesori sau sa se foloseasca accesul de tipfriendly. Totusi, daca o declaratie protected va scuteste de scrierea de cod stufos, atuncise poate recurge la ea.

5.2.2 Constructor si super

Fiecare clasa derivata trebuie sa ısi defineasca proprii constructori. Daca nu se scrie nici unconstructor, Java va genera un constructor implicit fara parametri. Acest constructor vaapela constructorul fara parametri al clasei de baza pentru membrii care au fost mosteniti,dupa care va aplica initializarea implicita pentru atributele adaugate (adica 0 pentrutipurile primitive si null pentru tipurile referinta).Construirea unei obiect al unei clase derivate are loc prin construirea prealabila a portiuniimostenite. Acest lucru este natural, deoarece principiul ıncapsularii afirma ca parteamostenita este o entitate unica, iar constructorul clasei de baza ne spune cum sa initializamaceasta entitate.Constructorii clasei de baza pot fi apelati explicit prin metoda super. Astfel, constructorulimplicit pentru o clasa derivata are de fapt forma:

public Derived()super() ;

Metoda super poate fi apelata si cu parametri care sa se potriveasca cu un constructordin clasa de baza. De exemplu, daca clasa de baza are un constructor care accepta doiparametri de tip int, atunci constructorul clasei derivate ar putea fi:

public Derived(int x, int y)super(x, y) ;// alte instructiuni

Metoda super poate sa apara doar ın prima linie dintr-un constructor. Daca nu se face unapel explicit, compilatorul va realiza automat un apel al metodei super fara parametri.

5.2.3 Metode si clase final

Asa cum am precizat deja mai devreme, clasa derivata poate sa redefineasca sau sa acceptenemodificate metodele din clasa de baza. In multe cazuri este clar faptul ca o anumitametoda trebuie sa fie invarianta de-a lungul ierarhiei, ceea ce ınseamna ca clasele derivate

Page 73: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

5.2. SINTAXA DE BAZA JAVA 73

nu trebuie sa o redefineasca. In acest caz, putem declara metoda ca fiind de tip final iarea nu va putea fi redefinita.Pe langa faptul ca declararea unei metode final este o practica buna de programare, eapoate genera si cod mai rapid. Declararea unei metode final (atunci cand este cazul) con-stituie o practica buna de programare deoarece intentiile noastre devin astfel clare pentrucititorul programului si al documentatiei si, pe de alta parte, putem preveni redefinireaaccidentala pentru o metoda care nu trebuie sa fie redefinita.Pentru a vedea de ce folosirea lui final poate conduce la cod mai eficient, sa presupunemca avem o clasa de baza numita Base care defineste o metoda final f , iar Derived este oclasa care extinde Base. Sa consideram functia:

void xxx(Base obj)obj.f()

Deoarece f este o metoda final, nu are nici o importanta daca ın momentul executiei objrefera un obiect de tip Base sau un obiect de tip Derived; definirea lui f este invarianta, decistim de la ınceput ceea ce f va face. O consecinta a acestui fapt este ca decizia pentru codulcare va fi executat se ia ınca de la compilare, si nu la executie. Acest proces este cunoscutsub numele de legare statica. Deoarece legarea se face la compilare si nu la executie,programul ar trebui sa ruleze mai repede. Daca acest fapt este sau nu perceptibil efectiv(tinand cont de viteza de prelucrare a procesoarelor din generatia actuala) depinde denumarul de ori ın care evitam deciziile din timpul executiei ın timpul rularii programului.Un corolar al acestei observatii ıl constituie faptul ca daca f este o metoda banala, cum arfi un accesor pentru un atribut, compilatorul ar putea sa ınlocuiasca apelul lui f, direct cucorpul functiei. Astfel, apelul functiei va fi ınlocuit cu o singura linie care acceseaza unatribut, economisindu-se astfel timp. Daca f nu ar fi fost declarata final, acest lucru ar fifost imposibil, deoarece obj ar fi putut referi un obiect al unei clase derivate, pentru caredefinirea lui f ar fi putut fi diferita.Metodele statice nu au un obiect care sa le controleze, deci apelul lor este rezolvat ınca dela compilare folosind legarea statica.Similare metodelor final sunt clasele final. O clasa final nu mai poate fi extinsa. Inconsecinta, toate metodele unei astfel de clase sunt metode final. Ca un exemplu, clasaInteger este o clasa final. De remarcat faptul ca daca o clasa are doar membri final, ea nueste neaparat o clasa final.

5.2.4 Redefinirea unei metode

Metodele din clasa de baza pot fi redefinite ın clase derivate prin furnizarea unei metodedin clasa derivata care sa aiba aceeasi semnatura. Metoda din clasa derivata trebuie saaiba aceeasi semnatura si nu are dreptul sa adauge exceptii la lista throws.Adeseori, metoda din clasa derivata trebuie sa apeleze metoda din clasa de baza. Acestproces este numit redefinire partiala. Cu alte cuvinte vrem sa facem ceea ce face si metodadin clasa de baza, plus ınca ceva. Apeluri ale clasei de baza pot fi realizate folosind super.Iata un exemplu:

public class Student extends Human

Page 74: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

74 CAPITOLUL 5. MOSTENIRE

public doWork()takeBreak(); //pauzele lungi si desesuper.doWork();//cheia marilortakeBreak(); //succese

5.2.5 Metode si clase abstracte

Pana acum am vazut faptul ca unele metode sunt invariante de-a lungul ierarhiei de clase(metodele final), iar alte metode ısi modifica semnificatia de-a lungul ierarhiei. O a treiaposibilitate este ca o metoda din clasa de baza sa aiba sens doar pentru clasele derivate,si vrem ca ea sa fie obligatoriu definita ın clasele derivate; totusi, implementarea metodeinu are nici un sens pentru clasa de baza. In aceasta situatie, putem declara metoda cafiind abstracta.O metoda abstracta este o metoda care declara functionalitati care vor trebui neaparatimplementate pana la urma ın clasele derivate. Cu alte cuvinte o metoda abstracta spuneceea ce obiectele derivate trebuie sa faca. Totusi, ea nu furnizeaza nici un fel de imple-mentare; fiecare clasa derivata trebuie sa vina cu propria implementare.O clasa care are cel putin o metoda abstracta este o clasa abstracta. Java pretinde ca toateclasele abstracte sa fie definite explicit ca fiind abstracte. Atunci cand o clasa derivata nuredefineste o metoda abstracta, metoda ramane abstracta si ın clasa de baza. In consecintadaca o clasa care nu intentionam sa fie abstracta nu redefineste toate metodele abstracte,compilatorul va detecta inconsistenta si va genera un mesaj de eroare.Un exemplu simplu de clasa abstracta este clasa Shape (shape ınseamna forma sau curba),pe care o vom folosi ıntr-un exemplu ın cadrul acestui capitol. Din Shape vom deriva formespecifice cum ar fi Circle sau Rectangle. Putem deriva apoi Square ca un caz particular deRectangle. Figura 5.3 prezinta ierarhia de clase care rezulta.

.

Figura 5.3 Ierarhia de clase pentru forme

Clasa Shape poate sa aiba membri care sa fie comuni pentru toate clasele. Intr-un exemplumai extins, aceasta ar putea include coordonatele extremitatilor obiectului. Ea declara sidefineste metode cum ar fi positionOf, care sunt independente de tipul formei; positionOfar fi o metoda final. Ea defineste si metode care se aplica fiecarui obiect ın parte. Uneledintre aceste metode nu au nici un sens pentru clasa abstracta Shape. De exemplu, estedificil de calculat aria unui obiect oarecare; metoda area va fi declarata abstract.Asa cum am mentionat anterior, existenta cel putin a unei metode abstracte, face clasasa devina si ea abstracta, deci ea nu va putea fi instantiata. Astfel, nu vom putea crea unobiect de tip Shape; vom putea crea doar obiecte derivate. Totusi, ca de obicei, o referintade tip Shape poate sa refere orice forma concreta derivata, cum ar fi Circle sau Rectangle.Exemplu:

Page 75: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

5.2. SINTAXA DE BAZA JAVA 75

Shape a,b ;a = new Circle( 3.0 ) ; //corectb = new Shape( "circle" ) ; //incorect

Codul din Figura 5.4 prezinta clasa abstracta Shape. La linia 13, declaram o variabilade tip String care retine tipul formei, folosita doar ın clasele derivate. Atributul este detip private, deci clasele derivate nu au acces direct la el. Restul clasei cuprinde o lista demetode.Constructorul nu va fi apelat niciodata direct, deoarece Shape este o clasa abstracta. Avemtotusi nevoie de un constructor care sa fie apelat din clasele derivate pentru a initializaatributele private. Constructorul clasei Shape stabileste valoarea atributului name.Linia 15 declara metoda abstracta area. area este o metoda abstracta deoarece nu putemfurniza nici un calcul implicit al ariei pentru o clasa derivata care nu ısi defineste propriametoda de calcul a ariei.Metoda de comparatie din liniile 22-25 nu este abstracta, deoarece ea poate fi aplicataın acelasi mod pentru toate clasele derivate. De fapt, definirea ei este invarianta de-alungul ierarhiei, de aceea am declarat-o final. Parametrul rhs (de la ”right-hand-side”)reprezinta un alt obiect de tip Shape, a carui arie se compara cu cea a obiectului curent.Este interesant de remarcat faptul ca variabila rhs poate sa refere orice instanta a uneiclase derivate din Shape (de exemplu o referinta a clasei Rectangle). Astfel este posibil cafolosind aceasta metoda sa comparam aria obiectului curent (care poate fi, de exemplu, oinstanta a clasei Circle) cu aria unui obiect de alt tip, derivat din Shape. Acesta este unexemplu excelent de folosire a polimorfismului.Metoda toString din liniile 27-30 afiseaza numele formei si aria ei. Ca si metoda decomparatie, ea este invarianta de-a lungul ierarhiei, de aceea a fost declarata final.Inainte de a trece mai departe, sa rezumam cele 4 tipuri de metode ale unei clase:

1. Metode finale. Apelul lor este rezolvat ınca de la compilare. Folosim metode finaldoar atunci cand metoda este invarianta de-a lungul ierarhiei (adica atunci candmetoda nu este niciodata redefinita).

2. Metode abstracte. Apelul lor este rezolvat ın timpul executiei. Clasa de baza nufurnizeaza nici o implementare a lor si este abstracta. Clasele derivate trebuie fie saimplementeze metoda, fie devin ele ınsele abstracte.

3. Metode statice. Apelul este rezolvat la compilare, deoarece nu exista obiect care sale controleze.

4. Alte metode. Apelul este rezolvat la executie. Clasa de baza furnizeaza o imple-mentare implicita care fie va fi redefinita ın clasele derivate, fie acceptata nemodifi-cata.

1. //Clasa de baza abstracta pentru forme2. //3. //CONSTRUIREA: nu este permisa; Shape este abstracta4. //constructorul cu un param. este furnizat ptr. clasele derivate5. //6. //***************Metode publice***************************

Page 76: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

76 CAPITOLUL 5. MOSTENIRE

7. //double area() --> Intoarce aria (abstracta)8. //boolean lessThan --> Compara doua forme dupa arie9. //String toString --> Metoda uzuala pentru scriere10.11.abstract class Shape12.13. private String name ;14.15. abstract public double area() ;16.17. public Shape( String shapeName )18. 19. name = shapeName ;20. 21.22. final public boolean lessThan( Shape rhs )23. 24. return area() < rhs.area() ;25. 26.27. final public String toString()28. 29. return name + "avand aria " + area() ;30. 31.

Figura 5.4 Clasa de baza abstracta Shape

5.3 Exemplu: Extinderea clasei Shape

In aceasta sectiune vom implementa clasele derivate din clasa Shape si vom prezenta cumsunt ele utilizate ıntr-o maniera polimorfica. Iata enuntul problemei:

Sortare de forme. Se citesc N forme (cercuri, dreptunghiuri sau patrate). Sa se afisezeordonate dupa arie.

Implementarea claselor, prezentata ın Figura 5.5 este simpla si nu ilustreaza aproapenici un concept pe care sa nu-l fi prezentat deja. Singura noutate este ca clasa Square estederivata din clasa Rectangle care este, la randul ei, derivata din Shape. La implementareaacestor clase trebuie sa:

1. Definim un nou constructor

2. Sa examinam fiecare metoda care nu este final sau abstract pentru a vedea daca dorimsa o acceptam nemodificata. Pentru fiecare astfel de metoda care nu corespunde cunecesitatile clasei trebuie sa furnizam o noua definire.

Page 77: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

5.3. EXEMPLU: EXTINDEREA CLASEI SHAPE 77

3. Definirea fiecarei metode abstracte.

4. Adaugarea de alte metode daca este necesar.

1. //clasele Circle, Square si Rectangle;2. //toate sunt derivate din Shape3. //4. //CONSTRUCTORI: (a) cu raza (pentru cerc), (b) cu latura5. //(pentru patrat), (c) cu lungime si latime (pentru dreptunghi)6. //*****************METODE PUBLICE****************************7. //double area()-->Implementeaza metoda abstracta din Shape8.9. //Fisierul trebuie separat in 3 pentru compilare!!!10.11.public class Circle extends Shape12.13. static final private PI = 3.141592653589793 ;14. private double radius ;15.16. public Circle( double rad )17. 18. super( "Circle" ) ;19. radius = rad ;20. 21.22. public double area()23. 24. return PI*radius*radius ;25. 26.27.28.public class Rectangle extends Shape29.30. private double length ;31. private double width ;32. public Rectangle( double len, double wid )33. 34. super( "Rectangle" ) ;35. length = len ;36. width = wid ;37. 38.39. public double area40. 41. return length*width ;42. 43.

Page 78: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

78 CAPITOLUL 5. MOSTENIRE

44.45.public class Square extends Shape46.47. public Square( double side)48. 49. super(side, side) ;50. 51.

Figura 5.5 Codul complet pentru clasele Circle, Rectangle si Square, care va fi salvat ıntrei fisiere sursa separate.

Pentru fiecare clasa am scris un constructor simplu care permite initializarea cu dimensi-unile de baza (raza pentru cercuri, lungimea laturilor pentru dreptunghiuri si patrate). Inconstructor, vom initializa mai ıntai partea mostenita prin apelul lui super. Fiecare clasatrebuie sa defineasca o metoda area, deoarece Shape a declarat-o ca fiind abstracta. Dacauitam sa scriem o metoda area pentru una dintre clase, eroarea va fi detectata ınca de lacompilare, deoarece - daca metoda area lipseste - atunci clasa derivata este si ea abstracta.Observati ca clasa Square este dispusa sa accepte metoda area mostenita de la Rectangle,de aceea nu o mai redefineste.Dupa ce am implementat clasele, suntem gata sa rezolvam problema originala. Vom folosiun sir de clase Shape. Retineti faptul ca prin aceasta nu alocam memorie pentru nici unobiect de tip Shape (ceea ce ar fi ilegal); se aloca memorie doar pentru un sir de referintecatre Shape. Aceste referinte vor putea referi obiecte de tip Circle, Rectangle sau Square1.In Figura 5.6 facem exact acest lucru. Mai ıntai citim obiectele. La linia 21, apelullui readShape consta ın citirea unui caracter, urmata de dimensiunile figurii si de creareaunui nou obiect de tip Shape. Figura 5.7 prezinta o implementare primitiva a acesteirutine. Observati ca ın cazul unei erori la citire se creeaza un cerc de raza 0 si se ıntoarce oreferinta la el. O solutie mai eleganta ın aceasta situatie ar fi fost sa definim si sa aruncamo exceptie.Dupa aceasta, fiecare obiect create de catre readShape este referit de catre un elemental sirului shapes. Se apeleaza insertionSort pentru a sorta formele. In final afisam sirulrezultat de forme, apeland astfel implicit metoda toString.

1. import java.io.* ;2.3. class TestShape4. 5. private static BufferedReader in ;6.7. public static void main( String[] args )8. 9. try10.

1Nici nu se poate inventa o ilustrare mai buna pentru polimorfism (poli=mai multe, morphos=forme).Intr-adeva referinta de tip Shape (shape=forma) poate referi clase de mai multe (poli) forme (morphos):Circle, Rectangle si Square.

Page 79: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

5.3. EXEMPLU: EXTINDEREA CLASEI SHAPE 79

11. //Citeste numarul de figuri12. in = new BufferedReader( new13. InputStreamReader( System.in ) ) ;14. System.out.println( "Numarul de figuri: ") ;15. int numShapes = Integer.parseInt(in.readLine()) ;16.17. //citeste formele18. Shape[] shapes = new Shape[ numShapes ] ;19. for( int i=0; i< numShapes; ++i)20. 21. shapes[i] = readShape() ;22. 23.24. //sortare si afisare25. insertionSort( shapes ) ;26. System.out.println("Figurile sortate dupa arie sunt: " );27. for( int i=0; i< numShapes; ++i)28. 29. System.out.println( shapes[i] ) ;30. 31. 32. catch( Exception e)33. 34. System.out.println(e) ;35. 36.37.38.private static Shape readShape()39.40. /*Implementarea in Figura 5.7*/41.42.43.//sortare porin insertie44.private static void insertionSort( Shape[] a )45.46. /*Implementarea in Figura 5.8*/47.48.

Figura 5.6 Rutina main pentru citirea de figuri si afisarea lor ın ordine crescatoare.

1. //creaza un obiect adecvat de tip Shape functie de2. //datele de intrare.3. //utilizatorul introduce ’c’, ’s’ sau ’r’ pentru a indica4. //forma, apoi introduce dimensiunile5. //in caz de eroare se intoarce un cerc de raza 0

Page 80: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

80 CAPITOLUL 5. MOSTENIRE

6.7. private static Shape readShape()8. 9. double rad ;10. double len ;11. double wid ;12. String s ;13.14. try15. 16. System.out.println( "Introduceti tipul formei: ") ;17. do18. 19. s = in.readLine() ;20. while( s.length() == 0 ) ;21.22. switch( s.charAt(0) )23. 24. case ’c’:25. System.out.println("Raza cercului: " ) ;26. rad = Integer.parseInt( in.readLine() ) ;27. return new Circle( rad ) ;28. case ’s’:29. System.out.println("Latura patratului: " ) ;30. len = Integer.parseInt( in.readLine() ) ;31. return new Square( len ) ;32. case ’r’ :33. System.out.println("Lung. si latimea dreptunghiului "34. + "pe linii separate: ") ;35. len = Integer.parseInt( in.readLine() ) ;36. wid = Integer.parseInt( in.readLine() ) ;37. return new Rectangle( len, wid ) ;38. default:39. System.err.println( "Introduceti c, r sau s") ;40. return new Circle( 0 ) ;41. 42. 43. catch( IOException e )44. 45. System.err.println( e ) ;46. return new Circle( 0 ) ;47. 48.

Figura 5.7 Rutina simpla pentru citirea si ıntoarcerea unei noi forme.

49.//sortare porin insertie

Page 81: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

5.4. MOSTENIRE MULTIPLA 81

50.private static void insertionSort( Shape[] a )51.52. for( int p=1; p < a.length; ++p )53. 54. Shape tmp = a[p] ;55. int j = p ;56. for( ; j>0 && tmp.lessThan( a[j-1] ) ; --j)57. 58. a[j] = a[j-1] ;59. 60. a[j] = tmp ;61. 62.

Figura 5.8 Sortare prin insertie

5.4 Mostenire multipla

Toate exemplele prezentate pana acum, derivau o clasa dintr-o singura clasa de baza. Incazul mostenirii multiple o clasa poate fi derivata din mai mult de o clasa de baza. Deexemplu, putem avea clasele Student si Angajat. Din aceste clase ar putea fi derivata oclasa AngajatStudent.Desi mostenirea multipla pare destul de atragatoare, si unele limbaje (cum ar fi C++)chiar o implementeaza, ea este ımbibata de subtilitati care fac proiectarea claselor deosebitde dificila. De exemplu, cele doua clase de baza ar putea contine metode care au aceeasisemnatura, dar implementari diferite, sau ar putea avea atribute cu acelasi nume. Caredintre ele ar trebui folosit?Din aceste motive, Java nu permite mostenirea multipla. Java furnizeaza ınsa o alterna-tiva, numita interfata.

5.5 Interfete

Interfata ın Java este cea mai abstracta clasa posibila. Ea consta doar din metode ab-stracte publice si din atribute statice si finale.Spunem ca o clasa implementeaza o anumita interfata daca furnizeaza definitii pentrutoate metodele abstracte din cadrul interfetei. O clasa care implementeaza o interfata secomporta ca si cand ar fi extins o clasa abstracta precizata de catre acea interfata.In principiu, diferenta esentiala dintre o clasa abstracta si o interfata este ca desi amandouafurnizeaza o specificatie a ceea ce clasele derivate trebuie sa faca, interfata nu poate furnizanici un fel de detaliu de implementare sub forma de atribute sau de metode implementate.Consecinta practica a acestui lucru este ca interfetele nu sufera de problemele potentialepe care le are mostenirea multipla, deoarece nu putem avea implementari diferite pen-tru aceeasi metoda. Astfel, desi o clasa poate sa extinda o singura clasa, ea poate saimplementeze mai mult de o singura interfata.

Page 82: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

82 CAPITOLUL 5. MOSTENIRE

5.5.1 Definirea unei interfete

Din punct de vedere sintactic, nimic nu este mai simplu decat precizarea unei interfete.Interfata arata ca o declaratie a unei clase, doar ca foloseste cuvantul cheie interface. Eaconsta dintr-o lista de metode care trebuie declarate. Un exemplu de interfata este Com-parable, prezentata ın Figura 5.9.Interfata Comparable precizeaza doua metode pe care orice clasa derivata din ea trebuiesa le implementeze: compareTo si lessThan. Metoda compareTo se va comporta similarcu metoda compareTo a clasei String. Observati ca nu este necesar sa precizam faptul caaceste metode sunt public sau abstract, deoarece acest lucru este implicit pentru metodeleunei interfete.

public Interface Comparableint compareTo(Comparable rhs) ;boolean lessThan(Comparable rhs) ;

Figura 5.9 Interfata Comparable

5.5.2 Implementarea unei interfete

O clasa implementeaza o interfata ın doi pasi:

1. declara ca implementeaza interfata si

2. defineste implementari pentru toate metodele din interfata.

Un exemplu este prezentat ın Figura 5.10, ın care se defineste clasa MyInteger. ClasaMyInteger are un comportament asemanator cu al clasei Integer, din pachetul java.lang.In linia 1 se vede ca atunci cand implementam o interfata folosim cuvantul cheie imple-ments ın loc de extends. In aceasta clasa putem scrie orice metode dorim, dar trebuiesa definim cel putin metodele din interfata. Interfata este implementata ın liniile 27-36.Remarcati faptul ca trebuie sa implementam exact metoda precizata ın cadrul interfetei;din acest motiv aceste metode au ca parametru un obiect de tip Comparable si nu unMyInteger.O clasa care implementeaza o interfata poate fi extinsa cu conditia sa nu fie finala. Astfel,daca MyInteger nu ar fi fost finala, am fi putut-o extinde.O clasa care implementeaza o interfata poate totusi sa extinda si o alta clasa. De exemplu,am fi putut, ın principiu, scrie:

public class MyInteger extends Integer implements Comparable

Acest cod este incorect doar pentru ca Integer este o clasa finala care nu poate fi extinsa.

Page 83: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

5.5. INTERFETE 83

1. final public class MyInteger implements Comparable2. 3. //constructor4. public MyInteger( int value )5. 6. this.value = value ;7. 8.9. //cateva metode10. public String toString()11. 12. return Integer.toString( value ) ;13. 14.15. public int intValue()16. 17. return value ;18. 19.20. public boolean equals( Object rhs )21. 22. return rhs instanceof MyInteger &&23. value == ( (MyInteger)rhs ).value ;24. 25.26. //implementarea interfetei27. public boolean lessThan( Comparable rhs )28. 29. return value < ( (MyInteger)rhs).value ;30. 31.32. public int compareTo( Comparable rhs )33. 34. return value < ( (MyInteger)rhs).value ? -1 :35. value == ( (MyInteger)rhs).value ? 0 : 1 ;36. 37.38. private int value ;39.

Figura 5.10 Clasa MyInteger (versiune preliminara), care implementeaza interfata Com-parable

5.5.3 Interfete multiple

Asa cum am mentionat mai devreme, o clasa poate sa implementeze mai mult de o singurainterfata. Sintaxa pentru a realiza acest lucru este simpla. O clasa poate implementa mai

Page 84: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

84 CAPITOLUL 5. MOSTENIRE

multe interfete prin:

1. precizarea interfetelor pe care le implementeaza

2. implementarea tuturor metodelor din interfete.

Interfata este cea mai abstracta clasa posibila si reprezinta o solutie eleganta la problemamostenirii multiple.

5.6 Implementarea de componente generice

Sa ne reamintim ca unul dintre scopurile principale ale programarii orientate pe obiecte estesuportul pentru reutilizarea codului. Unul dintre mecanismele importante folosite pentruındeplinirea acestui scop este programarea generica: Daca implementarea unei metode esteidentica pentru mai multe clase (cu exceptia tipului de baza al obiectului), se poate folosio implementare generica pentru a descrie functionalitatea de baza. De exemplu, putemscrie o metoda care sa sorteze un sir de elemente; algoritmul pentru aceasta metoda esteindependent de tipul de obiecte care sunt sortate, deci putem folosi un algoritm generic.Spre deosebire de multe dintre limbajele de programare mai noi (cum ar fi C++) careutilizeaza sabloane pentru a implementa programarea generica, Java nu ofera suport pentruimplementarea directa a programarii generice, deoarece programarea generica poate fiimplementata folosind conceptele de baza ale mostenirii. In aceasta sectiune vom prezentacum pot fi implementate metode si clase generice ın Java folosind principiile de baza alemostenirii.Ideea de baza ın Java este ca putem implementa o clasa generica folosind o superclasaadecvata, cum ar fi Object. In Java, daca o clasa nu extinde o alta clasa, atunci ea extindeimplicit clasa Object (definita ın pachetul java.lang). Ca o consecinta, fiecare clasa este osubclasa a lui Object. Sa consideram clasa MemoryCell din Figura 5.11. Aceasta clasapoate sa retina un obiect de tip Object. Deoarece Object este clasa de baza pentru oriceclasa din Java, rezulta ca clasa noastra poate sa stocheze orice fel de obiecte.

1. //clasa MemoryCell2. // Object read() --> Intoarce valoarea stocata3. // void write( Object x ) --> x este stocat4.5. public class MemoryCell6. 7. //metode publice8. public Object read()9. 10. return storedValue() ;11. 12.13. public void write( Object x )14. 15. storedValue = x ;16.

Page 85: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

5.6. IMPLEMENTAREA DE COMPONENTE GENERICE 85

17.18. private object storedValue ;19.

Figura 5.11 Clasa generica MemoryCell

Exista doua detalii care trebuie luate ın considerare atunci cand folosim aceasta strategie.Ambele sunt ilustrate ın Figura 5.12. Functia main scrie valoarea 5 ıntr-un obiect Mem-oryCell, dupa care citeste din obiectul MemporyCell. In primul rand, tipurile primitivenu sunt obiecte. Astfel, m.write(5) ar fi fost incorect. Totusi, aceasta nu este o problema,deoarece Java dispune de clase wrapper (de ”ımpachetare”) pentru cele opt tipuri primi-tive. Astfel, obiectul de tip MemoryCell va retine un obiect de tip Integer.Al doilea detaliu este ca rezultatul lui m.read() este un Object. Deoarece ın clasa Objecteste definita o metoda toString, nu este necesar sa facem conversia de la Object la Integer.Referinta returnata de m.read() (de tip Object) este polimorfica si ea refera de fapt unobiect de tip Integer. In consecinta, se va apela automat metoda toString a clasei Integer.Daca am fi vrut totusi sa extragem valoarea retinuta ın obiectul de tip MemoryCell, ar fitrebuit sa scriem o linie de genul

int i = ( (Integer)m.read() ).intValue() ;

ın care se converteste mai ıntai valoarea returnata de read la Integer, dupa care se folosestemetoda intValue() pentru a obtine un int.Deoarece clasele wrapper sunt clase finale, constructorul si accesorul intValue pot fi ex-pandate inline de catre compilator, generand astfel un cod la fel de eficient ca utilizareadirecta a unui int.

1. public class TestMemoryCell2. 3. public static void main( String[] args )4. 5. MemoryCell m = new MemoryCell() ;6.7. m.write( new MyInteger( 5 ) ) ;8. System.out.println( "Continutul este: " + m.read() ) ;9. 10.

Figura 5.12 Folosirea clasei generice MemoryCell

Un al doilea exemplu este problema sortarii. Am scris deja o metoda insertionSort carelucreaza cu un sir de clase Shape. Ar fi interesant sa rescriem aceasta metoda pentru unsir generic. Figura 5.13 prezinta o metoda de sortare generica insertionSort care estepractic identica cu metoda de sortare din Figura 5.8 doar ca foloseste Comparable ın locde Shape. Putem pune aceasta metoda ıntr-o clasa Sort. Observati ca nu sortam Object,ci Comparable. Metoda insertionSort sorteaza un sir de elemente Comparable, deoarecefoloseste lessThan. Aceasta ınseamna ca doar clasele care implementeaza interfata Com-parable pot fi sortate astfel. De remarcat faptul ca insertionSort nu poate sa sorteze un

Page 86: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

86 CAPITOLUL 5. MOSTENIRE

sir de obiecte Shape, deoarece clasa Shape din Figura 5.4 nu implementeaza interfataComparable. Unul dintre exercitii propune modificarea clasei Shape ın acest sens.Pentru a vedea cum poate fi folosita metoda generica de sortare vom scrie un program,prezentat ın Figura 5.14, care citeste un numar nelimitat de valori ıntregi, le sorteaza siafiseaza rezultatul. Metoda readIntArray a clasei Reader (prezentata ın anexa) este folositapentru a citi un sir de ıntregi. Transformam apoi sirul citit ıntr-un sir de elemente careimplementeaza interfata Comparable. In linia 15 cream sirul, iar ın liniile 16-19 obiectelecare sunt stocate ın sir. Sortarea este realizata ın linia 22. In final, afisam rezultatele ınliniile 26-29. De retinut ca metoda toString este implicit apelata pentru clasa MyInteger.

1. //sortare prin insertie2. public static void insertionSort( Comparable[] a )3. 4. for( int p=1; p < a.length; ++p )5. 6. Comparable tmp = a[p] ;7. int j = p ;8. for( ; j>0 && tmp.lessThan( a[j-1] ) ; --j)9. 10. a[j] = a[j-1] ;11. 12. a[j] = tmp ;13. 14.

Figura 5.13 Algoritm de sortare generic

1. import io.*;//pachet facut de noi pentru a citi de la tastatura2. public class SortIns3. 4. //program de test care citeste valori intregi de la terminal5. //(cate unul pe linie), le sorteaza si apoi le afiseaza6.7. public static void main( String[] args )8. 9.10. //citeste un sir de intregi11. System.out.print("Introduceti elementele sirului: ") ;12. int [] sir = Reader.readIntArray() ;13.14. //conversie la un sir de MyInteger15. MyInteger[] sirNou = new MyInteger[ sir.length ] ;16. for( int i=0; i<sir.length; ++i)17. 18. sirNou[i] = new MyInteger( sir[i] ) ;19. 20.

Page 87: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

5.7. ANEXA - CLASA READER 87

21. //aplica metoda de sortare22. Sort.insertionSort( sirNou ) ;23.24. //afiseaza rezultatul sortarii25. System.out.println("Rezultatul sortarii: ") ;26. for( int i=0; i<sirNou.length; ++i)27. 28. System.out.println( sirNou[i] ) ;29. 30. 31.

Figura 5.14 Citire un sir de ıntregi, sortarea si afisarea lor

5.7 Anexa - clasa Reader

1. package io ;2. //clasa va trebui salvata intr-un director cu numele "io"3. //directorul in care se afla "io" va trebui adaugat in CLASSPATH4.5. import java.io.* ;6. import java.util.StringTokenizer ;7. public class Reader8. 9. public static String readString()10. 11. BufferedReader in = new BufferedReader(12. new InputStreamReader( System.in ) ) ;13. try14. 15. return in.readLine() ;16. 17. catch(IOException e)18. 19. //ignore20. 21. return null ;22. 23.24. public static int readInt()25. 26. return Integer.parseInt(readString()) ;27. 28.29. public static double readDouble()30.

Page 88: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

88 CAPITOLUL 5. MOSTENIRE

31. return Double.parseDouble(readString()) ;32. 33.34. public static char readChar()35. 36. BufferedReader in = new BufferedReader(37. new InputStreamReader( System.in ) ) ;38. try39. 40. return (char)in.read() ;41. 42. catch(IOException e)43. 44. //ignore45. 46. return ’\0’ ;47. 48.49. public static int[] readIntArray()50. 51. String s = readString() ;52. StringTokenizer st = new StringTokenizer( s ) ;53. //aloca memorie pentru sir54. int[] a = new int[ st.countTokens() ] ;55.56. for(int i=0; i<a.length; ++i)57. 58. a[i] = Integer.parseInt( st.nextToken() ) ;59. 60.61. return a ;62. 63.64.

5.8 Probleme propuse

1. Scrieti doua metode generice min si max, fiecare acceptand doi parametri de tipComparable. Folositi aceste metode ıntr-o clasa numita MyInteger.

2. Scrieti doua metode generice min si max, fiecare acceptand un sir de Comparable.Folositi apoi aceste metode pentru tipul MyInteger.

3. Pentru exemplul cu clasa Shape modificati metodele readShape si main astfel ıncatele sa arunce si sa prinda exceptii (ın loc sa creeze un cerc de raza 0) atunci cand seobserva o eroare la citire.

Page 89: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

5.8. PROBLEME PROPUSE 89

4. Modificati clasa Shape astfel ıncat ea sa poata fi folosita de catre un algoritm desortare generic.

5. Rescrieti ierarhia de clase Shape pentru a retine aria ca un membru privat, careeste calculat de catre constructorul pentru Shape. Constructorii din clasele derivatetrebuie sa calculeze aria si sa trimita rezultatul catre metoda super. Faceti din areao metoda finala care doar returneaza valoarea acestui atribut.

6. Adaugati conceptul de pozitie la ierarhia Shape prin includerea coordonatelor cadate membru. Adaugati apoi o metoda distance.

7. Scrieti o clasa abstracta pentru Date din care sa derivati apoi o clasa Gregorian Date(data ın formatul nostru obisnuit).

Page 90: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

Capitolul 6

Analiza eficientei algoritmilor

In prima parte a acestei lucrari am examinat cum putem folosi programarea orientata peobiecte pentru proiectarea si implementarea programelor ıntr-un mod profesional. Totusi,aceasta este doar jumatate din problema.Calculatorul este folosit de obicei pentru a prelucra cantitati mari de informatie. Atuncicand executam un program pe date de intrare de dimensiuni mari, trebuie sa fim sigurica algoritmul se va termina ıntr-un timp rezonabil. Acest lucru este aproape ıntotdeaunaindependent de limbajul de programare folosit, ba chiar si de metodologia aplicata (cumar fi programarea orientata pe obiecte, sau programarea procedurala).Un algoritm este un set bine precizat de instructiuni pe care calculatorul le va executapentru a rezolva o problema. Odata ce am gasit un algoritm pentru o anumita problema,si am determinat ca algoritmul este corect, pasul urmator este de a determina cantitateade resurse, cum ar fi timpul si cantitatea de memorie, pe care algoritmul le cere. Acestpas este numit analiza algoritmului. Un algoritm care are nevoie de cativa gigabaiti dememorie pentru a rula nu este bun de nimic pe masinile de la ora actuala, chiar daca eleste corect.In acest capitol vom vedea:

• Cum putem estima timpul cerut de un algoritm

• Tehnici pentru reducerea drastica a timpului de executie al unui algoritm

• Un cadru matematic care descrie la un mod mai riguros timpul de executieal algoritmilor

6.1 Ce este analiza algoritmilor?

Cantitatea de timp pe care orice algoritm o cere pentru executie depinde aproape ıntotdeaunade cantitatea de date de intrare pe care o proceseaza. Este de asteptat ca sortarea a 10.000de elemente sa necesite mai mult timp decat sortarea a 10 elemente. Timpul de executieal unui algoritm este astfel o functie de dimensiunea datelor de intrare. Valoarea exacta aacestei functii depinde de mai multi factori, cum ar fi viteza calculatorului pe care ruleazaprogramul, calitatea compilatorului si, ın anumite situatii, calitatea programului. Pentruun program dat, care ruleaza pe un anumit calculator, putem reprezenta grafic timpul

90

Page 91: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

6.1. CE ESTE ANALIZA ALGORITMILOR? 91

de executie al algoritmilor. In Figura 6.1 am realizat un astfel de grafic pentru patruprograme. Curbele reprezinta patru functii care sunt foarte des ıntalnite ın analiza algo-ritmilor: liniara, O(nlogn), patratica si cubica. Dimensiunea datelor de intrare variaza dela 1 la 100 de elemente, iar timpii de executie de la 0 la 5 milisecunde. O privire rapidaasupra graficelor din Figura 6.1 si Figura 6.2, ne lamureste ca ordinea preferintelorpentru timpii de executie este liniar, O(nlogn), patratic si cubic.Sa luam ca exemplu descarcarea (download-area) unui fisier de pe Internet. Sa presupunemca la ınceput apare o ıntarziere de doua secunde (pentru a stabili conexiunea), dupa caredescarcarea se va face la 1.6 KB/sec. In aceasta situatie, daca fisierul de adus are Nkilobaiti, timpul de descarcare a fisierului este descris de formula T(N)=N/1.6+2. Aceastaeste o functie liniara. Se poate calcula usor ca descarcarea unui fisier de 80K va dura aprox-imativ 52 de secunde, ın timp ce descarcarea unui fisier de doua ori mai mare (160K) vadura 102 secunde, deci cam de doua ori mai mult. Aceasta proprietate, ın care timpul estepractic direct proportional cu cantitatea de date de intrare, este specifica unui algoritmliniar, si constituie adeseori o situatie ideala. Asa cum se vede din grafice, unele curbeneliniare pot conduce la timpi de executie foarte mari. In acest capitol vom prezentaurmatoarele probleme:

• Cu cat este mai buna o curba ın comparatie cu o alta curba?

• Cum putem calcula pe care curba se situeaza un anumit algoritm?

• Cum putem proiecta algoritmi care sa nu se situeze pe curbele nefavorabile?

.

Figura 6.1 Timpi de executie pentru date de intrare mici

.

Figura 6.2 Timpi de executie pentru date de intrare moderate

O functie cubica este o functie al carei termen dominant este N3, ınmultit cu o constanta.De exemplu, 10N3 + N2 + 40N + 80 este o functie cubica. Similar, o functie patraticaare termenul dominant N2 ınmultit cu o constanta, iar o functie liniara are un termendominant care este N ınmultit cu o constanta.Oricare dintre cele trei functii prezentate mai sus poate fi mai mica decat cealalta ıntr-unpunct dat. Acesta este motivul pentru care nu ne intereseaza valorile efective ale timpilorde executie, ci rata lor de crestere. Acest lucru este justificabil prin trei argumente. Inprimul rand, pentru functiile cubice, cum ar fi cea prezentata ın Figura 6.2, atunci candN are valoarea 1000, valoarea functiei cubice este aproape complet determinata de val-oarea termenului cubic. Functia 10N3 +N2 +40N +80 are valoarea 10.001.040.080 pentruN = 1000, din care 10.000.000.000 se datoreaza termenului 10N3. Daca am fi folosit doartermenul cubic pentru a estima valoarea functiei, ar fi rezultat o eroare de aproximativ0.01%. Pentru un N suficient de mare, valoarea functiei este determinata aproape completde termenul ei dominant (semnificatie termenului suficent de mare, depinde de functia ıncauza).Un al doilea motiv pentru care masuram doar rata de crestere a functiilor este ca valoarea

Page 92: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

92 CAPITOLUL 6. ANALIZA EFICIENTEI ALGORITMILOR

exacta a constantei multiplicative pentru termenul dominant difera de la un calculator laaltul. De exemplu, calitatea compilatorului poate sa influenteze destul de mult valoareaconstantei. In al treilea rand, valorile mici pentru N sunt de obicei nesemnificative. DinFigura 6.1 se observa ca pentru N = 10, toti algoritmii se ıncheie ın mai putin de 3 ms.Diferenta dintre cel mai bun si cel mai slab algoritm este mai mica decat un clipit de ochi.Pentru a reprezenta rata de crestere a unui algoritm se foloseste asa-numita notatie asimp-totica (engl. ”Big-Oh notation”). De exemplu, rata de crestere pentru un algoritm patraticeste notata cu O(N2). Notatia asimptotica ne permite sa stabilim o ordine partiala ıntrefunctii prin compararea termenului lor dominant.Vom dezvolta ın acest capitol aparatul matematic necesar pentru analiza eficientei algorit-milor, urmarind ca aceasta incursiune matematica sa nu fie excesiv de formala. Vom arataapoi, pe baza de exemple, cum poate fi analizat un algoritm. O atentie speciala o vomacorda tehnicilor de analiza a algoritmilor recursivi, prezentati ın capitolul 6 al acestuicurs.

6.2 Notatia asimptotica

Notatia asimptotica are rolul de a estima timpul de calcul necesar unui algoritm pentru afurniza rezultatul, functie de dimensiunea datelor de intrare.

6.2.1 O notatie pentru ordinul de marime al timpului de executie alunui algoritm

Fie N multimea numerelor naturale, < multimea numerelor reale. Fie f : N → [0,∞) ofunctie arbitrara. Definim multimea de functii:

O(f) = t : N→ [0,∞) | ∃c > 0,∃n0 ∈ N, astfel incit ∀ n ≥ n0 avem t(n) ≤c ∗ f(n)

Cu alte cuvinte, O(f) (se citeste ”ordinul lui f”) este multimea tuturor functiilor t marginitesuperior de un multiplu real pozitiv al lui f, pentru valori suficient de mari ale argumentuluin. Vom conveni sa spunem ca t este ın ordinul lui f (sau, echivalent, t este ın O(f) , saut ∈ O(f) ) chiar si atunci cand t(n) este negativ sau nedefinit pentru anumite valorin < n0. In mod similar, vom vorbi despre ordinul lui f chiar si atunci cand valoarea f(n)este negativa sau nedefinita pentru un numar finit de valori ale lui n; ın acest caz, vom alegen0 suficient de mare, astfel ıncat pentru n ≥ n0 acest lucru sa nu mai apara. De exemplu,vom vorbi despre ordinul lui n/log n , chiar daca pentru n=0 si n=1 functia nu estedefinita. In loc de t ∈ O(f), uneori este mai convenabil sa folosim notatia t(n) ∈ O(f(n)), subıntelegand aici ca t(n) si f(n) sunt functii.Fie un algoritm dat si fie o functie t : N → [0,∞) astfel ıncat o anumita implementare aalgoritmului sa necesite cel mult t(n) unitati de timp pentru a rezolva un caz de marimen, n ∈ N . Principiul invariantei1 ne asigura atunci ca orice implementare a algoritmuluinecesita un timp ın ordinul lui t. Cu alte cuvinte, acest algoritm necesita un timp ınordinul lui f pentru orice functie f : N→ [0,∞) pentru care t ∈ O(f). In particular avem

1Acest principiu afirma ca doua implementari diferite ale unui algoritm nu difera, ca eficienta, decatcel mult printr-o constanta multiplicativa

Page 93: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

6.2. NOTATIA ASIMPTOTICA 93

relatia: t ∈ O(t) . Vom cauta, ın general, sa gasim cea mai simpla functie f , astfel ıncatt ∈ O(f).

Exemplul 6.1 Fie functia t(n) = 3n2 − 9n + 13 . Pentru n suficient de mare, vom avearelatia t(n) ≤ 4n2 . In consecinta, luand c=4, putem spune ca t(n) ∈ O(n2). La fel debine puteam sa spunem ca t(n) ∈ O(13n2 −

√2n + 12.5), dar pe noi ne intereseaza sa

gasim o expresie cat mai simpla. Este adevarata si relatia t(n) ∈ O(n4), dar, asa cumvom vedea mai tarziu, suntem interesati de a margini cıt mai strans ordinul de marime alalgoritmului, pentru a putea obiectiva cat mai bine durata sa de executie.

Proprietatile de baza ale lui O(f) sunt date ca exercitii (1 - 5) si ar fi recomandabil sa lestudiati ınainte de a trece mai departe.Notatia asimptotica defineste o relatie de ordine partiala ıntre functii si deci ıntre eficientarelativa a diferitilor algoritmi care rezolva o anumita problema. Vom da ın continuare ointerpretare algebrica a notatiei asimptotice. Pentru oricare doua functii f, g : N → <∗definim urmatoarea relatie binara: f ≤ g daca O(f) ⊆ O(g). Relatia ”≤ ” este o relatie deordine partiala (reflexiva, tranzitiva, antisimetrica) ın multimea functiilor definite pe N sicu valori ın [0,∞) (exercitiul 4). Definim si o relatie de echivalenta: f ≡ g daca O(f)=O(g).Prin aceasta relatie obtinem clase de echivalenta, o clasa de echivalenta cuprinzand toatefunctiile care difera ıntre ele printr-o constanta multiplicativa. De exemplu, lg n ≡ ln nsi avem o clasa de echivalenta a functiilor logaritmice, pe care o notam generic cu O(logn) . Notand cu O(1) clasa de echivalenta a algoritmilor cu timpul marginit superior deo constanta (cum ar fi interschimbarea a doua numere, sau maximul a trei elemente),ierarhia celor mai cunoscute clase de echivalenta este:

O(1) ⊂ O(log n) ⊂ O(n) ⊂ O(nlog n) ⊂ O(n2) ⊂ O(n3) ⊂ O(2n)

Aceasta ierarhie corespunde unei clasificari a algoritmilor dupa un criteriu al performantei.Pentru o problema data, dorim mereu sa obtinem un algoritm corespunzator unei clase catmai ”de jos” (cu timp de executie cat mai mic). Astfel, se considera a fi o mare realizaredaca ın locul unui algoritm exponential gasim un algoritm polinomial. Exercitiul 5 ne dao metoda de simplificare a calculelor ın care apare notatia asimptotica. De exemplu:

n3 + 4n2 + 2n + 7 ∈ O(n3 + (4n2 + 2n + 7)) = O(max(n3, 4n2 + 2n + 7)) = O(n3)

Ultima egalitate este adevarata chiar daca max(n3, 4n2 + 2n + 7) 6= n3 pentru 0 ≤ n ≤ 4, deoarece notatia asimptotica se aplica doar pentru n suficient de mare. De asemenea,

n3−3n2−n−8 ∈ O(n3

2 +(n3

2 −3n2−n−8)) = O(max(n3

2 , n3

2 −3n2−n−8)) = O(n3

2 ) = O(n3)

chiar daca pentru 0 ≤ n ≤ 6 polinomul este negativ. Exercitiul 8 trateaza cazul unuipolinom oarecare.Notatia O(f) este folosita pentru a limita superior timpul necesar unui algoritm, masurandeficienta (complexitatea computationala) a algoritmului respectiv. Uneori este interesantsa estimam si o limita inferioara a acestui timp. In acest scop, definim multimea:

Ω(f) = t : N→ [0,∞) | ∃c > 0,∃n0 ∈ N, astfel incit ∀ n ≥ n0 avem t(n) ≥c ∗ f(n)

Exista o anumita dualitate ıntre notatiile O(f) si Ω(f): pentru doua functii oarecaref, g : N→ [0,∞), avem:

Page 94: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

94 CAPITOLUL 6. ANALIZA EFICIENTEI ALGORITMILOR

f ∈ O(g) daca si numai daca g ∈ Ω(f) .

O estimare foarte precisa a timpului de executie se obtine atunci cand timpul de executieal unui algoritm este limitat atat inferior cat si superior de cate un multiplu real pozitival aceleasi functii. In acest scop, introducem notatia:

Θ(f) = O(f) ∩ Ω(f)

numita ordinul exact al lui f. Pentru a compara ordinele a doua functii, notatia Θ nu esteınsa mai puternica decat notatia O , ın sensul ca O(f)=O(g) este echivalent cu Θ(f) =Θ(g).Exista situatii ın care timpul de executie al unui algoritm depinde simultan de mai multiparametri. Aceste situatii sunt tipice pentru anumiti algoritmi care opereaza cu grafuri sila care timpul depinde atat de numarul de varfuri cat si de numarul de muchii. Notatiaasimptotica se generalizeaza ın mod natural si pentru functii cu mai multe variabile. Astfel,pentru o functie arbitrara f : N×N→ [0,∞) definim

O(f) = t : N×N→ [0,∞) | ∃c > 0,∃n0,m0 ∈ N, astfel incit ∀ m ≥ m0,∀ n ≥n0 avem t(m,n) ≤ c ∗ f(m,n).

Similar se obtin si celelalte generalizari.

6.3 Tehnici de analiza algoritmilor

Nu exista o metoda standard pentru analiza eficientei unui algoritm. Este mai curand ochestiune de rationament, intuitie si experienta. Vom arata pe baza de exemple cum sepoate efectua o astfel de analiza.

6.3.1 Sortarea prin selectie

Consideram algoritmul de sortare prin selectia minimului, reprodus mai jos:

pentru i = 1, n− 1//se calculeaza pozitia minimului lui a(i), a(i + 1), . . . , a(n)PozMin← i //initializam minimul cu indicele primului elementpentru j = i + 1, n

daca a(i) < a(PozMin) atunciPozMin = i

sfarsit daca//se aseaza minimul pe pozitia iaux← a(i)a(i)← a(PozMin)a(PozMin)← aux

sfarsit pentru //dupa jsfarsit pentru //dupa i

Timpul pentru o singura executie a ciclului pentru dupa variabila j poate fi marginitsuperior de o constanta a. In total, pentru un i fixat, tinand cont de faptul ca se realizeaza

Page 95: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

6.3. TEHNICI DE ANALIZA ALGORITMILOR 95

n-i iteratii, acest ciclu necesita un timp de cel mult b + a(n − i) unitati, unde b este oconstanta reprezentand timpul necesar pentru initializarea buclei. O singura executie abuclei exterioare are loc ın cel mult c + b + a(n − i) unitati de timp, unde c este o altaconstanta. Tinand cont de faptul ca bucla dupa j se realizeaza de n-1 ori, timpul total deexecutie al algoritmului este cel mult:

d +∑n−1

i=1 (c + b + a(n− i))

unitati de timp, d fiind din nou o constanta. Simplificam aceasta expresie si obtinema2n2 +(b+ c− a

2 )n+(d− c− b), de unde deducem ca algoritmul necesita un timp ın O(n2).O analiza similara asupra limitei inferioare arata ca timpul este de fapt ın Θ(n2). Nueste necesar sa consideram cazul cel mai nefavorabil sau cazul mediu deoarece timpul deexecutie al sortarii prin selectie este independent de ordonarea prealabila a elementelor desortat.In acest prim exemplu am analizat toate detaliile. De obicei ınsa, detalii cum ar fi timpulnecesar initializarii ciclurilor nu se vor considera explicit, deoarece ele nu afecteaza ordinulde complexitate al algoritmului. Pentru cele mai multe situatii, este suficient sa alegemo anumita instructiune din algoritm ca barometru si sa numaram de cate ori se executaaceasta instructiune. In cazul nostru, putem alege ca barometru testul

a[i] < a[PozMin]

din bucla interioara. Este usor de observat ca acest test se executa de n(n−1)2 ori.

6.3.2 Sortarea prin insertie

Timpul pentru algoritmul de sortare prin insertie (sectiunea 5.3, Figura 5.8) este depen-dent de ordonarea prealabila a elementelor de sortat. Vom folosi comparatia

tmp.lessThan( a[j-1] )

din ciclul for ca barometru.Sa presupunem ca p este fixat si fie n = a.length lungimea sirului. Cel mai nefavorabilcaz apare atunci cand tmp < a[j − 1] pentru fiecare j ıntre p si 1, algoritmul facand ınaceasta situatie p − 1 comparatii. Acest lucru se ıntampla (pentru fiecare valoare a lui pde la 1 la n− 1) atunci cand tabloul a este initial ordonat descrescator. Numarul total decomparatii pentru cazul cel mai nefavorabil este:∑n

i=2 i− 1 = n(n−1)2 ∈ Θ(n2)

Vom estima acum timpul mediu necesar pentru un caz oarecare. Presupunem ca elementeletabloului a sunt distincte si ca orice permutare a lor are aceeasi probabilitate de aparitie.Atunci, daca 1 ≤ k ≤ p , probabilitatea ca a[p] sa fie cel de-al k-lea cel mai mare elementdintre elementele a[1], a[2], . . . , a[p] este 1

p . Pentru un p fixat, conditia a[p] < a[p− 1] estefalsa cu probabilitatea 1

p , deci probabilitatea ca sa se execute comparatia ”tmp < a[j−1]”o singura data ınainte de iesirea din bucla while este 1

p . Comparatia ”tmp < a[j − 1]” seexecuta de exact doua ori tot cu probabilitatea 1

p , etc. Probabilitatea ca sa se executecomparatia de exact p − 1 ori este 2

p , deoarece aceasta se ıntampla atat cand tmp <b[0] cat si cand b[0] ≤ tmp < b[1]! Numarul mediu de comparatii, pentru un p fixat,

Page 96: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

96 CAPITOLUL 6. ANALIZA EFICIENTEI ALGORITMILOR

este ın consecinta, suma numarului de comparatii pentru fiecare situatie, ınmultita cuprobabilitatea de aparitie a acelei situatii:

ci = 11i + 21

i + . . . + (i− 2)1i + (i− 1)2

i = i+12 −

1i

Pentru a sorta n elemente, avem nevoie de∑n

i=2 ci comparatii, ceea ce este egal cu n2+3n4 −

Hn ∈ Θ(n2). Prin Hn =∑n

i=1 i−1 ∈ Θ(log n) am notat al n-lea termen al seriei armonice.Se observa ca algoritmul de sortare prin inserare efectueaza pentru cazul mediu de doua orimai putine comparatii decat pentru cazul cel mai nefavorabil. Totusi, ın ambele situatii,numarul comparatiilor este ın Θ(n2).Cu toate ca algoritmul necesita un timp ın Ω(n2) atat pentru cazul mediu cat si pentrucel mai nefavorabil caz, pentru cazul cel mai favorabil (cand initial tabloul este ordonatcrescator) timpul este ın O(n). De fapt, pentru cazul cel mai favorabil, timpul este si ınΩ(n) (deci ın Θ(n)).

6.3.3 Turnurile din Hanoi

Matematicianul francez Eduard Lucas a propus ın 1883 o problema care a devenit apoicelebra mai ales datorita faptului ca a prezentat-o sub forma unei legende. Se spune caBrahma (Zeul Creatiei la hindusi) a fixat pe Pamant trei tije de diamant si pe una din elea pus ın ordine crescatoare 64 de discuri de aur de dimensiuni diferite, astfel ıncat disculcel mai mare era jos. Brahma a creat si o manastire, iar sarcina calugarilor era sa mutetoate discurile pe o alta tija. Singura operatiune permisa era mutarea cate unui singur discde pe o tija pe alta, astfel ıncat niciodata sa nu se puna un disc mai mare peste un discmai mic. Legenda spune ca sfarsitul lumii se va petrece atunci cand calugarii vor savarsilucrarea. Aceasta se dovedeste a fi o previziune extrem de optimista asupra sfarsituluilumii. Presupunand ca ın fiecare secunda se muta un disc si lucrand fara ıntrerupere, cele64 de discuri nu pot fi mutate nici ın 500 de miliarde de ani de la ınceputul actiunii!Pentru a rezolva problema, vom numerota cele trei tije cu 1, 2 si respectiv 3. Se observaca pentru a muta cele n discuri de pe tija cu numarul i pe tija cu numarul j (i si j iauvalori ıntre 1 si 3) este necesar sa transferam primele n − 1 discuri de pe tija i pe tija6− i− j (adica pe tija ramasa libera), apoi sa transferam discul n de pe tija i pe tija j, iarapoi retransferam cele n− 1 discuri de pe tija 6− i− j pe tija j. Cu alte cuvinte, reducemproblema mutarii a n discuri la problema mutarii a n − 1 discuri. Urmatoarea metodaJava descrie acest algoritm recursiv.

public static void hanoi(int n,int i,int j)if( n > 0 )hanoi(n-1, i, 6-i-j) ;System.out.println(i + "-->" + j) ;hanoi(n-1, 6-i-j, i) ;

Pentru rezolvarea problemei initiale, facem apelul

Page 97: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

6.4. ANALIZA ALGORITMILOR RECURSIVI 97

hanoi(64,1,2);

Consideram instructiunea println ca barometru. Timpul necesar algoritmului este expri-mat prin urmatoare recurenta:

t(n) =

1 daca n = 1

2t(n− 1) + 1 daca n > 1

Vom demonstra ın sectiunea 6.4 ca t(n) = 2n − 1. Rezulta t ∈ Θ(2n).Acest algoritm este optim ın sensul ca este imposibil sa mutam discuri de pe o tija pe altacu mai putin de 2n − 1 operatii. Pentru a muta 64 de discuri vor fi ın consecinta necesareun numar astronomic de 264 peratii. Implementarea ın oricare limbaj de programare careadmite exprimarea recursiva se poate face aproape ın mod direct.

6.4 Analiza algoritmilor recursivi

Am vazut ın exemplul precedent cat de puternica si ın acelasi timp cat de eleganta esterecursivitatea ın elaborarea unui algoritm. Cel mai important castig al exprimarii re-cursive este faptul ca ea este naturala si compacta, fara sa ascunda esenta algoritmuluiprin detaliile de implementare. Pe de alta parte, apelurile recursive trebuie folosite cudiscernamant, deoarece solicita si ele resursele calculatorului (timp si memorie). Analizaunui algoritm recursiv implica aproape ıntotdeauna rezolvarea unui sistem de recurente.Vom vedea ın continuare cum pot fi rezolvate astfel de recurente. Incepem cu tehnica ceamai simpla.

6.4.1 Metoda iteratiei

Cu putina experienta si intuitie putem rezolva de multe ori astfel de recurente prin metodaiteratiei: se executa primii pasi, se intuieste forma generala, iar apoi se demonstreaza prininductie matematica ca forma este corecta. Sa consideram de exemplu recurenta problemeiturnurilor din Hanoi. Se observa ca pentru a muta n discuri este necesar sa mutam n− 1discuri, apoi sa mutam un disc si ın final din nou n− 1 discuri. In consecinta, pentru unanumit n > 1 obtinem succesiv:

t(n) = 2t(n− 1) + 1 = 22t(n− 2) + 2 + 1 = . . . = 2n−1t(1) +∑n−2

i=0 2i

Rezulta t(n) = 2n − 1. Prin inductie matematica se demonstreaza acum cu usurinta caaceasta forma generala este corecta.

6.4.2 Inductia constructiva

Inductia matematica este folosita de obicei ca tehnica de demonstrare a unei asertiunideja enuntate. Vom vedea ın aceasta sectiune ca inductia matematica poate fi utilizata cusucces si ın descoperirea partiala a enuntului asertiunii. Aplicand aceasta tehnica, putemsimultan sa demonstram o asertiune doar partial specificata si sa descoperim specificatiilecare lipsesc si datorita carora asertiunea este corecta. Vom vedea ca aceasta tehnica ainductiei constructive este utila pentru rezolvarea anumitor recurente care apar ın contex-tul analizei algoritmilor. Incepem cu un exemplu.Fie functia F : N→ N definita prin recurenta:

Page 98: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

98 CAPITOLUL 6. ANALIZA EFICIENTEI ALGORITMILOR

f(n) =

0 daca n = 1

f(n− 1) + n altfel

Sa presupunem pentru moment ca nu stim ca f(n) = n(n+1)2 . Avem:

f(n) =∑n

i=0 i ≤∑n

i=0 n = n2

si deci f(n) ∈ O(n2). Aceasta ne sugereaza sa formulam ipoteza inductiei specificate partialIISP(n) conform careia f este de forma f(n) = an2 + bn + c. Aceasta ipoteza este partialaın sensul ca a, b si c nu sunt ınca cunoscute. Tehnica inductiei constructive consta ın ademonstra prin inductie matematica aceasta ipoteza incompleta si a determina ın acelasitimp valorile constantelor necunoscute a, b si c.Presupunem ca IISP(n-1) este adevarata pentru un anumit n ≥ 1. Atunci, f(n − 1) =a(n − 1)2 + b(n − 1) + c = an2 + (1 + b − 2a)n + (a − b + c). Daca dorim sa aratamca IISP(n) este adevarata, trebuie sa aratam ca f(n) = an2 + bn + c. Prin identificareacoeficientilor puterilor lui n, obtinem ecuatiile 1 + b − 2a = b si a − b + c = c, cu solutiaa = b = 1

2 , c putand fi oarecare. Avem acum o ipoteza mai completa, pe care o numimtot IISP(n), f(n) = n2

2 + n2 + c. Am aratat ca daca IISP(n-1) este adevarata pentru un

anumit n ≥ 1, atunci este adevarata si IISP(n). Ramane sa aratam ca este adevarata siIISP(0). Trebuie sa aratam ca f(0) = a02 + b0 + c. Stim ca f(0) = 0, deci IISP(0) esteadevarata pentru c = 0. In concluzie am demonstrat ca f(n) = n2

2 + n2 pentru orice n.

6.4.3 Recurente liniare omogene

Exista din fericire si tehnici care pot fi folosite aproape automat pentru a rezolva anumiteclase de recurente. Vom ıncepe prin a considera ecuatii recurente liniare omogene, adicaecuatii de forma:

a0tn + a1tn−1 + . . . + aktn−k = 0 (*)

unde ti sunt valorile pe care le cautam, iar coeficientii ai sunt constante.Conform intuitiei2, vom cauta solutii de forma:

tn = xn

unde x este o constanta (deocamdata necunoscuta). Daca ınlocuim aceasta solutie ın (*),obtinem

a0xn + a1x

n−1 + . . . + akxn−k = 0

Solutiile acestei ecuatii sunt fie solutia triviala x = 0, care nu ne intereseaza, fie solutiileecuatiei:

a0xk + a1x

k−1 + . . . + ak = 0

care se numeste ecuatia caracteristica a recurentei liniare si omogene(*). Presupunanddeocamdata ca cele k radacini r1, r2, . . . , rk ale acestei ecuatii caracteristice sunt distincte,se verifica usor ca orice combinatie liniara

2De fapt, adevarul este ca aici nu este vorba de intuitie, ci de experienta

Page 99: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

6.4. ANALIZA ALGORITMILOR RECURSIVI 99

tn =∑k

i=1 cirni

este o solutie a recurentei (*), unde constantele c1, c2, . . . , ck sunt determinate de conditiileinitiale. Se poate demonstra faptul ca (*) are solutii numai de aceasta forma.Sa exemplificam prin recurenta care defineste sirul lui Fibonacci (sectiunea 7.1)

tn = tn−1 + tn−2, n ≥ 2

iar t0 = 0, t1 = 1 . Putem sa rescriem aceasta recurenta sub forma

tn − tn−1 − tn−2 = 0

care are ecuatia caracteristica

x2 − x− 1 = 0

cu radacinile r1,2 = 1∓√

52 . Solutia generala are forma:

tn = c1rn1 + c2r

n2

Impunand conditiile initiale, t0 = 0, t1 = 1, obtinem

c1 + c2 = 0, n = 0

c1rn1 + c2r

n2 = 1, n = 1

de unde determinam

c1.2 = ∓ 1√5

Deci, tn = 1√5(rn

1 + rn2 ). Observam ca r1 = φ, r2 = −φ−1 si obtinem:

tn = 1√5(φn − (−φ)−n)

care este cunoscuta relatie a lui Moivre, descoperita la ınceputul secolului XVIII. Nuprezinta nici o dificultate sa aratam acum ca timpul pentru calculul recursiv al sirului luiFibonacci este ın Θ(φn) .Cum procedam ınsa atunci cand radacinile ecuatiei caracteristice nu sunt distincte? Sepoate arata ca daca r este o radacina de multiplicitate m a ecuatiei caracteristice, atuncitn = rn, tn = nrn, tn = n2rn, . . . , tn = nm−1rn sunt solutii pentru (*). Solutia generalapentru o astfel de recurenta este atunci o combinatie liniara a acestor termeni si a ter-menilor proveniti de la celelalte radacini ale ecuatiei caracteristice. Din nou, sunt dedeterminat exact k constante din conditiile initiale.Vom da din nou un exemplu. Fie recurenta

tn = 5tn−1 − 8tn−2 + 4tn−3

cu t0 = 0, t1 = 1, t2 = 2. Ecuatia caracteristica are radacinile 1 (de multiplicitate 1) si 2(de multiplicitate 2). Solutia generala este:

tn = c11n + c22n + c3n2n

Din conditiile initiale, obtinem c1 = −2, c2 = 2, c3 = −12 .

Page 100: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

100 CAPITOLUL 6. ANALIZA EFICIENTEI ALGORITMILOR

6.4.4 Recurente liniare neomogene

Consideram acum recurente de urmatoarea forma mai generala

a0tn + a1tn−1 + . . . aktn−k = bnp(n) (**)

unde b este o constanta, iar p(n) este un polinom ın n de grad d. Ideea generala este caprin manipulari convenabile sa reducem un astfel de caz la o forma omogena.De exemplu, o astfel de recurenta poate fi:

tn − 2tn−1 = 3n

In acest caz b = 3 si p(n) = 1 un polinom de grad 0. O simpla manipulare ne permite sareducem acest exemplu la forma (*). Inmultind recurenta cu 3, obtinem:

3tn − 6tn−1 = 3n+1

Inlocuind pe n cu n + 1 ın recurenta originala, avem:

tn+1 − 2tn = 3n+1

In final, scadem aceste doua ecuatii si obtinem:

tn+1 − 5tn + 6tn−1 = 0

Am obtinut o recurenta omogena pe care o putem rezolva ca ın sectiunea precedenta.Ecuatia caracteristica este:

x2 − 5x + 6 = 0

adica (x− 2)(x− 3) = 0.Intuitiv, observam ca factorul (x − 2) corespunde partii stangi a recurentei originale, ıntimp ce factorul (x− 3) a aparut ca rezultat al manipularilor efectuate pentru a scapa departea dreapta.Iata al doilea exemplu:

tn − 2tn−1 = (n + 5)3n

Manipularile necesare sunt putin mai complicate. Trebuie sa:

1. Inmultim recurenta cu 9

2. Inlocuim ın recurenta pe n cu n + 2

3. Inlocuim ın recurenta pe n cu n + 1 si sa ınmultim apoi cu -6.

Adunand cele trei ecuatii obtinute anterior avem:

tn+2 − 8tn+1 + 21tn − 18tn−1 = 0

Am ajuns din nou la o ecuatie omogena. Ecuatia caracteristica corespunzatoare este

x3 − 8x2 + 21x− 18 = 0

Page 101: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

6.4. ANALIZA ALGORITMILOR RECURSIVI 101

adica (x− 2)(x− 3)2. Inca o data, observam ca factorul (x− 2) provine din partea stangaa recurentei originale, ın timp ce factorul (x− 3)2 este rezultatul manipularii.Generalizand acest procedeu, se poate arata ca pentru a rezolva (**) este suficient sa luamurmatoarea ecuatie caracteristica:

(a0xk + a1x

k−1 + . . . + ak)(x− b)d+1 = 0

Odata ce s-a obtinut aceasta ecuatie, se procedeaza ca ın cazul omogen.Vom rezolva acum recurenta corespunzatoare problemei turnurilor din Hanoi

tn = 2tn−1 + 1, n ≥ 1

iar t0 = 0. Rescriem recurenta astfel

tn − 2tn−1 = 1

care este de forma (**) cu b = 1 si p(n) = 1, un polinom cu grad 0. Ecuatia caracteristicaeste atunci (x− 1)(x− 2), cu solutiile 1 si 2. Solutia generala a recurentei este:

tn = c11n + c22n

Avem nevoie de doua conditii initiale. Stim ca t0 = 0; pentru a gasi cea de-a doua conditiecalculam

t1 = 2t0 + 1

Din conditiile initiale, obtinem tn = 2n − 1.Observatie: daca ne intereseaza doar ordinul lui tn, nu este necesar sa calculam efectivconstantele ın solutia generala. Daca stim ca tn = c11n + c22n, rezulta ca tn ∈ O(2n). Dinfaptul ca numarul de mutari a unor discuri nu poate fi negativ sau constant (deoarece avemın mod evident tn ≥ n), deducem ca c2 > 0. Avem atunci tn ∈ Ω(2n) si deci tn ∈ Θ(2n).Putem obtine chiar ceva mai mult.Substituind solutia generala ınapoi ın recurenta originara, gasim

1 = tn − 2tn−1 = c1 + c22n − 2(c1 + c22n−1) = −c1

Indiferent de conditia initiala, c1 este -1.

6.4.5 Schimbarea variabilei

Uneori putem rezolva recurente mai complicate printr-o schimbare de variabila. In exem-plele care urmeaza, vom nota cu T(n) termenul general al recurentei si cu tk termenul noiirecurente obtinute printr-o schimbare de variabila. Presupunem pentru ınceput ca n esteo putere a lui 2.Un prim exemplu este recurenta

T (n) = 4T (n2 ) + n, n > 1

ın care ınlocuim pe n cu 2k, notam tk = T (2k) = T (n) si obtinem:

tk = 4tk−1 + 2k

Page 102: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

102 CAPITOLUL 6. ANALIZA EFICIENTEI ALGORITMILOR

Ecuatia caracteristica a acestei recurente liniare este

(x− 4)(x− 2) = 0

si deci tk = c14k + c22k. Inlocuim pe k cu lg n

T (n) = c1n2c2n

Rezulta ca T (n) ∈ O(n2 | n este o putere a lui 2).Un al doilea exemplu ıl reprezinta ecuatia

T (n) = 4T (n2 ) + n2, n > 1

Procedand la fel, ajungem la recurenta

T (n) = 4T (n2 ) + n2, n > 1

cu ecuatia caracteristica

(x− 4)2 = 0

si solutia generala tk = c14k + c2k4k. Atunci,

T (n) = c1n2 + c2n

2 lg n

si obtinem ca T (n) ∈ O(n2 log n | n este o putere a lui 2),In fine, sa consideram si exemplul

T (n) = 3T (n2 ) + cn, n > 1

c fiind o constanta. Obtinem succesiv

T (2k) = 3T (2k−1) + c2k

tk = 3tk−1 + c2k

cu ecuatia caracteristica

(x− 3)(x− 2) = 0

tk = c13k + c22k

T (n) = c13lg n + c2n

si, deoarece

alg b = blg a

obtinem

T (n) = c1nlg 3 + c2n

Page 103: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

6.5. PROBLEME PROPUSE 103

deci, T (n) ∈ O(nlg 3 | n este o putere a lui 2).In toate aceste exemple am folosit notatia asimptotica conditionata. Pentru a arata carezultatele obtinute sunt adevarate pentru orice n, este suficient sa adaugam conditia caT(n) sa fie eventual nedescrescatoare.Putem enunta acum o proprietate care este utila ca reteta pentru analiza algoritmilor curecursivitati de forma celor din exemplele precedente. Proprietatea, a carei demonstrareo lasam ca exercitiu, este foarte utila la analiza algoritmilor Divide et Impera prezentatiın capitolul 7.Propozitie. Fie T : N→ <+ o functie eventual nedescrescatoare

T (n) = aT (nb ) + cnk, n > n0

unde: n0 ≥ 1, b ≥ 2 si k ≥ 0 sunt ıntregi; a si c sunt numere reale pozitive; nn0

este oputere a lui b. Atunci avem:

T (n) ∈

Θ(nk) daca a < bk

Θ(nklog n) daca a = bk

Θ(nlogba) daca a > bk

6.5 Probleme propuse

1. Care din urmatoarele afirmatii sunt adevarate?

(a) n2 ∈ O(n3)

(b) n3 ∈ O(n2)

(c) 2n+1 ∈ O(2n)

(d) (n + 1)! ∈ O(n!)

(e) pentru orice functie f : N→ R∗, f ∈ O(n)⇒ [f2 ∈ O(n2)]

(f) pentru orice functie f : N→ R∗, f ∈ O(n)⇒ [2f ∈ O(2n)]

2. Demonstrati ca relatia ”∈ O” este tranzitiva: daca f ∈ O(g) si g ∈ O(h), atuncif ∈ O(h). Deduceti de aici ca daca g ∈ O(h), atunci O(g) ⊆ O(h).

3. Gasiti doua functii f, g : N→ R∗, astfel ıncat f /∈ O(g) si g /∈ O(f).

Indicatie: f(n) = n, g(n) = n1+sin n.

4. Pentru oricare doua functii f, g : N→ R∗ definim urmatoarea relatie binara: f ≤ gdaca O(f) ⊆ O(g). Demonstrati ca relatia ”≤” este o relatie de ordine partiala ınmultimea functiilor definite pe N si cu valori ın R∗.

Indicatie: Trebuie aratat ca relatia este partiala, reflexiva, tranzitiva si antisimetrica.Tineti cont de exercitiul 3.

5. Pentru oricare doua functii f, g : N→ R∗ demonstrati ca O(f + g) = O(max(f, g))unde suma si maximul se iau punctual.

Page 104: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

104 CAPITOLUL 6. ANALIZA EFICIENTEI ALGORITMILOR

6. Fie f(n) = amnm + . . . + a1n + a0 un polinom de grad m, cu am > 0. Aratati caf ∈ O(nm).

7. O(n2) = O(n3 + (n2 − n3)) = O(max(n3, n2 − n3)) = O(n3). Unde este eroarea?

8.∑n

i=1 i = 1 + 2 + . . . + n ∈ O(1 + 2 + . . . + n) = O(max(1 + 2 + . . . + n)) = O(n).Unde este eroarea?

9. Pentru oricare doua functii f, g : N→ R∗ demonstrati ca Θ(f)+Θ(g) = Θ(f +g) =Θ(max(f, g)) = max(Θ(f),Θ(g)), unde suma si maximul se iau punctual.

10. Analizati eficienta urmatorilor algoritmi:

(a) pentru i=1,npentru j=1,5

operatie elementara(b) pentru i=1,n

pentru j=1,i+1operatie elementara

(c) pentru i=1,npentru j=1,6

pentru k=1,noperatie elementara

(d) pentru i=1,npentru j=1,i

pentru k=1,noperatie elementara

11. Construiti un algoritm cu timpul ın Θ(n log n).

12. Fie un algoritm:pentru i=0,n

j←icat timp j<>0

j←j div 2Gasiti ordinul exact al timpului de executie.

13. Rezolvati urmatoarea recurenta: tn − 3tn−1 − 4tn−2 = 0, n ≥ 2 cu t0 = 0, t1 = 1.

14. Care este timpul de executie pentru un algoritm recursiv cu recurenta: tn = 2tn−1 +n.

Indicatie: Se ajunge la ecuatia caracteristica (x−2)(x−1)2 = 0, iar solutia generalaeste tn = c12n + c21n + c3n1n. Rezulta ca tn ∈ O(2n).Substituind solutia generala ın recurenta, obtinem ca, indiferent de conditia initiala,c2 = −2 si c3 = −1. Atunci, toate solutiile interesante ale recurentei trebuie sa aibac1 > 0 si ele sunt toate ın Ω(2n), deci ın Θ(2n).

Page 105: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

6.5. PROBLEME PROPUSE 105

15. Sa se calculeze secventa de suma maxima, formata din termeni consecutivi, a unuisir de numere.

Solutie: Problema are solutii de diferite ordine de complexitate timp. Puteti saıncercati voi sa le gasiti. In acest moment vom da solutia optima din punct de vedereal complexitatii. Este vorba de o singura parcurgere a sirului. Tehnica folosita estecea a programarii dinamice.

public void subSirvalMax()int max=0;//valoarea initiala a sumei subsirului de valoare maximaint ci=0;//indicele de inceput al subsirului de valoare maximaint i=0;//valoarea curenta a indicelui de inceput al subsirului actualint cj=n;//indicele de sfarsit al subsirului de valoare maximaint j=0;//valoarea curenta a indicelui de sfarsit al subsirului actualint m=0;//valoarea sumei subsirului actualfor(int k=0;k<n;k++)//parcurgerea sirului datm+=a[k];if (m>=max)

//daca suma actuala este mai mare de cea gasita pana acum atunci//subsirul actual va fi cel de suma marxima

max=m;j=k;ci=i;cj=j;//setam noile valoriif (m<0)

//daca subsirul actual are suma mai mica de 0 atunci daca//am adauga un numar oricat de mare tot nu va fi un subsir//cu suma maxima pentru ca suma astfel obtinuta este//mai mica ca numarul pe care l-am adaugat//si subsirul actual nu ne mai intereseaza

m=0;i=k+1;j=k+1;

//deci vom seta indicele de inceput si de sfarsit al subsirului actual//cu indicele elementului urmator iar suma actuala o vom seta 0 //am terminat verificareaif ((max==0)&&(ci==0)&&(cj==n))//daca nu s-au schimbat valorile initiale atunci inseamna//ca nu avem nici un subsir cu valoare maximaSystem.out.println("Suma maxima 0,secventa vida");

Page 106: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

106 CAPITOLUL 6. ANALIZA EFICIENTEI ALGORITMILOR

else//altfel afisam datele obtinuteSystem.out.println("Suma maxima este"+max);System.out.println("Secventa este"+(ci+1)+" "+(cj+1));

Page 107: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

Capitolul 7

Structuri de date

Multi algoritmi necesita o reprezentare adecvata a datelor pentru a fi cu adevarat eficienti.Reprezentarea datelor, ımpreuna cu operatiile care sunt permise asupra datelor formeaza ostructura de date. Fiecare structura de date permite inserarea de elemente. Structurile dedate difera ın privinta modului ın care permit accesul la membrii din grup. Unele permitaccesarea si stergerea arbitrara. Altele impun anumite restrictii, cum ar fi permitereaaccesului doar la ultimul sau la primul element inserat.Acest capitol prezinta sapte dintre cele mai uzuale structuri de date: stive, cozi, listeınlantuite, arbori, arbori binari de cautare, tabele de repartizare (hash-tables) si cozi deprioritate. Vom defini fiecare structura de date si vom furniza o estimare intuitiva pentrucomplexitatea ın timp a operatiilor de inserare, stergere si accesare.In acest capitol vom vedea:

• Descrierea structurilor de date uzuale, operatiile permise pe ele si timpiilor de executie

• Pentru fiecare structura de date, vom defini o interfata Java continandprotocolul care trebuie implementat

• Unele aplicatii ale structurilor de date

Vom urmari sa evidentiem faptul ca specificarea operatiilor suportate de o structura dedate, care ıi descrie functionalitatea, este independenta de modul de implementare.

7.1 De ce avem nevoie de structuri de date?

Structurile de date ne permit atingerea unui scop important ın programarea orientatape obiecte: reutilizarea componentelor. Asa cum vom vedea mai tarziu ın acest capitol,structurile de date descrise sunt folosite ın multe situatii. Odata ce o structura de date afost implementata, ea poate fi folosita din nou si din nou ın aplicatii de natura diversa1.Aceasta abordare - separarea interfetei de implementare - este o parte fundamentala aorientarii pe obiecte. Cel care foloseste structura de date nu trebuie sa vada implementareaei, ci doar operatiile admisibile. Aceasta tine de partea de ıncapsulare si ascundere a

1De altfel, bibliotecile Java cuprind majoritatea structurilor de date uzuale

107

Page 108: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

108 CAPITOLUL 7. STRUCTURI DE DATE

informatiei din programarea orientata pe obiecte. O alta parte importanta a programariiorientate pe obiecte este abstractizarea. Trebuie sa proiectam cu grija structura de date,deoarece vom scrie programe care folosesc aceste structuri de date fara sa aiba acces laimplementarea lor. Aceasta va face ın schimb ca interfata sa fie mai curata, mai flexibila,si, de obicei, mai usor de implementat.Toate structurile de date sunt usor de implementat daca nu ne punem problema eficientei.Acest lucru permite sa adaugam componente ”ieftine” ın program doar pentru depanare.Exercitiile de la sfarsitul capitolului va cer scrierea de implementari ineficiente, care suntadecvate pentru a procesa cantitati mici de date. Putem apoi ınlocui aceste implementari”ieftine” cu implementari care au o performanta (ın timp si/sau ın spatiu) mai bunasi care sunt adecvate pentru procesarea unei cantitati mai mari de informatie. Deoareceinterfetele sunt fixate, aceste modificari nu necesita practic nici o modificare ın programelecare folosesc aceste structuri de date.Vom descrie structurile de date prin intermediul interfetelor. De exemplu, stiva esteprecizata prin intermediul interfetei Stack. Clasa care implementeaza aceasta interfatava implementa toate metodele specificate ın Stack, la care se mai pot adauga anumitefunctionalitati.

1. public interface MemCell2. 3. Object read() ;4. void write( Object x ) ;5.

Figura 7.1 Interfata pentru clasa MemoryCell

Ca un exemplu, ın Figura 7.1 este descrisa o interfata pentru clasa MemoryCell din capi-tolul anterior. Interfata descrie functiile disponibile; clasa concreta trebuie sa defineascaaceste functii. Implementarea este prezentata ın Figura 7.2 si este identica cu cea dincapitolul anterior cu exceptia clauzei implements.

public class MemoryCell implements MemCell

public Object read() return storedValue ; public void write(Object x) storedValue = x ; private Object storedValue ;

Figura 7.2 Implementarea clasei MemoryCell

Este important sa observati faptul ca structurile de date definite ın acest capitol retinreferinte catre elementele inserate, si nu copii ale elementelor. Din acest motiv este bineca ın structura de date sa fie plasate obiecte nemodificabile (cum ar fi String, Integer etc.)pentru ca un utilizator extern sa nu poata sa schimbe starea unui obiect care este ınglobatıntr-o structura de date.

Page 109: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

7.2. STIVE 109

7.2 Stive

O stiva este o structura de date ın care orice tip de acces este permis doar pe ultimulelement inserat. Comportamentul unei stive este foarte asemanator cu cel al unui maldarde farfurii. Ultima farfurie adaugata va fi plasata ın varf, fiind ın consecinta usor deaccesat, ın timp ce farfuriile puse cu mai mult timp ın urma vor fi mai greu de accesat,putand periclita stabilitatea ıntregii gramezi. Astfel, stiva este adecvata ın situatiile ın careavem nevoie sa accesam doar elementul din varf. Toate celelalte elemente sunt inaccesibile.Cele trei operatii naturale, inserare, stergere si cautare, sunt denumite ın cazul unei stivepush, pop si top. Cele trei operatii sunt ilustrate ın Figura 7.4. O interfata Java pentruo stiva abstracta este prezentata ın Figura 7.5. Interfata declara si o metoda topAndPopcare combina doua operatii; tot aici apare si un element nou: clauza throws prin care sedeclara ca o metoda poate sa arunce catre metoda apelanta o anumita exceptie. In cazulnostru, metodele pop, top si topAndPop pot arunca o UnderflowException ın cazul ın carese ıncearca accesarea unui element cand stiva este goala. Aceasta exceptie va trebui safie prinsa pana la urma de o metoda apelanta. Clasa UnderflowException este definita ınFigura 7.3, si ea este practic identica cu clasa Exception. Important pentru noi este cadifera tipul, ceea ce ne permite sa prindem doar aceasta exceptie cu o secventa de tipulcatch(UnderflowException e):

1. package Exceptions ;2. public class UnderflowException extends Exception3. 4. public UnderflowException(String thrower)5. 6. super( thrower ) ;7. 8.

Figura 7.3 Codul pentru clasa UnderflowException. Aceasta clasa este practic identicacu Exception, difera doar tipul.

Figura 7.6 prezinta un exemplu de utilizare al clasei Stack. Observati ca stiva poatefi folosita pentru a inversa ordinea elementelor; de remarcat un mic artificiu folosit aici:iesirea din ciclul for se realizeaza ın momentul ın care stiva se goleste si metoda topAnd-Pop arunca UnderflowException. Am recurs la acest artificiu pentru a ıntelege mecanismulexceptiilor. Folosirea acestui artificiu ın mod curent nu este recomandata, deoarece reducelizibilitatea codului.Fiecare operatie pe stiva trebuie sa ia o cantitate constanta de timp, indiferent de dimen-siunea stivei, la fel cum accesarea farfuriei din varful gramezii este rapida, indiferent denumarul de farfurii din teanc. Accesul la un element oarecare din stiva nu este eficient,de aceea el nici nu este permis de catre interfata noastra.

Page 110: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

110 CAPITOLUL 7. STRUCTURI DE DATE

Stiva

pushJ

JJ

pop,top

Figura 7.4 Modelul unei stive: Inserarea ın stiva se face prin push, accesul prin top,stergerea prin pop.

Stiva este deosebit de utila deoarece sunt multe aplicatii pentru care trebuie sa accesamdoar ultimul element inserat. Un exemplu ilustrativ este salvarea parametrilor si vari-abilelor locale ın cazul apelului unei alte subrutine.

1. package DataStructures ;2. import Exceptions.* ;//pachet care contine UnderflowException3.4. //Interfata pentru stiva5. //6. //********************OPERATII PUBLICE*********************7. // void push( x ) --> insereaza x8. // void pop() --> Sterge ultimul element inserat9. //Object top() --> Intoarce ultimul element inserat10.//Object topAndPop()-->Intoarce si sterge ultimul element11.// boolean isEmpty( ) --> Intoarce true daca stiva e vida12.// void makeEmpty( ) --> Elimina toate elementele din stiva13.14.public interface Stack15.16. void push( Object x ) ;17. void pop( ) throws UnderflowException ;18. Object top( ) throws UnderflowException ;19. Object topAndPop( ) throws UnderflowException ;20. boolean isEmpty( ) ;21. void makeEmpty( ) ;22.

Figura 7.5 Interfata pentru stiva

1. import DataStructures.* ;2. import Exceptions.* ;3.4. public final class TestStack5. 6. public static void main( String args[])7. 8. Stack s = new StackAr() ;9.

Page 111: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

7.3. COZI 111

10. for(int i=0; i<5; ++i)11. 12. s.push( new Integer(i) ) ;13. 14.15. System.out.print("Continutul stivei este: " ) ;16. try17. 18. for( ; ; )19. 20. System.out.print( " " + s.topAndPop( ) );21. 22. 23. catch(UnderflowException e)24. 25. 26. System.out.println() ;27. 28.

Figura 7.6 Exemplu de utilizare a stivei; programul va afisa: Continutul stivei este 4 32 1 0.

7.3 Cozi

O alta structura simpla de date este coada. In multe situatii este important sa avem accessi/sau sa stergem ultimul element inserat. Dar, ıntr-un numar la fel de mare de situatii,acest lucru nu numai ca nu mai este important, este chiar nedorit. De exemplu, ıntr-oretea de calculatoare care au acces la o singura imprimanta este normal ca daca ın coadade asteptare se afla mai multe documente spre a fi tiparite, prioritatea sa ıi fie acordatadocumentului cel mai vechi. Acest lucru nu numai ca este corect, dar este si necesar pentrua garanta ca procesul nu asteapta la infinit. Astfel, pe sistemele mari este normal sa sefoloseasca cozi de tiparire. Operatiile fundamentale suportate de cozi sunt:

• enqueue - inserarea unui element la capatul cozii

• dequeue - stergerea primului element din coada

• getFront - accesul la primul element din coada

Queue

enqueue-

dequeue,getFront-

Figura 7.7 Modelul unei cozi: Intrarea se face prin enqueue, accesul prin getFront,stergerea prin dequeue.

Page 112: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

112 CAPITOLUL 7. STRUCTURI DE DATE

Figura 7.7 ilustreaza operatiile pe o coada. Traditional, metodele dequeue si getFrontsunt combinate ıntr-una singura. La fel am facut si noi aici. Metoda dequeue returneazaprimul element, dupa care ıl scoate din coada.

1. package Datastructures ;2.3. import Exceptions.* ;4.5. //Interfata Queue6. //7. //**********OPERATII PUBLICE******************8. //void enqueue( x )--> insereaza x9. //Object getFront( )-->intoarce cel mai vechi element10.//Object dequeue( )-->intoarce&sterge cel mai vechi elem11.//boolean isEmpty()-->intoarce true daca e goala12.//void makeEmpty()-->sterge elementele din coada13.//********************************************14.15.public interface Queue16.17. void enqueue(Object x) ;18. Object getFront() throws UnderflowException;19. Object dequeue() throws UnderflowException ;20. boolean isEmpty() ;21. void makeEmpty() ;22.

Figura 7.8 Interfata pentru coada

1. import Datastructures.* ;2. import Exceptions.* ;3.4. //Program simplu pentru testarea cozilor5.6. public final class TestQueue7. 8. public static void main(String[] args)9. 10. Queue q = new QueueAr() ;11.12. for(int i=0; i<5; ++i)13. 14. q.enqueue( new Integer( i ) ) ;15. 16.17. System.out.print("Continut: " ) ;18. try

Page 113: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

7.4. LISTE INLANTUITE 113

19. 20. for( ; ; )21. 22. System.out.print( " " + q.dequeue() ) ;23. 24. 25. catch( UnderflowException e )26. 27. //aici se ajunge cand stiva se goleste28. 29.30. System.out.println( ) ;31. 32.

Figura 7.9 Exemplu de utilizare a cozii; programul va afisa:Continut: 0 1 2 3 4

Figura 7.8 ilustreaza interfata pentru o coada, iar Figura 7.9 prezinta modul de utilizareal cozii. Deoarece operatiile pe o coada sunt restrictionate ıntr-un mod asemanator cuoperatiile pe o stiva, este de asteptat ca si aceste operatii sa fie implementate ıntr-un timpconstant. Intr-adevar, toate operatiile pe o coada pot fi implementate ın timp constant,O(1).

7.4 Liste ınlantuite

Intr-o lista ınlantuita elementele sunt retinute discontinuu, spre deosebire de siruri ın careelementele sunt retinute ın locatii continue. Acest lucru este realizat prin stocarea fiecaruiobiect ıntr-un nod care contine obiectul si o referinta catre urmatorul element ın lista, caın Figura 7.10. In acest model se retin referinte atat catre primul cat si catre ultimulelement din lista. Concret vorbind, un nod al unei liste arata la modul urmator:

class ListNodeObject data ; //continutul listeiListNode next ;

a1

-

firstJ

JJ

a2

-a3

-a4

-

last

Figura 7.10 O lista simplu ınlantuita

In orice moment, putem adauga ın lista un nou element x prin urmatoarele operatii:

Page 114: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

114 CAPITOLUL 7. STRUCTURI DE DATE

last.next = new ListNode() ; //creaza un nou nodlast = last.next ; //actualizeaza lastlast.data = x ; //plaseaza pe x in nodlast.next = null ; //ultimul nu are successor

In cazul unei liste ınlantuite un element oarecare nu mai poate fi gasit cu un singur acces.Aceasta este oarecum similar cu diferenta ıntre accesarea unei melodii pe CD (un singuracces) si accesarea unei melodii pe caseta (acces secvential). Desi din acest motiv listelepot sa para mai putin atractive decat sirurile, exista totusi cateva avantaje importante.In primul rand, inserarea unui element ın mijlocul listei nu implica deplasarea tuturorelementelor de dupa punctul de inserare. Deplasarea datelor este foarte costisitoare (dinpunct de vedere al timpului), iar listele ınlantuite permit inserarea cu un numar constantde instructiuni de atribuire.Merita observat ca daca permitem accesul doar la first, atunci obtinem o stiva, iar dacapermitem inserari doar la last si accesari doar la first, obtinem o coada.In general, atunci cand folosim o lista, avem nevoie de mai multe operatii, cum ar fi gasireasau stergerea unui element oarecare din lista. Trebuie sa permitem si inserarea unui nouelement ın orice punct. Aceasta este deja mult mai mult decat ne permite o stiva sau ocoada.Pentru a accesa un element ın lista, trebuie sa obtinem o referinta catre nodul care ıi core-spunde. Evident ca oferirea unei referinte catre un element ıncalca principiul ascunderiiinformatiei. Trebuie sa ne asiguram ca orice acces la lista prin intermediul unei referintenu pericliteaza structura listei. Pentru a realiza acest lucru, lista este definita ın douaparti: o clasa lista si o clasa iterator. Figura 7.11 furnizeaza interfata de baza pentru olista ınlantuita, oferind si metodele care descriu doar starea listei.Figura 7.12 defineste o clasa iterator care este folosita pentru toate operatiile de acce-sare a listei. Pentru a vedea cum functioneaza aceasta clasa, sa examinam secventa de codclasica pentru afisarea tuturor elementelor din cadrul unei structuri liniare. Daca lista arfi stocata ıntr-un sir, secventa de cod ar arata astfel:

//parcurge sirul a, afisand fiecare elementfor(int index = 0; index < a.length; ++index)

System.out.println( a[index] ) ;

In Java elementar, codul pentru a itera o lista este:

//parcurge lista theList de tip List, afisand fiecare elementfor(ListNode p = theList.first; p != null; p = p.next)

System.out.println( p.data ) ;

1. package DataStructures;2.3. //Interfata pentru lista4. //5. //Accesul se realizeaza prin clasa ListItr6. //7. //********OPERATII PUBLICE*************8. //boolean isEmpty()-->Intoarce true daca lista e goala

Page 115: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

7.4. LISTE INLANTUITE 115

9. //void makeEmpty()-->Sterge toate elementele10.11.public interface List12.13. boolean isEmpty() ;14. void makeEmpty() ;15.

Figura 7.11 Interfata pentru o lista abstracta

1. package DataStructures;2. import Exceptions.* ;3.4. //Interfata ListItr5. //6. //***********OPERATII PUBLICE7. //void insert( x ) --> Insereaza pe x dupa pozitia curenta8. //void remove( x )-->Sterge pe x9. //boolean find( x )-->Seteaza pozitia curenta pe elem. x10.//void zeroth()-->Seteaza pozitia inainte de primul elem.11.//void first()-->Seteaza pozitia curenta pe primul element12.//void advance()-->Avanseaza la urmatorul element13.//boolean isInList()-->True daca ne aflam in interiorul listei14.//Object retrieve()-->Intoarce elementul de la poz. curenta15.16.public interface ListItr17.18. void insert( Object x ) throws ItemNotFoundException ;19. boolean find( Object x ) ;20. void remove( Object x ) throws ItemNotFoundException ;21. boolean isInList() ;22. Object retrieve() ;23. void zeroth() ;24. void first( ) ;25. void advance( ) ;26.

Figura 7.12 Interfata pentru un iterator de lista abstract

Mecanismul de iterare pe care l-ar folosi limbajul Java ar fi similar cu ceva de genul(deoarece ListItr este o interfata, ListItr care urmeaza dupa new va fi ınlocuit cu o clasacare implementeaza ListItr):

//parcurge List, folosind abstractizarea si un iteratorListItr itr = new ListItr( theList ) ;for(itr.first(); itr.isInList() ; itr.advance() )

System.out.println( itr.retrieve() ) ;

Page 116: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

116 CAPITOLUL 7. STRUCTURI DE DATE

Initializarea dinaintea ciclului for creeaza un iterator al listei. Testul de terminare a cicluluifoloseste metoda isInList definita pentru clasa ListItr. Metoda advance trece la urmatorulnod din cadrul listei. Putem accesa elementul curent prin apelul metodei retrieve definitaın ListItr. Principiul general este ca accesul fiind realizat prin intermediul clasei ListItr,securitatea datelor este garantata. Putem avea mai multi iteratori care sa traversezesimultan o singura lista.Pentru ca sa functioneze corect, clasa ListItr trebuie sa mentina doua obiecte. In primulrand, are nevoie de o referinta catre nodul curent. In al doilea rand are nevoie de oreferinta catre obiectul de tip List pe care ıl indica; aceasta referinta este initializata osingura data ın cadrul constructorului.

1. import DataStructures.* ;2. import Exceptions.* ;3.4. //program simplu de testare a listelor5.6. public final class TestList7. 8. public static void main(String args[])9. 10. List theList = new LinkedList() ;11. ListItr itr = new LinkedListItr( theList ) ;12. //se insereaza noi elemente pe prima pozitie13. for(int i=0; i<5; ++i)14. 15. try16. 17. itr.insert( new Integer(i) ) ;18. 19. catch( ItemNotFoundException e )20. 21. 22. itr.zeroth() ;23. 24.25. System.out.println("Continutul listei: ") ;26. for( itr.first() ; itr.isInList() ; itr.advance() )27. 28. System.out.println( " " + itr.retrieve() ) ;29. 30. 31.

Figura 7.13 Exemplu de utilizare al listei.Programul va afisa: 4 3 2 1 0

Desi discutia s-a axat pe liste simplu ınlantuite, interfetele din Figura 7.11 si Figura7.12 pot fi folosite pentru oricare tip de lista, indiferent de implementarea pe care o are

Page 117: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

7.5. ARBORI BINARI DE CAUTARE 117

la baza. Interfata nu precizeaza faptul ca este nevoie de liste simplu ınlantuite.

7.5 Arbori binari de cautare

In capitolul anterior am vazut ca cea mai eficienta cautare ıntr-un sir este cautarea binara,care se poate aplica atunci cand elementele sirului sunt ordonate, timpul de executie fiindlogaritmic.Sa presupunem ca avem nevoie de o structura de date ın care, pe langa cautare, dorimsa putem adauga sau sterge eficient elemente. O astfel de structura este arborele binar decautare. Figura 7.14 ilustreaza operatiile de baza permise asupra unui arbore binar decautare. O posibila interfata este prezentata ın Figura 7.15.

Arbore binar de cautare

insertJ

JJ

find,remove

Figura 7.14 Modelul pentru arborele binar de cautare. Cautarea binara este extinsa pen-tru a permite inserari si stergeri.

Setul de operatii permise este acum extins pentru a permite gasirea unui element arbitrar,alaturi de inserare si stergere. Metoda find ıntoarce o referinta catre un obiect care esteegal (ın sensul metodei compareTo din interfata Comparable) cu elementul cautat. Daca nuexista nici un element egal cu cel cautat find arunca o exceptie. Aceasta este o decizie deproiectare. O alta posibilitate ar fi fost sa ıntoarcem null ın cazul ın care elementul cautatnu este gasit. Diferenta ıntre cele doua abordari consta ın faptul ca metoda noastra ılobliga pe programator sa trateze explicit situatia ın care cautare nu are succes. In cealaltasituatie, daca am fi ıntors null, si nu s-ar fi facut verificarile necesare, programul ar figenerat un NullPointerException la prima tentativa de a folosi referinta. Din punct devedere al eficientei, versiunea cu exceptii ar putea fi ceva mai lenta, dar este putin probabilca rezultatul sa fie observabil, cu exceptia situatiei ın care codul ar fi foarte des executatın cadrul unor situatii ın care viteza este critica.In mod similar, inserarea unui element care deja este ın arbore este semnalata printr-oDuplicateItemException. Exista si alte posibile alternative. Una dintre consta ın a permitenoii valori sa suprascrie valoarea stocata.

1. package DataStructures ;2.3. import Exceptions.* ;4.5. //*********OPERATII PUBLICE*********6. // void insert( x )-->insereaza x7. // void remove( x )-->sterge x8. // void removeMin()-->sterge cel mai mic element

Page 118: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

118 CAPITOLUL 7. STRUCTURI DE DATE

9. // Comparable find()-->intoarce elementul egal cu x10.// Comparable findMin()-->intoarce cel mai mic element11.// Comparable findMax()-->intoarce cel mai mare element12.// boolean isEmpty()-->intoarece true daca arborele e gol13.// void makeEmpty()-->sterge toate elementele14.// void printTree()-->afiseaza nodurile ordonate15.16.//************ERORI****************17.//majoritatea rutinelor arunca ItemNotFoundException18.//in diverse cazuri degenerate19.//insert poate sa arunce DuplicateItemException daca elem20.//este deja in arbore21.22.public interface SearchTree23.24. void insert(Comparable x) throws DuplicateItemException ;25. void remove(Comparable x) throws ItemNotFoundException ;26. void removeMin() throws ItemNotFoundException ;27. Comparable find(Comparable x) throws ItemNotFoundException;28. Comparable findMin( ) throws ItemNotFoundException ;29. Comparable findMax( ) throws ItemNotFoundException ;30. boolean isEmpty() ;31. void makeEmpty() ;32. void printTree() ;33.

Figura 7.15 Interfata pentru un arbore binar de cautare

Vom da ca exemplu un arbore binar care retine stringuri. Deoarece ın arbore putem retinedoar obiecte de tip Comparable, nu putem folosi direct clasa String (deoarece aceasta nuimplementeaza clasa Comparable). Din acest motiv, vom scrie o clasa MyString, prezentataın Figura 7.16 (aceasta clasa implementeaza si interfata Hashable, care va fi folosita ınsectiunea urmatoare). Figura 7.17 prezinta modul ın care arborele binar de cautarepoate fi folosit pentru obiecte de tip MyString.Interfata SearchTree mai are doua metode suplimentare: una pentru a gasi cel mai micelement si una pentru a gasi cel mai mare element. Se poate arata ca transpirand unpic mai mult se poate gasi foarte eficient si cel mai mic al k-lea element, pentru oricare ktrimis ca parametru.Iata o sinteza a timpilor de executie pentru operatiile pe un arbore binar de cautare. Estenormal sa speram ca timpii de executie pentru find, insert si remove sa fie logaritmici,deoarece aceasta este valoarea pe care am obtinut-o pentru cautarea binara. Din nefericire,pentru cea mai simpla implementare a arborelui binar de cautare, acest lucru nu esteadevarat. Timpul mediu de executie este logaritmic, dar ın cazul cel mai nefavorabil,timpul de executie este O(n), caz care apare destul de frecvent. Totusi, prin aplicareaanumitor trucuri de algoritmica se pot obtine anumite structuri mai complexe (arborirosu-negru) care au ıntr-adevar un cost logaritmic pentru fiecare operatie.

Page 119: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

7.5. ARBORI BINARI DE CAUTARE 119

1. import Datastructures.* ;2.3.public final class MyString implements Comparable, Hashable4. 5. private String value ;6.7. public MyString(String x)8. 9. value = x ;10. 11.12. public String toString()13. 14. return value ;15. 16.17. public int compareTo(Comparable rhs)18. 19. return value.compareTo( ((MyString)rhs).value ) ;20. 21.22. public boolean lessThan(Comparable rhs)23. 24. return compareTo(rhs) < 0 ;25. 26.27. public boolean equals( Object rhs )28. 29. return value.equals( ( (MyString)rhs.value ) );30. 31.32. public int hash(int tableSize)33. 34. return QuadraticProbingTable.hash(value, tableSize) ;35. 36.

Figura 7.16 Clasa MyString

1. //program simplu pentru testarea arborilor de cautare2.3. public final class TestSearchTree4. 5. public static void main(String[] args)6. 7. SearchTree t = new BinarySearchTree() ;

Page 120: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

120 CAPITOLUL 7. STRUCTURI DE DATE

8. MyString result = null ;9.10. try11. 12. t.insert( new MyString( "Georgica" ) ) ;13. 14. catch(DupicateItemException e)15. 16. 17.18. try19. 20. result = (MyString) t.find(new MyString("Georgica"));21. System.out.print("Gasit " + result + " " ) ;22. 23. catch(ItemNotFoundException e)24. 25. System.out.print("Georgica nu a fost gasit") ;26. 27.28. try29. 30. result = (MyString) t.find( new MyString( "Ionel" ) ) ;31. System.out.print("Gasit " + result + " " ) ;32. 33. catch(ItemNotFoundException e)34. 35. System.out.print("Ionel nu a fost gasit") ;36. 37.38. System.out.println() ;39. 40.

Figura 7.17 Model de program care utilizeaza arbori de cautare.Programul va afisa: Georgica, Ionel nu a fost gasit.

Ce putem spune despre operatiile findMin si findMax? In mod cert, aceste operatii necesitaun timp constant ın cazul cautarii binare, deoarece implica doar accesarea unui elementindiciat. In cazul unui arbore binar de cautare aceste operatii iau acelasi timp ca o cautareobisnuita, adica O(log n) ın cazul mediu si O(n) ın cazul cel mai nefavorabil. Dupa cumıi sugereaza si numele, arborele binar de cautare este implementat ca un arbore binar,necesitand astfel cate doua referinte pentru fiecare element.

Page 121: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

7.6. TABELE DE REPARTIZARE 121

7.6 Tabele de repartizare

Exista foarte multe aplicatii care necesita o cautare dinamica bazata doar pe un nume.O aplicatie clasica este tabela de simboluri a unui compilator. Pe masura ce compileazaprogramul, compilatorul trebuie sa retina numele (ımpreuna cu tipul, scopul, locatia dememorie) tuturor identificatorilor care au fost declarati. Atunci cand vede un identificatorın afara unei instructiuni de declarare, compilatorul verifica sa vada daca acesta a fostdeclarat. Daca a fost, compilatorul verifica informatia adecvata din tabela de simboluri.Avand ın vedere faptul ca arborele binar de cautare permite acces logaritmic la obiectecu denumiri oarecare, de ce am avea nevoie de o alta structura de date? Raspunsul esteca arborele binar de cautare poate sa dea un timp de executie liniar pentru accesul unuielement, iar pentru a ne asigura de cost logaritmic este nevoie de algoritmi mult maisofisticati.Tabela de repartizare este o structura de date care evita timpul de executie liniar, bamai mult, suporta aceste operatii ın timp (mediu) constant. Astfel, timpul de acces la unelement din tabela nu depinde de numarul de elemente care sunt ın tabela. In acelasi timp,tabela de repartizare nu foloseste apeluri la rutinele de alocare a memoriei (ca arborelebinar). Aceasta face ca tabela de repartizare sa fie rapida ın practica. Un alt avantaj fatade arborele binar de cautare este ca elementele stocate ın tabela de repartizare nu trebuiesa implementeze interfata Comparable.Operatiile permise sunt date ın Figura 7.18 iar o interfata este prezentata ın Figura7.19. In acest caz, inserarea unui element care este duplicat ne genereaza o exceptie, cielementul va fi ınlocuit cu noua valoare. Aceasta este o alternativa la metoda pe caream aplicat-o ın cazul arborilor binari de cautare. Tabela de repartizare functioneazadoar pentru elemente care implementeaza interfata Hashable2. Interfata Hashable cere ofunctie de repartizare, care converteste obiectul de tip Hashable ıntr-un ıntreg. Metodaare urmatorul antet:

//intoarce un intreg intre 0 si tableSize-1int hash(int tableSize) ;

Elementele dintr-o tabela de repartizare trebuie sa redefineasca si metoda equals. Figura7.16 arata cum clasa MyString implementeaza interfata Hashable prin implementareametodelor hash si equals. Un exemplu de utilizare a tabelelor de repartizare este dat ınFigura 7.20.Una dintre utilizarile obisnuite ale tabelelor de repartizare sunt dictionarele. Un dictionarretine obiecte care constau ıntr-o cheie, care este cautata ın dictionar, si definitia cheii,care este returnata. Putem utiliza tabele de repartizare pentru a implementa dictionarulastfel:

• obiectul stocat este o clasa care retine atat cheia cat si definitia ei

• egalitatea, comparatia si functia de repartizare se bazeaza doar pe cheia din obiectulstocat

2 In limba engleza, tabelele de repartizare se numesc ”Hashtable”, de aceea un element care poate firepartizat se numeste ”Hashable”.

Page 122: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

122 CAPITOLUL 7. STRUCTURI DE DATE

• cautarea se face prin construirea unui obiect e cu cheia dorita, urmata de apelareametodei find din tabela de repartizare

• definitia este obtinuta prin folosirea unei referinte f careia ıi este atribuita valoareaıntoarsa de find.

Tabela de repartizare

insertJ

JJ

find,remove

Figura 7.18 Modelul pentru tabela de repartizare: Oricare element etichetat poate fiadaugat sau sters ın timp practic constant.

1. package DataStructures ;2.3. import Exceptions.* ;4.5. //Interfata Hashtable6. //7. //void insert( x )-->adauga x8. //void remove(x)-->remove x9. //Hashable find(x)-->intoarce elementul care se potriveste cu x10.//void makeEmpty()-->sterge toate elementele11.12.//**************OPERATII PUBLIC******13.14.public interface HashTable15.16. void insert( Hashable x ) ;17. void remove( Hashable x) throws ItemNotFoundException ;18. Hashable find( Hashable x) throws ItemNotFoundException ;19. void makeEmpty() ;20.

Figura 7.19 Interfata pentru tabele de repartizare.

1. import DataStructures.* ;2. import Exceptions.* ;3.4. //program simplu pentru testarea arborilor de cautare5.6. public final class TestHashTable7.

Page 123: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

7.7. COZI DE PRIORITATE 123

8. public static void main(String[] args)9. 10. HashTable h = new QuadraticProbingTable() ;11. MyString result = null ;12. try13. h.insert( new MyString( "Georgica" ) ) ;14. 15. catch(DupicateItemException e)16. 17. 18.19. try20. 21. result = (MyString) h.find(new MyString("Georgica"));22. System.out.println("Gasit " + result + " " ) ;23. 24. catch(ItemNotFoundException e)25. 26. System.out.println("Georgica nu a fost gasit") ;27. 28. 29.

Figura 7.20 Exemplu de program care foloseste tabele de repartizare.

Programul va afisa: Gasit Georgica.

7.7 Cozi de prioritate

Desi documentele trimise unei imprimante sunt asezate, de obicei, ıntr-o coada, aceasta nueste ıntotdeauna cea mai buna varianta. De exemplu, un document poate sa fie deosebit deimportant, deci el ar trebui executat imediat ce imprimanta este disponibila. De asemeni,daca imprimanta a terminat de tiparit un document, iar ın coada se afla cateva documenteavand 1-2 pagini si un document avand 100 de pagini, ar fi normal ca documentul lung safie tiparit ultimul, chiar daca nu este ultimul document trimis.Analog, ın cazul unui sistem multiutilizator, sistemul de operare trebuie sa decida la unmoment dat care dintre mai multe procese trebuie sa fie executat. In general, un procespoate sa se execute doar o perioada de timp fixata. Si aici este normal ca procesele careau nevoie de un timp foarte mic sa aiba prioritate.Daca vom atribui fiecarei sarcini cate un numar, atunci numarul mai mic (pagini tiparite,resurse folosite) va indica o prioritate mai mare. Astfel, vom dori sa accesam cel mai micelement dintr-o colectie de elemente si sa ıl stergem din cadrul colectiei. Acestea suntoperatiile findMin si deleteMin. Structura de date care ofera o implementare foarte efi-cienta a acestor operatii se numeste coada de prioritati. Figura 7.21 ilustreaza operatiilefundamentale pentru coada de prioritati.

Page 124: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

124 CAPITOLUL 7. STRUCTURI DE DATE

Coada de prioritate

insertJ

JJ

findMin,deleteMin

Figura 7.21 Modelul pentru coada de prioritati: Doar elementul minim este accesibil.

7.8 Aplicatie

Vom da ın continuare o varianta a clasei SearchTree, prezentata anterior, numita Node carecontine metodele elementare, prezentate ın comentariu, dar ın plus va permite adaugareaunui nod indiferent ca acesta mai exista ın arbore precum si faptul ca rezultatul funtieide cautare este boolean deci nu necesita tratarea ca exceptie. Clasa BinaryTree este oaplicatie a clasei Node iar clasa TestBinaryTree contine metoda main.

class Nodeprivate Node left,right;//legaturile arborelui binarprivate double data;//informatia utila din nod

Node()//constructorul fara parametriileft=null;right=null;data=0;

Node(double data)//constructorul cu parametru pentru initializare informatiei utileleft=null;right=null;this.data=data;

public static Node linkNode(Node root,double data)//inserarea unui nou nodif (root==null)root=new Node();//alocam informatie pentru noul nodroot.setData(data);//adaugam informatia utilaroot.setLeftNode(null);//setam descendentii ca fiind nuliroot.setRightNode(null);else//altfel cautam locul lui

Page 125: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

7.8. APLICATIE 125

if (data>root.getData())//daca avem o valoare mai mare atunci//facem inserarea in subarborele drept

root.setRightNode(linkNode(root.getRightNode(),data));else//altfel in subarborele dreptroot.setLeftNode(linkNode(root.getLeftNode(),data));

return root;

public void displayInordine(Node root)//parcurgerea in inordine a unui arbore binar (SRD)if (root!=null)displayInordine(root.getLeftNode());//se parcuge subarborele stangSystem.out.print(" "+root.getData()+" ");//se viziteaza noduldisplayInordine(root.getRightNode());//se parcurge subarborele drept

public void displayPreordine(Node root)//parcurgerea in preordine a unui arbore binar (RSD)if (root!=null)System.out.print(" "+root.getData()+" ");//se viziteaza noduldisplayPreordine(root.getLeftNode());//se parcurge subarborele stangdisplayPreordine(root.getRightNode());//se parcurge subarborele drept

public void displayPostordine(Node root)//parcurgerea in postordine a unui arbore binarif (root!=null)displayPostordine(root.getLeftNode());displayPostordine(root.getRightNode());System.out.print(" "+root.getData()+" ");

public static boolean search(Node root,double data)//cautarea intr-un arbore de cautareif (root!=null)

Page 126: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

126 CAPITOLUL 7. STRUCTURI DE DATE

if (root.getData()==data)//daca informatia utila din nodul curent este cea

//cautata atunci atunci returnam adevarat altfel continuam cautareareturn true;elseif(root.getdata()<data)//daca informatia utila decat cea cautatasearch(root.getLeftNode(),data);//se merge in stangaif(root.getdata()>data)//altfelsearch(root.getRightNode(),data);//se merge in dreapta

return false;

public double getData()//returneaza informatia utila din nodul curentreturn this.data;

public void setData(double data)//modifica informatia utila din nodul curentthis.data = data;

public Node getLeftNode()//trece la subarborele stang al nodului curentreturn this.left;

public void setLeftNode(Node left)//adauga subarborele stang nodului curentthis.left = left;

public Node getRightNode()//trece la subarborele drept al nodului curentreturn this.right;

public void setRightNode(Node right)//adauga subarborele drept nodului curentthis.right = right;

Page 127: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

7.8. APLICATIE 127

Figura 7.22 Clasa Node.

class BinaryTreeNode rootNode=null;

public void addNode(double data)//adaugarea unui nod in arborerootNode=Node.linkNode(rootNode,data);

public void displayInordine()//se afiseaza arborele parcurs in inordineSystem.out.print("Inordine:");rootNode.displayInordine(rootNode);System.out.println("");

public void displayPreordine()//se afiseaza arborele parcurs in preordineSystem.out.print("Preordine:");rootNode.displayPreordine(rootNode);System.out.println("");

public void displayPostordine()//se afiseaza arborele parcurs in postordineSystem.out.print("Postordine:");rootNode.displayPostordine(rootNode);System.out.println("");

public boolean search(double data)//cautarea unui nod in arborereturn Node.search(rootNode,data);

Figura 7.23 Clasa BinaryTree.

class TestBinaryTree// se testeaza clasa BinaryTreepublic static void main(String args[])BinaryTree btree=new BinaryTree();btree.addNode(6);btree.addNode(3);

Page 128: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

128 CAPITOLUL 7. STRUCTURI DE DATE

btree.addNode(7); //se adauga arborelui aceste noduribtree.addNode(2);btree.addNode(4);btree.addNode(8);btree.displayInordine();//parcurge arborele binar in inordinebtree.displayPreordine();//parcurge arborele binar in preordinebtree.displayPostordine();//parcurge arborele binar in postordineSystem.out.println(btree.search(7));

Figura 7.24 Clasa TestBinaryTree.

Page 129: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

Capitolul 8

Metoda Backtracking

8.1 Prezentare generala

In informatica apar frecvent situatii ın care rezolvarea unei probleme conduce la deter-minarea unor vectori de forma:

x = (x1, x2, . . . , xn)

unde:

• fiecare componenta xi apartine unei multimi finite Vi

• componentele vectorului x respecta anumite relatii, numite conditii interne, astfelıncat x este o solutie a problemei daca si numai daca aceste conditii sunt satisfacutede componentele x1, x2, . . . , xn ale vectorului.

Produsul cartezian V1 × V2 × . . . × Vn se numeste spatiul solutiilor posibile. Elementeleacestui produs cartezian care respecta conditiile interne se numesc solutii ale problemei.

Exemplul 8.1 Fie doua multimi de litere V1 = A,B, C si V2 = M,N. Se cere sase determine acele perechi (x1, x2) cu proprietatea ca daca x1 este A sau B, atunci x2 nupoate fi N.Rezolvarea problemei de mai sus conduce la perechile:

(A,M), (B,M), (C,M), (C,N)

deoarece din cele sase solutii posibile doar acestea ındeplinesc conditiile puse ın enuntulproblemei.

Exemplul 8.2 Se da multimea cu elementele A,B, C, D. Se cere sa se genereze toatepermutarile elementelor acestei multimi.Se cer deci multimile x = x1, x2, x3, x4 care respecta conditiile:

• xi 6= xj pentru i 6= j

• xi apartine multimii V = V1 = V2 = V3 = V4 = A,B, C.D .

129

Page 130: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

130 CAPITOLUL 8. METODA BACKTRACKING

Exista multi vectori care respecta aceste conditii: A,B, C, D, B,A, C, D, B,C,D,A,B,D,A, C etc. Mai exact, numarul de permutari ale elementelor unei multimi cu 4 el-emente este 4! = 24.O modalitate de rezolvare a problemei ar fi sa se genereze toate cele 44 = 256 elementeale produsului cartezian V1 × V2 × V3 × V4 (reprezentand solutiile posibile) si sa se aleagadintre ele cele 24 care respecta conditiile interne. Sa observam ınsa ca daca ın loc de 4elemente multimea noastra ar avea 7 elemente, vor exista 77 = 823.543 variante posibile,dintre care doar 7! = 5040 vor respecta conditiile interne.Conform celor aratate mai sus, este indicat ca, pentru a rezolva o problema, sa elaboramalgoritmi al caror timp de lucru sa nu fie atat de mare, sau, daca este posibil, sa nu fieexponential. Metoda backtracking este o metoda foarte importanta de elaborare a algorit-milor pentru problemele de genul celor descrise mai sus. Desi algoritmii de tip backtrackingau si ei, ın general, complexitate exponentiala, ei sunt totusi net superiori unui algoritmde genul celui descris mai sus, care genereaza toate solutiile posibile.

8.2 Prezentarea metodei

Metoda backtracking urmareste sa evite generarea tuturor solutiilor posibile, scurtandu-seastfel drastic timpul de calcul.Componentele vectorului x primesc valori ın ordinea crescatoare a indicilor (noi vom notaaceste valori cu v1, v2, . . . , vn cu scopul de a face diferenta ıntre o componenta care nuare o valoare atribuita, xk , si o componenta care are atribuita o valoare, vk). Aceastaınseamna ca lui xk nu i se atribuie o valoare decat dupa ce x1, x2, . . . , xk−1 au primit valoricare nu contrazic conditiile interne. Mai mult decat atat, valoarea vk atribuita lui xk vatrebui astfel aleasa ıncat v1, v2, . . . , vk sa respecte si ele anumite conditii, numite conditiide continuare, care sunt deduse de catre programator pe baza conditiilor interne. Astfel,daca ın exemplul 8.2, prima componenta, x1, a primit valoarea v1 = A, este clar ca luix2 nu i se va mai putea atribui aceasta valoare (elementele unei permutari trebuie sa fiediferite).Neındeplinirea conditiilor de continuare exprima faptul ca oricum am alege valorile pentrucomponentele xk+1, . . . , xn, nu vom obtine nici o solutie (deci conditiile de continuare suntstrict necesare pentru obtinerea unei solutii). Prin urmare, se va trece la atribuirea uneivalori lui xk, doar daca conditiile de continuare pentru componentele x1, x2, . . . , xk (careau valorile v1, v2, . . . , vk ) sunt ındeplinite. In cazul neındeplinirii conditiilor de continuare,se alege o noua valoare pentru xk sau, ın cazul ın care multimea valorilor posibile, Vk, afost epuizata, se ıncearca sa se faca o noua alegere pentru componenta precedenta, xk−1,a vectorului, micsorand pe k cu o unitate. Aceasta revenire la componenta precedenta danumele metodei, exprimand faptul ca daca nu putem avansa, urmarim (engl. track = ”aurmari”) ınapoi (engl. back = ”ınapoi”) secventa curenta din solutie.Trebuie observat faptul ca respectarea conditiilor de continuare de catre v1, v2, . . . , vk nureprezinta o garantie a faptului ca vom obtine o solutie continuand cautarea cu aceste val-ori. Deci conditiile de continuare sunt conditii necesare pentru ca v1, v2, . . . , vk sa conducala o solutie, dar nu sunt conditii suficiente.Alegerea conditiilor de continuare este foarte importanta, o alegere buna ducand la o re-ducere substantiala a numarului de calcule. In cazul ideal, aceste conditii ar trebui sa

Page 131: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

8.2. PREZENTAREA METODEI 131

fie nu numai necesare, ci chiar suficiente pentru obtinerea unei solutii. In practica seurmareste gasirea unor conditii de continuare care sa fie cat mai ”dure”, adica sa eliminedin start cat mai multe solutii neviabile. De obicei, conditiile de continuare reprezintarestrictia conditiilor interne la primele k componente ale vectorului. Evident, conditiile decontinuare ın cazul k=n sunt chiar conditiile interne.De exemplu, o conditie de continuare ın cazul celei de-a doua probleme luata ca exempluar fi:

vk 6= vi,∀i = 1, k − 1

Prin metoda backtracking, orice vector este construit progresiv, ıncepand cu prima com-ponenta si mergand catre ultima, cu eventuale reveniri asupra valorilor atribuite anterior.Reamintim ca x1, x2, . . . , xn primesc valori ın multimile V1, V2, . . . , Vn. Prin atribuiri sauıncercari de atribuiri esuate din cauza nerespectarii conditiilor de continuare, anumitevalori sunt ”consumate”. Pentru o componenta oarecare xk vom nota prin Ck multimeavalorilor consumate la momentul curent. Evident, Ck ⊂ Vk.O descriere completa a starii ın care se afla algoritmul la un moment dat se poate faceprin precizarea urmatoarelor elemente:

1. numarul de componente ale vectorului x, carora li s-au atribuit valori, avand valoareak-1.

2. valorile curente v1, v2, . . . , vk−1 ale primelor k-1 componente ale vectorului x: x1, x2,. . ., xk−1.

3. multimile de valori consumate C1, C2, . . . , Ck pentru fiecare din componentele x1, x2,. . ., xk.

Aceasta descriere poate fi sintetizata ıntr-un tabel numit configuratie, avand urmatoareaforma: (

v1, . . . , vk−1 xk, xk+1, . . . , xn

C1, . . . , Ck−1 Ck, φ, . . . , φ

)Semnificatia unei astfel de configuratii este urmatoarea:

1. ın ıncercarea de a construi un vector solutie, componentelor x1, x2, . . . , xk−1 li s-auatribuit valorile v1, v2, . . . , vk−1.

2. aceste valori satisfac conditiile de continuare

3. urmeaza sa se atribuie o valoare componentei xk; deoarece valorile consumate panaın prezent sunt cele din multimea Ck, componenta xk va primi o valoare vk dinVk − Ck.

4. valorile consumate pentru componentele x1, x2, . . . , xk sunt cele din multimile C1, C2,. . ., Ck , cu precizarea ca valorile curente v1, v2, . . . , vk−1 sunt consumate, deci aparın multimile C1, C2, . . ., Ck−1 .

5. pentru componentele xk+1, . . . , xn nu s-a ıncercat nici o atribuire, deci nu s-a con-sumat nici o valoare si, prin urmare, Ck+1, . . . , Cn sunt vide.

Page 132: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

132 CAPITOLUL 8. METODA BACKTRACKING

6. pana ın acest moment au fost construite eventualele solutii de forma:

• (c1, . . .) cu c1 ∈ C1 − v1;• (v1, c2, . . .) cu c2 ∈ C2 − v2;

.....

• (v1, v2, . . . , vk−2, ck−1, . . .) cu ck−1 ∈ Ck−1 − vk−1;• (v1, v2, . . . , vk−1, ck, . . .) cu ck ∈ Ck;

Aceasta ultima afirmatie este mai dificil de ınteles si recomandam reluarea ei dupa lec-turarea exemplului de mai jos.In construirea permutarilor multimii cu elementele A,B, C, D din Exemplul 8.2, pentruk = 4, configuratia (

3 1 2 x4

1, 2, 3 1 1, 2 1, 2

)

are, conform celor aratate mai ınainte, urmatoarea semnificatie:

1. componentele x1, x2, x3 au primit valorile 3,1,2.

2. tripletul 3,1,2 satisface conditiile de continuare

3. urmeaza sa se atribuie o valoare componentei x4. Componenta x4 ia valori dinmultimea V4 − C4, adica una din valorile 3, 4.

4. C1 = 1, 2, 3, C2 = 1, C3 = 1, 2, C4 = 1, 2

5. k + 1 = 5 > n, deci acest subpunct nu are obiect ın aceasta situatie

6. pana ın acest moment au fost deja construite solutiile de forma (ın ordinea ın careau fost descrise la punctul 6 de mai sus):

• (1, . . .) adica (1,2,3,4), (1,2,4,3), (1,3,2,4), (1,3,4,2), (1,4,2,3), (1,4,3,2) (2, . . .)adica (2,1,3,4), (2,1,4,3), (2,3,1,4), (2,3,4,1), (2,4,1,3), (2,4,3,1)

• solutii de aceasta forma nu exista, deoarece C2 − v2 = φ

• solutii de forma (3, 1, 1, . . .); nu exista solutii de aceasta forma ;

• solutii de forma (3,1,2,1), (3,1,2,2); nu exista solutii de aceasta forma ;

Metoda backtracking ıncepe a fi aplicata ın situatia ın care nu s-a facut nici o atribuireasupra componentelor lui x, deci nu s-a consumat nici o valoare, si se ıncearca atribuireaunei valori primei componente. Acest lucru este specificat prin configuratia initiala, a careiforma este: (

x1, . . . , xn

φ, . . . , φ

)

ın care toate multimile Ck sunt vide.Un alt caz special este cel al configuratiilor solutie, avand forma:

Page 133: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

8.2. PREZENTAREA METODEI 133

(v1, . . . , vn

C1, . . . , Cn

)cu semnificatia ca vectorul (v1, . . . , vn) este solutie a problemei. Astfel, pentru Exemplul8.2, configuratia: (

A B C DA A,B A,B, C A,B, C, D

)are semnificatia ca vectorul (A,B,C,D) constituie o solutie a problemei.Metoda backtracking consta ın a porni de la configuratia initiala si a-i aplica acesteiauna dintre cele patru tipuri de transformari prezentate ın continuare, pana la epuizareatuturor variantelor. Fiecare transformare se aplica ın anumite conditii bine precizate. Laun moment dat doar o singura transformare poate fi aplicata. Presupunem ca ne aflamın configuratia descrisa anterior, ın care s-au atribuit valori primelor k − 1 componente.Transformarile care pot fi aplicate unei configuratii sunt:

8.2.1 Atribuie si avanseaza

Acest tip de modificare are loc atunci cand mai exista valori neconsumate pentru xk (deciCk ⊂ Vk), iar valoarea aleasa vk are proprietatea ca (v1, . . . , vk) respecta conditiile decontinuare. In acest caz valoarea vk se atribuie lui xk si se adauga multimii Ck, dupa carese avanseaza la componenta urmatoare, xk+1. Aceasta modificare a configuratiei poate fireprezentata ın felul urmator:(

. . . , vk−1 xk, xk+1, . . .. . . , Ck−1 Ck, φ, . . .

) vk

-

(. . . , vk−1, vk xk+1, . . .. . . , Ck−1, Ck ∪ Vk φ, . . .

)De exemplu, la generarea permutarilor, avem urmatoarea schimbare de configuratie pornindde la starea initiala:(

x1 x2 x3 x4

φ φ φ φ

) A-

(A x2 x3 x4

A φ φ φ

)

8.2.2 Incercare esuata

Acest tip de modificare are loc atunci cand, ca si ın cazul anterior, mai exista valorineconsumate pentru xk, dar valoarea vk aleasa nu respecta conditiile de continuare. Inacest caz, vk este adaugata multimii Ck (deci este consumata), dar nu se avanseaza lacomponenta urmatoare. Modificarea este notata prin:(

. . . , vk−1 xk, xk+1, . . .. . . , Ck−1 Ck, φ, . . .

) vk︷ ︸︸ ︷===

(. . . , vk−1 xk, xk+1 . . .. . . , Ck−1 Ck ∪ Vk, φ, . . .

)In exemplul nostru cu generarea permutarilor, urmatoarea transformare este:(

A x2 x3 x4

A φ φ φ

) A︷ ︸︸ ︷===

(A x2 x3 x4

A Aφ φ

)

Page 134: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

134 CAPITOLUL 8. METODA BACKTRACKING

8.2.3 Revenire

Acest tip de transformare apare atunci cand toate valorile pentru componenta xk au fostconsumate (Ck = Vk). In acest caz se revine la componenta precedenta, xk−1, ıncercandu-se atribuirea unei noi valori acestei componente. Este important de remarcat faptul carevenirea la xk−1 implica faptul ca pentru xk se vor ıncerca din nou toate varianteleposibile, deci multimea Ck trebuie din nou sa fie vida. Transformarea este notata prin:

(. . . , vk−1 xk, xk+1, . . .. . . , Ck−1 Ck, φ, . . .

)

(. . . , vk−2 xk−1, xk, . . .. . . , Ck−2 Ck−1, φ, . . .

)O situatie de revenire, ın exemplul cu generarea permutarilor este data de configuratia:

(3 1 2 x4

1, 2, 3 1 1, 2 1, 2, 3, 4

)

(3 1 x3 x4

1, 2, 3 1 1, 2φ

)

8.2.4 Revenire dupa construirea unei solutii

Acest tip de transformare se realizeaza atunci cand toate componentele vectorului auprimit valori care satisfac conditiile interne, adica a fost gasita o solutie. In aceastasituatie se revine din nou la cazul ın care ultima componenta, xn urmeaza sa primeasca ovaloare. Transformarea se noteaza astfel:

(. . . , vn

. . . , Cn

)sol

(. . . , vn−1 xn

. . . , Cn−1 Cn

)In exemplul nostru cu generarea permutarilor, revenirea dupa gasirea primei solutii estedata de diagrama:

(1 2 3 4

1 1, 2 1, 2, 3 1, 2, 3, 4

)sol

(1 2 3 x4

1 1, 2 1, 2, 3 1, 2, 3, 4

)Revenirea dupa construirea unei solutii poate fi considerata ca fiind un caz particular alrevenirii daca adaugam vectorului solutie x o componenta suplimentara xn+1, care nupoate lua nici o valoare (Vn+1 = φ).O problema importanta este cea a ıncheierii procesului de cautare a solutiilor, sau, cu altecuvinte ne putem pune ıntrebarea: transformarile succesive aplicate configuratiei initialese ıncheie vreodata sau continua la nesfarsit? Evident ca pentru ca metoda backtrackingsa constituie un algoritm trebuie sa respecte si ea proprietatile unui algoritm enuntateınca din primul capitol, ıntre care se afla si proprietatea de finitudine. Demonstrareafinitudinii algoritmilor de tip backtracking se bazeaza pe urmatoarea observatie simpla:prin transformarile succesive de configuratie nu este posibil ca o configuratie sa se repete,iar numarul de elemente al produsului cartezian V1×V2× . . .×Vn este finit; prin urmare,la un moment dat se va ajunge la configuratia:

(x1 x2 . . . xn

V1 φ . . . φ

)

Page 135: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

8.3. IMPLEMENTAREA METODEI BACKTRACKING 135

numita configuratie finala. In configuratia de mai sus ar trebui sa aiba loc o revenire(deoarece toate valorile pentru prima componenta au fost consumate), adica o deplasare abarei verticale la stanga. Acest lucru este imposibil, si algoritmul se ıncheie deoarece niciuna din cele patru transformari nu poate fi aplicata. In practica, aceasta ıncercare de adeplasa bara de pe prima pozitie (k = 1) pe o pozitie anterioara (k = 0) este utilizata pepost de conditie de terminare a algoritmului.Inainte de a trece la implementarea efectiva a metodei backtracking ın pseudocod, sageneram diagramele de stare pentru Exemplul 8.1:

(x1 x2

φ φ

) A-

(A x2

A φ

) M-

(A MA M

)sol

(A x2

A M

) N︷ ︸︸ ︷===

(A x2

A M,N

)

(x1 x2

) B-

(B x2

A,B φ

) M-

(B M

A,B M

)

sol

(B x2

A,B M

) N︷ ︸︸ ︷===

(B x2

A,B M,N

)

(x1 x2

A,Bφ

) C-

(C x2

A,B, C φ

) M-

(C MA,B, C M

)sol

(C x2

A,B, C M

) N-

(C N

A,B, C M,N

)sol

(C x2

A,B, C M,N

)

(x1 x2

A,B, Cφ

)

8.3 Implementarea metodei backtracking

Procesul de obtinere a solutiilor prin metoda backtracking este usor de programat deoarecela fiecare pas se modifica foarte putine componente (indicele k, reprezentand pozitia barei,componenta xk si multimea Ck).Algoritmul corespunzator (ın pseudocod) este urmatorul:

initializeaza (citeste) multimile de valori, V1, . . . , Vn

k ← 1 //se construieste configuratia initialapentru i = 1, n

Ci ← φ//acum ıncepe efectiv aplicarea celor 4 transformari, functie de cazcat timp k > 0 //k = 0 ınseamna terminarea cautarii

daca k = n + 1 atunci //configuratia este tip solutieretine solutia v1, . . . , vn

k ← k − 1 //revenire dupa solutiealtfel

dacaCk 6= Vk atunci //mai exista valori neconsumate

Page 136: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

136 CAPITOLUL 8. METODA BACKTRACKING

alege o valoare vk dinCk − Vk

Ck = Ck ∪ vk //valoarea vkeste consumatadaca v1, . . . , vk respecta conditiile de continuare atunci

xk ← vk; //atribuie sik ← k + 1; //avanseaza

altfel //ıncercare esuata, nu fac nimicaltfel //revenire

Ck ← φ, k ← k − 1sfarsit cat timp

Algoritmul de mai sus functioneaza pentru cazul cel mai general, dar este destul de dificilde programat din cauza lucrului cu multimile Ck si Vk. Din fericire, adeseori ın practicamultimile Vk au forma

Vk = 1, 2, . . . , sk

deci fiecare multime Vk poate fi reprezentata foarte simplu, prin numarul sau de elemente,sk. Pentru a simplifica si mai mult lucrurile, vom alege valorile pentru fiecare componentaxk ın ordine crescatoare, pornind de la 1, si pana la sk. In aceasta situatie, multimea devalori consumate Ck va fi de forma 1, 2, . . . , vk si, drept consecinta va putea fi reprezen-tata doar prin valoarea vk.Consideratiile de mai sus permit ınlocuirea algoritmului anterior, bazat pe multimi, cu unalgoritm simplificat, care lucreaza numai cu numere.La Exemplul 8.1, vom conveni sa reprezentam pe A,B,C prin valorile 1,2,3, iar pe M si Nprin 1 si 2. In aceasta situatie, configuratiile succesive se vor reprezenta mai simplu astfel:

(| x1 x2

) 1

-(

1 | x2

) 1

-(

1 2 |)

sol(

1 | x2

)etc

Algoritmul ın pseudocod pentru cazul particular prezentat mai sus se concretizeaza ınurmatoarea metoda Java:

public void backtracking()int k =0 ;while(k>=0)if(k==n) //am gasit o solutieretSol() ; //afisam solutiak-- ; //revenire dupa gasirea unei solutiielseif(x[k]<s[k]) //mai sunt valori neconsumate

Page 137: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

8.4. PROBLEME CLASICE CARE ADMIT REZOLVARE PRIN METODA BACKTRACKING 137

x[k]++ ; //se ia urmatoarea valoareif( continuare(k) ) //respecta cond. de cont?k++ ; //avanseaza

elsex[k--] = 0 ; //revenire

Se observa ca metoda backtracking apeleaza ınca doua metode:

• metoda retSol, care, asa cum sugereaza si numele ei, retine solutia, constand ınvalorile vectorului x. Cel mai adesea aceasta metoda realizeaza o simpla afisare asolutiei si, eventual, o comparare cu solutiile gasite anterior.

• metoda continuare(k) verifica daca valorile primelor k componente ale vectorului xsatisfac conditiile de continuare; ın cazul afirmativ este ıntoarsa valoarea true, iar ıncaz contrar este ıntoarsa valoarea false.

8.4 Probleme clasice care admit rezolvare prin metoda back-tracking

8.4.1 Problema generarii permutarilor

Se da multimea A cu elementele a1, a2, . . . , an. Sa se genereze toate permutarile ele-mentelor acestei multimi.Se observa ca aceasta problema este o simpla generalizare a Exemplului 8.2 din subcapi-tolul precedent. Mai mult decat atat, problema poate fi redusa la a genera permutarilemultimii de indici 1, 2, . . . , n. In aceasta situatie vom avea V1 = V2 = . . . = Vn =1, 2, . . . , n, deci putem aplica varianta simplificata a metodei bactracking.Conditiile interne pe care trebuie sa le respecte un vector solutie sunt:

xi 6= xj pentru ∀ i, j = 1, n, i 6= j.

Conditiile de continuare pentru componenta numarul k, sunt o simpla restrictie a conditiilorinterne:

xi 6= xk pentru ∀ i = 1, k − 1 .

Prin urmare codul pentru functia de continuare este foarte simplu:

public boolean cont(int k)for(int i=0; i<k; ++i)

Page 138: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

138 CAPITOLUL 8. METODA BACKTRACKING

if(x[k]==x[i])return false ;return true ;

Metoda retSol este si ea extrem de simpla ın aceasta situatie: se scriu elementele multimiiA, ordonate dupa permutarea x.

public void retSol()for(int i=0; i<n; ++i)System.out.print(a[x[i]-1] + " ") ;System.out.println() ;

Metoda backtracking pentru generarea permutarilor se obtine din metoda backtrackingpentru cazul general ınlocuind numarul de elemente al multimilor Vk, sk cu valoarea n.

8.4.2 Generarea aranjamentelor si a combinarilor

Vom prezenta acum modalitatea prin care se poate adapta foarte usor algoritmul degenerare a permutarilor unei multimi pentru a genera aranjamentele si combinarile aceleimultimi. Pentru a simplifica lucrurile, vom presupune ca multimea A este formata dinprimele n numere naturale, adica A = 1, 2, . . . , n.Reamintim faptul ca prin aranjamente de n luate cate m (n ≥ m), notate Am

n se ıntelegtoate multimile ordonate cu m elemente formate din elemente ale multimii A, cu altecuvinte toti vectorii de forma:

x = (x1, . . . , xn), unde xi ∈ 1, 2, . . . ,m, xi 6= xj , ∀i, j = 1, n

Se observa ca, din punct de vedere al reprezentarii formale, singura diferenta dintre aran-jamente si permutari este ca aranjamentele au lungime m ın loc de n. De altfel, pentrum=n aranjamentele si permutarile coincid.

Exemplul 8.3 Aranjamentele de 3 luate cate 2 (A23)sunt:

(1,2), (1,3), (2,1), (2,3), (3,1), (3,2).

Conditiile interne si, ın consecinta, conditiile de continuare, sunt identice cu cele de lagenerarea permutarilor. Prin urmare si functia de continuare este identica cu cea de lapermutari. Unde este totusi diferenta? Avand ın vedere ca lungimea vectorului este m sinu n, conditia de gasire a unei solutii trebuie ınlocuita cu k=m. Prin urmare, ın metodabacktracking linia:

Page 139: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

8.4. PROBLEME CLASICE CARE ADMIT REZOLVARE PRIN METODA BACKTRACKING 139

if(k==n)

va fi ınlocuita cu:

if(k==m)

Desigur, aceeasi modificare este necesara si ın metoda retSol, ın care secventa

for(int i=0; i<n; ++i)

se va ınlocui cu

for(int i=0; i<m; ++i)

Sa vedem acum modalitatea de generarea a combinarilor. Reamintim ca prin combinaride n luate cate m (notat Cm

n ) se noteaza toate submultimile cu m elemente ale multimiiA = 1, 2, . . . , n.

Exemplul 8.4 Combinarile de 3 luate cate 2 (C23) sunt:

(1,2), (1,3), (2,3).

Diferenta ıntre combinari si aranjamente este data de faptul ca, ın cazul combinarilor, or-dinea ın care apar componentele nu conteaza ( combinarea (1,2) este aceeasi cu combinarea(2,1) etc.; tocmai din acest motiv noi am optat ın exemplul de mai sus sa aranjam com-ponentele unei combinari ın ordine crescatoare). Prin urmare, combinarile unei multimicu n elemente luate cate m sunt definite de vectorii:

x = (x1, . . . , xm), unde x1 < x2 < . . . < xm.

Conditia de continuare ın cazul combinarilor va fi pur si simplu:

xk > xk−1 pentru k > 1.

Metodele backtracking si retSol sunt ın cazul combinarilor identice cu cele de la aranja-mente. Diferenta apare la functia de continuare, care are urmatoarea forma (forma dindreapta este mai criptica, dar mai eleganta):

public boolean cont(int k) public boolean cont(int k)

if (k > 0 && x[k] <= x[k − 1]) return k == 0 || x[k] > x[k − 1];

return false;else

return true;

Una din problemele de la finalul capitolului propune o varianta mai eficienta de generarea combinarilor ın care functia de continuare este complet eliminata.

Page 140: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

140 CAPITOLUL 8. METODA BACKTRACKING

8.4.3 Problema damelor

Sa se aseze n dame pe o tabla de sah de dimensiune n×n astfel ıncat damele sa nu fie peaceeasi linie, aceeasi coloana sau aceeasi diagonala (damele sa nu se atace ıntre ele).Reamintim ca ın jocul de sah, o dama ”ataca” pozitiile aflate pe aceeasi linie sau coloanasi pe diagonala. O posibila asezare a damelor pe o tabla de sah de dimensiuni 4x4 estedata ın figura de mai jos:

××

××

Figura 8.1 O solutie pentru problema damelor ın cazul unei table de dimensiuni 4x4

Sa vedem cum putem reformula problema damelor pentru a o aduce la o problema de tipbacktracking. Se observa cu usurinta ca pe o linie a tablei de sah se poate afla o singuradama, prin urmare putem conveni ca prima dama se va aseza pe prima linie, a doua damape a doua linie etc. Rezulta ca pentru a cunoaste pozitia damei numarul k este suficient sastim coloana pe care aceasta se gaseste. O solutie a problemei se poate astfel reprezentaprintr-un vector

x = (x1, x2, . . . , xn), xk ∈ 1, 2, . . . , n,

unde xk reprezinta coloana pe care se gaseste dama numarul k.Cu aceasta notatie, vectorul solutie corespunzator exemplului din figura de mai sus este:(2,4,1,3).Sa vedem acum care este conditia ca doua dame distincte, k si i, sa se atace:

• ın mod cert damele nu pot fi pe aceeasi linie

• damele sunt pe aceeasi coloana daca xk = xi

• damele sunt pe aceeasi diagonala daca distanta dintre abscise este egala cu distantadintre ordonate, adica:

|xk − xi| = |k − i|

Conditia de continuare este ca dama curenta, k, sa nu atace nici una dintre damele caredeja sunt asezate pe tabla, adica:

xk 6= xi si |xk − xi| 6= |k − i| pentru ∀ i = 1, k − 1.

Functia de continuare este:

public boolean continuare(int k)for(int i=0; i<k; ++i)if(x[i]==x[k] || k-i == Math.abs(x[k]-x[i]) )

Page 141: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

8.4. PROBLEME CLASICE CARE ADMIT REZOLVARE PRIN METODA BACKTRACKING 141

return false ;return true ;

Modificarile care trebuie aduse metodelor retSol si backtracking sunt minime si le lasamca exercitiu.Observatie: Problema damelor este primul exemplu de problema ın care conditiile decontinuare sunt necesare, dar nu sunt suficiente. De exemplu (pentru n=4), la ınceput,algoritmul va aseza prima dama pe prima coloana, a doua dama pe a treia coloana, iarcea de-a treia dama nu va putea fi asezata pe nici o pozitie, fiind necesara o revenire.

8.4.4 Problema colorarii hartilor

Se da o harta ca cea din figura de mai jos, ın care sunt reprezentate schematic 6 tari,dintre care unele au granite comune. Presupunand ca dispunem doar de trei culori (rosu,galben, verde), se cere sa se determine toate variantele de colorare a hartii astfel ıncatoricare doua tari vecine (care au frontiera comuna) sa fie colorate diferit.

T2

T1 T3 T4 T5

T6

0 1 1 0 0 11 0 1 1 1 01 1 0 1 0 10 1 1 0 1 10 1 0 1 0 11 0 1 1 1 0

Figura 8.2 O harta reprezentand sase tari si matricea de vecinatati desenata alaturat

Pentru a memora relatia de vecinatate ıntre doua tari vom utiliza o matrice de dimensiuni6× 6 numita vecin definita prin:

vecin[i, j] =

true daca tarile Ti si Tj sunt vecine

false altfel

Figura 8.2 reprezinta matricea de vecinatati pentru harta cu 6 tari ın care s-a facutconventia ca 1 reprezinta true si 0 reprezinta false.Problema se poate generaliza usor si la o harta cu n tari care trebuie colorata cu m culori.Vom utiliza pentru usurarea expunerii harta cu 6 tari de mai sus.In aceasta problema, un vector solutie x = (x1, x2, . . . , xn) reprezinta o varianta de colorarea hartii, avand semnificatia ca tara numarul i va fi colorata cu culoarea xi. In exemplulnostru, xi poate fi 1,2 sau 3, corespunzand respectiv culorilor rosu, galben, verde.Conditia de continuare este ca tara careia urmarim sa ıi atribuim o culoare sa aiba oculoare distincta de tarile cu care are granita. Cu alte cuvinte, trebuie sa avem:

xi 6= xk daca A[i, k] = 1, ∀i = 1, k − 1

Functia de continuare este ın aceasta situatie:

Page 142: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

142 CAPITOLUL 8. METODA BACKTRACKING

public boolean continuare(int k)for(int i=0; i<k; ++i)if(x[i]==x[k] && vecin[k][i]==1 )return false ;return true ;

Metoda retSol este:

public void retSol()for(int i=0; i<n; ++i)if(x[i]==1)System.out.print("rosu") ;elseif(x[i]==2)System.out.print("galben") ;else if(x[i]==3)System.out.print("verde") ;

System.out.println() ;

Metoda backtracking este aproape identica cu cea standard si o lasam ca exercitiu.Programul principal trebuie sa realizeze citirea datelor (numarul de tari n, numarul deculori disponibile m si matricea de vecinatati).Pentru o mai buna ıntelegere a mecanismului metodei backtracking aplicata la problemacolorarii hartilor, putem sa ne imaginam ca dispunem de 6 cutii identice V1, V2, . . . , V6 ,fiecare dintre cutii continand trei creioane colorate notate cu r - rosu, g - galben, v - verde.Fiecare cutie Vk contine creioanele care pot fi utilizate pentru colorarea tarii Tk.O vizualizare a procesului de cautare a solutiilor poate fi obtinut daca aranjam cele 6cutii ın ordine (fiecarei tari ıi asociem o cutie) si punem un semn ınaintea cutiei din careurmeaza sa se aleaga un creion (marcajul corespunde barei verticale de la configuratii);initial acest semn este ın stanga primei cutii. Atunci cand se alege un creion dintr-o cutiecorespunzatoare unei tari el va fi asezat fie pe tara respectiva daca nu exista o tara vecina

Page 143: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

8.5. PROBLEME PROPUSE 143

cu aceeasi culoare, fie langa cutie ın caz contrar. Astfel, multimile Ci de valori consumatela un moment dat sunt alcatuite din creioanele de langa cutia Vi si de pe tara Ti. Cuaceste precizari, cele 4 modificari de configuratie au urmatoarele semnificatii concrete:

• atribuie si avanseaza: se aseaza creionul ales pe tara corespunzatoare si se trece lacutia urmatoare

• ıncercare esuata: creionul ales este asezat langa cutia din care a fost scos

• revenire: creioanele corespunzatoare tarii curente sunt repuse ın totalitate la loc sise trece la cutia precedenta

• revenire dupa gasirea unei solutii: semnul este adus la stanga ultimei cutii.

Procesul se ıncheie ın momentul ın care toate creioanele ajung din nou ın cutiile ın carese aflau initial.

8.5 Probleme propuse

1. Sa se afiseze toate modurile ın care n persoane pot fi asezate la o masa rotundaprecum si numarul acestora.

Indicatie: Exista doua posibilitati de rezolvare:

(a) Se vor genera toate variantele posibile, prin metoda backtracking, si se vor con-toriza. Se va afisa apoi numarul lor. Va trebui ınsa sa tineti seama de faptulca unele dispuneri sunt identice din cauza mesei circulare.

(b) Mult mai elegant, se va tine cont de combinatorica. Astfel, cu n obiecte se potforma n! permutari. Cum, ın cazul dispunerii lor circulare 1, 2, . . . , n, respec-tiv 2, 3, . . . , n, 1, . . ., n, 1, 2, . . . , n − 1 sunt identice, rezulta ca din n astfel depermutari trebuie considerata doar una. Numarul de permutari va fi asadarn!n = (n− 1)!.

2. Idem problema 1, cu precizarea ca anumite persoane nu se agreeaza, deci nu pot fiasezate una langa cealalta. La intrare se mai furnizeaza o matrice simetrica A, cuurmatoarea semnificatie:

A(i, j) =

1 daca nu se ageaza0 altfel

3. Sa se modifice algoritmul de generare a combinarilor prezentat ın paragraful 7.4.2astfel ıncat functia de continuare sa nu mai fie necesara.

Indicatie: Pentru fiecare componenta x[k] se porneste cu valoarea x[k − 1] + 1.

4. Gasiti toate solutiile de colorare cu trei culori a hartii din Figura 8.2.

Page 144: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

144 CAPITOLUL 8. METODA BACKTRACKING

5. Se dau n multimi A1, A2, . . . , An. Sa se afiseze produsul lor cartezian.

Indicatie: Generarea produsului cartezian ınseamna de fapt generarea ıntreguluispatiu de solutii, adica un backtracking ın care functia de continuare lipseste iarpe fiecare nivel al stivei sunt incarcate pe rand toate elementele unei multimi fixate.

6. Se da o multime A = 1, 2, . . . , n. Sa se afiseze toate submultimile acestei multimi.

Indicatie: Se genereaza toti vectorii caracteristici de lungime n. Prin vector car-acteristic se ıntelege un vector ce are doar valorile 1 sau 0 pentru fiecare element cusemnificatia:

x[i] =

1 daca i apartine submultimii0 altfel

Exista si o solutie ce genereaza vectorii caracteristici de lungime n prin adunarea ınbaza 2. Initial vectorul este nul, corespunzator multimi vide, iar apoi prin adunarirepetate se genereaza toate submultimile. Atentie, numarul total de submultimi este2n!

7. O firma dispune de n angajati, dintre care p sunt femei. Firma trebuie sa formeze odelegatie de m persoane dintre care k sunt femei. Sa se afiseze toate delegatiile carese pot forma.

Indicatie: Pentru a forma o delegatie de k femei din p disponibile avem la dispozitieCk

p variante. Delagatia de m persoane poate fi completata cu oricare din variantelede Cm−k

n−p de alegere a barbatilor din delegatie. Asadar numarul total de variante esteCk

p ∗ Cm−kn−p , ce urmeaza a fi calculat.

Generarea efectiva se bazeaza pe un vector caracteristic cu semnificatia:

x[i] =

1 daca persoana e femeie0 altfel

Functia de continuare va numara femeile din delegatie si nu va lasa ca numarul lorsa-l depaseasca pe k.

8. Se considera multimea A = 1, 2, . . . , n. Sa se furnizeze toate partitiile aces-tei multimi. (O partitie a unei multimi este o scriere a multimii ca reuniune desubmultimi disjuncte).

Indicatie: Vom genera partitia sub forma unui vector cu n componente ın carex[i] = k are semnificatia ca elementul i apartine submultimii k a partitiei considerate.Ca exemplu, pentru n = 4 putem avea, la un moment dat, vectorul x = (1, 2, 1, 2) ceeace corespunde partitiei: A = 1, 3 ∪ 2, 4. Ar fi de remarcat ca vectorul caracter-istic poate lua valori ce vor avea aceeasi interpretare, ca de exemplu x = (2, 1, 2, 1)ceea ce corespunde partitiei: A = 2, 4 ∪ 1, 3. Dar reuniunea e comutativa sipartitia astfel obtinuta e identica cu anterioara. Pentru a evita acest lucru vom

Page 145: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

8.5. PROBLEME PROPUSE 145

impune ca fiecare componenta a vectorului sa poata avea cel mult valoarea k, undek este indicele elementului. Semnificatia ar fi ca elementul cu indicele 1 va puteaface parte doar din submultimea 1, cel cu indicele 2 doar din submultimile 1 si 2etc. O alta restrictie ar fi aceea ca un element nu poate lua o valoarea mai mare camax + 1 unde max este valoare maxima a elementelor de rang inferior. Acest lucruse justifica prin faptul ca x = (1, 1, 3, 1) nu ar avea nici o semnificatie.

9. Un comis-voiajor trebuie sa viziteze un numar n de orase, pornind din orasul numarul1. El trebuie sa viziteze fiecare oras o singura data, dupa care sa se ıntoarca ın orasul1. Cunoscand legaturile existente ıntre orase, se cere sa se gaseasca toate rutele posi-bile pe care le poate efectua comis-voiajorul.

Indicatie: Se va crea o matrice de adiacenta (cunoscuta din teoria grafurilor), careeste o matrice simetrica:

A(i, j) =

1 daca exista legatura ıntre orasul i si j0 altfel

Functia de continuare va testa daca la elmentul actual se poate ajunge din anteriorul,ın vectorul x. Ca observatie trebuie spus ca pentru a obtine solutiile distincte trebuiefacut un artificiu asemanator cu cel de la problema anterioara.

10. Idem problema anterioara, cu precizarea ca pentru fiecare drum ıntre doua orasese cunoaste distanta care trebuie parcursa. Se cere sa se gaseasca ruta de lungimeminima.

Indicatie: In momentul retinerii solutiei se va calcula lungimea drumului parcurs.Se va compara aceasta lungime cu lungimea anterioara considerata minima si se varetine valoarea actuala minima ımpreuna cu drumul parcurs.Aceasta problema este celebra prin faptul ca este un exemplu pentru imposibilitateaaflarii solutiei exacte altfel decat prin backtracking. Datorita complexitatii mari ametodei s-au gasit metode mai putin complexe (metodele euristice) dar care dau osolutie cu o marja de aproximare.

11. Presupunem ca avem de platit o suma s si avem la dispozitie un numar nelimitatde bancnote si monezi de valoare ν1, ν2, . . . , νn. Sa se furnizeze toate variantele deplata a sumei utilizand numai aceste monezi.

Solutie: Rezolvarea acestei probleme va avea la baza o varianta mai modularizata aclasei Bactracking prezentata anterior. Aceasta varianta o vom da ın clasa Back-tracking1, ce urmeaza, dar ea pastreaza ıntru totul ideile teoretice din paragrafeleanterioare.

public abstract class BackTracking1int n ; //dimensiunea problemeiboolean as,ev;

Page 146: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

146 CAPITOLUL 8. METODA BACKTRACKING

int[] x ; //vectorul solutiepublic BackTracking1(int n)this.n = n ;x = new int[n] ;for(int i=0; i< n ; ++i)x[i] = -1 ;

//initializarea elementelor sirului cu -1

public void back()int k =0 ;init(k,x);while(k>=0)doas=succesor(k,x);//verifica daca am succesor pe nivelul respectivif(as) ev=valid(k,x);//daca am succesor e valid?while(!((!as)||(as &&ev)));if(as)if(solutie(k)) tipar(k);else++k;init(k,x);else k--;

public abstract boolean succesor( int k, int[] x) ;public abstract boolean valid( int k, int[] x) ;public abstract void tipar(int k) ;public abstract void init( int k, int[] x);public abstract boolean solutie( int k);

Figura 8.3 Clasa Backtracking1

Ideea rezolvarii, data ın clasa Plati ce implementeaza clasa abstracta Backtracking1,este de a ıncarca ın stiva fiecare moneda sau bacnota. Conditia de continuare esteaceea ca suma pe care o am pana ın acel moment sa fie mai mica sau egala decatsuma dorita. Daca aceasta conditie este ındeplinita pot continua cautarea sau am

Page 147: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

8.5. PROBLEME PROPUSE 147

dat peste solutie, altfel voi fi nevoit sa cobor un nivel ın stiva. Ca un artificiu amintrodus ın metoda succesor o conditie suplimentara pentru a nu mai cauta o solutiedeja gasita. De exemplu: solutia 1+2 este aceeasi cu 2+1. Am presupus de asemeneaca valorile monedelor si bacnotelor sunt ordonate crescator. Cu un pic de efort sepoate evita acest lucru retinand valorile ıntr-un vector separat iar ın rezolvare se vorreferi doar indicii vectorului.

import io.*;

public class Plati extends BackTracking1int m[]=Reader.readIntArray();int s=9;

public boolean succesor( int k, int[] x)if((x[k]<m.length-1)&&((k==0)||((k>0)&&(x[k]<x[k-1]))))x[k]++;return true;else return false;

public boolean valid( int k, int[] x)int i,s1=0;for(i=0;i<=k;i++)s1=s1+m[x[i]];if(s<s1) return false;else return true;

public void tipar(int k)System.out.println("solutie");for(int i=0;i<=k;i++)System.out.print(m[x[i]]);System.out.println();

public void init( int k, int[] x)x[k]=-1;

Page 148: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

148 CAPITOLUL 8. METODA BACKTRACKING

public boolean solutie( int k)int i,s1;for(i=0,s1=0;i<=k;i++)s1=s1+m[x[i]];if(s==s1) return true;else return false;

public Plati(int n)super(n) ;//lansarea constructorului din clasa backtracking1;

public static void main(String[] args)Plati plati= new Plati(200) ;plati.back() ;

Figura 8.4 Clasa Plati

12. Idem problema anterioara, cu precizarea ca trebuie sa platim suma respectiva cu unnumar cat mai mic de monezi si bancnote.

Indictie: Fata de rezolvarea problemei anterioare se poate face, spre exemplu, o mod-ificare care sa compare, ın momentul gasirii unei solutii, indicele stivei cu cel gasitla solutiile anterioare.

13. Idem problema anterioara pentru cazul ın care dispunem doar de un numar n1, n2, . . . , nn

de monezi de valoare ν1, ν2, . . . , νn.

Indicatie: Deosebirile fata de problemele anterioare constau:

• ın stiva vom ıncarca numarul de monezi sau bacnote folosite nu si valoarea lor.Astfel, fiecarui nivel ıi corespunde o anumita valoare;

• la functia succesor vom avea grija sa nu depasim numarul alocat din fiecarevaloare iar pe de alta parte indicele stivei va trebui sa nu depaseasca numarulde valori disponibil;

• sumele se vor calcula prin cumularea produselor dintre valoare si numarul devalori folosite.

14. Fiind dat un numar natural n, sa se genereze toate partitiile sale. O partitie a unuinumar reprezinta scrierea sa ca suma de numere naturale nenule.

Page 149: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

8.5. PROBLEME PROPUSE 149

Indicatie: O solutie ar putea fi cea de la problema cu Plata unei Sume de Bani.

15. Fiind dat un numar natural n, sa se genereze toate descompunerile sale ca suma denumere prime.

Indicatie: Fata de problema anterioara se poate verifica, la continuare, daca numarulales este prim.

16. O fotografie alb-negru este reprezentata sub forma unei matrice cu elemente 0 sau1. In fotografie sunt reprezentate unul sau mai multe obiecte. Portiunile core-spunzatoare obiectelor au valoarea 1 ın matrice. Se cere sa se determine daca fo-tografia reprezinta unul sau mai multe obiecte.Exemplu: Matricea de mai jos reprezinta doua obiecte:

0 1 1 01 0 0 00 0 1 11 1 1 0

17. Un teren dreptunghiular este ımpartit ın m×n parcele, reprezentate sub forma unei

matrice A, cu m linii si n coloane. Fiecare element al matricei este un numar realcare reprezinta ınaltimea parcelei respective. Pe una dintre parcele se afla plasatao bila. Se cere sa se furnizeze toate posibilitatile prin care bila poate sa paraseascaterenul, cunoscut fiind faptul ca bila se poate rostogoli numai pe parcele ınvecinatea caror ınaltime este strict inferioara ınaltimii parcelei pe care bila se afla.

Indicatie: Aceasta si problema anterioara sunt cazuri tipice de backtracking ın plan.Ideea rezolvarii consta ın ıncercarea de a ajunge la o pozitie vecina respectand conditi-ile problemei. Modalitatea de miscare este data de cele 8 directii cardinale N, NV,V, SV, S, SE, E, NE. La fiecare pas avem grija sa nu iesim din spatiul alocat decatcu o solutie noua. Este posibil sa dam peste aceeasi solutie asa ıncat o vom retine peanterioara. Practic, stiva va ıncarca cordonatele curente si directia ın care urmeazasa ne deplasam.

18. Pe o tabla de sah de dimensiune 8 × 8 se ıncearca pozitionarea a n pioni dupaurmatoarele reguli:

(a) Pe fiecare linie se afla doi pioni.

(b) Pe fiecare coloana se afla cel mult doi pioni.

(c) Pe fiecare paralela la diagonala principala se afla cel mult doi pioni.

Solutie: Programul dat ın continuare prezinta ın Figura 8.5 clasa de baza Back-tracking care este usor modificata fata de cea data ın sectiunea 8.4, iar ın Figura 8.6clasa derivata Pions ce implementeaza metodele abstracte si contine functia main.

public abstract class BackTracking

Page 150: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

150 CAPITOLUL 8. METODA BACKTRACKING

int n; //dimensiunea problemeiint[] x; //vectorul solutie

public BackTracking(int n, int m)//constructorulthis.n = n ;x = new int[n] ;for(int i=0; i< n ; ++i)x[i] = -1 ;//initializarea cu -1 a elem vect solutie

public void back()int k =0 ;while(k>=0)if(k==n) //am gasit o solutie si am afisat-o

k-- ; //pasul "back" dupa gasirea solutieielseif(x[k]<7)//in vectorul solutie x[k] este coloanax[k]++ ;if( cont(k) ) //verifica conditia ce continuarek++;//cautam locul urmatorului pionif (k!=n)//daca nu am ajuns la solutie

//verificam daca suntem la al doilea pion pe aceeasi linie.//Pentru usurinta verificarilor de asezare//(pentru primul pion deja verificasem) am presupus ca cei doi pioni//de pe aceeasi linie se afla pe nivelele consecutive in stiva//k si k+1 unde k este par, si initial pe aceeasi coloana.

if (((k-1)%2==0))x[k]=x[k-1];

elseretSol();//afisarea rezultatului

elsex[k--] = -1 ;

//intoarcere cu o pozitie pentru ca nu se poate continua

Page 151: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

8.5. PROBLEME PROPUSE 151

public abstract boolean cont( int k ) ;public abstract void retSol() ;

Figura 8.5 Clasa Backtracking

import java.io.* ;

public class Pions extends BackTrackingpublic boolean cont(int k)int l=0,m=0;for(int i=0; i<k; ++i)if (x[i]==x[k]) //aflam citi pioni se mai afla pe aceeasi coloanal++; //cu ultimul pion introdus

if ((k%2==1)&&(x[k]==x[k-1]))return false;int e=k/2-1,f=x[k]-1;while ((e>-1)&&(f>-1))

//aflam daca pe orice paralela la diagonala principala//se afla mai mult de 2 pioni dupa introd pionului de pe nivelul kif ((x[e*2]==f)||(x[2*e+1]==f))

//x[e*2]este primul element de pe linia e//iar x[e*2+1]este al doilea element de pe linia e

m++;e--;f--;if ((l<2) &&(m<2))return true;//se poate continua daca pe aceeasi coloana sau

//paralela cu diagonala pricipala nu se afla mai mult de doi pionielsereturn false;

public void retSol()//afisarea rezultatuluiSystem.out.println();for(int l=0;l<8;l++)

Page 152: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

152 CAPITOLUL 8. METODA BACKTRACKING

for(int j=0; j<=7; ++j)if((j==x[2*l])||(j==x[2*l+1]))System.out.print("X ");//daca pe linia l unul dintre pioni este//pe coloana j atunci il afisam (marcat cu X)

elseSystem.out.print("O ") ;System.out.println() ;

public Pions(int n)super(n,n) ;

//apelarea constructorului din clasa de baza backtracking;

public static void main(String[] args)Pions pion = new Pions(16);

//constructorul este apelat pentru 16 pionipion.back();

//ce vor fi amplasati pe o tabla de sah obisnuita de dimensiune 8x8

Figura 8.6 Clasa Pions

Page 153: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

Capitolul 9

Divide et Impera

In acest capitol vom studia o alta metoda fundamentala de elaborare a algoritmilor, nu-mita Divide et Impera. Ca si Backtracking, Divide et Impera se bazeaza pe un principiuextrem de simplu: descompunem problema ın doua (sau mai multe) subprobleme de di-mensiuni mai mici; rezolvam subproblemele, iar solutia pentru problema initiala se obtinecombinand solutiile subproblemelor ın care a fost descompusa. Rezolvarea subproblemelorse face ın acelasi mod cu problema initiala. Procedeul se reia pana cand subproblemeledevin atat de simple ıncat admit o rezolvare imediata.Inca din descrierea globala a acestei tehnici s-au strecurat elemente de recursivitate. Pentrua putea ıntelege mai bine aceasta metoda de elaborare a algoritmilor care este eminamenterecursiva, vom prezenta ın sectiunea 9.1 cateva elemente fundamentale referitoare la re-cursivitate. Continuam apoi ın sectiunea 9.2 cu prezentarea generala a metodei, urmatade rezolvarea anumitor probleme de Divide et Impera deosebit de importante: cautarebinara, sortarea prin interclasare, sortarea rapida si evaluarea expresiilor aritmetice.

9.1 Notiuni elementare referitoare la recursivitate

In acest paragraf vom reaminti cateva elemente esential referitoare la recursivitate. Ceicare stapanesc deja acest mecanism, pot sa treaca direct la prezentarea metodei Divide etImpera din paragraful 9.2.Avand ın vedere faptul ca recursivitatea este un mecanism de programare general, care nutine doar de limbajul Java, prezentarea facuta va folosi si limbajul pseudocod la descriereaalgoritmilor recursivi, pentru a nu ıncarca prezentarea cu detalii de implementare.

9.1.1 Functii recursive

Recursivitatea este un concept care deriva ın mod direct din notiunea de recurenta matem-atica. Recursivitatea este un instrument elegant si puternic pe care programatorii ıl au ladispozitie pentru a descrie algoritmii. Este interesant de retinut faptul ca programatoriiobisnuiau sa utilizeze recursivitatea pentru a descrie algoritmii cu mult ınainte ca limba-jele de programare sa suporte implementarea directa a acestui concept.Din punct de vedere informatic, o subrutina (procedura sau functie) recursiva este osubrutina care se autoapeleaza. Sa luam ca exemplu functia factorial, a carei definitiematematica recurenta este:

153

Page 154: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

154 CAPITOLUL 9. DIVIDE ET IMPERA

Fact(n) =

n ∗ Fact(n− 1) pentru n ≥ 11 pentru n = 0

Din exemplul de mai sus se observa ca factorialul este definit functie de el ınsusi, darpentru o valoare a parametrului mai mica cu o unitate. Iata acum care este implementarearecursiva a factorialului, folosind o functie algoritmica (stanga) si implementarea Java(dreapta):

functie fact(n) public static long fact(int n)daca n=0 atunci

fact← 1 if( n==0 )altfel return 1 ;

fact← n*fact(n-1) elsereturn return n*fact(n-1);

Se observa ca functia de mai sus nu este decat o ”traducere” aproape directa a formuleimatematice anterioare. Trebuie sa remarcam ca, asa cum vom vedea ın continuarea acestuicapitol, la baza functionarii acestor functii sta un mecanism foarte precis, care nu este atatde trivial cum ar parea la prima vedere.Sa luam ca al doilea exemplu, calculul celebrului sir al lui Fibonacci, care este definitrecurent astfel:

Fib(n) =

Fib(n− 1) + Fib(n− 2) pentru n > 1n pentru n = 0, 1

Implementarea ın pseudocod, respectiv Java a sirului lui Fibonacci este:

functie fib(n) public static long fib(int n)daca n=0 sau n=1 atunci

fib← n if( n==0 || n==1 )altfel return n ;

fib← fib(n-1)+fib(n-2) elsereturn return fib(n-1)+fib(n-2);

Se observa ca ın ambele exemple am ınceput cu asa numita conditie de terminare:

daca n=0 sau n=1 atunciFib←n

care corespunde cazului ın care nu se mai fac apeluri recursive. O functie recursiva carenu are conditie de terminare va genera apeluri recursive interminabile, care se soldeazainevitabil cu eroarea java.lang.StackOverflowError (depasire de stiva, deoarece asa cumvom vedea, fiecare apel recursiv presupune salvarea anumitor date pe stiva, iar stiva are odimensiune finita). Conditia de terminare ne asigura de faptul ca atunci cand parametrulfunctiei devine suficient de mic, nu se mai realizeaza apeluri recursive si functia este cal-culata direct.Ideea fundamentala care sta la baza ıntelegerii profunde a mecanismului recursivitatii este

Page 155: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

9.1. NOTIUNI ELEMENTARE REFERITOARE LA RECURSIVITATE 155

aceea ca ın esenta, un apel recursiv nu difera cu nimic de un apel de functie obisnuit.Pentru a veni ın sprijinul acestei afirmatii trebuie sa studiem mai ın amanuntime ce sepetrece ın cazul unui apel de functie.Se cunoaste faptul ca ın situatia ın care compilatorul ıntalneste un apel de functie, acestapreda controlul executiei functiei respective, dupa care se revine la urmatoarea instructiunede dupa apel. ıntrebarea care apare ın mod firesc este: de unde stie compilatorul unde sase ıntoarca la terminarea functiei? De unde stie care au fost valorile variabilelor ınaintede a se preda controlul functiei? Raspunsul este simplu: ınainte de a realiza un apelde functie compilatorul salveaza complet starea programului (linia de la care s-a realizatapelul, valorile variabilelor locale, valorile parametrilor de apel) pe stiva, urmand ca larevenirea din subrutina sa reıncarce starea care a fost ınainte de apel de pe stiva.Pentru exemplificare sa consideram urmatoarea procedura (nerecursiva) care afiseaza olinie a unei matrice. Atat linia care trebuie afisata, cat si matricea sunt transmise caparametru:

procedura AfisLin(a: tmatrice; n,lin: integer)pentru i = 1,n

scrie a[lin,i]return

Procedura AfisLin este apelata de procedura AfisMat descrisa mai jos, care afiseaza liniecu linie o ıntreaga matrice pe care o primeste ca parametru:

procedura AfisMat(a: tmatrice; n: integer)pentru i = 1,n

AfisLin(a,n,i)return

Sa presupunem ca procedura AfisMat este apelata ıntr-un program astfel:

...AfisMat(a,5)...

pentru a afisa o matrice de dimensiuni 5× 5.In momentul ın care compilatorul ıntalneste acest apel, el salveaza pe stiva linia de la cares-a facut apelul (sa spunem 2181), valoarea matricei a si alte variabile locale declarate ınprogram:

2181;AfisMat(n, a); . . .

Figura 9.1 Stiva programului dupa apelul procedurii AfisMat

Page 156: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

156 CAPITOLUL 9. DIVIDE ET IMPERA

Controlul va fi apoi preluat de catre procedura AfisMat, care intra ın ciclul pentru cuapelul: AfisLin(a,n,i) aflat sa zicem la linia 2198.In acest moment controlul va fi preluat de catre procedura AfisLin, dar nu ınainte de aadauga la varful stivei linia de la care s-a facut apelul, valorile parametrilor si a variabileilocale i:

2198;AfisLin(n, a, i); i = 1; . . .2181;AfisMat(n, a); . . .

Figura 9.2 Continutul stivei programului dupa apelul procedurii AfisLin

Procedura AfisLin va tipari prima linie a matricei, dupa care executia ei se ıncheie. ınacest moment compilatorul consulta varful stivei pentru a vedea unde trebuie sa revina sicare au fost valorile parametrilor si variabilelor locale ınainte de apel. Variabila i devine2, si din nou se apeleaza procedura AfisLin etc.Remarcam aici faptul ca atat procedura AfisMat cat si procedura AfisLin utilizeaza o vari-abila locala numita i. Nu poate exista nici o confuzie ıntre cele doua variabile, deoareceın momentul executiei lui AfisLin, valoarea variabilei i din AfisMat este salvata pe stiva.Sa vedem acum evolutia stivei program ın cazul calculului recursiv al lui Fact(5). Pre-supunem ca linia 2145 are loc apelul recursiv: fact← n ∗ fact(n− 1).Pentru a realiza ınmultirea respectiva, trebuie ca ıntai sa se calculeze Fact(n-1). Cum nare valoarea 5, pe stiva se va depune Fact(4). Abia dupa ce valoarea lui Fact(4) va ficalculata se poate calcula valoarea lui Fact(5). Calculul lui Fact(4) implica ınsa calculullui Fact(3), care implica la randul lui calculul lui Fact(2), Fact(1), Fact(0). Calculul luiFact(0) se realizeaza prin atribuire directa, fara nici un apel recursiv:

daca n=0 atuncifact← 1

ın acest moment, stiva programului contine toate apelurile recursive realizate pana acum:

2145;Fact(0);2145;Fact(1);2145;Fact(2);2145;Fact(3);2145;Fact(4);xxxx;Fact(5);

Figura 9.3 Continutul stivei programului cand n devine 0, ın cazul calcului lui Fact(5).Se presupune ca linia cu apelul recursiv este situata la adresa 2145.

Page 157: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

9.1. NOTIUNI ELEMENTARE REFERITOARE LA RECURSIVITATE 157

Fact(1) fiind calculat se poate reveni la calculul ınmultirii 2*fact(1) = 2, apoi, Fact(2)fiind calculat se revine la calculul ınmultirii 3*fact(2)=6 s.a.m.d. pana se calculeaza5*fact(4)=120 si se revine ın programul apelant.Sa vedem acum modul ın care se realizeaza calculul recursiv al sirului lui Fibonacci. Vomvedea ca timpul de calcul al acestei recurente este incomparabil mai mare fata de cal-culul factorialului. Sa presupunem ca functia fib se apeleaza cu parametrul n = 3.In aceasta situatie, se depune pe stiva apelul fib(3) ımpreuna cu linia de unde s-a re-alizat apelul (de exemplu, 2160). In linia 2160 a procedurii are loc apelul recursiv:fib← fib(n− 1) + fib(n− 2) care ın cazul nostru, n fiind 3, presupune calcularea sumeifib(2) + fib(1). Aceasta suma nu poate fi calculata ınainte de a-l calcula pe fib(2). Cal-culul lui fib(2) presupune calcularea sumei fib(1) + fib(0). fib(1) si fib(0) se calculeazadirect la urmatorul apel recursiv, dupa care se calculeaza suma lor, fib(2) = 2. Abiaacum se revine la suma fib(2) + fib(1) si se calculeaza fib(1), dupa care se revine si secalculeaza fib(3).Modul de calcul al lui fib(n) recursiv se poate reprezenta foarte sugestiv arborescent.Radacina arborelui este fib(n), iar cei doi fii sunt apelurile recursive pe care fib(n) legenereaza, si anume fib(n− 1) si fib(n− 2). Apoi se reprezinta apelurile recursive gener-ate de fib(n− 2) s.a.m.d:

Fib(n)"!#

Fib(n-1)

&%'$

@@

@

Fib(n-2)

&%'$

Fib(n-2)

&%'$

Fib(n-3)

&%'$@

@@

Fib(n-3)

&%'$

Fib(n-4)

&%'$

Figura 9.4 Reprezentarea arborescenta a apelurilor recursive din sirul lui Fibonacci

Din figura de mai sus se observa ca anumite valori ale sirului lui Fibonacci se calculeazade mai multe ori. fib(n) si fib(n−1) se calculeaza o data, fib(n−2) se calculeaza de douaori, fib(n− 3) de 3 ori s.a.m.d.

9.1.2 Recursivitatea nu ınseamna recurenta

Implementarea recursiva a functiilor recurente este usor de ınteles, datorita mecanismuluisimplu de transpunere a recurentei ıntr-o functie recursiva. Totusi, recursivitatea nu selimiteaza doar la implementarea recurentelor matematice. Putem defini la fel de bine

Page 158: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

158 CAPITOLUL 9. DIVIDE ET IMPERA

si operatii recursive. O operatie recursiva este definita functie de ea ınsasi, pentru oproblema de dimensiune mica. De exemplu, operatia de inversare a n caractere se poatedefini recursiv astfel: se extrage primul caracter din sir, apoi se inverseaza cele n − 1caractere ramase dupa care se adauga la final caracterul extras. Acest principiu ıl aplicamın exemplul care urmeaza.

Exemplul 9.1 Sa se scrie o functie care citeste o secventa de caractere pana ce ıntalnestecaracterul ”.”, dupa care afiseaza caracterele ın ordine inversa.

Rezolvarea acestei probleme se poate formula recursiv astfel: inversarea caracterelor unuisir de n elemente implica inversarea caracterelor ramase dupa citirea primului caracter, siscrierea ın final a primului caracter:

Inv(n) = Citeste(a) + Inv(n− 1) + Scrie(a)

ın consecinta procedura de inversare va avea forma (parametrul n a fost eliminat, el fiinddat ın formula doar pentru claritate):

functie inversare public static void inversare( )citeste a daca a <>′ .′atunci inversare char a;scrie inversare a=Reader.readChar();

return if(a!=’.’)

inversare();System.out.print(a);

Este important de notat ca pentru ca functia sa functioneze corect, variabila a trebuiedeclarata ca variabila locala; astfel, toate valorile citite vor fi salvate pe stiva, de unde vorfi extrase succesiv (ın ordinea inversa citirii) dupa ıntalnirea caracterului ”.”.

Exemplul 9.2 Transformarea unui numar din baza 10 ıntr-o baza b, mai mica decat 10.

Sa ne reamintim algoritmul clasic de trecere din baza 10 ın baza b. Numarul se ımpartela b si se retine restul. Catul se ımparte din nou la b si se retine restul ... pana cand catuldevine mai mic decat b. Rezultatul se obtine prin scrierea ın ordine inversa a resturilorobtinute.Formularea recursiva a acestei rezolvari pentru trecerea unui numar n ın baza b este:

Transf(n) =

Transf(n div b) + Scrie(n mod b) pentru n ≥ b− pentru n < b

Procedura care realizeaza acest cod este descrisa astfel:

Page 159: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

9.2. PREZENTAREA METODEI DIVIDE ET IMPERA 159

functie transform(n:integer) public static void transform(int n)rest=n mod b daca n < b atunci transform(n div b) int rest=n%b;scrie rest if(n < b)

return transform(n/b);

System.out.print(rest);

De remarcat ca ın aceasta functie variabila rest trebuie sa fie declarata local, pentru a fisalvata pe stiva, ın timp ce variabila b este bine sa fie declarata global, deoarece valoareaei nu se modifica, salvarea ei pe stiva ocupand spatiu inutil.

9.2 Prezentarea metodei Divide et Impera

Divide et Impera este o metoda speciala prin care se pot aborda anumite categorii deprobleme. Ca si celelalte metode de elaborare a algoritmilor, Divide et Impera se bazeazape un principiu extrem se simplu: se descompune problema initiala ın doua (sau mai multe)subprobleme de dimensiune mai mica, care se rezolva, dupa care solutia problemei initialese obtine combinand solutiile subproblemelor ın care a fost descompusa. Se presupune cafiecare dintre problemele ın care a fost descompusa problema initiala se poate descompuneın alte subprobleme, la fel cum a fost descompusa si problema initiala. Procedeul dedescompunere se repeta pana cand, dupa descompuneri succesive, se ajunge la problemede dimensiune mica, pentru care exista rezolvare directa.Evident, nu orice gen de problema se preteaza la a fi abordata cu Divide et Impera. Dindescrierea de mai sus reiese ca o problema abordabila cu aceasta metoda trebuie sa aibadoua proprietati:

1. Sa se poata descompune ın subprobleme

2. Solutia problemei initiale sa se poata construi simplu pe baza solutiei subproblemelor

Modul ın care metoda a fost descrisa, conduce ın mod natural la o implementare recursiva,avand ın vedere faptul ca si subproblemele se rezolva ın acelasi mod cu problema initiala.Iata care este forma generala a unei functii Divide et Impera:

functie DivImp(P: Problema)daca Simplu(P) atunci

RezolvaDirect(P) ;altfel

Descompune(P, P1, P2) ;DivImp(P1) ;DivImp(P2) ;Combina(P1, P2) ;

return

Page 160: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

160 CAPITOLUL 9. DIVIDE ET IMPERA

In consecinta, putem spune ca abordarea Divide et Impera implica trei pasi la fiecare nivelde recursivitate:

1. Divide problema ın doua subprobleme.

2. Stapaneste (Cucereste) cele doua subprobleme prin rezolvarea acestora ın mod re-cursiv.

3. Combina solutiile celo doua subprobleme ın solutia finala pentru problema initiala.

9.3 Cautare binara

Cautarea binara este o metoda eficienta de regasire a unor valori ıntr-o secventa ordonata.Desi este trecuta ın acest capitol, cautarea binara nu este un exemplu tipic de problemaDivide et Impera, deoarece ın cazul ei se rezolva doar una din cele doua subprobleme, decilipseste faza de recombinare a solutiilor. Enuntul problemei de cautare binara este:

Se da un vector cu n componente (ıntregi), ordonate crescator si un numar ıntreg. Sa sedecida daca se gaseste ın vectorul dat, si, ın caz afirmativ, sa se furnizeze indicele pozitieipe care se gaseste.

O rezolvare imediata a problemei presupune parcurgerea secventiala a vectorului dat, panacand p este gasit, sau am ajuns la sfarsitul vectorului. Aceasta rezolvare ınsa nu folosestefaptul ca vectorul este sortat.Cautarea binara procedeaza ın felul urmator: se compara p, cu elementul din mijloculvectorului; daca p este egal cu acel element, cautarea s-a ıncheiat. Daca este mai mare, secauta doar ın prima jumatate, iar daca este mai mic, se cauta doar ın a doua jumatate.Cititorul atent a observat cu siguranta ca ın aceasta situatie problema nu se descompuneın doua subprobleme care se rezolva, dupa care se construieste solutia, ci se reduce la unasau la alta din subprobleme. Cei trei pasi ai lui Divide et Impera sunt ın aceasta situatie:

1. Divide: ımparte sirul de n elemente ın care se realizeaza cautarea ın doua siruri cun/2 elemente.

2. Stapaneste: Cauta ın una dintre cele doua jumatati, functie de valoarea elementuluidin mijloc.

3. Combina: Nu exista.

Iata care este functia care realizeaza cautarea elementului x ın sirul a, ıntre indicii low sihigh .

public static int binarySearch(int[] a, int x, int low, int high)if(low <= high)int middle = (low + high)/2 ;if( x == a[middle] )

Page 161: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

9.4. SORTAREA PRIN INTERCLASARE (MERGESORT) 161

return middle ;elseif( x < a[middle] )return binarySearch(a, x, low, middle -1 ) ;

elsereturn binarySearch(a, x, middle+1, high) ;

return -1 ;

Pozitia pe care se gaseste elementul x ın sirul a este data de apelul:

poz = binarySearch(a,x,0,a.length-1)

9.4 Sortarea prin interclasare (MergeSort)

Sortarea prin interclasare este, alaturi de sortarea rapida (QuickSort) si sortarea cu ansam-ble (HeapSort), una dintre metodele eficiente de ordonare a elementelor unui sir. Enuntulproblemei este urmatorul:

Sa se ordoneze crescator un vector cu n componente ıntregi.

Principiul de rezolvare este urmatorul: se ımparte vectorul ın doua parti egale si sesorteaza fiecare jumatate, apoi se interclaseaza cele doua jumatati. Descompunerea ındoua jumatati se realizeaza pana cand se ajunge la vectori cu un singur element, carenu mai necesita sortare. Algoritmul de sortare prin interclasare urmeaza ındeaproapeparadigma Divide et Impera. In mare, modul ei de operare este:

1. Divide: ımparte sirul de n elemente care urmeaza a fi sortat ın doua siruri cu n/2elemente

2. Stapaneste: Sorteaza recursiv cele doua subsiruri utilizand sortarea prin interclasare

3. Combina: Interclaseaza subsirurile sortate pentru a obtine rezultatul final.

Procedura MergeSort de mai jos implementeaza algoritmul de sortare prin interclasare.Apelul initial al functiei este:

mergeSort(a,0,a.length-1)

unde n este numarul de elemente al tabloului a.

Page 162: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

162 CAPITOLUL 9. DIVIDE ET IMPERA

public static void mergeSort(int[] a, int low, int high)if(low<high)int mid=(low+high)/2;mergeSort(a,low,mid);mergeSort(a,mid+1,high);intercls(a,low,mid,high);

Functia de interclasare ın acest caz este analoaga cu functia de interclasare obisnuitaa doua siruri, diferenta constand ın faptul ca acum se interclaseaza doua jumatati aleaceluiasi sir, iar rezultatul se va depune ın final tot ın sirul interclasat. Lasam scriereaacestei rutine ca exercitiu.

9.5 Sortarea rapida (QuickSort)

Sortarea rapida este, asa cum ıi spune si numele, cea mai rapida metoda de sortare cunos-cuta ın prezent . Exista foarte multe variante ale acestei metode, o parte dintre ele avanddoar rolul de a micsora timpul de executie ın cazul cel mai nefavorabil. Vom prezentaaici varianta clasica, despre care veti remarca cu surprindere ca este neasteptat de simpla.Enuntul problemei este identic cu cel de la sortarea prin interclasare.

Sa se ordoneze crescator un vector de numere ıntregi.

Metoda de sortare rapida prezentata ın acest paragraf este dintr-un anumit punct devedere complementara metodei Mergesort. Diferenta ıntre cele doua metode este data defaptul ca ın timp ce la Mergesort mai ıntıi vectorul se ımpartea ın doua parti dupa care sesorta fiecare parte si apoi se interclasau cele doua jumatati, la Quicksort ımpartirea se faceın asa fel ıncat cele doua siruri sa nu mai trebuiasca a fi interclasate dupa sortare, adicaprimul sir sa contina doar elemente mai mici decat al doilea sir. Cu alte cuvinte, ın cazullui Quicksort etapa de recombinare este triviala, deoarece problema este astfel ımpartita ınsubprobleme astfel ıncat sa nu mai fie necesara interclasarea sirurilor. Etapele lui Divideet Impera pot fi descrise ın aceasta situatie astfel:

1. Divide: Imparte sirul de n elemente care urmeaza a fi sortat ın doua siruri, astfelıncat elementele din primul sir sa fie mai mici decat elementele din al doilea sir

2. Stapaneste: Sorteaza recursiv cele doua subsiruri utilizand sortarea rapida

3. Combina: Sirul sortat este obtinut din concatenarea celor doua subsiruri sortate.

Functia care realizeaza ımpartirea ın subprobleme (astfel ıncat elementele primului sir safie mai mici decat elementele celui de-al doilea) se datoreaza lui C. A. Hoare, care a gasito metoda de a realiza aceasta ımpartire (numita partitionare) ın timp liniar.Procedura de partitionare rearanjeaza elementele tabloului functie de primul element,numit pivot, astfel ıncat elementele mai mici decat primul element sunt trecute ın stanga

Page 163: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

9.5. SORTAREA RAPIDA (QUICKSORT) 163

lui, iar elementele mai mari decat primul element sunt trecute ın dreapta lui. De exemplu,daca avem vectorul:

a = (7, 8, 5, 2, 3),

atunci procedura de partitionare va muta elementele 5,2 si 3 ın stanga lui 7, iar 8 va fiın dreapta lui. Cum se realizeaza acest lucru? Sirul este parcurs simultan de doi indici:primul indice, low, pleaca de la primul element si este incrementat succesiv, al doilea indice,high, porneste de la ultimul element si este decrementat succesiv. In situatia ın care a[low]este mai mare decat a[high], elementele se interschimba. Partitionarea este ıncheiata ınmomentul ın care cei doi indici se ıntalnesc (devin egali) undeva ın interiorul sirului. Laun pas al algoritmului, fie se incrementeaza low, fie se decrementeaza high; ıntotdeaunaunul dintre cei doi indici, low sau high, este pozitionat pe pivot. Atunci cand low indicapivotul, se decrementeaza high, iar atunci cand high indica pivotul se incrementeaza low.Iata cum functioneaza partitionarea pe exemplul de mai sus. La ınceput, low indica primulelement, iar high indica ultimul element:

a = (7, 8, 5, 2,3)↑ ↑

low high

Deoarece a[low] > a[high] elementele 7 si 3 se vor interschimba. Dupa interschimbare,pivotul va fi indicat de high, deci low va fi incrementat:

a = (3, 8, 5, 2,7)↑ ↑

low high

Din nou avem a[low] > a[high], elementele 8 si 7 se vor interschimba. Dupa interschimbare,pivotul va fi indicat de low, deci high va fi decrementat:

a = (3,7, 5, 2, 8)↑ ↑

low high

Din nou avem a[low] > a[high], elementele 7 si 2 se vor interschimba. Dupa interschimbare,pivotul va fi indicat de high, deci low va fi incrementat:

a = (3, 2, 5,7, 8)↑ ↑

low high

De data aceasta avem a[low] <= a[high], deci low va fi incrementat din nou, fara a serealiza interschimbari.In acest moment low si high s-au suprapus (au devenit egale), deci partitionarea s-aıncheiat. Pivotul este pe pozitia a 4-a, care este de fapt si pozitia lui finala ın sirulsortat.Functia de partitionare de mai jos primeste ca parametri limitele inferioara, respectiv su-perioara ale sirului care se partitioneaza si returneaza pozitia pe care se afla pivotul ınfinalul partitionarii. Pozitia pivotului este importanta deoarece ne da locul ın care sirulva fi despartit ın doua subsiruri.

Page 164: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

164 CAPITOLUL 9. DIVIDE ET IMPERA

public static int partitionare(int low, int high)boolean pozPivot=false;//variabila care ne spune daca low indica pivotulwhile(low<high)//indicii de parcurgere nu s-au suprapusif(a[low]>a[high])interschimba(a,low,high);pozPivot= !pozPivot;//alt indice indica acum pivotulpozPivot?low++:high--;return(low);//se returneaza pozitia pivotului

Doua observatii importante merita facute asupra acestei functii:

1. Variabila pozPivot poate lua valoarea false daca pivotul este indicat de low sau truedaca pivotul este indicat de high. Atribuirea pozP ivot =!pozP ivot are ca efectschimbarea starii acestei variabile din false ın true sau invers.

2. Functia se foloseste ın mod inteligent de transmiterea prin valoare a parametrilor,deoarece modifica variabilele low si high, fara ca aceasta modificare sa afecteze pro-gramul principal.

Procedura de sortare propriu-zisa, respecta structura Divide et Impera obisnuita, doar cafunctia de recombinare a solutiilor nu mai este necesara, deoarece am realizat partitionareaınainte de apel:

public static void quickSort(int low,int high)if(low<high)//daca subsirul mai are cel putin doua elementeint mid=partitionare(low, high);//partitioneazaquickSort(low, mid-1);//sorteaza prima jumatatequickSort(mid+1, high);//sorteaza a doua jumatate

9.6 Expresii aritmetice

Se da o expresie aritmetica ın care operanzii sunt simbolizati prin litere mici (de la a laz), iar operatorii sunt ’+’, ’-’, ’/’, si ’*’ cu semnificatia cunoscuta. Se cere sa se scrie unprogram care transforma expresia ın forma poloneza postfixata.

Reamintim faptul ca forma poloneza postfixata (Lukasiewicz) este obtinuta prin scriereaoperatorului dupa cei doi operanzi, si nu ıntre ei. Aceasta forma are avantajul ca nu

Page 165: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

9.6. EXPRESII ARITMETICE 165

necesita paranteze pentru a schimba prioritatea operatorilor. Ea este utilizata adeseori ıninformatica pentru a evalua expresii.

Exemplul 9.3 1. a+b se scrie ab+

2. a*(b+c) se scrie abc+*

3. (a+b)*(c+d) se scrie ab+cd+*

Unul dintre cei mai simpli algoritmi de a trece o expresie ın forma poloneza consta ın acauta care este operatorul din expresie cu prioritatea cea mai mica, si de a aseza acestoperator ın finalul expresiei, urmand ca prima parte a formei poloneze sa fie formata dintransformarea expresiei din stanga operatorului, iar a doua parte a formei poloneze sa fieformata din transformarea expresiei din dreapta operatorului.Cele doua subexpresii urmeaza a se trata ın mod analog, pana cand se ajunge la o subex-presie de lungime 1, care va fi obligatoriu un operand si care nu mai necesita transformare.Schematic, daca avem expresia:

E=E1 op E2

unde E1 si E2 sunt subexpresii, iar op este operatorul cu prioritatea cea mai mica (decioperatorul unde expresia se poate ”rupe” ın doua), atunci forma poloneza a lui E, notataPol(E), se obtine astfel:

Pol(E)=Pol(E1) Pol(E2) op .

Expresia de mai sus exprima faptul ca forma poloneza postfixata a lui E se obtine prinscrierea ın forma poloneza postfixata a celor doua subexpresii, urmate de operatorul carele separa. Expresia de mai sus este o expresie recursiva specifica tehnicii Divide et Impera.Etapele sunt ın aceasta situatie (ilustrate ın metoda din Figura9.6):

1. Divide: ımparte expresia aritmetica ın doua subexpresii legate printr-un operator deprioritate minima.

2. Stapaneste: Transforma recursiv ın forma poloneza cele doua subexpresii.

3. Combina: Scrie cele doua subexpresii ın forma poloneza urmate de operatorul carele leaga.

Functia de trecere ın forma poloneza (Figura9.6) primeste ca parametru indicele infe-rior si superior reprezentand limitele ıntre care se ıncadreaza subexpresia ın expresia faraparanteze. Sirul original este continut ın string-ul a. Functia eliminare paranteze trans-forma sirul original ıntr-un sir fara paranteze pe baza prioritatii operatorilor. Functiapolonez returneaza un sir de caractere care contine expresia ın forma poloneza:

1. public void eliminare_paranteze()2. 3. int[] p=new int[a.length()];4. int i,j=0;5. for(i=0;i<a.length();i++)

Page 166: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

166 CAPITOLUL 9. DIVIDE ET IMPERA

6. 7. switch(a.charAt(i))8. 9. case ’(’:10. j=j+10;11. p[i]=0;12. break;13. case ’)’:14. j=j-10;15. p[i]=0;16. break;17. case ’+’:18. case ’-’:19. p[i]=1+j;20. break;21. case ’*’:22. case ’/’:23. p[i]=10+j;24. break;25. default:26. p[i]=1000;27. 28. 29. j=0;30. for(i=0;i<a.length();i++)31. 32. if(p[i]!=0)33. 34. eps=eps+a.charAt(i);35. epf[j]=p[i];36. j++;37. 38. 39.

Figura 9.5 Metoda ce transforma expresia data ıntr-o expresie fara paranteze

In sirul p declarat ın linia 3 vom trece valori ce corespund prioritatilor membrilor expresiei.Calculul prioritatilor se face astfel:

• prioritatea initiala a operatorilor ”+” si ”-” este 1;

• prioritatea initiala a operatorilor ”*” si ”/” este 10;

• la prioritatea unui operator se aduna 10 pentru fiecare pereche de paranteze ıntrecare se gaseste;

• prioritatea unui operand este 1000;

Page 167: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

9.7. PROBLEME PROPUSE 167

• prioritatea parantezelor este 0.

In liniile 29-38 se creaza noua expresie fara paranteze ın variabila de tip referinta eps printestarea valorilor lui p. In paralel se va crea si referinta epf ce va contine toate valorile dinp diferite de 0.

1. public String polonez(int li,int ls)2. 3. String s="";4. int i=li,j,min;5. for(min=epf[li], j=li; j<=ls;j++)6. 7. if(min>epf[j])8. 9. min=epf[j];10. i=j;11. 12. 13. if(li==ls)14. 15. s=s+eps.charAt(i);16. return s;17. 18. else19. return polonez(li,i-1)+polonez(i+1,ls)+eps.charAt(i);20.

Figura 9.6 Metoda ce transforma expresia fara paranteze ıntr-o forma poloneza

Linia 12 a functiei testeaza daca nu s-a ajuns la o expresie care are un singur caracter.In caz afirmativ, valoarea functiei este chiar caracterul (operandul) respectiv. Altfel, asacum spuneam la descrierea problemei, vom ımparti expresia ın doua subexpresii legate prinoperatorul de prioritate minima. Acesta va fi gasit ın liniile 5-12 prin testarea valorilor luiepf.

9.7 Probleme propuse

1. Reprezentati evolutia stivei pentru procedurile si functiile recursive din acest capitol.

2. Calculati de cate ori se recalculeaza valoarea Fk ın cazul calcului recursiv al valoriiFn a sirului lui Fibonacci.

3. Sa se calculeze recursiv si iterativ cel mai mare divizor comun a doua numere dupaformulele (Euclid):

cmmdc(a, b) =

cmmdc(b, a mod b) pentru a mod b 6= 0b altfel

Page 168: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

168 CAPITOLUL 9. DIVIDE ET IMPERA

cmmdc(a, b) =

cmmdc(b, |a− b|) pentru a 6= bb altfel

4. Sa se calculeze recursiv si iterativ functia lui Ackermann, data de formula:

Ack(m,n) =

n + 1 pentru m = 0Ack(m− 1, 1) pentru n = 0Ack(m− 1, Ack(m,n− 1)) altfel

5. Sa se calculeze combinarile dupa formula de recurenta din triunghiul lui Pascal:

Ckn = Ck

n−1 + Ck−1n−1.

Calculati apoi combinarile dupa formula clasica:

Ckn = n!

k!(n−k)! .

Ce constati? Cum explicati ceea ce ati constat? (daca ati constat...)

6. Sa se calculeze recursiv si iterativ functia Manna-Pnueli, data de formula:

F (x) =

x− 1 pentru x ≥ 12F (F (x + 2)) altfel

7. Sa se scrie o functie care calculeaza recursiv suma cifrelor unui numar dupa formula:

S(n) =

n mod 10 + S(n div 10) pentru n > 00 pentru n = 0

8. Se considera doua siruri definite recurent dupa formulele:

an = an−1+bn−1

2 si bn =√

an−1bn−1, cu a0 = a si b0 = b.

Sa se scrie un program recursiv care calculeaza aceste siruri.

9. (Partitiile unui numar) Un numar natural n se poate descompune ca suma de-screscatoare de numere naturale. De exemplu, pentru numarul 4 avem descom-punerile 2+1+1 sau 3+1. Prin P (n, k) se noteaza numarul de posibilitati de a-ldescompune pe n ca suma (descrescatoare) de k numere. De exemplu, P (4, 2) =2 (4 = 3 + 1, 4 = 2 + 2). Numerele P (n, k) verifica relatia de recurenta:

P (n + k, k) = P (n, 1) + P (n, 2) + . . . + P (n, k) cu P (n, 1) = P (n, n) = 1.

Sa se calculeze numarul total de descompuneri ale numarului n.

Page 169: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

9.7. PROBLEME PROPUSE 169

10. Sa se scrie o functie care calculeaza maximul elementelor unui vector utilizandtehnica Divide et Impera.

Indicatie: Se ımparte vectorul ın doua jumatati egale, se calculeaza recursiv maximulcelor doua jumatati si se alege numarul mai mare.

11. (Turnurile din Hanoi) Se dau trei tije simbolizate prin literele A, B si C. Pe tijaA se afla n discuri de diametre diferite asezate descrescator ın ordinea diametrelor,cu diametrul maxim la baza. Se cere sa se mute discurile pe tija B respectandurmatoarele reguli:

(a) la fiecare pas se muta un singur disc

(b) nu este permisa asezarea unui disc cu diametru mai mare peste un disc cudiametrul mai mic

Indicatie: Formularea recursiva a solutiei este: se muta primele n-1 discuri de petija A pe tija C folosind ca tija intermediara tija B; se muta discul ramas pe A petija B; se muta discurile de pe tija C pe tija B folosind ca tija intermediara tija A.Parcurgerea celor trei etape permite definirea recursiva a sirului H(n,a,b,c) astfel:

H(n, a, b, c) =

ab daca n = 1H(n− 1, a, c, b), ab, H(n− 1, c, b, a) daca n > 1

Exemplu: Pentru n = 2 avem:

H(2, a, b, c) = H(1, a, c, b), ab, H(1, c, b, a) = ac, ab, cb

12. Scrieti un program ın care calculatorul sa ghiceasca cıt se poate de repede un numarnatural la care v-ati gandit. Numarul este cuprins ıntre 1 si 32.000. Atunci candcalculatorul propune un numar i se va raspunde prin 1, daca numarul este prea mare,2 daca numarul este prea mic si 0 daca numarul a fost ghicit.

Indicatie: Problema foloseste metoda cautarii binare prezentata ın acest capitol.

13. (Problema taieturilor) Se da o bucata dreptunghiulara de tabla de dimensiune l×h,avand pe suprafata ei n gauri de coordonate numere ıntregi (coltul din stanga jos altablei este considerat centrul sistemului de coordonate). Sa se determine care estebucata de arie maxima fara gauri care poate fi decupata din suprafata originala.Sunt permise doar taieturi orizontale sau verticale.

Indicatie: Se cauta ın bucata curenta prima gaura. Daca o astfel de gaura exista,atunci problema se ımparte ın alte patru subprobleme de acelasi tip. Daca suprafatanu are nici o gaura, atunci se compara suprafata ei cu suprafetele fara gaura obtinutepana la acel moment. Daca suprafata este mai mare, atunci se retin coordonateleei.Coordonatele gaurilor sunt date ın doi vectori xv si yv. Coordonatele dreptunghiurilor

Page 170: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

170 CAPITOLUL 9. DIVIDE ET IMPERA

ce apar pe parcursul problemei sunt retinute prin coltul stanga jos (x,y), lungime silatime (l,h).Pentru a se afla ın interioul unui dreptunghi o gaura trebuie sa indeplineasca simul-tan conditiile:

(a) xv(i) > x;

(b) xv(i) < x + l;

(c) yv(i) > y;

(d) yv(i) < y + h.

Taietura verticala prin aceasta gaura determina doua dreptunghiuri:

(a) x,y,xv(i)-x,h;

(b) xv(i),y,l+x-xv(i),h.

Taietura orizontala prin aceasta gaura determina alte doua dreptunghiuri:

(a) x,y,l,yv(i)-y;

(b) x,yv(i),l,h+y-yv(i).

Clasa Taieturi este prezentata ın continuare:

public class Taieturiint[] xv,yv;public T(int[] a, int[] b)xv=new int[a.length];xv=a;yv=new int[a.length];yv=b;

public void taietura_max(int x, int y, int l, int h, int[] max)int i=0;boolean gasit=false;while ((i<=xv.length-1)&&(!gasit))if((xv[i]>x)&&(xv[i]<x+l)&&(yv[i]>y)&&(yv[i]<y+h))gasit=true;elsei++;

if (gasit)taietura_max(x,y,xv[i]-x,h,max);

Page 171: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

9.7. PROBLEME PROPUSE 171

taietura_max(xv[i],y,l+x-xv[i],h,max);taietura_max(x,y,l,yv[i]-y,max);taietura_max(x,yv[i],l,h+y-yv[i],max);elseif((l*h)>(max[0]*max[1]))max[2]=x;max[3]=y;max[0]=l;max[1]=h;

Page 172: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

Capitolul 10

Algoritmi Greedy

Algoritmii aplicati problemelor de optimizare sunt, ın general, compusi dintr-o secventade pasi, la fiecare pas existand mai multe alegeri posibile. Un algoritm Greedy va alegela fiecare moment de timp solutia care pare a fi cea mai buna. Deci este vorba despre oalegere optima, facuta local, cu speranta ca ea va conduce la un optim global. Acest capi-tol trateaza probleme de optimizare care pot fi rezolvate cu ajutorul algoritmilor Greedy.Algoritmii Greedy conduc ın multe cazuri la solutii optime, dar nu ıntotdeauna... Insectiunea 10.1 vom prezenta mai ıntai o problema simpla dar netriviala, problema se-lectarii activitatilor, a carei solutie poate fi calculata ın mod eficient cu ajutorul uneimetode de tip Greedy. In sectiunea 10.2 se recapituleaza cateva elemente de baza alemetodei Greedy. Urmeaza apoi prezentarea catorva probleme specifice.Metoda Greedy este destul de puternica si se aplica cu succes unui spectru larg de prob-leme. Cursurile de teoria grafurilor contin mai multi algoritmi care pot fi priviti ca aplicatiiale metodei Greedy, cum ar fi algoritmii de determinare a arborelui partial de cost minim(Kruskal, Prim) sau algoritmul lui Dijkstra pentru determinarea celor mai scurte drumuripornind dintr-un varf.

10.1 Problema spectacolelor (selectarea activitatilor)

Primul exemplu pe care ıl vom considera este o problema de repartizare a unei resurse (osala de spectacol) mai multor activitati care concureaza pentru a obtine resursa respectiva(diferite spectacole care vor sa ruleze ın sala respectiva). Vom vedea ca un algoritm de tipGreedy reprezinta o metoda simpla si eleganta pentru programarea unui numar maxim despectacole care nu se suprapun (numite activitati compatibile reciproc).

Sa presupunem ca dispunem de o multime S = 1,2,...,n de n activitati (spectacole) caredoresc sa foloseasca o aceeasi resursa (sala de spectacole). Aceasta resursa poate fi folositade o singura activitate la un anumit moment de timp. Fiecare activitate i are un timp destart si si un timp de terminare ti, unde si ≤ ti. Daca este selectata activitatea i, ea sedesfasoara pe durata intervalului [si, ti) . Doua activitati sunt compatibile daca duratelelor de desfasurare sunt disjuncte. Problema spectacolelor (selectarii activitatilor) constadin selectarea unei multimi maximale de activitati compatibile ıntre ele.

172

Page 173: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

10.1. PROBLEMA SPECTACOLELOR (SELECTAREA ACTIVITATILOR) 173

Un algoritm Greedy pentru aceasta problema este descris de urmatoarea functie, prezen-tata ın pseudocod. Vom presupune ca spectacolele (adica datele de intrare) sunt ordonatecrescator dupa timpul de terminare:

t1 ≤ t2 ≤, . . . ,≤ tn.

In cazul ın care activitatile nu sunt ordonate astfel, ordonarea poate fi facuta ın timpulO(nlgn) (folosind Mergesort sau Quicksort). Algoritmul de mai jos presupune ca datelede intrare s si t sunt reprezentate ca vectori.

functie SELECT-SPECTACOLE-GREEDY(s, t)A← 1 //A este multimea spectacolelor care sunt selectatej ← 1 //j este indicele ultimului spectacol selectatpentru i = 2, n

daca si ≥ tj atunci //spectacolul i ıncepe dupa ce j s-a terminatA← A ∪ i //se adauga i la spect. selectatej ← i //ultimul spectacol este acum i

return A

In multimea A se introduc activitatile selectate. Variabila j identifica ultima activitateintrodusa ın A. Deoarece activitatile sunt considerate ın ordinea crescatoare a timpilor lorde terminare, tj va reprezenta ıntotdeauna timpul maxim de terminare a oricarei activitatidin A. Aceasta ınseamna ca:

tj = maxtk|k ∈ A

In liniile 2-3 din algoritm se selecteaza activitatea 1 (activitatea 1 trebuie planificata prima,deoarece se termina cel mai repede), se initializeaza A astfel ıncat sa nu contina decataceasta activitate, iar variabila j ia ca valoare aceasta activitate. In continuare ın ciclulpentru se considera pe rand fiecare activitate i se adauga multimii A daca este compatibilacu celelalte activitati deja selectate. Pentru a vedea daca activitatea i este compatibilacu toate celelalte activitati existente la momentul curent ın A, este suficient ca momentulde start si sa nu fie mai devreme decat momentul de terminare tj al activitatii cel mairecent adaugate multimii A. Daca activitatea i este compatibila, atunci ea este adaugatamultimii A, iar variabila j este actualizata. Procedura SELECT-SPECTACOLE-GREEDYeste foarte eficienta. Ea poate planifica o multime S de n activitati ın O(n), presupunandca activitatile au fost deja ordonate dupa timpul lor de terminare. Activitatea aleasade procedura SELECT-SPECTACOLE-GREEDY este ıntotdeauna cea cu primul timpde terminare care poate fi planificata legal. Activitatea astfel selectata este o alegere”Greedy” (lacoma) ın sensul ca, intuitiv, ea lasa posibilitatea celorlalte activitati ramasepentru a fi planificate. Cu alte cuvinte, alegerea Greedy maximizeaza cantitatea de timpneplanificata ramasa.

10.1.1 Demonstrarea corectitudinii algoritmului

Pana la acest moment, noi nu ne-am pus problema de a demonstra corectitudinea algorit-milor prezentati. Totusi, trebuie sa mentionam ca exista o ıntreaga ramura a algoritmicii

Page 174: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

174 CAPITOLUL 10. ALGORITMI GREEDY

care se ocupa exclusiv de demonstrarea corectitudinii algoritmilor. In general, demon-strarea corectitudinii unui algoritm nu este deloc simpla, de aceea am evitat sa prezentamdemonstratiile ın acest curs introductiv. Am ales sa demonstram corectitudinea acestuialgoritm, deoarece pe de o parte ea este ilustrativa pentru o ıntreaga clasa de probleme,iar pe de alta parte, demonstratia nu este dificila.

Teorema 10.1 Algoritmul SELECT-SPECTACOLE-GREEDY furnizeaza solutia optima(numar maxim de spectacole) pentru problema selectarii activitatilor.

Demonstratie: Fie S = 1, 2, . . . , n multimea activitatilor care trebuie planificate, ordo-nate crescator dupa timpul de terminare. In consecinta, activitatea 1 se termina cel maidevreme. Vom arata ca exista o solutie optima care ıncepe cu activitatea 1.Sa presupunem ca avem o solutie A ⊆ S optima pentru o instanta a problemei. Pentrusimplitate, presupunem ca activitatile din A sunt ordonate dupa timpul de terminare.Daca primul spectacol din A este chiar 1, atunci demonstratia este ıncheiata. Daca primulspectacol din A nu este 1, atunci ınlocuim primul spectacol cu spectacolul 1, obtinandevident o solutie corecta, deoarece spectacolul 1 se va termina mai devreme decat primulspectacol din A. Am aratat astfel ca exista o solutie optima pentru S care ıncepe cu ac-tivitatea 1.Mai mult, odata ce este facuta alegerea activitatii 1, problema se reduce la determinareasolutiei optime pentru activitatile din S care sunt compatibile cu activitatea 1. FieS′ = i ∈ S|si ≥ t1 multimea activitatilor care ıncep dupa ce 1 se termina. Rezultaca daca A este o solutie optima pentru S, atunci A′ = A−1 este o solutie optima pentruS’. Daca nu ar fi asa, atunci ar exista o solutie optima B’ pentru S’ care sa aiba mai multeactivitati decat A’. Adaugand activitatea 1 la B’, vom obtine o solutie pentru S cu maimulte activitati decat solutia A, ceea ce este absurd.Astfel, prin inductie dupa numarul de alegeri facute se poate arata ca alegand primulspectacol compatibil la fiecare pas, se obtine o solutie optima.

10.2 Elemente ale strategiei Greedy

Un algoritm Greedy determina o solutie optima a unei probleme ın urma unei succesiunide alegeri. La fiecare moment de decizie din algoritm este aleasa optiunea care pare afi cea mai potrivita. Aceasta strategie euristica nu produce ıntotdeauna solutia optima,dar exista si cazuri cand aceasta este obtinuta, cum ar fi ın cazul problemei selectarii ac-tivitatilor. In acest paragraf vom prezenta cateva proprietati generale ale metodei Greedy.Cum se poate decide daca un algoritm Greedy poate rezolva o problema particulara deoptimizare? In general nu exista o modalitate de a stabili acest lucru, dar exista anumitecaracteristici pe care le au majoritatea problemelor care se rezolva prin tehnici Greedy:proprietatea de alegere Greedy si substructura optima.In cazul general o problema de tip Greedy, are urmatoarele componente:

• o multime de candidati (lucrari de planificat, varfuri ale grafului, etc);

• o functie care verifica daca o anumita multime de candidati constituie o solutieposibila (nu neaparat optima) a problemei;

Page 175: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

10.2. ELEMENTE ALE STRATEGIEI GREEDY 175

• o functie care verifica daca o multime de candidati este fezabila, adica daca esteposibil sa completam aceasta multime astfel ıncat sa obtinem o solutie posibila (nuneaparat optima) a problemei (verifica daca planificarea este formata din activitaticare nu se suprapun etc.);

• o functie de selectie care indica la orice moment care este cel mai promitator dintrecandidatii ınca nefolositi (se alege spectacolul compatibil care se termina cel mairepede);

• o functie obiectiv care da valoarea unei solutii (numarul de lucrari planificate, tim-pul necesar executarii tuturor lucrarilor ıntr-o anumita ordine, lungimea drumuluipe care l-am gasit, etc.); aceasta este functia pe care urmarim sa o optimizam (min-imizam/maximizam).

Pentru a rezolva o problema de optimizare cu Greedy, cautam o solutie posibila care saoptimizeze valoarea functiei obiectiv. Un algoritm Greedy construieste solutia pas cu pas. Initial, multimea candidatilor selectati este vida. La fiecare pas, ıncercam sa adaugamacestei multimi cel mai promitator candidat, conform functiei de selectie. Daca, dupao astfel de adaugare, multimea de candidati selectati nu mai este fezabila, eliminam ul-timul candidat adaugat; acesta nu va mai fi niciodata considerat. Daca, dupa adaugare,multimea de candidati selectati este fezabila, ultimul candidat adaugat va ramane de acumıncolo ın ea. De fiecare data cand largim multimea candidatilor selectati, verificam dacaaceasta multime nu constituie o solutie posibila a problemei noastre. Daca algoritmulGreedy functioneaza corect, prima solutie gasita va fi totodata o solutie optima a proble-mei. (Solutia optima nu este ın mod necesar unica: se poate ca functia obiectiv sa aibaaceeasi valoare optima pentru mai multe solutii posibile.) Descrierea ın pseudocod a unuialgoritm Greedy general este:

functie greedy( C ) // C este multimea candidatilorS ← φ // S este multimea ın care construim solutiacat timp not solutie(S) si C 6= φ

x← un element din C care maximizeaza select(x)C ← C − xdaca fezabil(S ∪ x) atunci S ← S ∪ x

daca solutie(S) atuncireturn S

altfelreturn ”nu exista solutie”

Este de ınteles acum de ce un astfel de algoritm se numeste ”lacom” (am putea sa-l numimsi ”nechibzuit”). La fiecare pas, procedura alege cel mai bun candidat la momentul re-spectiv, fara sa-i pese de viitor si fara sa se razgandeasca. Daca un candidat este inclus ınsolutie, el ramane acolo; daca un candidat este exclus din solutie, el nu va mai fi niciodatareconsiderat. Asemenea unui ıntreprinzator care urmareste castigul imediat ın dauna celuide perspectiva, un algoritm Greedy actioneaza simplist. Totusi, ca si ın afaceri, o astfelde metoda poate da rezultate foarte bune tocmai datorita simplitatii ei.Functia de selectare este de obicei derivata din functia obiectiv; uneori aceste doua functii

Page 176: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

176 CAPITOLUL 10. ALGORITMI GREEDY

sunt chiar identice.Un exemplu simplu de algoritm Greedy este algoritmul folosit pentru rezolvarea urmatoareiprobleme.

Sa presupunem ca dorim sa dam restul unui client, folosind un numar cat mai mic demonezi.

In acest caz, elementele problemei sunt:

• Candidatii: multimea initiala de monezi de 1, 5 si 25 unitati, ın care presupunem cadin fiecare tip de moneda avem o cantitate nelimitata;

• O solutie posibila: valoarea totala a unei astfel de multimi de monezi selectate trebuiesa fie exact valoarea pe care trebuie sa o dam ca rest;

• O multime fezabila: valoarea totala a unei astfel de multimi de monezi selectate nueste mai mare decat valoarea pe care trebuie sa o dam ca rest;

• Functia de selectie: se alege cea mai mare moneda din multimea de candidati ramasa;

• Functia obiectiv: numarul de monezi folosite ın solutie; se doreste minimizarea aces-tui numar.

Se poate demonstra ca algoritmul Greedy va gasi ın acest caz mereu solutia optima - restulcu un numar minim de monezi. Pe de alta parte, presupunand ca exista si monezi de 12unitati sau ca unele din tipurile de monezi lipsesc din multimea initiala de candidati, sepot gasi contraexemple pentru care algoritmul nu gaseste solutia optima, sau nu gasestenici o solutie cu toate ca exista solutie.Evident, solutia optima se poate gasi ıncercand toate combinarile posibile de monezi (abor-dare backtracking). Acest mod de lucru necesita ınsa foarte mult timp de calcul.Un algoritm Greedy nu duce deci ıntotdeauna la solutia optima sau la o solutie. Este doarun principiu general, urmand ca pentru fiecare caz ın parte sa determinam daca obtinemsau nu solutia optima.

10.2.1 Proprietatea de alegere Greedy

Prima caracteristica a unei probleme de tip Greedy este aceea de a avea proprietateaalegerii Greedy, adica se poate ajunge la o solutie optima global, realizand alegeri (Greedy)optime local . Intr-un algoritm Greedy se realizeaza orice alegere care pare a fi cea maibuna la momentul respectiv, iar subproblema rezultata este rezolvata dupa ce alegereaeste facuta. Alegerea realizata de un algoritm Greedy poate depinde de alegerile facutepana ın momentul respectiv, dar nu poate depinde de alegerile ulterioare sau de solutiilesubproblemelor.Desigur, trebuie sa demonstram ca o alegere Greedy la fiecare pas conduce la o solutieoptima global, si aceasta este o problema mai delicata. De obicei, demonstratia examineazao solutie optima global. Apoi se arata ca solutia poate fi modificata astfel ıncat la fiecarepas este realizata o alegere Greedy, iar aceasta alegere reduce problema la una similara darde dimensiuni mai reduse. Se aplica apoi principiul inductiei matematice pentru a arata

Page 177: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

10.3. MINIMIZAREA TIMPULUI MEDIU DE ASTEPTARE 177

ca o alegere Greedy poate fi utilizata la fiecare pas. Faptul ca o alegere Greedy conducela o problema de dimensiuni mai mici reduce demonstratia corectitudinii la demonstrareafaptului ca o solutie optima trebuie sa evidentieze o substructura optima.

10.2.2 Substructura optima

O problema evidentiaza o substructura optima daca o solutie optima a problemei continesolutii optime ale subproblemelor. Aceasta proprietate este cheia pentru aplicarea pro-gramarii dinamice sau a unui algoritm Greedy. Ca exemplu al unei structuri optime, sane reamintim demonstratia corectitudinii algoritmului pentru problema selectarii specta-colelor, unde se arata ca daca o solutie optima A a problemei selectarii activitatilor ıncepecu activitatea 1, atunci multimea activitatilor A′ = A1 este o solutie optima pentruproblema selectarii activitatilor S′ = i ∈ S|si ≥ t1.

10.3 Minimizarea timpului mediu de asteptare

O singura statie de servire (procesor, pompa de benzina, etc) trebuie sa satisfaca cererilea n clienti. Timpul de servire necesar fiecarui client este cunoscut ın prealabil: pentruclientul i este necesar un timp ti, i = 1, n. Dorim sa minimizam timpul total de asteptare:

T =∑n

i=1(timpul de asteptare pentru clientul i)

Ceea ce este acelasi lucru cu a minimiza timpul mediu de asteptare, care este Tn .

De exemplu, daca avem trei clienti cu t1 = 5, t2 = 10, t3 = 3, sunt posibile sase ordini deservire:

Ordine T impul(T )1 2 3 5 + (5 + 10) + (5 + 10 + 3)1 3 2 5 + (5 + 3) + (5 + 3 + 10)2 1 3 10 + (10 + 5) + (10 + 5 + 3)2 3 1 10 + (10 + 3) + (10 + 3 + 5)3 1 2 3 + (3 + 5) + (3 + 5 + 10) optim

3 2 1 3 + (3 + 10) + (3 + 10 + 5)

In primul caz, clientul 1 este servit primul, clientul 2 asteapta pana este servit clientul1 si apoi este servit, clientul 3 asteapta pana sunt serviti clientii 1, 2 si apoi este servit.Timpul total de asteptare a celor trei clienti este 38.Algoritmul Greedy este foarte simplu - la fiecare pas se selecteaza clientul cu timpul minimde servire din multimea de clienti ramasa. Vom demonstra ca acest algoritm este optim.Fie I = (i1 i2 . . . in) o permutare oarecare a ıntregilor 1, 2, . . . , n. Daca servirea are locın ordinea I, avem:

T (I) = ti1 +(ti1 + ti2)+ (ti1 + ti2 + ti3)+ . . . = nti1 +(n−1)ti2 + . . . =∑n

k=1 (n− k + 1)tik

Presupunem acum ca I este astfel ıncat putem gasi doi ıntregi a < b cu

tia > tib

Page 178: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

178 CAPITOLUL 10. ALGORITMI GREEDY

deci exista un client care necesita un timp mai lung de deservire, si care este servit ınainte.Interschimbam pe ia cu ib ın I; cu alte cuvinte, clientul care a fost servit al b-lea va fi servitacum al a-lea si invers. Obtinem o noua ordine de servire I’, care este de preferat deoarece

T (I ′) = (n− a + 1)tib + (n− b + 1)tia +∑n

k=1,k 6=a,b (n− k + 1)tik

T (I)− T (I ′) = (n− a + 1)(tia − tib) + (n− b + 1)(tib − tia) = (b− a)(tia − tib) > 0

Aplicand succesiv pasul de mai sus se obtine o permutare optima J = (j1, j2, . . . , jn)pentru care avem:

tj1 ≤ tj2 ≤ . . . ≤ tjn .

Prin metoda Greedy, selectand permanent clientul cu timpul cel mai mic de deservire,obtinem deci ıntotdeauna planificarea optima a clientilor.Problema poate fi generalizata pentru un sistem cu mai multe statii de servire.Scrierea algoritmului se reduce la o banala ordonare a clientilor crescator dupa timpul dedeservire si o lasam ca exercitiu.

10.4 Interclasarea optima a mai multor siruri ordonate

Sa presupunem ca avem doua siruri S1 si S2, de lungime m si n, ordonate crescatorsi ca dorim sa obtinem prin interclasarea lor sirul ordonat crescator care contine exactelementele din cele doua siruri. Daca interclasarea are loc prin deplasarea elementelor dincele doua siruri ın noul sir rezultat, atunci numarul deplasarilor este m + n.Generalizand, sa consideram acum n siruri S1, S2, . . . Sn, fiecare sir Si, i = 1, n, fiind formatdin qi elemente ordonate crescator (vom numi qi lungimea lui Si). Ne propunem sa obtinemsirul S ordonat crescator, continand exact elementele din cele n siruri. Vom realiza acestlucru prin interclasari succesive de cate doua siruri. Problema consta ın determinareaordinii optime ın care trebuie efectuate aceste interclasari, astfel ıncat timpul total sa fiecat mai mic. Exemplul de mai jos ne arata ca problema astfel formulata nu este banala,adica nu este deloc indiferent ın ce ordine se fac interclasarile.

Exemplul 10.1 Fie sirurile S1, S2, S3 de lungimi q1 = 30, q2 = 20, q3 = 10. Daca inter-clasam pe S1 cu S2, iar rezultatul ıl interclasam cu S3, numarul total al deplasarilor este(30+20)+(50+10)=110. Daca interclasam pe S3 cu S2, iar rezultatul ıl interclasam cuS1, numarul total al deplasarilor este (10+20)+(30+30)=90, deci cu 20 de operatii maiputin.

Atasam fiecarei strategii de interclasare cate un arbore binar ın care valoarea fiecarui varfeste data de lungimea sirului pe care ıl reprezinta. Daca sirurile S1, S2, . . . , S6 au lungimileq1 = 30, q2 = 10, q3 = 20, q4 = 30, q5 = 50, q6 = 10 doua astfel de strategii de interclasaresunt reprezentate prin arborii din Figura 10.1.

Page 179: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

10.4. INTERCLASAREA OPTIMA A MAI MULTOR SIRURI ORDONATE 179

150

140@

@

10

130@

@

10

110@

@

20

80@

@

30

30@

@

50

150

90@

@@

60

40@

@

50

20@

@

20

10@

@

10

30@

@

30

Figura 10.1 Reprezentarea strategiilor de interclasare

Observam ca fiecare arbore are 6 varfuri terminale, corespunzand celor 6 siruri initiale si5 varfuri neterminale, corespunzand celor 5 interclasari care definesc strategia respectiva.Numerotam varfurile ın felul urmator: Varful terminal i, i = 1, 6, va corespunde sirului Si,iar varfurile neterminale se numeroteaza de la 7 la 11 ın ordinea obtinerii interclasarilorrespective (Figura 10.2).Strategia Greedy apare ın Figura 10.1b si consta ın a interclasa mereu cele mai scurtedoua siruri disponibile la momentul respectiv.

11

10@

@

6

9@

@

2

8@

@

3

7@

@

4

1@

@

5

11

10@

@@

9

8@

@

5

7@

@

3

2@

@

6

1@

@

4

Figura 10.2 Numerotarea varfurilor arborilor din Figura 10.1

Pentru a interclasa sirurile S1, S2, . . . , Sn, de lungimi q1, q2, . . . , qn, obtinem pentru fiecarestrategie cate un arbore binar cu n varfuri terminale numerotate de la 1 la n si n − 1varfuri neterminale numerotate de la n + 1 la 2n− 1. Definim pentru un arbore oarecareA de acest tip lungimea externa ponderata:

Page 180: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

180 CAPITOLUL 10. ALGORITMI GREEDY

L(A) =∑n

i=1 aiqi

unde ai este adancimea varfului i. Se observa ca numarul total de deplasari de elementepentru strategia corespunzatoare lui A este chiar L(A). Solutia optima a problemei noastreeste atunci arborele (strategia) pentru care lungimea externa ponderata este minima.

Teorema 10.2 Prin metoda Greedy, ın care se interclaseaza la fiecare pas cele doua siruride lungime minima, se obtine sirul s cu un numar minim de operatii.

Demonstratie: Demonstram prin inductie. Pentru n = 1, proprietatea este verificata.Presupunem ca proprietatea este adevarata pentru n− 1 siruri. Fie A arborele strategieiGreedy de interclasare a n siruri de lungime q1 ≤ q2 ≤ . . . ≤ qn. Fie B un arbore culungimea externa ponderata minima, corespunzator unei strategii optime de inerclasare acelor n siruri. In arborele A apare subarborele:

q1 + q2

q1@

@@

@@

q2

reprezentand prima interclasare facuta conform strategiei Greedy. In arborele B, fie unvarf neterminal de adancime maxima. Cei doi fii ai acestui varf sunt atunci doua varfuriterminale qj si qk. Fie B’ arborele obtinut din B schimband ıntre ele varfurile q1 si qj ,respectiv q2 si qk. Evident, L(B′) ≤ L(B). Deoarece B are lungimea externa ponderataminima, rezulta ca L(B) = L(B′). Eliminand din B’ varfurile q1 si q2, obtinem un ar-bore B” cu n − 1 varfuri terminale q1 + q2, q3, . . . , qn. Arborele B′ are lungimea externaponderata minima si L(B′) = L(B”) + (q1 + q2). Rezulta ca si B” are lungimea externaponderata minima. Atunci, conform ipotezei inductiei, avem L(B”) = L(A′) unde A’ estearborele strategiei Greedy de interclasare a sirurilor de lungime q1 + q2, q3, . . . , qn. Cum Ase obtine din A’ atasand la varful q1 + q2 fiii q1 si q2, iar B’ se obtine ın acelasi mod dinB”, rezulta ca L(A) = L(B′) = L(B) . Proprietatea este deci adevarata pentru orice n.Implementarea eficienta a acestui algoritm presupune utilizarea unei structuri de date nu-mita Heap.Se poate implementa algoritmul utilizand urmatorul principiu:

• se parcurge sirul de lungimi si se aleg doua siruri de lungime minima

• se deplaseaza cele doua lungimi la sfarsitul sirului

• se ınlocuiesc ultimele doua elemente din sir, cu un element reprezentand suma lor

• se descreste lungimea sirului cu o unitate

Scrierea efectiva a algoritmului o propunem ca exercitiu.

Page 181: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

10.5. PROBLEME PROPUSE 181

10.5 Probleme propuse

1. (Problema rucsacului) Avem la dispozitie un rucsac de capacitate M si n obiectediferite (cate unul din fiecare) cu costurile ci si greutatea gi. Scrieti un algoritmcare aseaza aceste obiecte ın rucsac astfel ıncat costul total sa fie maxim. Sumagreutatilor obiectelor din rucsac nu poate depasi capacitatea rucsacului. Daca unobiect nu ıncape ın rucsac, se poate lua doar o parte (fractiune) din el.

Indicatie: Este usor de intuit ca pentru obtinerea unui profit (cost) total maximtrebuie carate obiecte de greutate mica si cost mare. Demonstratia riguroasa aafirmatiei anterioare v-o propunem spre rezolvare.Asadar, va trebui, initial, sa ordonam descrescator obiectele dupa raportul ci

gi. Apoi,

vom folosi urmatorul algoritm:

functie RUCSAC(c,g,M,x,n)pentru i=1,n executa

x(i)←0rest←Mi←1cat timp i ≤ n si rest > 0 executa

daca g(i)>rest atuncix(i)←rest/g(i)

altfelx(i)←1

rest←rest-x(i)w(i)return x

2. (Problema discreta a rucsacului) Acelasi enunt ca la problema precedenta, cu diferentaca dintr-un obiect nu se poate pune o fractiune (un obiect fie se pune ıntreg, fie nuse pune). Aratati ca algoritmul Greedy de la problema precedenta nu furnizeazaıntotdeauna solutie optima ın acest caz. Gasiti un algoritm care furnizeaza ıntotdeaunasolutie optima! Ce complexitate are acest algoritm?

Indicatie: Aceasta problema are solutia exacta determinata doar printr-un algoritmde tip backtracking. Exemplu ın sprijinul celor afirmate anterior: fie c=(5,3,3) sig=(3,2,2) iar M=4. Prin aplicarea algoritmului de la problema precedenta am selectadoar obiectul 1 (obiectul 2 nu ar ıncapea ıntreg ın rucsac deci nu ar putea fi selectat)si am obtine costul 5 ın timp ce daca am selecta obiectele 2 si 3 am obtine costul 6.

3. Scrieti algoritmul pentru problema interclasarii optimale din paragraful 10.4.

4. Gasiti o solutie Greedy pentru problema comis-voiajorului propusa la capitolul 8.Este aceasta solutie optima?

Page 182: holdsoft.free.frholdsoft.free.fr/Mnsoft/facultate/CursJava.pdf · Cuprins 1 Instalarea mediului Java 6 1.1 Ob¸tinerea mediului Java pentru platforma dumneavoastra . . . . . .

182 CAPITOLUL 10. ALGORITMI GREEDY

Indicatie: Conform strategiei greedy, vom construi ciclul pas cu pas, adaugand lafiecare iteratie cea mai scurta muchie disponibila cu urmatoarele proprietati:

• nu formeaza un ciclu cu muchiile deja selectate (exceptand pentru ultima muchiealeasa, care completeaza ciclul)

• nu exista ınca doua muchii deja selectate, astfel ıncat cele trei muchii sa fieincidente ın acelasi varf.

Aceasta solutie nu este optima iar un exemplu va propunem sa-l gasiti.