Sunteți pe pagina 1din 45

Proiectarea Algoritmilor

Miticã Craus
Algorimi, probleme,
performanţe
Algoritmi

z Un algoritm este o secventa finita de operatii (pasi) care, atunci când


este executata, produce o solutie corecta pentru o problema precizata.
z Tipul operatiilor si ordinea lor în secventa respecta o logica specifica.
z Algoritmii pot fi descrisi în orice limbaj, pornind de la limbajul natural
pîna la limbajul masina al unui calculator specific.
z Un limbaj al carui scop unic este cel de a descrie algoritmi se numeste
limbaj algoritmic.
z Limbajele de programare sunt exemple de limbaje algoritmice.
Tipuri de date
z Un tip de date consta dintr-o multime de entitati (componente) de tip data
(informatie reprezentabila în memoria unui calculator), numita si domeniul
tipului, si o multime de operatii peste aceste entitati.
z tipuri de date elementare
z entitatile sunt indivizibile.
z tipuri de date structurate de nivel jos
z entitatile sunt structuri relativ simple obtinute prin asamblarea de date
elementare sau date structurate iar operatiile sunt definite, de regula, la nivel
de componenta.
z tipuri de date structurate de nivel înalt
z componentele sunt structuri mai complexe iar operatiile sunt implementate
de algoritmi proiectati de catre utilizatori.
z Primele doua categorii sunt dependente de limbaj.
z Tipurile de nivel înalt pot fi descrise într-o maniera independenta de limbaj.
z Un tip de date descris într-o maniera independenta de reprezentarea valorilor si
implementarea operatiilor se numeste tip de date abstract.
Programe

z Pasii (operatiile) unui algoritm si ordinea logica a acestora sunt descrise


cu ajutorul instructiunilor.
z O secventa de instructiuni care actioneaza asupra unor structuri de
date precizate se numeste program.
z Memoria este reprezentata ca o secventa de celule (locatii),
z fiecare celula are asociata o adresa;
z poate memora (stoca) o data de un anumit tip.
z Accesul la memorie este realizat cu ajutorul variabilelor.
Variabile

z O variabila este caracterizata de:


z numele cu ajutorul careia variabila este referita;
z adresa care desemneaza o locatie de memorie;

z tipul de date care descrie natura datelor memorate în locatia de


memorie asociata variabilei.
z Daca în plus adaugam si data memorata la un moment dat în locatie,
atunci obtinem o instanta a variabilei.
Atribuirea
Sintaxa :
〈variabila〉 ← 〈expresie〉
z 〈variabila〉 este numele unei variabile
z 〈expresie〉 este o expresie corect formata de acelasi tip cu 〈variabila〉.
Semantica :
z Se evalueaza 〈expresie〉 si rezultatul obtinut se memoreaza în locatia de
memorie desemnata de 〈variabila〉.
z Valorile tuturor celorlalte variabile ramân neschimbate.
z Atribuirea este singura instructiune cu ajutorul careia se poate modifica
memoria.
Instructiunea if
Sintaxa:
if 〈expresie〉
then 〈secventa-instructiuni1〉
else 〈secventa-instructiuni2〉
z 〈expresie〉 este o expresie care prin evaluare da rezultat boolean iar 〈secventa-
instructiunii〉, i=1,2, sunt secvente de instructiuni scrise una sub alta si aliniate
corespunzator.
z Partea else este facultativa. Daca partea else lipseste si 〈secventa-instructiuni1〉
este formata dintr-o singura instructiune atunci instructiunea if poate fi scrisa si
pe un singur rând.
Semantica :
z Se evalueaza 〈expresie〉;
z Daca rezultatul evaluarii este true atunci se executa 〈secventa-instructiuni1〉
dupa care executia instructiunii if se termina;
z Daca rezultatul evaluarii este false atunci se executa 〈secventa-instructiuni2〉
dupa care executia instructiunii if se termina.
If -> Case
if (e1)
then i1
else if (e2)
then i2
else if (e3)
then i3
else if (e4)
...
else in
va fi scrisa mai simplu astfel:
switch
case (e1) i1
case (e2) i2
case (e3) i3
case (e4) i4
...
otherwise in
Instructiunea while
Sintaxa :
while 〈expresie〉 do
〈secventa-instructiuni〉
z 〈expresie〉 este o expresie care prin evaluare da rezultat boolean
z 〈secventa-instructiuni〉 este o secventa de instructiuni scrise una sub
alta si aliniate corespunzator.
Semantica :
1. Se evalueaza 〈expresie〉;
2. Daca rezultatul evaluarii este true atunci se executa 〈secventa-
instructiuni〉 dupa care se reia procesul începând cu pasul 1;
3. Daca rezultatul evaluarii este false atunci executia instructiunii while se
termina.
Instructiunea for
Sintaxa :
for 〈variabila〉 ← 〈expresie1〉 to 〈expresie2〉 do
〈secventa-instructiuni〉
sau
for 〈variabila〉 ← 〈expresie1〉 downto 〈expresie2〉 do
〈secventa-instructiuni〉
z 〈variabila〉 este o variabila de tip întreg.
z 〈expresiei〉, i=1,2, sunt expresii care prin evaluare dau valori întregi, 〈secventa-instructiuni〉 este o
secventa de instructiuni scrise una sub alta si aliniate corespunzator.
Semantica :
z Instructiunea for i ← e1 to e2 do S simuleaza executia urmatorului program:
i ← e1
temp ← e2
while (i ≤ temp) do
S
i ← succ(i)
/* prin succ(i) notam functia care întoarce succesorul numarului întreg i */
z Instructiunea for i=e1 downto e2 do S simuleaza executia urmatorului program:
i ← e1
temp ← e2
while (i ≥ temp) do
S
i ← pred(i)
/* prin pred(i) notam functia care întoarce predecesorul numarului întreg i */
Instructiunea repeat
Sintaxa
Repeat
〈secventa-instructiuni〉
until 〈expresie〉
z 〈expresie〉 este o expresie care prin evaluare da rezultat boolean iar 〈secventa-
instructiuni〉 este o secventa de instructiuni scrise una sub alta si aliniate
corespunzator.
Semantica : Instructiunea
repeat
S
until e
simuleaza executia urmatorului program:
S
while (not e) do
S
Instructiunea throw
Sinataxa:
throw 〈mesaj〉
z 〈mesaj〉 este un sir de caractere (un text).
Semantica :
z Executia programului se opreste si este afisat textul 〈mesaj〉.
z Cel mai adesea, throw este utilizata împreuna cu if: if 〈expresie〉 then throw
〈mesaj〉
z Obtinerea rezultatului true în urma evaluarii expresiei are ca semnificatie
aparitia unei exceptii, caz în care executia programului se opreste.
z Un exemplu de exceptie este cel in care procedura new nu poate aloca
memorie pentru variabilele dinamice: ¯new(p)
if (p = NULL) then throw 'memorie insuficienta'
Proceduri
z Limbajul nostru algoritmic este unul modular, unde un modul este identificat de un
subprogram.
z Exista doua tipuri de subprograme: proceduri si functii.
z Interfata dintre o procedura si modulul care o apeleaza este realizata numai prin
parametri si variabilele globale.
z De aceea apelul unei proceduri apare numai ca o instructiune separata.
z Forma generala a unei proceduri este:
procedure 〈nume〉(〈lista-parametri〉)
〈secventa-instructiuni〉
end
z Lista parametrilor este optionala.
z Exemplu: procedura swap - interschimba valorile a doua variabile:
procedure swap(x, y)
aux x
xy
y aux
end
z Permutarea circulara a valorilor a trei variabile a, b, c se face apelând de doua ori
procedura swap: ¯
swap(a, b)
swap(b, c)
Functii
z În plus fata de proceduri, functiile întorc valori asociate cu numele functiei.
z Apelul unei functii poate participa la formarea de expresii.
z Forma generala a unei functii
function 〈nume〉(〈lista-parametri〉)
〈secven ta-instructiuni〉
return 〈expresie〉
end
z Lista parametrilor este optionala.
z Valoarea întoarsa de functie este cea obtinuta prin evaluarea expresiei.
z O instructiune return poate apare în mai multe locuri în definitia unei functii si executia ei implica
terminarea evaluarii functiei.
z Exemplu: functie max3 - calculeaza maximul dintre valorile a trei variabile:
function max3(x, y, z)
temp ← x
if (y > temp) then temp y
if (z > temp) then temp z
return temp
end
z Are sens sa scriem 2*max3(a, b, c) sau max3(a, b, c) < 5.
Comentarii

z Comentariile sunt notate similar ca în limbajul C, utilizând combinatiile de caractere /* si */.

z Comentariile au rolul de a introduce explicatii suplimentare privind descrierea algoritmului:

function absDec(x)
if (x > 0) /* testeaza daca x este pozitiv */
then x ← x-1 /* decrementeaza x */
else x ← x+1 /* incrementeaza x */
end
Algoritmi si probleme
z Notiunile de algoritmi, programe si probleme sunt diferite si trebuie înteles bine
care sunt relatiile dintre ele.
z Un algoritm exprima o modalitate de a obtine solutia unei probleme specifice.
z Descrierea algoritmului într-un limbaj algoritmic se face prin intermediul unui
program.
z Toate notiunile definite pentru program sunt extinse în mod natural la algoritm.
z Astfel, putem vorbi despre executia (calculul) unui algoritm, configuratie initiala,
etc.
z O problema are doua componente:
z domeniul, care descrie elementele care intervin în problema si relatiile dintre
aceste elemente,
z o întrebare despre proprietatile anumitor elemente sau o cerinta de
determinare a unor elemente ce au o anumita proprietate.
z În functie de scopul urmarit, exista mai multe moduri de a formaliza o problema.
Formalizarea problemelor

z Putem formaliza problema rezolvata de algoritm ca o pereche (Intrare,Iesire).


z Componenta Intrare descrie datele de intrare;
z Componenta Iesire descrie datele de iesire.
z Un exemplu simplu de problema reprezentata astfel este urmatorul:
z Intrare: Un numar întreg pozitiv x.
z Iesire: Cel mai mare numar prim mai mic decât sau egal cu x.
Probleme de decizie
z Atunci când iesirea este de forma 'DA' sau 'NU'.
z O astfel de problema este reprezentata ca o pereche ( Instanta, Întrebare)
z componenta Instanta descrie datele de intrare ;
z componenta Întrebare se refera, în general, la existenta unui obiect sau a
unei proprietati.
z Un exemplu tipic îl reprezinta urmatoarea problema:
z Instanta: Un numar întreg pozitiv x.
z Întrebare: Este x numar prim?
z Problemele de decizie sunt preferate atât în teoria complexitatii cât si în teoria
calculabilitatii datorita reprezentarii foarte simple a iesirilor.
Probleme de decizie

z De obicei, pentru reprezentarea problemelor de decizie


z se considera o multime U
z o instanta este de forma R ⊆ U,x ∈ U si întrebarea de forma x ∈ R ?
z Problema din exemplul anterior poate fi reprezentata astfel:
z [Instanta:] P = multimea numerelor prime, P ⊆ Z, x ∈ Z+.
z [Întrebare:] x ∈ P ?
z Convenim sa consideram întotdeauna intrarile p ale unei probleme P ca fiind
instante si, prin abuz de notatie, scriem p∈P.
Problema rezolvata de un
algoritm
z Fie A un algoritm si P o problema. Spunem ca o configuratie initiala c0 a lui A
include instanta p ∈ P daca exista o structura de date inp, definita în A, astfel
încât valoarea lui inp din c0 constituie o reprezentare a lui p.

z Analog, spunem ca o configuratie finala cn a unui algoritm A include iesirea P(p)


daca exista o structura de date out, definita în A, astfel încât valoarea lui out din
cn constituie o reprezentare a lui P(p).

1. Un algoritm A rezolva o problema P în sensul corectitudinii totale daca pentru


orice instanta p, calculul unic determinat de configuratia initiala ce include p este
finit si configuratia finala include iesirea P(p).

2. Un algoritm A rezolva o problema P în sensul corectitudinii partiale daca pentru


orice instanta p pentru care calculul unic determinat de configuratia initiala ce
include p este finit, configuratia finala include iesirea P(p).

z Ori de câte ori spunem ca un algoritm A rezolva o problema P vom întelege de


fapt ca A rezolva o problema P în sensul corectitudinii totale.
Problema
rezolvabila/nerezolvabila

1. O problema P este rezolvabila daca exista un algoritm care sa o rezolve în


sensul corectitudinii totale. Daca P este o problema de decizie, atunci spunem
ca P este decidabila.

2. O problema de decizie P este semidecidabila sau partial decidabila daca exista


un algoritm A care rezolva P în sensul corectitudinii partiale astfel încât calculul
lui A este finit pentru orice instanta p pentru care raspunsul la întrebare este
'DA'.

3. O problema P este nerezolvabila daca nu este rezolvabila, adica nu exista un


algoritm care sa o rezolve în sensul corectitudinii totale. Daca P este o
problema de decizie, atunci spunem ca P este nedecidabila.
Problema opririi
z Pentru a arata ca o problema este decidabila este suficient sa gasim un algoritm
care sa o rezolve. Mai complicat este cazul problemelor nedecidabile.
z În legatura cu acestea din urma se pun, în mod firesc, urmatoarele întrebari:
z Exista probleme nedecidabile?
z Cum putem demonstra ca o problema nu este decidabila?
z Raspunsul la prima întrebare este afirmativ. Un prim exemplu de problema
nedicidabila este cea cunoscuta sub numele de problema opririi.
z Notam cu U multimea perechilor de forma <A,x> unde A este un algoritm si x este
o intrare pentru A, iar R este submultimea formata din acele perechi <A,x> pentru
care calculul lui A pentru intrarea x este finit.
z Daca notam prin A(x) (a se citi A(x)=true) faptul ca <A,x> ∈ R, atunci problema
opririi poate fi scrisa astfel:
Executia programelor

z Intuitiv, o executie a unui program consta în succesiunea de pasi elementari


determinati de executiile instructiunilor ce compun programul.

z Convenim sa utilizam notiunea de calcul pentru executia unui program.


Calculul descris de un program P

x←0
i←1
while (i < 10) do
x ← x*10+i
i ← i+2
Configuratii
z Acest calcul este notat formal printr-o secventa
c0⏐⎯ c2…. ⏐⎯ c18
z Prin ci am notat configuratiile ce intervin în calcul.
z O configuratie include
z instructiunea curenta (starea programului) si
z starea memoriei (valorile curente ale variabilelor din program).
z În exemplul anterior, o configuratie este reprezentata de o linie în tabel.
z Relatia ci-1 ⏐⎯ ci are urmatoarea semnificatie:
z prin executia instructiunii din ci-1, se transforma ci-1 în ci.
z c0 se numeste configuratie initiala iar c18 configuratie finala.
z Pot exista si calcule infinite.
z Exemplu: instructiunea while (true) do i← i+1 genereaza un calcul infinit.
Evaluarea algoritmilor
z Evaluarea algorimilor din punctul de vedere al performantelor obtinute de
acestia în rezolvarea problemelor este o etapa esentiala în procesul de decizie
a utilizarii acestora în aplicatii.
z La evaluarea (estimarea) algoritmilor se pune în evidenta consumul celor doua
resurse fundamentale: timpul de executie si spatiul de memorare a datelor.
z În functie de prioritatile alese, se aleg limite pentru resursele timp si spatiu.
z Algoritmul este considerat eligibil daca consumul celor doua resurse se
încadreaza în limitele stabilite.
z Fie P o problema si A un algoritm pentru P. Fie c0|-c1|- …|-cn un calcul finit al
algoritmului A. Notam cu tA(ci) timpul necesar obtinerii configuratiei ci din ci-1, 1 ≤
i ≤ n, si cu sA(ci) spatiul de memorie ocupat în configuratia ci, 0 ≤ i ≤ n.
Parametri de performanta
z Fie A un algoritm pentru problema P, p ∈ P o instanta a problemei P si c0|-c1|- …|-cn calculul lui A
corespunzator instantei p. Timpul necesar algoritmului A pentru rezolvarea instantei p este:

z Spatiul (de memorie) necesar algoritmului A pentru rezolvarea instantei p este:

z În general este dificil de calculat cele doua valori în functie de instante. Aceasta dificultate poate fi
simplificata astfel:
z Asociem unei instante p ∈ P o marime g(p), care este un numar natural, pe care o numim dimensiunea
instantei p. De exemplu, g(p) poate fi suma lungimilor reprezentarilor corespunzând datelor din instanta p.
z Daca reprezentarile datelor din p au aceeasi lungime, atunci se poate lua g(p) egala cu numarul datelor. Astfel
daca p consta dintr-un tablou atunci se poate lua g(p) ca fiind numarul de elemente ale tabloului; daca p
consta dintr-un polinom se poate lua g(p) ca fiind gradul polinomului (= numarul coeficientilor minus 1); daca p
este un graf se poate lua g(p) ca fiind numarul de vârfuri, numarul de muchii sau numarul de vârfuri + numarul
de muchii, etc.
Masurarea performantelor

„ Fie A un algoritm pentru problema P:


Exemplul 1
Problema cautarii unui element într-o secventa de numere întregi
Exemplul 2
Problema determinarii elementului de valoare maxima dintr-o
secventa de numere intregi
Exemplul 3
Sortarea prin inserare
Exemplul 3
Sortarea prin inserare
Exemplul 4
Cautarea binara
Algoritmul cautarii binare
Observatii

z Timpii de executie pentru cazul cel mai favorabil nu ofera informatii


relevante despre eficienta algoritmului.
z Mult mai semnificative sunt informatiile oferite de timpii de executie în cazul
cel mai nefavorabil: în toate celelalte cazuri algoritmul va avea performante
mai bune sau cel putin la fel de bune.
z Pentru evaluarea timpului de executie nu este necesar totdeauna sa
numaram toate operatiile.
z Pentru exemplul anterior, observam ca operatiile de atribuire (fara cea
initiala) sunt precedate de comparatii. Astfel ca putem numara numai
comparatiile, pentru ca numarul acestora determina numarul atribuirilor.
z Putem merge chiar mai departe si sa numaram numai comparatiile între z si
componentele tabloului.
Timpul mediu de executie
z Uneori, numarul instantelor p cu g(p)=n pentru care TA(p) = TA(n) sau TA(p) are o
valoare foarte apropiata de TA(n) este foarte mic.
z Pentru aceste cazuri este preferabil sa calculam comportarea în medie a algoritmului.
z Pentru a putea calcula comportarea în medie este necesar sa privim marimea TA(p) ca
fiind o variabila aleatoare (o experienta = executia algoritmului pentru o instanta p,
valoarea experientei = durata executiei algoritmului pentru instanta p) si sa precizam
legea de repartitie a acestei variabile aleatoare.
z Comportarea în medie se calculeaza ca fiind media acestei variabile aleatoare
(consideram numai cazul timpului de executie):

z Daca multimea valorilor variabilei aleatoare TA(p)∈{x1,...} este finita sau numarabila
(TA(p) ∈{x1,...,xi...} si probabilitatea ca TA(p)=xi este pi, atunci media variabilei aleatoare
TA (timpul mediu de executie) este:
Calculul timpului mediu de executie
Calcul asimptotic
Calcul asimptotic
Reguli pentru calculul timpului de executie

z Un algoritm poate avea o descriere complexa si deci evaluarea sa poate pune unele
probleme. De aceea prezentam câteva strategii ce sunt aplicate în determinarea timpului de
executie pentru cazul cel mai nefavorabil.
z Deoarece orice algoritmul este descris de un program, în cele ce urmeaza consideram A o
secventa de program.
z Regulile prin care se calculeaza timpul de executie sunt date în functie de structura lui A:
1. A este o instructiune de atribuire. Timpul de executie a lui A este egal cu timpul
evaluarii expresiei din partea dreapta.
2. A este forma: A1 A2 Timpul de executie a lui A este egala cu suma timpilor de executie a
algoritmilor A1 si A2.
3. A este de forma if e then A1 else A2. Timpul de execu tie a lui A este egala cu maximul
dintre timpii de executie a algoritmilor A1 si A2 la care se aduna timpul necesar evaluarii
expresiei e.
4. A este de forma while e do A1. Se determina numarul maxim de executii ale buclei while
si se face suma timpilor calculati pentru fiecare iteratie. Daca nu este posibila
determinarea timpilor pentru fiecare iteratie, atunci timpul de executie a lui A este egal cu
produsul dintre timpul maxim de executie a lui A1 si numarul maxim de executii ale
algoritmului A1

z Plecând de la aceste reguli de baza, se pot obtine în mod natural reguli de calcul a
timpului de executie pentru toate instructiunile.
O(nK)
z Teorema: Daca g este o functie polinomiala de grad k, atunci g = O(nk).
Presupunem g(n) = ak·nk + ak-1·nk-1 + …+ a1·n + a0.
z Demonstratie:
Efectuând majorari în membrul drept, obtinem:
g(n) ≤ |ak| ·nk + |ak-1|·nk-1 + …+|a1|·n +|a0| < nk· (|ak| + |ak-1| + |a0|)c < nk·c pentru
"n > 1 ⇒ g(n) < c ·nk, cu n0 = 1. Deci g = O(nk)

z Nota tiile Ο,Ω si Θ ofera informatii cu care putem aproxima comportarea unei
functii.
z Pentru ca aceasta aproximare sa aiba totusi un grad de precizie cât mai mare,
este necesar ca multimea desemnata de partea dreapta a ecuatiei sa fie cât mai
mica.
z De exemplu, avem atât 3n = O(n) cât si 3n=O(n2). Prin incluziunea O(n)⊂O(n2)
rezulta ca prima ecuatie ofera o aproximare mai buna.
z De fiecare data vom cauta multimi care aproximeaza cel mai bine comportarea
unei functii.
Clase de algoritmi
z Urmatoarele incluziuni sunt valabile în cazul notatiei O:
O(1) ⊂ O(logn) ⊂ O(logkn) ⊂ O(n) ⊂ O(n2) ⊂ … ⊂ O(nk+1) ⊂ O(2n)

z Notatia O este utilizata si pentru clasificarea algoritmilor. Cele mai


cunoscute clase sunt: ¯
1. {A | TA(n)=O(1)} = clasa algoritmilor constanti;
2. {A | TA(n)=O(logn)} = clasa algoritmilor logaritmici;
3. {A | TA(n)=O(logkn)} = clasa algoritmilor polilogaritmici;
4. {A | TA(n)=O(n)} = clasa algoritmilor liniari;
5. {A | TA(n)=O(n2)} = clasa algoritmilor patratici;
6. {A | TA(n)=O(nk)} = clasa algoritmilor polinomiali;
7. {A | TA(n)=O(2n)} = clasa algoritmilor exponentiali.
Compararea algoritmilor

z Cu notatiile de mai sus, doi algoritmi, care rezolva aceeasi


problema, pot fi comparati numai daca au timpii de executie în
clase de functii (corespunzatoare notatiilor Ο,Ω si Θ) diferite.
z De exemplu, un algoritm A cu TA(n) = O(n) este mai eficient decât
un algoritm A′ cu TA′(n) = O(n2).
z Daca cei doi algoritmi au timpii de executie în aceeasi clasa, atunci
compararea lor devine mai dificila pentru ca trebuie determinate si
constantele cu care se înmultesc reprezentantii clasei.
z Putem acum incadra algoritmii prezentati în exemplele anterioare
în clasele lor.
Incadrarea in clase a algoritmilor din exemplele
anterioare

z Pentru algoritmul ce rezolva problema P1 (cautarea unui element într-o


secventa de numere), TA1(n)=3n+2. Deci, timpul de executie a acestui
algoritm pentru cazul cel mai nefavorabil este în clasa O(n).
z Pentru problema P2 (cautarea într-un sir a elementului de valoare maxima),
TA2(n) = 1 +2(n-1). Timpul de executie a algoritmului ce rezolva aceasta
problema pentru cazul cel mai nefavorabil este în clasa O(n).
z În cazul problemei P3 (sortarea prin inserare), TA3(n) = 3(n-1) + 3n (n - 1)/2.
Rezulta ca timpul de executie a algorimtului respectiv este în clasa O(n2).
z Pentru problema P4 (cautarea binara) nu se poate aplica teorema
anterioara. Deoarece TA4(n) ≤ log2n + 1, timpul de executie a acestui
algoritm este în clasa O(log2n).
z Baza logaritmului se poate ignora deoarece: logax = logab logbx si logab
este o constanta, deci ramâne O(logn), adica o functie logaritmica.
Bibliografie
z D. Lucanu, M. Craus - Proiectarea algoritmilor, Editura Polirom, 2008
z E. Horowitz, S. Sahni - Fundamentals of Computer Algoritms, Computer Science Press,
1985
z T. Cormen, C. Leiserson, R. Rivest – Introducere în algoritmi, Computer Libris
Agora, 2000
z G. Heileman – Data Structures, Algorithms and Object-Oriented Programming,
McGraw-Hill, 1996
z S. Sahni - Data Structures and Algorithms in C++, WCB McGraw-Hill, 1998
z K.Mehlhorn – Data Structures and Efficient Algorithms, Springer Verlag, 1984
z Mark Allen Weiss – Data Structures and Algorithm Analysis in C, The
Benjamin/Cummings Publishing Company, Inc., 1992
z C.Croitou – Tehnici de bază în optimizarea combinatorie, Editura Universităţii “Al. I.
Cuza”, Iaşi, 1992

S-ar putea să vă placă și