Documente Academic
Documente Profesional
Documente Cultură
1 Introducere n algoritmi 3
1.1 Limbajul pseudocod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.3 Elemente de analiza algoritmilor . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.3.1 Metoda substitutiei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.3.2 Schimbarea de variabila . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.3.3 Metoda iterativa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.3.4 Teorema master . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1.4 Exercitii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
1
5 Arbori oarecare 102
5.1 Moduri de reprezentare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
5.2 Metode de parcurgere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
5.3 Arbori de acoperire de cost minim . . . . . . . . . . . . . . . . . . . . . . . . . 110
5.3.1 Algoritmul lui Boruvka . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
5.3.2 Algoritmul lui Prim . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
5.3.3 Structuri de date pentru multimi disjuncte . . . . . . . . . . . . . . . . 116
5.3.4 Algoritmul lui Kruskal . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
5.4 Exercitii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
7 Heap-uri 149
7.1 Heap-uri binare (Min-heapuri sau Max -heapuri) . . . . . . . . . . . . . . . . . 149
7.1.1 Inserarea unui element . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
7.1.2 Stergerea elementului minim . . . . . . . . . . . . . . . . . . . . . . . . 152
7.1.3 Crearea unui heap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
7.2 Ordonare prin metoda HeapSort . . . . . . . . . . . . . . . . . . . . . . . . . . 155
7.3 Aplicatie - Coada cu prioritate . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
7.4 Exercitii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
2
Capitolul 1
Introducere n algoritmi
Definitia 1.1 Algoritmul constituie o reprezentare finit a a unei metode de calcul ce per-
mite rezolvarea unei anumite probleme. Se poate spune c a un algoritm reprezinta o secventa
finita de operatii, ordonata si complet definit
a, care, pornind de la datele de intrare, produce
rezultate.
1. tipuri de date elementare (de exemplu tipul ntreg, tipul real ) - valorile sunt unitati
atomice de informatie;
2. tipuri de date structurate (de exemplu tipul tablou, tipul nregistrare) - valorile sunt
structuri relativ simple rezultate n urma combinatiei unor valori elementare;
3. tipuri de date structurate de nivel nalt (de exemplu stiva) - se pot descrie independent
de limbaj iar valorile au o structura mai complexa.
3
3. generalitate - algoritmul trebuie sa ofere solutia nu numai pentru o singura problema
ci pentru o ntreaga clasa de probleme;
4. finitudine - algoritmul trebuie sa se termine ntr-un timp finit;
limbajul pseudocod.
In continuare vom prezenta pricipalele constructii din cadrul limbajului pseudocod.
Intr
ari/iesiri
Citirea datelor de intrare se poate realiza prin intermediul enuntului Input:
1: Input {lista variabile}
Afisarea rezultatelor este reprezentata cu ajutorul instructiunii Output:
1: Output {lista de valori}
4
Instructiunea de atribuire
Este instructiunea cel mai des utilizata ntr-un algoritm si realizeaza ncarcarea unei variabile
(locatii de memorie) cu o anumita valoare. Are urmatoarea sintaxa:
1: < variabila >< expresie >
unde < expresie > este o expresie aritmetica sau logica.
Se evalueaza expresia < expresie > iar rezultatul se atribuie variabilei < variabila >,
memorandu-se n locatia de memorie asociata. Aceasta variabila trebuie sa fie de acelasi tip
de data cu expresia sau un tip de data care sa includa si tipul expresiei.
O expresie este constituita din operanzi si operatori. Operanzii pot fi variabile si valori
constante, iar operatorii pot fi:
operatori aritmetici - + (adunare), (scadere), (nmultire), / (mpartire), (ridicare
la putere), div (catul mpartirii ntregi), mod (restul mpartirii ntregi);
operatori relationali - = (egal), 6= (diferit), < (strict mai mic), (mai mic sau egal),
> (strict mai mare), (mai mare sau egal);
operatori logici - OR sau (disjunctie), AND sau (conjunctie), NOT sau (negatie).
Cea mai simpla expresie este formata dintr-o variabil a sau o constant a (operand). Ex-
presiile mai complicate se obtin din operatii efectuate ntre variabile si constante. La scrierea
expresiilor trebuie sa se tina cont de faptul ca, n momentul evaluarii lor, n primul rand
se vor evalua expresiile din paranteze, iar operatiile se executa n ordinea determinata de
prioritatile lor.
Enunturi de ramificare
1: if <expresie-booleana> then
2: <instructiune1> 1: if (a mod 2 = 0) then
[ 2: Output {Numarul este par }
3: else 3: else
4: <instructiune2> 4: Output {Numarul este impar }
] 5: end if
5: end if
Enuntul if . . . then . . . else evalueaza mai ntai expresia booleana pentru a determina
unul din cele doua drumuri pe care le poate lua executia algoritmului. Ea poate include
optional o clauza else.
Daca <expresie-booleana> se evalueaza la valoarea de adevar true, atunci se executa
<instructiune1> si se continua cu urmatoarea instructiune dupa if. Daca <expresie-booleana>
are valoarea de adevar false, atunci se executa <instructiune2>. <instructiune1> si
<instructiune2> sunt instructiuni compuse ce pot sa contina, la randul lor, o alta instructiune
if.
Exemplul 1.1 Un algoritm simplu este cel ce rezolv a ecuatia de gradul I, ax+b = 0, a, b R
(algoritmul 1). Acesta se bazeaza pe rezolvarea matematica a ecuatiei de gradul I:
1. daca a = 0, atunci ecuatia devine 0 x + b = 0. Pentru cazul n care b 6= 0 avem
0 x + b = 0, egalitate ce nu poate fi satisf a pentru nicio valoare a lui x R sau
acut
C. Spunem, n acest caz, ca ecuatia este incompatibila. Daca b = 0, avem 0 x + 0 =
0, relatia fiind adevarata pentru orice valoare a lui x R. Spunem c a ecuatia este
compatibil nedeterminata.
5
a solutie, x1 = ab R.
2. daca a 6= 0, ecuatia are o singur
Avand acest algoritm drept model s a se realizeze un algoritm pentru rezolvarea ecuatiei
2
generale de gradul al II-lea, ax + bx + c = 0, unde a, b, c R.
Enunturi repetitive
Enunturile repetitive permit descrierea unor prelucrari ce trebuie efectuate n mod repetat,
n functie de pozitia conditiei de continuare existand doua variante de structuri repetitive:
structura repetitiva conditionata anterior si structura repetitiv
a conditionat
a posterior.
Structura repetitiva conditionata anterior while (sau instructiune de ciclare cu test
initial) are urmatoarea sintaxa:
1: sum 0
2: i1
1: while <expresie-booleana> do
3: while (i n) do
2: <instructiune>
4: sum sum + i
3: end while
5: i i+1
6: end while
Cat timp <expresie-booleana> este adevarat
a, se executa <instructiune>; daca <expresie-
booleana> este falsa chiar la prima evaluare, atunci <instructiune> nu ajunge sa fie re-
alizata niciodata. Acest comportament este opus celui corespunzator structurii repetitive
conditionata posterior repeat, unde <instructiune> este executata cel putin o data. (Daca
o expresie are valoarea de adevar true spunem atunci ca expresia este adev arata ; daca o
expresie are valoarea de adevar false spunem atunci ca expresia nu este adevarata - este
falsa).
Exemplul 1.2 Vom realiza un algoritm pentru calculul c atului si restului mp artirii a doua
numere ntregi, prin scaderi succesive (a se vedea algoritmul 2).
Mai ntai, se initializeaza catul cu valoarea zero (linia 3) si restul cu valoarea dempartitului
(linia 4). Apoi, atata timp cat restul este mai mare dec at valoarea mp artitorului (liniile 5 -
8), vom incrementa catul cu o unitate, si decrementa restul cu valoarea mp artitorului. La
final, sunt afisate valorile catului si restului.
Un caz particular de structura repetitiva conditionata anterior este for, utilizata n cazul
n care o instructiune sau un grup de instructiuni trebuie sa se repete de 0 sau mai multe
ori - numarul de repetitii fiind cunoscut nainte de nceperea sa. Enunturile repetitive bazate
6
Algoritm 2 Algoritm pentru calculul mpartirii ntregi
1: Input {a, b}
2: if ((a > 0) (b > 0)) then
3: cat 0
4: rest a
5: while (rest b) do
6: rest rest b
7: cat cat + 1
8: end while
9: Output {cat, rest}
10: else
11: Output {Numerele trebuie sa fie strict pozitive!}
12: end if
pe instructiunile while si repeat sunt mult mai potrivite n cazul n care conditia de ter-
minare trebuie reevaluata n timpul cicl
arii (atunci c
and num
arul de repetitii nu este cunoscut
apriori).
Instructiunea for are urmatoarea sintaxa:
1: for <Var> <Expresie1>, <Expresie2>, 1: sum 0
Step < p > do 2: for i 1, n do
2: <instructiune> 3: sum sum + i
3: end for 4: end for
Comportamentul enuntului repetitiv for se descrie cel mai bine prin comparatie cu
enuntul repetitiv while. Astfel secventa urmatoare de limbaj pseudocod
1: for v e1 , e2 ST EP p do
2: < instructiune >
3: end for
este echivalenta cu secventa ce contine enuntul repetitiv while:
1: v e1
2: while (v e2 ) do
3: <instructiune>
4: v v+p
5: end while
e1 reprezinta valoarea initiala, e2 limita finala, iar p pasul de lucru: la fiecare pas, valoarea
variabilei v este incrementata cu valoarea variabilei p, valoarea ce poate fi atat pozitiva cat
si negativa. Daca p 0 atunci v va lua valori n ordine crescatoare, iar daca p < 0 atunci v
va primi valori n ordine descrescatoare.
In cazul n care pasul de incrementare este 1, atunci el se poate omite din enuntul repetitiv
for, variabila contor fiind incrementata automat la fiecare repetitie cu valoarea 1. Daca pasul
de incrementare p are valoarea 1 atunci avem urmatoarele situatii posibile pentru constructia
for:
7
sir de elemente poate fi pastrat ntr-o structur
a de date elementara, cunoscut a sub numele
de vector. Un vector este un caz particular de matrice av and o singura linie si n coloane.
Prezentam n continuare algoritmul pentru determinarea elementului de valoare minima
ce apartine unui sir de numere naturale (a se vedea algoritmul 3).
S1 = 1
S2 = 1 + 2 = S1 + 2
S3 = (1 + 2) + 3 = S2 + 3
S4 = (1 + 2 + 3) + 4 = S3 + 4
...
Sn = (1 + 2 + . . . + n 1) + n = Sn1 + n
8
Algoritm 4 Algoritm pentru calculul sumei primelor n numere naturale (prima varianta)
1: Input {N }
2: S0
3: i1
4: repeat
5: S S+i
6: ii+1
7: until (i > N )
8: Output {S}
Suma primelor n numere naturale se constituie n suma termenilor unei progresii arit-
metice ce se poate calcula direct cu formula sumei, astfel 1 + 2 + . . . + n = n(n+1)
2
. Avand
n vedere aceasta identitate, algoritmul de calcul a sumei primelor n numere naturale se
simplifica foarte mult (a se vedea algoritmul 5).
Algoritm 5 Algoritm pentru calculul sumei primelor n numere naturale (a doua varianta)
1: Input {N }
2: S n (n + 1)/2
3: Output {S}
9
Proceduri si functii
Procedurile sunt subrutine ale caror instructiuni se executa ori de cate ori acestea sunt apelate
prin numele lor.
Apelarea procedurilor se face n unitatea de program apelanta prin numele procedurii,
primit n momentul definirii, urmat de lista parametrilor actuali. Aceasta lista trebuie sa
corespunda ca numar si tip cu lista parametrilor formali, n situatia n care exista o lista de
parametri formali n antetul subrutinei. Definitia unei proceduri are urmatoarea sintaxa:
1: procedure <nume>(<lista parametri formali>)
2: <instructiune>
3: end procedure
lista parametri formali (optionala) simbolizeaza o lista de identificatori (parametri)
ce permite transferul datelor ntre subrutina apelant a si subrutina apelat
a. Acesti parametri
se specifica prin nume (identificator) urmat, eventual, de tipul de data al parametrului. Mai
multi parametri de acelasi tip pot fi grupati, folosindu-se drept separator virgula.
De fapt, lista parametrilor formali poate fi descrisa mai detaliat astfel:
1: procedure <nume>(<PFI; PFO>)
2: end procedure
unde
PFI = lista parametrilor formali de intrare.
PFO = lista parametrilor formali de iesire.
Enuntul de apel al unei proceduri are urmatoarea sintaxa:
1: CALL <nume>(PAI; PAO)
PAI - lista parametrilor actuali de intrare, ce corespund parametrilor formali de intrare.
Corespondenta se face de la stanga la dreapta si trebuie sa fie de acelasi tip cu parametrii
formali.
PAO - lista parametrilor actuali de iesire.
Exemplul 1.6 Sa se realizeze un algoritm care determin a cel mai mare divizor comun a
doua numere naturale.
Reamintim cateva rezultate teoretice ce vor fi folositoare pentru ntelegerea procesului de
calcul al algoritmului ce va fi prezentat.
a = b q + r, 0 r < |b|
Definitia 1.2 Cel mai mare divizor comun (notat cmmdc) a dou
a numere naturale a si b
este un numar natural d cu proprietatile:
2. d N astfel ncat d |a, d |b avem d |d (oricare alt divizor comun al lui a si b, l divide
si pe d).
10
3. Intre cel mai mic multiplu comun si cel mai mare divizor comun exist
a urmatoarea
relatie:
ab
cmmmc(a, b) = . (1.1)
cmmdc(a, b)
Metoda de calcul pentru a obtine cel mai mare divizor comun a dou a numere a si b prin
mpartiri succesive, cunoscuta sub numele de algoritmul lui Euclid, se poate descrie astfel:
Se mparte a la b si se retine restul r. Dac a r este nul atunci cel mai mare divizor comun
este b. In caz contrar, se mparte b la r si se p astreaza noul rest. Calculele se continua,
folosindu-se ca dempartit vechiul mp artitor, iar, ca mp
artitor, ultimul rest obtinut, pana
cand restul obtinut devine nul.
Operatiile anterioare pot fi transpuse prin intermediul urm atoarelor formule matematice:
a = bq1 + r1 0 r1 < b
b = r1 q2 + r2 0 r 2 < r1
r1 = r2 q3 + r3 0 r 3 < r2
...
rk = rk+1 qk+2 + rk+2 0 rk+2 < rk+1
...
rn2 = rn1 qn + rn 0 rn < rn1
rn1 = rn qn+1 + rn+1 0 rn+1 < rn
Aceste mpartiri nu se constituie ntr-un proces infinit, deoarece secventa de numere natu-
rale r1 , r2 , . . . , rn+1 , . . . este strict descresc
atoare (r1 > r2 > . . . > rn > . . .) si marginita
inferior de 0. Rezulta ca p N astfel nc at rp 6= 0, si rp+1 = 0.
Daca analizam ntregul proces de calcul, se observa faptul ca dempartitul este mpartitorul
de la etapa anterioara, iar mpartitorul este restul de la etapa anterioara. De asemenea, tre-
buie mentionat ca n acest proces de calcul, catul nu participa n mod activ.
Algoritmul 6 este un foarte bun exemplu de utilizare a instructiunii de ciclare cu test
initial, while. Din analiza algoritmului reiese faptul ca, mai ntai, se efectueaza o evaluare a
restului r (calculul sau, liniile 7 si 11), dupa care se testeaza conditia egalitatii acestuia cu
valoarea 0 (linia 8).
11
Functiile au aceeasi sintaxa ca si procedurile:
1: function <nume>(<parametri>)
2: <instructiune>
3: end function
Functiile pot fi apelate prin numele lor, ca termen al unei expresii.
1.2 Exemple
Exemplul 1.9 Sa se verifice printr-un algoritm dac
a un num
ar natural este prim sau nu.
Daca n este numar prim, corpul enuntului de ciclare while se va executa de n 2 ori (a
se vedea algoritmul 7).
Conditia i n 1 poate fi mbunat a cu i n/2, deoarece ntre jum
atit atatea numarului
si n nu mai exista niciun alt divizor, pentru orice valoare a lui n.
Daca 2 este un divizor al numarului n atunci si n/2 este un divizor al lui n. Daca 2 nu
este divizor al lui n atunci nici n/2 nu este divizor al lui n. Un enunt asemanator este valabil
12
si pentru numerele 3, 4, 5, . . .. Astfel se formeaz
a dou
a siruri:
corespunde n
2
2
corespunde n
3
3
corespunde n
4
4
.. ..
. .
corespunde n
k
|{z} k
|{z}
Sir1 Sir2
Se observa faptul ca primul sir (Sir1 ) este compus din elemente cu valori consecutive,
a proprietate. Numerele dintr-o pereche (p, np ) sunt
iar al doilea sir (Sir2 ) nu respecta aceast
legate ntre ele astfel: n momentul n care am verificat faptul c a num arul p nu este divizor
al lui n, implicit am dedus ca nici n/p nu este un divizor al lui n. Prin urmare nu mai este
nevoie sa verificam si valoarea n/p. Primul sir este strict cresc ator iar cel de-al doilea este
strict descrescator. Elementele din primul
sir devin mai mari dec at cele din al doilea atunci
cand k n/k k 2 n k n. Astfel rezult a conditia pentru limita superioara a
ciclarii: i n (a se vedea algoritmul 8).
Algoritm 8 Algoritm pentru verificarea daca un numar este prim (varianta optimizata)
1: Input {N }
2: if (n < 2) then
3: Output {Numarul nu este prim.}
4: else
5: i2
6: prim true
7: while ((i n) (prim = true)) do
8: if (n mod i = 0) then
9: prim f alse
10: else
11: i i+1
12: end if
13: end while
14: if (prim = true) then
15: Output {Numarul este prim.}
16: else
17: Output {Numarul nu este prim.}
18: end if
19: end if
Exemplul 1.10 Se spune ca un vector este simetric dac a primul element este egal cu ultimul,
al doilea cu penultimul etc. Algoritmul urm ator verific
a dac
a un vector de numere ntregi este
simetric.
Vom utiliza doua variabile de indici, ce pornesc, una din st anga, si cealalt
a din dreapta.
Atata timp cat variabila din stanga este mai mica decat variabila din dreapta iar elementele
din vector corespunzatoare variabilelor de indiciere sunt egale, se incrementeaz a variabila din
stanga si se decrementeaza variabila din dreapta. Dup a par
asirea instructiunii de ciclare, se
13
face o verificare pentru determinarea conditiei care nu a mai fost ndeplinit a si a condus la
iesirea din bucla. Daca i j atunci conditia (i < j) nu a mai fost ndeplinit a. Acest lucru
conduce la concluzia ca cealalta conditie, (xi = xj ) a fost ndeplinit
a tot timpul (i < j, i, j =
1, n). Deci sirul este simetric n aceast
a situatie.
Exemplul 1.11 Se da un numar format din n cifre. Vom prezenta un algoritm n care se
determina cel mai mare numar obtinut prin eliminarea a k cifre (k < n) dintre cele n (vezi
algoritmul 10).
Compararea a doua numere naturale ce prezint a acelasi num ar de cifre se face ncepand
de la stanga spre dreapta (de la cifrele mai semnificative c atre cele mai putin semnificative).
Fie A = a1 a2 . . . an numarul nostru.Vom nota prin m num arul de cifre ce vor r
amane din
numarul initial dupa eliminarea celor k cifre (m = nk). Problema se reduce la determinarea
celui mai mare numar format cu m cifre dintre cele n cifre initiale.
La nceput numarul contine n cifre, p astrate n ordine, n vectorul A: a1 , a2 , . . . , an .
Solutia se construieste n mod iterativ, la pasul i aleg anduse o valoare corespunzatoare: se
determina prima cifra de valoare maxim a si pozitia acesteia n cadrul subsecventei de limite
l si n m + i, al , . . . , anm+i .
In instructiunea for (linia 4) pentru fiecare pozitie libera din numarul rezultat, se va
alege cifra maxima dintre cele disponibile;
linia 5 - se trece la pozitia urmatoare: l reprezinta indexul din sirul A al cifrei aleasa
la pasul anterior (i 1) si contine pozitia pe care a fost gasita cifra maxima. Cifra
maxima pentru pasul curent va fi cautata n intervalul l . . . n m + i, unde n m + i
reprezinta limita superioara a indexului pana la care se poate alege un element la pasul
curent;
a1 . . . a ...a . . . an
| l {znm+i}
subsecventa pentru care se determin
a cifra maxim
a
linia 8 - se cauta cifra maxima pana la limita din dreapta n m + i (daca se merge mai
la dreapta dincolo de aceasta limita nu se mai pot alege restul de m i cifre);
linia 10 - se actualizeaza valoarea maxima;
14
Algoritm 10 Algoritm pentru determinarea numarului maxim obtinut dupa eliminarea a k
cifre
1: Input {n, a1 , . . . , an , k}
2: m nk
3: l0
4: for i 1, m do
5: l l+1
6: jl
7: max 0
8: while (j n m + i) do
9: if (max < aj ) then
10: max aj
11: lj
12: end if
13: j j+1
14: end while
15: Output {max}
16: end for
Exemplul 1.12 Vom prezenta doi algoritmi ce determin a toate tripletele de numere naturale
(a, b, c) ce verifica relatia a2 + b2 = c2 unde c 100.
Prima varianta de lucru prezinta generarea tuturor tripletelor (i, j, k) unde 2 i < j <
k n. Dintre acestea se vor alege doar acelea ce verific a relatia k k = i i + j j. Daca vom
considera doar instructiunea de ciclare 4, atunci conditia 5 se va verifica de n (j + 1) + 1 =
n j ori. Deoarece j = i + 1, n 1, vom obtine faptul c a instructiunea 5 se va executa de:
ni1
X (n i) (n i 1)
(n i 1) + (n i 2) + . . . + (n n + 1) = j=
j=1
2
ori.
Din i = 2, n 2 rezulta ca
n2 ni1 n2 n2
X X X (n i) (n i 1) 1X 2
j = = (i + (1 2n)i + n2 n)
i=2 j=1 i=2
2 2 i=2
n2 n2
1X 2 X
= [ i + (1 2n) i + (n2 n) (n 3)]
2 i=2 i=2
15
1: Input {n} 1: Input {n}
2: for i 2, n 2 do 2: for i 2, n 1 do
3: for j i + 1, n 1 do 3: for j i + 1, n do
4: for k j + 1, n do 4: s ii+j j
5: if (k k = i i + j j) then 5: k s
6: Output {i, j, k} 6: if ((k k = s) (k n)) then
7: end if 7: Output {i, j, k}
8: end for 8: end if
9: end for 9: end for
10: end for 10: end for
11: return 11: return
Pentru cea de-a doua varianta, cea din dreapta, se genereaz a perechile de numere (i, j) cu
proprietatea ca 2 i < j n, se calculeaz
2 a s i i + j j si se verific
a daca numarul
obtinut este patrat perfect (linia 6: ( s) = s). Conditia de la linia 6 se va executa de
n (i + 1) + 1 = n i ori.
Deoarece linia 2 se va executa de n 1 2 + 1 = n 2 ori, valoarea total a reprezentand
numarul de verificari (linia 6) va fi:
n1 n1 n1
X X X n(n 1)
(n i) = (n i) (n 1) = i (n 1) = n+1
i=2 i=1 i=1
2
Definitia 1.4 Timpul de executie n cazul cel mai defavorabil al unui algoritm A este o
functie TA : N N unde TA (n) reprezint a numarul maxim de instructiuni executate de
catre A n cazul unor date de intrare de dimensiune n.
Definitia 1.5 Timpul mediu de executie al unui algoritm A este o functie TAmed : N N
unde TAmed (n) reprezinta numarul mediu de instructiuni executate de c
atre A n cazul unor
date de intrare de dimensiune n.
Definitia 1.6 Fiind data o functie g : N R vom nota cu O(g(n)) multimea: O(g(n)) =
{f (n)| c > 0, n0 0 a.. 0 f (n) c g(n), n n0 }.
16
Vom spune despre f ca nu creste n mod sigur mai repede decat functia g. Pentru a indica
faptul ca o functie f (n) este un membru al multimii O(g(n)), vom scrie f (n) = O(g(n)), n
loc de f (n) O(g(n)).
Definitia 1.7 Fiind data o functie g : N R vom nota cu (g(n)) multimea: (g(n)) =
{f (n)| c1 , c2 > 0, n0 0 a.. 0 c1 g(n) f (n) c2 g(n), n n0 }
Definitia 1.8 Fiind data o functie g : N R vom nota cu (g(n)) multimea: (g(n)) =
{f (n)| c > 0, n0 0 a.. 0 c g(n) f (n), n n0 }
Teorema 1.14 Pentru orice doua functii f (n) si g(n), avem f (n) = (g(n)) f (n) =
O(g(n)) si f (n) = (g(n)).
Definitia 1.9 Fiind data o functie g : N R vom nota cu o(g(n)) multimea: o(g(n)) =
{f (n)| c > 0, n0 > 0 a.. 0 f (n) < c g(n), n n0 }
f (n)
Observatia 1.15 f (n) = o(g(n)) limn g(n)
= 0.
Definitia 1.11 O functie f are o crestere exponential a c > 1 a.i. f (x) = (cx ) si
a dac
d a.. f (x) = O(dx ). O functie f este polinomial a f (n) = (nd ) si
a de gradul d dac
d
f (n) = O(n ), d d.
17
Teorema 1.17
c g(n) (f (n)), c > 0
g(n)
lim = 0 g(n) o(f (n)) (1.2)
n f (n)
f (n) o(g(n))
rezulta ca
xn
lim =0 (1.4)
n n!
log n o(n).
1
log x (log x) x ln 2
lim = lim = lim =0
x x x (x) x 1
Prezentam n continuare cateva reguli generale pentru evaluarea complexitatii unui algoritm:
timpul de executie al unei instructiuni de atribuire, citire sau afisare a unei variabile
este O(1).
timpul de executie al unei instructiuni de decizie (if) este timpul executarii instructiu-
nilor de pe ramura aleasa plus timpul necesar evaluarii conditiei.
timpul de executie pentru o instructiune de ciclare este suma, dupa numarul de pasi
pe care i realizeaza instructiunea de ciclare, dintre timpul necesar executarii corpului
instructiunii plus timpul necesar evaluarii conditiei.
1: for i 1, n do
18
2: A(i)
3: end for
1: for i 1, n do
2: s0
3: for j 1, i do
4: s s + aj
5: end for
6: bi si
7: end for
8: return
19
Daca notam cu o constanta cx timpul necesar pentru efectuarea unei operatii atomice,
vom obtine: costul efectuarii liniei 1 este c1 n, al liniei 2 este c2 n, al liniei 3 este c3 i, al
liniei 4 este c4 i, al liniei 6 este c5 n iar al liniei 8 este c6 .
n
X n
X n
X n
X
T (n) = c1 n + c2 n + c3 i + c4 i + c5 n + c6 = c1 n + c2 n + c3 i + c4 i + c5 n + c6
i=1 i=1 i=1 i=1
n(n + 1)
= (c3 + c4 ) + n(c1 + c2 + c5 ) + c6
2
1 1
= (c3 + c4 )n2 + (c3 + c4)n + n(c1 + c2 + c5 ) + c6
2 2
1 1
= (c3 + c4 )n2 + [ (c3 + c4 ) + c1 + c2 + c5 ]n + c6 = n2 p + n q + c6
2 2
De obicei nu se efectueaza o analiza atat de detaliat
a a algoritmului, dar se ncearca o eval-
uare a blocurilor principale, cum ar fi instructiunile de ciclare, atribuindu-le direct valori
corespunzatoare complexitatii timp, atunci c
and este posibil:
Vom presupune ca solutia acestei relatii de recurenta este T (n) = O(n log n) (notam
log2 n = log n). Folosind metoda inductiei matematice, vom ncerca sa demonstram inegali-
tatea:
T (n) cn log n. (1.9)
Presupunem, mai ntai, ca inecuatia (1.9) are loc pentru k < n, inclusiv pentru n2 , adica
T ( n2 ) c n2 log ( n2 ).
20
Vom demonstra ca inecuatia (1.9) este ndeplinita si pentru n: T (n) = 2T ( n2 ) + n.
n n n
T (n) 2(c log ) + n = cn log + n
2 2 2
= cn log n cn log 2 + n = cn log n cn + n (1.10)
cn log n
Metoda inductiei matematice presupune sa aratam ca solutia respecta si cazurile particulare
(T (1) = 1). Pentru n = 1 vom avea T (1) = c 1 log 1 = 0 ceea ce contrazice relatia T (1) = 1.
Aceasta situatie poate fi rezolvata daca consideram ca T (n) = cn log n, n n0 (n0 este
o constanta).
Din T (2) = 4 si T (3) = 5 vom alege valoarea parametrului c astfel ncat sa fie ndeplinite
inegalitatile T (2) 2c log 2 si T (3) 3c log 3. Se observa ca orice valoare a lui c 2 satisface
inegalitatile anterioare.
S(m) = O(m log m) T (n) = T (2m ) = S(m) = O(m log m) = O(log n log log n) (1.13)
In concluzie avem T (n) = O(log n log log n).
Inlocuind succesiv pe n cu n
vom obtine urmatoarea serie de identitati:
2
n
T (n) = 2T ( ) + n
2
n n n
= 2(2T ( ) + ) + n = 4T ( ) + 2n
2 2 4
n n n (1.17)
= 4(2T ( ) + ) + 2n = 8T ( ) + 3n
8 4 8
...
n
= 2k T ( k ) + kn
2
n
Deoarece substitutia se opreste atunci cand 2k
= 1, adica pentru k = log n, vom avea:
Teorema 1.21 (Teorema master) [30] Fie a 1 si b > 1 dou a constante, f (n) o functie
asimptotic pozitiva, si T (n) definita de relatia de recurenta T (n) = aT ( nb ) + f (n), unde nb va
fi nb sau nb .
Atunci T (n) este marginita asimptotic dup a cum urmeaz a:
2. daca f (n) = (nlogb a logk n), atunci T (n) = (nlogb a logk+1 n) (de obicei k = 0);
Corolarul 1.22 Pentru o functie f de tip polinomial unde f (n) = cnk avem:
22
3. daca a < bk atunci T (n) = O(nk ).
p0 p1 p2 p3 p4 p5 p6
9 6 3 4 2 5 7
d0 d1 d2 d3 d4 d5 d6
1 1 1 2 1 4 6
23
Algoritmul 11 calculeaza deschiderea unei actiuni pentru un interval mai lung de timp pe
baza evolutiei preturilor acesteia. Timpul de executie al acestui algoritm este O(n2 ).
Vom prezenta un alt mod de calcul al deschiderii unei actiuni folosind o stiv a (a se vedea
algoritmul 12).
Stiva este o structura de date de tip container deoarece ea depoziteaz a elemente de un
anumit tip. Pentru a putea sa operam asupra unei colectii de elemente p astrate ntr-o stiva,
se definesc urmatoarele operatii, n afar
a de operatiile fundamentale push si pop:
init(capacitate:integer) - initializeaz
a stiva.
24
1.4 Exercitii
1. (a) Sa se determine daca un numar natural este simetric sau nu.
(b) Sa se determine toate cifrele distincte dintr-un numar natural.
(c) Sa se determine reprezentarea n baza 2 a unui numar natural.
(d) Sa se determine forma corespunzatoare n baza 10 a unui numar reprezentat n
baza 2.
(e) Sa se elimine dintrun numar natural toate cifrele de forma 3k + 1 si sa se afiseze
numarul rezultat.
(f) Pentru un numar natural dat sa se construiasca din cifrele acestuia cel mai mare
numar prin amplasarea mai ntai a cifrelor impare si apoi a cifrelor pare.
25
Algoritm 13 Algoritm pentru descompunerea n factori primi a unui numar natural
1: Input {n}
2: j2
3: while (j n) do
4: if (n mod j = 0) then
5: k0
6: while (n mod j = 0) do
7: k k+1
8: n n div j
9: end while
10: Output {j k }
11: end if
12: j j +1
13: end while
(a) T (n) = 2T ( n2 ) + n
log n
;
(b) T (n) = 16T ( n4 ) + n!;
(c) T (n) = 3T ( n3 ) + n;
(d) T (n) = 3T ( n3 ) + n2 .
26
11. Sa se rezolve urmatoarele ecuatii recurente:
T (n) = 2 T ( n2 ) + n lg n, n = 2k ;
T (n) = 3 T ( n2 ) + c n, n = 2k > 1.
14. Sa se determine primele n elemente ale sirurilor ak si bk date prin urmatoarele relatii
de recurenta:
5ak + 3 ak + 3
ak+1 = , bk = , k 0, a0 = 1. (1.22)
ak + 3 ak + 1
15. Sa se verifice daca urmatoarele afirmatii sunt adevarate:
(a) n2 O(n3 );
(b) n3 O(n2 );
(c) 2n+1 O(2n );
(d) (n + 1)! O(n!);
(e) f : N R , f O(n) f 2 O(n2 );
(f) f : N R , f O(n) 2f O(2n ).
27
Capitolul 2
28
mai numesc capetele muchiei. Doua muchii sunt adiacente daca au un varf comun. Un graf
este trivial daca are un singur varf. Daca E = atunci graful G = (V, E) se numeste graf
nul.
Observatia 2.1 Un graf neorientat, simplu, finit poate fi utilizat drept model de reprezentare
al unei relatii simetrice peste o multime.
a) G = (V, E), V = {1, 2, 3, 4, 5, 6}, E = {[1, 2], [1, 5], [2, 3], [2, 6], [3, 4], [4, 5], [5, 6]} (vezi
figura 2.1 a));
Fig. 2.2: a) Un exemplu de graf neorientat cu 5 varfuri. b) Subgraf al grafului din figura 2.1 b). c) Graf
partial al grafului din figura 2.1 b).
Definitia 2.3 Un graf partial al unui graf dat G = (V, E) este un graf G1 = (V, E1 ) unde
E1 E.
Definitia 2.4 Un subgraf al unui graf G = (V, E) este un graf H = (V1 , E1 ) unde V1 V
iar muchiile din E1 sunt toate muchiile din E care au ambele extremitati n multimea V1
(E1 = E|V1 V1 = {[x, y]|[x, y] E, x, y V1 }).
- G x = subgraful G {x};
29
- G E1 =< E \ E1 >G ;
Exemplul 2.3 Graful partial din figura 2.2 c) se obtine din graful 2.1 b) prin stergerea
muchiilor [2, 4], [2, 5], [3, 5], [4, 5]. Subgraful din figura 2.2 b) este indus de multimea V1 =
{1, 3, 4, 5} din graful complet K5 (H = K5 |V1 ).
Definitia 2.5 Gradul unui varf este egal cu num arul muchiilor incidente cu v
arful x si se
noteaza cu d(x) (d(x) = |{[x, u]|[x, u] E, u V }|). Un v
arf cu gradul 0 (d(x) = 0) se
numeste varf izolat.
Exemplul 2.4 Graful lui Petersen din figura 2.3 este un exemplu de graf trivalent sau cubic
(toate varfurile grafului au acelasi grad, 3).
Propozitia 2.5 Pentru un graf G = (V, E), V = {x1 , x2 , . . . , xn }, |E| = m, avem urmatoarea
relatie:
Xn
d(xk ) = 2m. (2.1)
k=1
Astfel n orice graf G exista un numar par de varfuri al caror grad este un numar impar.
30
Daca varfurile v0 , v1 , . . . , vm sunt distincte doua cate doua, lantul se numeste elementar
(vi 6= vj , i, j = 0, m).
Definitia 2.9 Se numeste ciclu hamiltonian un ciclu elementar ce trece prin toate varfurile
grafului. Un graf ce admite un ciclu hamiltonian se numeste graf Hamilton sau graf
hamiltonian.
Exemplul 2.7 [1, 2, 3, 1, 4] este un exemplu de lant n graful din figura 2.2 c). Varfurile 1
si 4 sunt extremitatile lantului. Lantul nu este elementar deoarece v arful 1 se ntalneste de
doua ori, n schimb [1, 2, 3, 4, 1] este un ciclu elementar.
[3, 4, 5, 1, 2, 3] este un ciclu hamiltonian n graful din figura 2.2 a). [4, 5, 1, 2, 4, 3] este un
acest graf nu exist
lant eulerian, precum si [2, 4, 3, 2, 1, 5, 4]. In a nici un ciclu eulerian.
Definitia 2.13 Un graf planar este un graf ce poate fi reprezentat n plan astfel ncat
muchiile sale sa nu se intersecteze dou
a c
ate dou
a.
31
Propozitia 2.9 Un graf este bipartit dac
a si numai dac
a nu contine cicluri de lungime
impara.
Exemplul 2.10 In figura 2.5 a) este ilustrat un graf bipartit (V1 = {1, 2, 3}, V2 = {4, 5}),
iar n cazul b) avem un graf bipartit complet, K2,3 .
Vom prezenta n continuare cateva operatii dintre cele mai ntalnite ce se pot efectua
asupra unui graf oarecare:
fiind dat un nod se cere lista vecinilor sai. Aceasta operatie este cea mai utilizata pentru
o serie ntreaga de algoritmi pe grafuri;
fiind data o pereche de noduri {u, v} se cere sa se determine daca aceasta constituie o
muchie sau un arc al grafului;
fiind data o muchie sau un nod se cere o informatie asociata acestui element: de exemplu
lungimea muchiei sau distanta de la sursa la nodul specificat.
32
2.2 Operatii pe grafuri
1. Complementarul grafului G = (V, E) se defineste astfel: este graful Gc = (V c , E c ), unde
V c = V si E c = {[x, y]|x, y V, [x, y]
/ E}. Altfel spus E c = (V V ) \ E.
In figura 2.6 b) este reprezentat graful complementar Gc al grafului G = (V, E), din
figura 2.6 a) (V = {1, 2, 3, 4, 5}, E = {[1, 2], [1, 5], [2, 3], [3, 4], [4, 5]}). Conform definitiei
Gc = (V c , E c ) unde V c = V si E c = {[1, 3], [1, 4], [2, 4], [2, 5], [3, 5]}.
2. Graful obtinut din G = (V, E) prin insertia unui varf v / V pe o muchie [x, y] E
este graful Gi = (V i , E i ) unde V i = V {v}, E i = (E \ {[x, y]}) {[x, v], [v, y]}.
Considerand graful din figura 2.6 a), prin insertia varfului 6 pe muchia [1, 5] se obtine
graful din figura 2.7 a).
3. Graful obtinut din G = (V, E) prin contractia unei muchii u = [x, y] la un varf t este
graful Gct = (V ct , E ct ), unde V ct = (V \ {x, y}) {t}.
Fig. 2.7: a) Graful obtinut prin inserarea varfului 6 n graful din figura 2.6 a). b) Graful obtinut prin
contractia muchiei [3, 4] n graful din figura 2.6 a).
In figura 2.7 b) este reprezentat graful obtinut prin contractia muchiei [3, 4] din graful
G = (V, E) (vezi figura 2.6 a)).
4. Se numeste graf reprezentativ al muchiilor unui graf G = (V, E), graful GR = (VR , ER )
n care |VR | = |E| si ER = {[e, f ]|e, f E adiacente} (vezi figura 2.8).
5. Definim graful total al unui graf G = (V, E) ca fiind graful GT = (VT , ET ) unde VT =
V E si ET = {[u, v]|u, v V E iar u si v sunt adiacente sau incidente n G}.
33
Fig. 2.8: Figura b) prezinta graful reprezentativ al muchiilor grafului din figura a).
Fig. 2.9: a) Un exemplu de graf neorientat. b) Graful total al grafului din figura a).
Graful total GT = (VT , ET ), unde VT = {1, 2, 3, 4, v1, v2 , v3 , v4 }, si E = {[1, 2], [1, 4],
[2, 3], [3, 4], [v1, v2 ], [v1 , v4 ], [v2 , v3 ], [v3 , v4 ], [v1 , 1], [v1 , 2], [v2 , 2], [v2 , 3], [v3 , 3], [v3 , 4], [v4 , 4], [v4 , 1]},
corespunde grafului G = (V, E), V = {1, 2, 3, 4}, E = {[1, 2], [1, 4], [2, 3], [3, 4]} (vezi
figura 2.9).
34
Fig. 2.10: a) Doua grafuri neorientate, G1 si G2 . b) Grafurile complementare Gc1 si Gc2 . c) Reuniunea
grafurilor Gc1 si Gc2 . d) Graful complementar al grafului reuniune (Gc1 Gc2 )c .
35
Fig. 2.12: Graful rezultat n urma compunerii grafurilor G1 si G2 din figura 2.11
36
Fig. 2.14: Un exemplu de graf neorientat ponderat
Matricea costurilor va ocupa un spatiu de memorie mai mare decat cel ocupat de
matricea de adiacenta: pentru reprezentarea unui element al matricii de adiacenta este
suficient un bit, pe cand pentru reprezentarea unui element al matricii costurilor se
foloseste un byte, un ntreg (lung) sau un num
ar real, n functie de valoarea maxima pe
care o poate lua d.
37
Observatia 2.15 Matricea costurilor pentru un graf neorientat este simetrica.
3. Liste de adiacenta - pentru fiecare nod se retine lista tuturor nodurilor sale adiacente.
Mai exact, unui nod xk se ataseaza lista tuturor vecinilor sai.
Aceasta reprezentare se preteaza mai bine pentru grafuri cu un numar mare de noduri si
un numar mic de muchii (graf rar ). In cazul unui graf neorientat, numarul elementelor
din listele de adiacenta este 2 |E|, deoarece o muchie [u, v] va fi prezenta de doua ori,
atat n lista de adiacenta a nodului u cat si n lista de adiacenta a nodului v.
Aceste liste de adiacenta (sau liste de vecini) pot fi reprezentate, ca structuri de date,
prin intermediul tablourilor sau prin intermediul listelor simplu sau dublu nlantuite.
Pentru reprezentarea ce utilizeaza tablouri, se vor defini doua tablouri notate Cap si
List (Cap M1n (N), List M22m (N), n = |V |, m = |E|) unde:
0 , daca nodul respectiv nu are vecini
Capk = j , unde j indica numarul coloanei din matricea List unde se afla memorat
primul vecin al varfului xk .
38
1 2 3 4 NULL
2 1 5 NULL
3 1 5 NULL
4 1 6 NULL
5 2 3 7 NULL
6 4 7 8 NULL
7 5 6 NULL
8 6 NULL
4. Lista de muchii - ntr-o structura de date se pastreaza lista tuturor muchiilor grafului,
practic, pentru fiecare muchie fiind memorate valorile indicilor nodurilor-extremitati
ale acesteia. Aceasta constituie cea mai simpla modalitate de reprezentare a unui graf.
39
Operatia de adaugare a unui nod sau a unei muchii se realizeaza ntr-un timp constant,
alte operatii fiind mai costisitoare: de exemplu, determinarea listei de vecini a unui nod
necesita un timp (m).
Exemplul 2.18 Pentru pastrarea n memorie se poate folosi o matrice M cu doua linii
si |E| coloane (M M2|E|(N)), unde:
M1,k - indicele varfului ce reprezint
a prima extremitate a muchiei k;
M2,k - indicele varfului ce reprezint
a cea de-a doua extremitate a muchiei k.
1 1 1 2 3 4 5 6 6
M=
2 3 4 5 5 6 7 7 8
Complexitatea timp pentru diverse operatii efectuate asupra fiecaruia dintre structurile
de date utilizate de modurile de reprezentare amintite se poate sintetiza astfel:
Matricea de adiacent
a Liste de vecini Lista de muchii
Gradul unui nod xi O(n) O(d(xi )) O(m)
(xi , xj ) E? O(1) O(d(xi )) O(m)
Urm
atorul vecin al lui xi O(n) O(d(xi )) O(m)
2.4.1 Parcurgerea n l
atime (BFS-Breadth First Search)
Metoda de parcurgere n latime viziteaza nodurile grafului n felul urmator (a se vedea algo-
ritmul 16):
40
Fig. 2.16: Arbore de acoperire n latime
i nt n ; // Numarul de n o d u r i d i n g r a f
char v e c i n [MAXN] [MAXN] ; // M a t r i c e a de a d i a c e n t a
i nt coada [MAXN] ; // S t r u c t u r a de da te c e p a s t r e a z a e l e m e n t e l e c o z i i
i nt f i r s t ; // I n d i c e l e p r i m u l u i element d i n coada
i nt l a s t ; // I n d i c e l e u l t i m u l u i element d i n coada
41
i nt v i d a ; // P a s t r e a z a s t a r e a c o z i i
/
Initializarea cozii circulare .
/
void i n i t Q u e u e ( void ) {
v i d a = TRUE;
f i r s t = 0;
l a s t = MAXN;
}
/
I n t o a r c e pentr u p o z i t i a k , ur ma to a r ea p o z i t i e d i n coada .
/
i nt next ( i nt k ) {
return ( k + 1 ) % MAXN;
}
/
I n s e r e a z a e l e m e n t u l a c a r u i v a l o a r e e s t e p a s t r a t a i n v a r i a b i l a v i n coada .
/
void enQueue ( i nt v ) {
l a s t = next ( l a s t ) ;
coada [ l a s t ] = v ;
i f ( vida )
v i d a = FALSE ;
}
/
E xtr a g e un element d i n coada .
/
i nt deQueue ( void ) {
i nt v = coada [ f i r s t ] ;
f i r s t = next ( f i r s t ) ;
i f ( f i r s t == next ( l a s t ) )
v i d a = TRUE;
return v ;
}
/
P a r cur g e i n l a t i m e g r a f u l po r nind de l a no dul de s t a r t k .
/
void b f s ( i nt k ) {
i nt i ;
char v i z i t a t [MAXN] ;
memset ( v i z i t a t , 0 , s i z e o f ( v i z i t a t ) ) ;
vizitat [ k] = 1;
enQueue ( k ) ;
p r i n t f ( %d , k ) ;
while ( ! v i d a ) {
k = deQueue ( ) ;
for ( i = 0 ; i < n ; i ++)
i f ( ( v i z i t a t [ i ] == 0 ) && ( v e c i n [ k ] [ i ] == 1 ) ) {
vizitat [ i ] = 1;
enQueue ( i ) ;
p r i n t f ( %d , i ) ;
}
}
42
}
/
Se c i t e s c numarul de n o d u r i precum s i m a t r i c e a de a d i a c e n t a .
/
void r e a d I n p u t ( void ) {
i nt i , j ;
p r i n t f ( n = ) ; s c a n f ( %d ,&n ) ;
for ( i = 0 ; i < n 1 ; i ++)
for ( j = i + 1 ; j < n ; j ++) {
p r i n t f ( a[%d,%d ] = , i , j ) ;
s c a n f ( %d , &v e c i n [ i ] [ j ] ) ;
vecin [ j ] [ i ] = vecin [ i ] [ j ] ;
}
}
In exemplul anterior, functia de citire readInput() este una foarte simpla: se citeste
mai ntai numarul de noduri al grafului, iar apoi, se citesc elementele matricei de
adiacenta a grafului. Variabilele n si vecin sunt declarate drept variabile globale.
Deoarece matricea vecin este simetrica, pentru a reduce numarul de citiri, se vor
solicita numai valorile aflate deasupra diagonalei principale:
void r e a d I n p u t ( void ) {
i nt i , j ;
p r i n t f ( n = ) ; s c a n f ( %d ,&n ) ;
for ( i = 0 ; i < n 1 ; i ++)
for ( j = i + 1 ; j < n ; j ++) {
p r i n t f ( a[%d,%d ] = , i , j ) ;
s c a n f ( %d , &v e c i n [ i ] [ j ] ) ;
vecin [ j ] [ i ] = vecin [ i ] [ j ] ;
}
}
Functia memset() este o functie de biblioteca a carei declaratie poate fi gasita n header-
ele mem.h si string.h la Borland C sau memory.h si string.h la Visual C++ 2010 1.
Functia respectiva prezinta urmatoarea semnatura2
void *memset(void *dest, int c, size t count);
si are rolul de a initializa cu valoarea c primii count octeti din zona de memorie ce
ncepe de la adresa identificata de pointerul dest.
vida este o variabila globala ce ia valoarea true atunci cand coada nu contine nici un
element, sau valoarea false n caz contrar.
1
http://msdn.microsoft.com/en-us/library/1fdeehz6.aspx
2
http://en.wikibooks.org/wiki/C_Programming/Strings#The_memset_function
43
Structura de date abstracta de tip coada este implementata static sub forma unei cozi
circulare.
la pasul urmator se considera ultimul nod vizitat v si se ncearca vizitarea vecinilor nca
nevizitati ai acestuia. Daca nodul v nu mai are vecini nevizitati, se continua procesul
de parcurgere cu nodul ce a fost vizitat exact naintea nodului v, etc.
Metoda de parcurgere D este o combinatie ntre metoda de parcurgere n latime (a se
vedea sectiunea 2.4.1) si metoda de parcurgere n adancime (a se vedea sectiunea 2.4.3).
Pentru un nod, se viziteaza toti vecinii nca nevizitati ai acestuia, nsa spre deosebire de
metoda de parcurgere n latime unde ordinea n care au fost vizitate nodurile se pastreaza si
la parcurgere, la metoda de parcurgere D se prelucreaza mereu ultimul nod vizitat.
Pentru aceasta, n algoritmul BFS se nlocuieste structura de date de tip coada, cu o
structura de date de tip stiva si se obtine algoritmul D (a se vedea algoritmul 17).
44
Algoritm 17 Algoritm de vizitare D
1: procedure D(k, n, V ecin)
2: for i 1, n do
3: vizitati 0
4: end for
5: vizitatk 1 marcarea nodului curent ca fiind vizitat
6: Sk inserarea nodului curent k n stiva
7: call V izitare(k) vizitarea nodului curent
8: while (S 6= ) do
9: Sk extragerea nodului curent din stiv a
10: for j 1, n do
11: if ((vizitatj = 0) (vecink,j = 1)) then se cauta un vecin nevizitat
12: vizitatj 1 marcarea nodului j ca fiind vizitat
13: Sj inserarea nodului j in stiva
14: call V izitare(j) vizitarea nodului j
15: end if
16: end for
17: end while
18: end procedure
2.4.3 Parcurgerea n ad
ancime (DFS-Depth First Search)
In cadrul acestei metode se va merge n adancime ori de cate ori este posibil: prelucrarea
unui varf consta n prelucrarea primului dintre vecinii sai nca nevizitati.
se cauta primul vecin, nca nevizitat, al primului vecin nevizitat al nodului de start,
s.a.m.d.;
se merge n adancime pana cand se ajunge la un varf ce nu are vecini, sau pentru care
toti vecinii sai au fost vizitati. In acest caz, se revine n nodul sau parinte (nodul din
care a fost vizitat nodul curent), si se continua algoritmul, cu urmatorul vecin nca
nevizitat al nodului curent.
Pentru graful considerat (vezi figura 2.13), n urma parcurgerii n adancime, vom obtine
nodurile n ordinea urmatoare: 1, 2, 5, 3, 7, 6, 4, 8 (a se vedea figura 2.18). Daca ne propunem
sa vizitam exact o singura data toate nodurile unui graf, aplicand algoritmul DFS de cate ori
este nevoie, si selectam numai muchiile utilizate n timpul explorarii, rezulta o padure de
arbori. Fiecare dintre acesti arbori constituie un arbore de acoperire n ad ancime.
In urma parcurgerii n adancime, muchiile unui graf pot fi clasificate n urmatoarele ca-
tegorii:
2. muchie de ntoarcere - muchia [u, v] este o muchie de ntoarcere daca dfs(u) apeleaza
indirect dfs(v) (x V a.. df s(u) df s(x) df s(v)) sau invers, dfs(v) apeleaza
indirect dfs(u).
45
Fig. 2.18: Arbore de acoperire n adancime
muchiile [1, 2], [2, 5], [3, 5], [4, 6], [5, 7], [6, 7], [6, 8] sunt muchii ale arborelui de acoperire;
46
Enuntul repetitiv for i 1, n (linia 4) poate fi optimizat astfel ncat sa nu se mai
verifice toate varfurile grafului, ci numai nodurile adiacente cu nodul curent: n aceasta
situatie reprezentarea cu liste de adiacenta este optima din punct de vedere al numarului de
operatii efectuate, fiind preferata reprezentarii prin matricea de adiacenta.
Algoritmul 19 utilizeaza o structura de date de tip stiva S pentru a pastra tot timpul nodul
grafului din care s-a ajuns la nodul curent (tatal nodului curent din arborele de acoperire n
adancime). Secventa de instructiuni 19.13-19.25 nu este optima deoarece, de fiecare data cand
se revine la un nod parinte, se verifica ntreaga multime de noduri V pentru a identifica un
vecin nevizitat al nodului curent. In vederea reducerii numarului de verificari, se poate utiliza
reprezentarea prin liste de adiacenta, si se salveaza pe stiva, pe langa valoarea nodului curent
k, valoarea vecinului nevizitat gasit al acestuia (sa l notam cu u), astfel ncat, la revenire,
sa se continue cautarea urmatorului vecin nevizitat al nodului curent k, pornind de la acest
nod u. Daca nu mai exista nici un vecin nevizitat al varfului k (linia 10), atunci se revine la
parintele nodului curent, pastrat pe stiva. Algoritmul se opreste n cazul n care stiva este
vida (atunci cand nodul curent este radacina arborelui de vizitare n adancime).
Am ales n vederea implementarii, varianta recursiva pentru usurinta de programare si
eleganta ei. Prezentam n continuare aceasta implementare a algoritmului 18 n limbajul C:
47
Listing 2.2: dfsrec.c
#include <s t d i o . h>
#include <mem. h>
/
Se c i t e s c numarul de n o d u r i precum s i m a t r i c e a de a d i a c e n t a .
/
void r e a d I n p u t ( void ) {
i nt i , j ;
p r i n t f ( n = ) ; s c a n f ( %d , &n ) ;
for ( i = 0 ; i < n 1 ; i ++)
for ( j = i + 1 ; j < n ; j ++) {
p r i n t f ( a[%d,%d ] = , i , j ) ;
s c a n f ( %d , &v e c i n [ i ] [ j ] ) ;
vecin [ j ] [ i ] = vecin [ i ] [ j ] ;
}
p r i n t f ( Nodul i n i t i a l = ) ; s c a n f ( %d , &no di ) ;
memset ( v i z i t a t , 0 , s i z e o f ( v i z i t a t ) ) ;
}
/
P a r c u r g e r e a i n adancime po r nind d i n no dul k .
/
void d f s ( i nt k ) {
i nt i ;
vizitat [ k] = 1;
p r i n t f ( %d , k ) ;
for ( i = 0 ; i < n ; i ++)
i f ( v i z i t a t [ i ] == 0 && v e c i n [ k ] [ i ] == 1 )
dfs ( i ) ;
}
Matricea de adiacent
a Liste de vecini Lista de muchii
BFS 2
O(n ) O(n + m) O(n + m2 )
D O(n2 ) O(n + m) O(n + m2 )
DFS O(n2 ) O(n + m) O(n + m2 )
48
2.5 Componente conexe
Definim pe multimea varfurilor V a unui graf neorientat G o relatie n felul urmator: xy
daca x = y sau exista n G un lant de la x la y.
Se demonstreaza foarte usor ca relatia este o relatie de echivalenta (exercitiu). Se
cunoaste faptul ca o relatie de echivalenta determina pe multimea pe care este definita o
partitie. Componentele conexe vor fi elementele acestei partitii, formate din varfurile ce sunt
echivalente ntre ele (conform relatiei de echivalenta).
Cu alte cuvinte, exista o partitie a multimii varfurilor V
m
[
V = Vi , Vi Vj = , i, j = 1, m, i 6= j (2.4)
i=1
( ComponenteConexe(n, V ecin)
1: procedure
n - numarul de noduri din graf
Input:
V ecin - matricea de adiacenta a grafului
2: for i 1, n do
3: vizitati 0
4: end for
5: cmp conex nr 0
6: for i 1, n do
7: if (vizitati = 0) then
8: cmp conex nr cmp conex nr + 1
9: call DF S(i, n, V ecin) determinarea componentei conexe ce contine nodul i
10: end if
11: end for
12: end procedure
Pas 3. Incepand cu acesta se parcurg toate nodurile accesibile si nevizitate, avand grija sa
marcam vizitarea acestora. Toate aceste noduri formeaza o componenta conexa.
Pas 4. Daca mai exista noduri nevizitate, se reia procesul de calcul de la pasul 2, altfel procesul
de calcul se ncheie.
49
2.6 Muchie critic
a
Reamintim faptul ca ntr-un graf neorientat G = (V, E), o muchie critic a este acea muchie
care prin eliminarea ei conduce la cresterea numarului de componente conexe ale grafului.
Se cere sa se determine toate muchiile critice ale unui graf dat. In continuare sunt prezen-
tate doua variante de rezolvare.
Solutia I
Pentru simplitate, vom trata situatia n care graful considerat este conex (daca graful nu este
conex se determina componentele conexe si numarul acestora si se aplica algoritmul pentru
fiecare componenta conexa n parte). Se elimina, pe rand, fiecare muchie a grafului si apoi
se verifica daca graful rezultat mai este conex (a se vedea algoritmul 21).
Subrutina Conex() verifica daca graful identificat prin matricea de adiacenta Vecin este
conex (este compus dintr-o singura componenta conexa). Subrutina ntoarce valoarea true
n cazul n care graful considerat este conex si false, n caz contrar. Pentru aceasta, la
nceput, se marcheaza toate nodurile ca fiind nevizitate, si se ncearca parcurgerea nodurilor
grafului, prin intermediul unui apel al subrutinei de vizitare, DFS(1, n, Vecin).
Implementarea algoritmului 21 n limbajul C este urmatoarea:
Listing 2.3: muchiecriticav1.c
#include <s t d i o . h>
#include <mem. h>
50
#define MAXN 100
#define TRUE 1
#define FALSE 0
/
Se c i t e s c numarul de n o d u r i precum s i m a t r i c e a de a d i a c e n t a .
/
void r e a d I n p u t ( void ) {
i nt i , j ;
p r i n t f ( n = ) ; s c a n f ( %d , &n ) ;
for ( i = 0 ; i < n 1 ; i ++)
for ( j = i + 1 ; j < n ; j ++) {
p r i n t f ( a[%d,%d ] = , i , j ) ;
s c a n f ( %d , &v e c i n [ i ] [ j ] ) ;
vecin [ j ] [ i ] = vecin [ i ] [ j ] ;
}
}
/
P a r c u r g e r e a i n adancime a g r a f u l u i po r nind d i n no dul de s t a r t k .
/
void d f s ( i nt k ) {
i nt i ;
v i z i t a t [ k ] = TRUE;
p r i n t f ( %d , k ) ;
for ( i = 0 ; i < n ; i ++)
i f ( ( v i z i t a t [ i ] == FALSE) && ( v e c i n [ k ] [ i ] == 1 ) )
dfs ( i ) ;
}
/
F u n c t i a v e r i f i c a daca g r a f u l e s t e conex .
/
i nt conex ( void ) {
i nt i ;
memset ( v i z i t a t , 0 , s i z e o f ( v i z i t a t ) ) ;
dfs (0 );
for ( i = 0 ; i < n ; i ++)
i f ( v i z i t a t [ i ] == FALSE) {
return FALSE ;
}
return TRUE;
}
readInput ( ) ;
for ( i = 1 ; i < n ; i ++)
for ( j = 0 ; j < i ; j ++)
i f ( v e c i n [ i ] [ j ] == 1 ) {
51
vecin [ i ] [ j ] = 0;
vecin [ j ] [ i ] = 0;
i f ( conex ( ) == FALSE) {
p r i n t f ( Muchia (%d,%d ) e s t e c r i t i c a ! \n , i , j ) ;
}
vecin [ i ] [ j ] = 1;
vecin [ j ] [ i ] = 1;
}
}
Solutia a II-a
Cea de-a doua varianta de abordare pentru identificarea unei solutii foloseste urmatoarea
proprietate:
Observatia 2.21 O muchie nu este critic
a dac
a ea face parte din cel putin un ciclu elemen-
tar al grafului respectiv.
In urma vizitarii DFS, toate muchiile unui graf se mpart n muchii ale arborelui de
acoperire si muchii de ntoarcere.
Vom numerota toate nodurile grafului n preordine (numerotarea se realizeaza efectiv n
momentul n care un nod este marcat ca fiind vizitat), toate valorile fiind pastrate ntr-un
vector prenum. Fie lowu valoarea unui nod u, calculata dupa formula urmatoare:
prenumu
lowu = min prenumx , daca [u, x] este muchie de ntoarcere (2.6)
lowy , y descendent direct al lui u.
Daca prenumu lowv , v descendent direct al lui u, atunci nseamna ca nodul v sau un
descendent al lui v prezinta o muchie de ntoarcere la u sau la un stramos al acestuia. Astfel
muchia [u, v] apartine unui ciclu elementar, si, prin urmare, nu este muchie critica (a se vedea
algoritmul 22). Prin negatie, daca exista cel putin un varf v, descendent direct al lui u, cu
proprietatea ca prenumu < lowv atunci [u, v] este muchie critica. Dupa fiecare apel recursiv
al subrutinei DFS (linia 16) se verifica gradul de adevar al expresiei (prenumk < lowi ) (linia
18).
Variabila counter este globala pentru cele doua subrutine, MuchieCriticaII() si
DFS critic(). La numerotarea n preordine se folosesc atribuirile: counter counter + 1,
si prenumk counter. Cand un nod i este vecin cu nodul curent k, si nu a fost nca vizitat,
valoarea lui lowk se calculeaza astfel: lowk min {lowk , lowi}. In situatia n care pentru un
nod i, vecin cu nodul curent k, deja vizitat, avem o muchie de ntoarcere (nodului k i s-a
atribuit deja un numar n preordine), atunci valoarea lowk se calculeaza dupa formula
52
Algoritm 22 Algoritm de determinare a muchiei critice (a doua varianta)
( MuchieCriticaII(n, V ecin)
1: procedure
n - num arul de noduri din graf
Input:
V ecin - matricea de adiacent a
2: for i 1, n do
3: vizitati 0
4: end for
5: counter 0
6: call DF S critic(1, n, V ecin) vizitarea n ad
ancime a grafului
7: end procedure
8: procedure DFS critic(k, n, V ecin)
9: vizitatk 1
10: counter counter + 1
11: prenumk counter, lowk counter
12: for i 1, n do
13: if (vecink,i = 1) then
14: if (vizitati = 0) then
15: tatai k
16: call DF S critic(i, n, V ecin)
17: lowk M in(lowk , lowi )
18: if (prenumk < lowi ) then
19: Output {Muchia (k,i) este critica}
20: end if
21: else
22: if (tatak 6= i) then
23: lowk M in(lowk , prenumi )
24: end if
25: end if
26: end if
27: end for
28: end procedure
void r e a d I n p u t ( void ) {
i nt i , j ;
p r i n t f ( n = ) ; s c a n f ( %d , &n ) ;
for ( i = 0 ; i < n 1 ; i ++)
53
for ( j = i + 1 ; j < n ; j ++) {
p r i n t f ( a[%d,%d ] = , i , j ) ;
s c a n f ( %d , &v e c i n [ i ] [ j ] ) ;
vecin [ j ] [ i ] = vecin [ i ] [ j ] ;
}
}
i nt min ( i nt x , i nt y ) {
return ( x < y ) ? x : y ;
}
void d f s ( i nt k ) {
i nt i ;
vizitat [ k] = 1;
c o u n t e r++;
prenum [ k ] = c o u n t e r ; low [ k ] = c o u n t e r ;
for ( i = 0 ; i < n ; i ++)
i f ( v e c i n [ k ] [ i ] == 1 )
i f ( v i z i t a t [ i ] == 0 ) {
tata [ i ] = k ;
dfs ( i ) ;
low [ k ] = min ( low [ k ] , low [ i ] ) ;
i f ( prenum [ k ] < low [ i ] )
p r i n t f ( %d > %d \n , k , i ) ;
}
else
i f ( t a t a [ k ] != i )
low [ k ] = min ( low [ k ] , prenum [ i ] ) ;
/ p r i n t f ( prenum[%d ] = %d low[%d ] = %d\n , k , prenum [ k ] , k , low [ k ] ) ; /
}
void c r i t i c ( void ) {
memset ( v i z i t a t , 0 , s i z e o f ( v i z i t a t ) ) ;
counter = 0;
dfs (0 );
}
Exemplul 2.22 Pentru graful din figura 2.13, valorile prenum si low sunt cele din figura
2.19 (valoarea din partea dreapta a unui v arf reprezinta valoarea sa la numerotarea n preor-
dine, prenum, iar numarul din stanga reprezint a valoarea calculat
a low). Conditia prenumu <
lowv , unde [u, v] este o muchie a grafului, este ndeplinita doar pentru u = 6 si v = 8. Prin
urmare [6, 8] este muchie critica n graful considerat.
Deoarece acest algoritm este constituit din algoritmul modificat de vizitare n adancime
a unui graf, complexitatea-timp este O(n + m) atunci cand graful este reprezentat prin liste
de adiacenta si O(n2 ) n situatia n care graful este reprezentat prin matricea de adiacenta.
54
Fig. 2.19: Algoritmul 22 aplicat pentru graful din figura 2.13
2.7 Exercitii
1. Un graf neorientat cu n noduri, G = (V, E) se numeste graf scorpion daca poseda trei
noduri speciale:
(c) corpul - dG (u) = n 2 fiind legat de toate nodurile din graf cu exceptia acului.
4. Compania TLC (Telephone Line Company) a stabilit o noua retea de cablu telefonic.
Ea leaga mai multe locatii numerotate cu numere ntregi de la 1 la N. Nu exista doua
locatii numerotate cu acelasi numar. Liniile sunt bidirectionale si conecteaza doua
locatii; fiecare locatie are un post telefonic. Din orice locatie poate fi apelata oricare
55
Fig. 2.20: Un exemplu de graf neorientat cu 8 varfuri
alta locatie, prin legatura directa sau conexiuni intermediare. Uneori reteaua cade n
unele locatii si conexiunea aferenta nu mai este posibila. Tehnicienii de la TLC au
realizat ca, n acest caz, nu numai ca locatia respectiva nu mai poate fi apelata, dar
ea ntrerupe legatura si ntre alte locatii pentru care asigura conexiunea. In aceasta
situatie spunem ca locatia (unde a aparut caderea) este critic a.
Se cere sa se elaboreze un algoritm care sa determine numarul tuturor acestor puncte
critice.
(ACM Europa Centrala, 1996)
9. Pe o tarla agricola sunt mai multe parcele ce trebuie cosite. Parcelele de diferite culturi
(lucerna, trifoi, iarba, furaje) vor fi cosite de muncitori diferiti ce lucreaza numai la
56
un anumit fel de cultura (de exemplu un muncitor este specializat numai pe cositul
lucernei).
Terenul agricol se reprezinta printr-o matrice n m (n linii si m coloane). Fiecare
element al matricii corespunde unei multimi de un anumit tip. Doua elemente ale
matricii sunt vecine daca au o latura comuna. O parcela este o multime maximala de
elemente astfel ncat un muncitor se poate deplasa ntre oricare doua elemente de-a
lungul a doua elemente vecine.
Se cere sa se determine numarul de muncitori care pot sa coseasca o cultura data.
57
Capitolul 3
Definitia 3.1 Un lant L ce contine fiecare muchie a unui graf G o singur a data se numeste
lant Euler sau lant eulerian. Dac a extremit
atile lantului sunt identice si lantul este
eulerian atunci ciclul se numeste ciclu Euler sau ciclu eulerian. Un graf ce contine un
ciclu eulerian se numeste graf eulerian.
O problema mai cunoscuta legata de notiunile de ciclu si lant eulerian este aceea de a
desena cu o singura linie nentrerupta o anumita figura (a se vedea figura 3.2).
58
Fig. 3.2: Doua figuri geometrice ce pot fi desenate folosind o singura linie
Teorema 3.4 Pentru un graf conex cu 2k v arfuri de grad impar, k 1, exist a k lanturi n
G ale caror multimi de muchii formeaz
a o partitie a multimii E, si din care cel mult unul
are lungimea impara.
59
Fig. 3.3: Exemplu de graf eulerian
Dupa cum se poate vedea din functia IsEulerian() numai componentele conexe ce au
ordinul mai mare sau egal cu 3 vor fi luate n considerare.
60
In algoritmul 24 este prezentata functia EulerRec() pentru determinarea unui ciclu eu-
lerian.
Procedura FindCycle(G) (a se vedea algoritmul 24) construieste un ciclu: se alege un
varf u0 si se cauta prima muchie [u0 , u1 ]. In varful u1 se cauta o muchie [u1 , u2 ] astfel ncat
varful u2 sa fie distinct de varful din care s-a ajuns n u1 (u2 6= u0 ) si sa nu mai fi fost vizitat.
Daca u2 a fost vizitat atunci functia se termina. Se continua procedeul de cautare pana cand
se ajunge la un varf ce a fost vizitat anterior si se returneaza ciclul cuprins ntre cele doua
aparitii ale aceluiasi varf.
Procedura F indComponents(G ; k, G11 , G12 , . . . , G1k ) determina componentele conexe ale
grafului G = G E(C), obtinut prin eliminarea din graful G a muchiilor ciclului C, unde
G11 , G12 , . . . , G1k sunt cele k componente conexe returnate de aceasta.
Procedura MergeCycles(C, C11 , . . . , Ck1 ; C0 ) construieste un ciclul C0 obtinut prin adaugarea
la ciclul C, succesiv, a ciclurilor C11 , . . . , Ck1 .
Fig. 3.4: Grafurile partiale obtinute n urma primelor doua etape ale algoritmului 24
Exemplul 3.5 Sa aplicam algoritmul 24 pentru graful din figura 3.3. Presupunem ca primul
element (elementul de start) este varful u0 = 1. Procedura F indCycle construieste lantul
61
Fig. 3.5: Grafurile partiale obtinute n urma etapelor 3 si 4 ale algoritmului 24
L = [1, 2, 3, 4, 5, 3]. Se observa prezenta ciclului C1 = [3, 4, 5, 3]. In urma elimin arii acestuia
ramane graful din figura 3.4 a) care este un graf conex.
Continuam procesul de construire a unui ciclu eulerian, prin apelul procedurii F indCycle
pentru noul graf. Aceasta returneaza ciclul C2 = [1, 2, 3, 9, 1]. Prin eliminarea muchiilor
acestuia, E(C2 ), se obtine graful din figura 3.4 b). In continuare se construieste lantul
L = [2, 8, 7, 5, 6, 4, 10, 2], ce contine ciclul C3 = [2, 8, 7, 5, 6, 4, 10, 2]. Graful obtinut prin
eliminarea muchiilor ciclului este cel din figura 3.5 a). Apelul procedurii F indCycle conduce
la determinarea lantului L = [6, 7, 9, 8, 10, 6] si a ciclului C4 = [6, 7, 9, 8, 10, 6].
Ciclul eulerian se formeaza prin reuniunea ciclurilor intermediare determinate: C =
(((C4 C3 ) C2 ) C1 ) adica C = [1, 2, 8, 7, 5, 6, 7, 9, 8, 10, 6, 4, 10, 2, 3, 4, 5, 3, 9, 1] (C4 C3 =
[2, 8, 7, 5, 6, 7, 9, 8, 10, 6, 4, 10, 2]).
Exemplul 3.6 Sa aplicam algoritmul 25 pentru graful din figura 3.3. S a presupunem ca
varful u de la care porneste algoritmul este v
arful 1. La sf
arsitul primului pas al enuntului
repetitiv while (liniile 6 14) avem valorile:
62
Algoritm 25 Algoritm lui Rosenstiehl de determinare a unui ciclu eulerian ntrun graf
conex
1: function Rosenstiehl(u, G)
2: for e E do
3: vizite 0
4: end for
5: Su Se insereaz
a pe stiv
a nodul u
6: while (S 6= ) do
7: Su Se extrage din stiv
a nodul curent
8: while (e = [u, v] E) (vizite = 0)) do
9: vizite 1 Se marcheaz a muchia e ca fiind utilizat
a
10: Su Se salveaz
a pe stiv
a nodul curent u
11: uv Nodul curent devine nodul v
12: end while
13: Lu Se adaug
a la lista L nodul curent
14: end while
15: return L
16: end function
avem:
S = [1, 2, 3, 4, 5, 3, 9, 7, 5, 6, 4, 10, 2, 8, 7, 6, 10, 8], L = [1, 9], u = 9.
In continuare, deoarece nu mai exista muchii nevizitate, se vor extrage elementele aflate pe
stiva S cate unul la fiecare pas, formand secventa (8, 10, 6, 7, 8, 2, 10, 4, 6, 5, 7, 9, 3, 5, 4, 3, 2, 1),
si se vor introduce n lista L:
u s i n g namespace s t d ;
typedef v e c t o r <int> L i n i e ;
typedef v e c t o r <L i n i e > M a t r i c e ;
i nt r e a d I n p u t ( M a t r i c e& ma) {
i nt n , i , j , v a l u e ;
i f s t r e a m f i n ( INPUT FILE ) ;
f i n >> n ;
ma = M a t r i c e ( n , L i n i e ( n , 0 ) ) ;
63
for ( i = 0 ; i < n ; i ++)
for ( j = 0 ; j < n ; j ++) {
f i n >> ma [ i ] [ j ] ;
}
void p r i n t ( l i s t <int>& l ) {
l i s t <int > : : i t e r a t o r i t ;
co ut << C i c l u e u l e r i a n : [ ;
for ( i t = l . b e g i n ( ) ; i t != l . end ( ) ; i t ++)
co ut << i t << , ;
co ut << ] \ n ;
}
s . push ( u ) ;
while ( ! s . empty ( ) ) {
u = s . top ( ) ;
s . pop ( ) ;
v = 0;
while ( v < n ) {
i f (ma [ u ] [ v ] == 1 ) {
ma [ u ] [ v ] = 0 ;
ma [ v ] [ u ] = 0 ;
s . push ( u ) ;
u = v;
v = 0;
}
else
v++;
}
l . push ba ck ( u ) ;
}
}
r o s e n s t i e h l ( n , ma , 0 , l ) ;
print ( l ) ;
}
Datele de intrare vor fi preluate dintr-un fisier. Continutul fisierului de intrare graf1.txt
corespunzator grafului din figura 3.6 este urmatorul:
6
0 1 1 1 1 0
1 0 1 1 1 0
1 1 0 1 0 1
64
1 1 1 0 0 1
1 1 0 0 0 0
0 0 1 1 0 0
Pentru a reduce timpul de implementare, am utilizat cateva structuri de date deja exis-
tente n limbajul C++, mai exact structuri de date implementate cu ajutorul template-urilor
din cadrul librariei Standard Template Library - STL2 3 :
lista - list 5 . Pentru a pastra elementele ciclului eulerian am folosit o lista liniara,
informatia utila din cadrul nodurilor fiind constituita din valori ntregi: list<int> l
= list<int>();.
Dupa cum se poate observa din programul anterior, pentru reprezentarea interna a
grafului s-a folosit matricea de adiacent
a.
65
void p r i n t ( l i s t <int>& l ) {
l i s t <int > : : i t e r a t o r i t ;
co ut << C i c l u e u l e r i a n : [ ;
for ( i t = l . b e g i n ( ) ; i t != l . end ( ) ; i t ++)
co ut << i t << , ;
co ut << ] \ n ;
}
Deoarece muchiile sunt parcurse o singura data n cadrul acestui algoritm, si pentru a
nu mai pastra o structura auxiliara care sa marcheze faptul ca o muchie a fost vizitata, vom
marca parcurgerea unei muchii prin stergerea acesteia din matricea de adiacenta astfel:
ma [ u ] [ v ] = 0 ;
ma [ v ] [ u ] = 0 ;
Ciclul eulerian obtinut pentru graful din figura 3.6 este: L = [1, 5, 2, 4, 6, 3, 4,
1, 3, 2, 1].
Se porneste cu un varf oarecare al grafului G (G = (V, E), |V | = n, |E| = m). Ideea consta
n a construi un lant prin alegerea la fiecare pas a unei muchii nealeas a la pasii anteriori, si
care, de preferat, sa nu fie muchie critica n graful partial obtinut prin eliminarea muchiilor
deja alese.
Sa consideram ca, la un moment dat, avem construit un lant Lk = [v0 , [v0 , v1 ], v1 , . . . ,
[vk1 , vk ], vk ]. Cautam o muchie ek+1 = [vk , vk+1 ] astfel ncat ek+1 / Lk si care nu este muchie
critica n graful partial Gk = G {e1 , e2 , . . . , ek } (graful obtinut prin eliminarea tuturor
muchiilor lantului Lk ). Daca nu exista decat muchii critice n graful partial Gk incidente cu
vk atunci alegem una dintre ele.
Daca exista o muchie ek+1 cu aceste proprietati atunci construim lantul Lk+1 = [Lk , ek+1, vk+1 ]
(Lk+1 = [v0 , [v0 , v1 ], v1 , . . . , [vk1 , vk ], vk , [vk , vk+1 ], vk+1 ]). In momentul n care E(Lk ) = E(G)
algoritmul lui Fleury se opreste (vezi algoritmul 26).
66
Algoritm 26 Algoritm lui Fleury de determinare a unui ciclu eulerian ntrun graf conex
1: function Fleury(u0 , G = (V, E))
2: k0
3: L0 [u0 ]
4: while (k m) do
5: caut ek+1 = [vk , vk+1 ] a.i. ek+1
/ Lk si ek+1 , de preferat, nu este muchie critic
a n graful
Gk = G {e1 , e2 , . . . , ek }
6: Lk+1 [Lk , ek+1 , vk+1 ]
7: k k+1
8: end while
9: return Lk
10: end function
Exemplul 3.7 Sa aplicam algoritmul 26 pentru graful din figura 3.7. La nceput L0 = [u1 ].
Apoi se intra n ciclul while (liniile 4 8): tabelul urm
ator indic
a muchia ce a fost aleasa
la fiecare pas k, precum si configuratia lantului Lk .
67
3.2 Grafuri Hamiltoniene
Definitia 3.2 Se numeste lant Hamilton sau lant hamiltonian un lant L ce trece o
singura data prin toate varfurile unui graf.
Definitia 3.3 Se numeste ciclu hamiltonian un ciclu elementar ce trece prin toate varfurile
grafului. Un graf ce admite un ciclu hamiltonian se numeste graf hamiltonian.
Problema determinarii daca un graf oarecare G este hamiltonian este o problema dificila,
atentia cercetatorilor ndreptanduse catre enuntarea unor conditii suficiente de existenta a
unui ciclu hamiltonian.
Demonstratie: Daca G este hamiltonian, atunci cu atat mai mult, graful G+[u, v]
este hamiltonian.
Presupunem ca G + [u, v] este un graf hamiltonian. Atunci exista un ciclu hamil-
tonian n graful G + [u, v] pe care l notam cu C.
1. daca [u, v]
/ C atunci C este un ciclu hamiltonian si n graful G G este un graf
hamiltonian.
2. daca [u, v] C atunci C = [u, v, x3 , x4 , . . . , xn1 , xn , u]. Pentru fiecare muchie [u, xk ]
E putem avea urmatoarele situatii:
(a) [v, xk+1] E. Atunci ciclul C1 = [u, xk , xk1 , . . . , x3 , v, xk+1, . . . , xn , u] este hamil-
tonian n graful G graful G este hamiltonian.
(b) [v, xk+1] / E. Notam dG+[u,v] (u) = k. Atunci dG+[u,v] (v) n k 1. si De aici,
rezulta ca dG (u) + dG (v) < dG+[u,v] (u) + dG+[u,v] (v) n k + k 1 = n 1 < n.
Contradictie cu dG (u) + dG (v) n, u, v V, u 6= v, [u, v] / E.
Teorema 3.9 (Dirac, 1952) Un graf G = (V, E) (|V | 3) este hamiltonian daca u V
avem dG (u) n2 (orice varf al grafului are gradul mai mare dec
at jum
atate din numarul de
varfuri din graf ).
Teorema 3.10 (Ore, 1961) Un graf G = (V, E) (|V | 3) este hamiltonian dac a u, v V
avem dG (u) + dG (v) n unde u 6= v, [u, v] / E (pentru oricare doua v
arfuri distincte,
neadiacente, ale grafului suma gradelor lor este mai mare dec
at num
arul de v
arfuri din graf ).
68
Definitia 3.4 Pentru un graf G construim un sir de grafuri G = G1 , G2 , . . . astfel: graful
Gk+1 se obtine din graful Gk prin ad augarea muchiei [uk , vk ], unde v arfurile uk , vk V nu
sunt adiacente n Gk si dG (uk ) + dG (vk ) n. Procesul se ncheie n momentul n care nu
at dG (up ) + dG (vp ) n. Graful Gp se
mai exista doua varfuri neadiacente distincte astfel nc
numeste nchiderea lui G si se noteaz a cu cl(G).
Lema 3.12 Orice graf prezinta o singur
a nchidere.
Corolarul 3.13 Un graf G = (V, E) (|V | 3) este hamiltonian dac
a cl(G) Kn (daca
nchiderea lui G este izomorfa cu graful complet de ordinul n).
Definim (G) = min{dG (u)|u V } si (G) = max{dG (u)|u V }.
a (G) n2 .
Corolarul 3.14 Un graf G = (V, E) (|V | 3) este hamiltonian dac
Definitia 3.5 O multime de varfuri A a unui graf G se spune c a este independenta daca
oricare doua elemente distincte din A sunt independente. Num arul de independenta al lui G,
notat cu (G), reprezinta numarul maxim de v arfuri dintro multime independenta.
(G) = 1 daca si numai daca graful G este complet.
Definitia 3.6 Pentru un graf G se numeste conectivitatea lui G, si not am cu (G),
numarul minim de varfuri ale unei taieturi. O t
aietur
a este o submultime U a lui V astfel
ncat graful G U sa fie neconex.
and ordinul n 3, este hamiltonian daca (G)
Teorema 3.15 Un graf G = (V, E) av
(G).
Observatia 3.16 Pentru un graf bipartit G = (V1 , V2 , E) conditia necesar
a pentru a fi hamil-
tonian este |V1 | = |V2 |.
Problema comisvoiajorului include problema determinarii existentei unui ciclu hamilto-
nian ntrun graf.
Cautarea optimului printre toate variantele de cicluri nu este o solutie fezabila deoarece
pentru un graf G numarul de cicluri poate fi foarte mare. De exemplu pentru graful complet
Kn avem (n1)!
2
cicluri hamiltoniene distincte.
Exemplul 3.17 Graful din figura 2.20 nu este hamiltonian (nu admite un ciclu hamilto-
nian).
Graful din figura 3.6 are mai multe cicluri hamiltoniene: de exemplu ciclurile C1 = [1, 5,
2, 3, 6, 4, 1] si C2 = [1, 3, 6, 4, 2, 5, 1].
Nod 1 2 3 4 5 6
dG 4 4 4 4 2 2
Teorema lui Dirac (1952) nu se poate aplica deoarece nu este ndeplinit
a conditia orice varf
al grafului are gradul mai mare decat jum
atate din num
arul de varfuri din graf:
n 6
dG (5) = 2 < = = 3.
2 2
De asemenea, pentru teorema lui Ore (1961) nu este ndeplinit a conditia pentru oricare doua
varfuri dinstincte, neadiacente, ale grafului suma gradelor lor este mai mare dec at numarul
de varfuri din graf: fie varfurile 5 si 6, neadiacente, pentru care avem
dG (5) + dG (6) = 2 + 2 = 4 < 6.
69
Pentru graful din figura 3.8 avem:
Nod 1 2 3 4 5 6 7 8
dG 3 4 3 4 4 5 3 4
a: dG (1) = 3 < 4 = n2 ;
conditia teoremei lui Dirac (1952), nu este ndeplinit
conditia teoremei lui Ore (1961), nu este ndeplinit
a: dG (2) + dG (7) = 4 + 3 = 7 < 8;
observam ca nici conditiile teoremei lui Chvatal (1972) nu sunt ndeplinite: fie d1 , d2 , . . . , dn
o secventa grafica.
Nod 1 3 7 2 4 5 8 6
dG 3 3 3 4 4 4 4 5
70
Fig. 3.8: Exemplu de graf pentru problema comis-voiajorului
C1 = [1, 2, 4, 5, 6, 7, 8, 3, 1].
C2 = [1, 2, 5, 4, 8, 7, 6, 3, 1].
C3 = [1, 5, 6, 2, 4, 7, 8, 3, 1].
C4 = [1, 3, 6, 7, 8, 4, 2, 5, 1].
Fig. 3.9: Rezultatul rularii programului pentru determinarea ciclului hamiltonian optim
71
Algoritm 27 Algoritm pentru problema comis-voiajorului
(
A - matricea de costuri ce contine distantele dintre orase
Input:
n - num arul de orase
1: function CanContinue(A, X, k, cost)
2: if (vizitatxk = 1) (cost + axk1 ,xk > costoptim ) ((k 6= n + 1) (xk = x1 ))) then
3: return f alse
4: else
5: return true
6: end if
7: end function
72
#define LIMIT 100000 // lung imea maxima a u n e i muchii e c h i v a l e n t a cu i n f i n i t
long c o s t o p t i m ; // c o s t u l c i r c u i t u l u i h a m i l t o n i a n optim
Vecto r x o ptim ; // e l e m e n t e l e c i r c u i t u l u i h a m i l t o n i a n optim
/
F u n c t i a c i t e s t e v a l o r i l e d a t e l o r de i n t r a r e .
/
i nt r e a d I n p u t ( M a t r i c e a ) {
i nt n ;
i nt i , j , max = 0 ;
s c a n f ( %d , &n ) ;
c o s t o p t i m = n max ;
return n ;
}
/
F u n c t i a v e r i f i c a daca a + b > c .
/
i nt mai mare ( long a , long b , long c ) {
if (a + b > c)
return 1 ;
else
return 0 ;
}
/
Functia a f i s e a z a s o l u t i a c a l c u l a t a a problemei .
/
void A f i s a r e S o l u t i e ( i nt n ) {
i nt i ;
p r i n t f ( C o s t u l c i c l u l u i optim : %l d \n , c o s t o p t i m ) ;
p r i n t f ( Ciclu hamiltonian : [ ) ;
for ( i = 1 ; i < n+1; i ++)
p r i n t f ( %d , , x o ptim [ i ] ) ;
p r i n t f ( %d ] \ n , x o ptim [ n + 1 ] ) ;
}
/
F u n c t i a de c o n t i n u a r e : a i c i s e v e r i f i c a daca e l e m e n t u l de pe p o z i t i a k
nu s e mai a f l a i n s i r u l A.
/
i nt CanContinue ( i nt n , M a t r i c e a , i nt k , long c o s t , Vecto r v i z i t a t , Vecto r x ) {
i f ( ( v i z i t a t [ x [ k ] ] == 1 ) | | ( mai mare ( c o s t , a [ x [ k 1 ] ] [ x [ k ] ] , c o s t o p t i m ) )
73
| | ( ( k != n+1) && ( x [ k ] == 1 ) ) )
return 0 ;
else
return 1 ;
}
/
F u n c t i a s a l v e a z a s o l u t i a optima ( c i r c u i t u l h a m i l t o n i a n de c o s t minim )
d e t e r m i n a t a / g a s i t a pana i n momentul c u r e n t .
/
void E v a l u a r e S o l u t i e ( i nt n , long c o s t , Vecto r x ) {
i nt i ;
/
Metoda b a c k t r a c k i n g implementa ta .
/
void C o m i s V o i a j o r B a c k t r a c k i n g ( i nt n , M a t r i c e a ) {
Vecto r x ;
Vecto r v i z i t a t ;
i nt k , g a s i t ;
long c o s t ;
memset ( v i z i t a t , 0 , s i z e o f ( v i z i t a t ) ) ;
x [1] = 1;
cost = 0;
k = 2; x[ k] = 1;
while ( k > 1 ) {
gasit = 0;
while ( ( x [ k ] + 1 <= n ) && ( g a s i t == 0 ) ) {
x[k] = x [k] + 1;
g a s i t = CanContinue ( n , a , k , c o s t , v i z i t a t , x ) ;
}
i f ( g a s i t == 1 ) {
i f ( k == n+1)
EvaluareSolutie (n , cost + a [ x [ k 1]][ x [ k ] ] , x ) ;
else {
vizitat [ x[k ] ] = 1;
cost = cost + a [ x [ k 1]][ x [ k ] ] ;
k = k + 1;
x[ k] = 0;
}
}
else {
k = k 1;
vizitat [x[ k ] ] = 0;
i f (k > 1)
cost = cost a [ x [ k 1]][ x [ k ] ] ;
}
}
}
74
i nt n = r e a d I n p u t ( a ) ;
ComisVoiajorBacktracking (n , a ) ;
}
75
for ( i = 1 ; i <= n+1; i ++)
x o ptim [ i ] = x [ i ] ;
A f i s a r e S o l u t i e (n ) ;
}
memset ( v i z i t a t , 0 , s i z e o f ( v i z i t a t ) ) ;
x [1] = 1;
cost = 0;
k = 2; x[ k] = 1;
while ( k > 1 ) {
gasit = 0;
while ( ( x [ k ] + 1 <= n ) && ( g a s i t == 0 ) ) {
x[k] = x [k] + 1;
g a s i t = CanContinue ( n , a , k , c o s t , v i z i t a t , x ) ;
}
i f ( g a s i t == 1 ) {
i f ( k == n+1)
EvaluareSolutie (n , cost + a [ x [ k 1]][ x [ k ] ] , x ) ;
else {
vizitat [ x[k ] ] = 1;
cost = cost + a [ x [ k 1]][ x [ k ] ] ;
k = k + 1;
x[ k] = 0;
}
}
else {
k = k 1;
vizitat [x[ k ] ] = 0;
i f (k > 1)
cost = cost a [ x [ k 1]][ x [ k ] ] ;
}
}
}
76
Pentru k = 4, prima valoare ce verifica conditiile de continuare este 5:
x1 x2 x3 x4 x5 x6 x7 x8 x9
1 2 4 5 0
In mod asemanator se aleg valorile pentru pasii k = 5, 6, 7, 8:
x1 x2 x3 x4 x5 x6 x7 x8 x9
1 2 4 5 6 3 8 7 0
La pasul k = 9, nici una dintre valorile 1, 2, . . . , 8 nu verifica conditiile de continuare (fiind
la ultimul oras, acesta ar trebui sa fie 1, adica orasul de unde s-a pornit). Astfel, se trece la
pasul anterior:
k = k - 1;
vizitat[x[k]] = 0;
if (k > 1)
cost = cost - a[x[k-1]][x[k]];
adica la pasul k = k 1 = 9 1 = 8.
Valoarea 8, urmatoarea valoare din domeniul de valori neverificata si singura ce mai
ramasese de atribuit pentru x8 , nu verifica conditiile de continuare, si deoarece nu mai sunt
valori de testat la pasul k = 8, se trece la nivelul anterior: k = k 1 = 8 1 = 7.
La pasul k = 7, deoarece nu au mai ramas valori netestate din multimea A7 = {1, 2, . . . , 8},
se trece la pasul k = 6:
x1 x2 x3 x4 x5 x6 x7 x8 x9
1 2 4 5 6 3
Aici continuam cu verificarea valorilor 4, 5, 6 si 7. Pentru x6 = 7 sunt verificate conditiile
de continuare si se poate trece la pasul urmator, k = 7:
x1 x2 x3 x4 x5 x6 x7 x8 x9
1 2 4 5 6 7 0
Doar pentru valoarea 8 sunt ndeplinite conditiile de continuare:
x1 x2 x3 x4 x5 x6 x7 x8 x9
1 2 4 5 6 7 8 0
La pasul k = 8, xk = 3 ndeplineste contiile de continuare si se poate trece la nivelul
urmator, k = 9:
x1 x2 x3 x4 x5 x6 x7 x8 x9
1 2 4 5 6 7 8 3 0
Aici solutia optima gasita este urmatoarea:
x1 x2 x3 x4 x5 x6 x7 x8 x9
1 2 4 5 6 7 8 3 1
avand un cost al ciclului hamiltonian de 105.
Se pastreaza solutia optima identificata pana n acest moment, se afiseaza, si se continua
efectuarea calculelor metodei pana la epuizarea spatiului solutiilor posibile.
77
Fig. 3.10: Pasii efectuati de algoritm pentru determinarea ciclului hamiltonian asociat grafului din figura 3.8
78
Fig. 3.11: Pasii efectuati de algoritm pentru determinarea ciclului hamiltonian asociat grafului din figura 3.8
(continuare)
79
Capitolul 4
Definitia 4.1 Fiind data o multime M de elemente denumite noduri, vom numi arbore o
submultime finita de noduri astfel nc
at:
Aceasta definitie este una recursiva, constructia unui arbore depinzand de alti arbori.
Descendentii directi ai radacinii arborelui sunt radacinile subarborilor A1 , A2 , . . . , An , n 1.
Orice nod al unui arbore cu radacina constituie radacina unui subarbore compus din nodul
respectiv si toti descendentii sai.
Se observa ca un arbore impune o structura de organizare ierarhica asupra elementelor
unei multimi.
Definitia 4.2 Se numeste arbore liber (free tree) un graf G = (V, E) neorientat, conex
si aciclic.
Teorema 4.1 (Proprietatile arborilor liberi) Fie G = (V, E) un graf neorientat. Urmatoarele
afirmatii sunt echivalente:
Daca se alege un varf sau nod drept r adacina arborelui, atunci un arbore liber devine
arbore cu radacina. In general vom folosi termenul de arbore n loc de termenul corect arbore
cu radacina.
Un varf terminal (frunza) este un varf fara descendenti. Varfurile care nu sunt terminale
sunt neterminale (sau noduri interioare). De obicei, se considera ca exista o ordonare a
descendentilor aceluiasi parinte.
80
1
2 3
4 5 6 7
8 9 10 11 12 13 14 15
Definitia 4.3 Adancimea unui varf este dat a de lungimea drumului de la r adacina la acel
varf. Inaltimea unui varf se determina ca fiind lungimea celui mai lung drum dintre acel varf
si un varf terminal. Inaltimea radacinii determin a n
altimea arborelui. Nivelul unui varf se
calculeaza ca diferenta dintre naltimea arborelui si adancimea acestui v arf.
Toate varfurile unui arbore ce au aceeasi adancime se spune ca sunt pe acelasi nivel. Un
arbore oarecare cu radacina este n-ar daca fiecare varf are pana la n descendenti directi.
O multime de arbori disjuncti formeaza o padure.
Din punct de vedere grafic, un arbore se poate reprezenta descriind nodurile cu ajutorul
unor cercuri sau patrate n care se afla informatia aferenta, iar relatia de descendenta prin
legaturile ce unesc nodurile cu fii lor.
Se observa ca fiecare varf al arborelui are doi descendenti, descendentul stang si descen-
dentul drept, cu exceptia nodurilor terminale.
In figura 4.1 este prezentat un arbore binar plin n care varfurile sunt asezate pe 4 niveluri
(k = 4). De regula varfurile unui arbore binar plin se numeroteaza n ordinea nivelurilor, iar
n cadrul unui nivel, de la stanga la dreapta.
Definitia 4.6 Se numeste arbore binar complet cu n v arfuri un arbore binar plin avand
k nivele, unde 2k1 n < 2k , din care se elimin arfurile numerotate n+ 1, n+ 2, . . . , 2k 1.
a v
81
1
2 3
4 5 6 7
8 9 10 11 12
1. expresii cu paranteze;
2. forma standard ;
2 8
3 5 9
4 6 7
1. expresii cu paranteze expresia ncepe cu radacina si dupa fiecare varf k urmeaza expre-
siile subarborilor ce au ca radacini descendentii varfului respectiv, separate prin virgula
si incluse ntre paranteze. Daca un descendent al unui varf nu exista atunci expresia
respectiva este nlocuita cu 0 sau NULL. Un arbore cu un singur varf (radacina),
etichetat cu a1 , se reprezinta astfel: a1 (0, 0). Un arbore format din radacina a1 si doi
descendenti, a2 si a3 , se reprezinta astfel: a1 (a2 (0, 0), a3(0, 0)).
Pentru arborele din figura 4.3 avem:
1(2(3(0, 4(0, 0)), 5(6(0, 0), 7(0, 0))), 8(0, 9(0, 0)))
82
2. forma standard se indica radacina arborelui, iar pentru fiecare varf k se precizeaza
descendentul sau stang si/sau drept.
(
i , daca nodul i este descendentul stang al nodului k
Stangk =
0 , daca nu exista descendentul stang
(
i , daca nodul i este descendentul drept al nodului k
Dreptk =
0 , daca nu exista descendentul drept.
Pentru arborele din figura 4.3 avem urmatoarea reprezentare (Rad = 1):
Nod 1 2 3 4 5 6 7 8 9
Stang 2 3 0 0 6 0 0 0 0
Drept 8 5 4 0 7 0 0 9 0
Dupa modelul listei liniare dublu nlatuita, putem folosi o structura de date asemanatoare
pentru reprezentarea unui arbore binar. Un nod al arborelui are urmatoarea configuratie:
Nod rad; rad este o variabila de tip pointer la Nod (variabila pastreaza adresa
unei zone de memorie ce contine date numerice de tip Nod). rad desemneaza adresa
radacinii arborelui.
In figura 4.5 avem reprezentarea arborelui din figura 4.4.
Nod 1 2 3 4 5 6 7 8 9
Tata 0 1 2 3 2 5 5 1 8
Observatia 4.2 Urmatoarea numerotare este utilizat a pentru anumite tipuri de arbori
binari, si are drept caracteristic
a faptul c
a structura arborelui se poate deduce printr-o
numerotare adecvata a varfurilor:
83
Fig. 4.5: Exemplu de arbore binar cu 8 noduri
(
2i , i2
T atai =
nu exista , i=1
( (
2i , 2in 2i+1 , 2i+1 n
Stangi = Drepti =
nu exista , 2i>n a , 2i+1>n
nu exist
84
Fig. 4.6: Parcurgerea n preordine a unui arbore binar a) arborele initial; b) arborele vizitat n preordine,
primul nivel; c) arborele vizitat n preordine, al doilea nivel; d) arborele vizitat n preordine, al treilea nivel.
#include <stdio.h>
void readInput(void) {
int k;
85
Algoritm 28 Algoritm de parcurgere n preordine
1: procedure Preordine(Rad, Stang, Drept, T ata)
2: k rad
3: q0
4: while (q = 0) do
5: while (stangk 6= 0) do
6: call V izit(k) prelucrarea informatiei din nodul vizitat
7: k stangk
8: end while
9: call V izit(k)
10: while (q = 0) (dreptk = 0) do
11: f ound 0
12: while (q = 0) (f ound = 0) do
13: jk
14: k tatak
15: if (k = 0) then
16: q1
17: else
18: if (j = stangk ) then
19: f ound 1
20: end if
21: end if
22: end while
23: end while
24: if (q = 0) then
25: k dreptk
26: end if
27: end while
28: return
29: end procedure
i = rad; q = 0;
while (q == 0) {
while (stang[i] != 0) {
vizit(i);
i = stang[i];
86
}
vizit(i);
while ((q == 0) && (drept[i] == 0)) {
found = 0;
while ((q == 0) && (found == 0)) {
j = i;
i = tata[i];
if (i == 0)
q = 1;
else
if (j == stang[i])
found = 1;
}
}
if (q == 0)
i = drept[i];
}
}
void main(void){
readInput();
preord(rad);
}
#include <stdio.h>
void readInput(void) {
87
Algoritm 29 Algoritm de parcurgere n inordine
1: procedure Inordine(Rad, Stang, Drept)
2: k rad
3: q0
4: S initializare stiva vid
a
5: while (q = 0) do
6: while (stangk 6= 0) do
7: Sk S.push(k)
8: k stangk
9: end while
10: call V izit(k)
11: while (q = 0) (dreptk = 0) do
12: if (S = ) then verificare dac
a stiva este vid
a
13: q1
14: else
15: Sk S.pop(k)
16: call V izit(k)
17: end if
18: end while
19: if (q = 0) then
20: k dreptk
21: end if
22: end while
23: return
24: end procedure
int k;
i = rad; q = 0;
cap = -1;
while (q == 0) {
while (stang[i] > 0) {
88
cap++;
stack[cap] = i;
i = stang[i];
}
vizit(i);
while ((q == 0) && (drept[i] == 0)) {
if (cap < 0)
q = 1;
else {
i = stack[cap];
cap--;
vizit(i);
}
}
if (q == 0)
i = drept[i];
}
}
void main(void) {
readInput();
inord(rad);
}
89
1
2 8
3 5 9
6 7
fiecarui operator i corespunde un nod neterminal avand drept subarbori stang si drept
cei doi operanzi corespunzatori.
Arborele din figura 4.8 corespunde expresiei matematice: 2 ((a + b) (c + b)). In urma
vizitarii n postordine a acestui arbore se obtine forma polonez
a postfixat
a asociata expresiei
anterioare: 2ab + cb + . Pentru a obtine valoarea expresiei respective, arborele poate fi
parcurs n postordine.
90
pentru orice varf i apartinand arborelui, avem
(
Infi Infj , pentru toate varfurile j ce apartin descendentului st
ang al v
arfului i
Infi Infj , pentru toate varfurile j ce apartin descendentului drept al v
arfului i
unde Infi este informatia asociata varfului i. Aceast
a informatie este de un tip de date peste
care avem o relatie de ordine (de obicei un tip numeric sau tipul sir de caractere).
Un arbore binar de cautare mai este cunoscut si sub numele de arbore binar de sortare
deoarece parcurgerea n inordine a unui arbore binar de cautare ne conduce la obtinerea
elementelor vectorului Inf n ordine crescatoare. In principal aceasta structura de date este
utilizata pentru regasirea eficienta a unor informatii.
Operatiile de creare a arborelui, stergere a unui nod, modificare a informatiei asociate
unui nod sau inserare a unui nod se pot realiza ntr-un mod optim astfel ncat sa nu se
distruga proprietatea de arbore de cautare.
Daca dorim ca arborele sa nu contina chei duplicate, vom modifica definitia de mai sus
astfel:
pentru orice varf i apartinand arborelui, avem
(
Infi < Infj , pentru toate varfurile j ce apartin descendentului stang al varfului i
Infi > Infj , pentru toate varfurile j ce apartin descendentului drept al varfului i.
C
autarea unei informatii ntr-un arbore de c
autare
Informatia asociata unui nod mai poarta numele de cheie.
Sa presupunem ca dorim sa cautam o cheie x ntr-un arbore de cautare. Vom compara
mai ntai cheia x cu informatia asociata radacinii arborelui (vezi algoritmul 31):
1. daca cheia x este mai mica decat cheia radacinii, atunci se va continua cautarea n
subarborele stang;
2. daca cheia x este mai mare decat cheia radacinii, atunci se va continua cautarea n
subarborele drept;
3. daca cheia x este egala cu cheia radacinii, atunci cautarea se ncheie cu succes.
91
Algoritm 31 Algoritm de cautare a unei valori ntr-un arbore binar de cautare
1: function SearchNode(p, Key)
2: if (p 6= N U LL) then
3: if (p.inf o > key) then
4: return SearchN ode(p.lef t, key)
5: else
6: if (p.inf o < key) then
7: return SearchN ode(p.right, key)
8: else
9: return p
10: end if
11: end if
12: else
13: return N U LL
14: end if
15: end function
S
tergerea unui nod dintr-un arbore de c
autare
Mai ntai se va cauta nodul ce se doreste sa fie eliminat.
Sa presupunem ca am gasit nodul ce urmeaza a fi sters. Avem urmatoarele situatii (vezi
algoritmul 34):
1. nodul ce urmeaza sa fie sters este un nod terminal - se face stergerea normal avand
grija sa se nlocuiasca legatura din nodul parinte catre el cu null;
2. nodul ce urmeaza sa fie sters are un singur descendent - nodul respectiv se sterge iar
parintele va contine acum noua legatura catre descendentul fostului fiu;
se determina cel mai din stanga nod (notat B) din subarborele drept al nodului
ce trebuie sters (vezi algoritmul 33); acesta va fi sters n mod fizic, dar la un pas
ulterior;
92
Fig. 4.9: Arbore binar de cautare. Inserarea unui nod.
Exemplul 4.3 Fie un arbore binar, eticheta fiec arui nod fiind un sir de caractere (un cuvant).
Se cere sa se realizeze un program care s a creeze un arbore binar de sortare, s a-l parcurga n
inordine si sa permita inserarea si stergerea unui nod specificat.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <conio.h>
93
Fig. 4.10: Arbore binar de cautare. Stergerea unui nod. a) Nod fara descendenti b) Nod cu un singur
descendent c) Nod cu doi descendenti.
94
Algoritm 33 Algoritm pentru determinarea celui mai din stanga nod
1: function LeftmostNode(P arent, Curent)
2: while (curent.lef t 6= N U LL) do
3: parent curent
4: curent curent.lef t
5: end while
6: if (parent.right = curent) then
7: parent.right curent.right
8: else
9: parent.lef t curent.right
10: end if
11: return curent
12: end function
char *cuvint;
struct nod *left,*right;
} NOD;
NOD *rad;
if (p != NULL) {
ind = strcmp(p->cuvint, sir);
if (ind < 0)
return Search(p->right, sir);
else
if (ind > 0)
return Search(p->left, sir);
else
return p;
}
else
return NULL;
}
p = (NOD*)malloc(sizeof(NOD));
p->left = p->right = NULL;
p->cuvint = (char*)malloc(sizeof(char) * (strlen(sir) + 1));
strcpy(p->cuvint, sir);
return p;
}
95
Algoritm 34 Algoritm de stergere a unui nod ntr-un arbore binar de cautare
1: function DeleteNode(p, Key)
2: if (p 6= N U LL) then
3: if (p.inf o > key) then
4: p.lef t DeleteN ode(p.lef t, key)
5: else
6: if (p.inf o < key) then
7: p.right DeleteN ode(p.right, key)
8: else
9: if (p.lef t = N U LL) (p.right = N U LL) then
10: if (p.lef t 6= N U LL) then
11: tmp p.lef t
12: else
13: tmp p.right
14: end if
15: call DisposeN ode(p)
16: p tmp
17: else
18: tmp p.right
19: tmp Lef tmostN ode(p, tmp)
20: tmp.lef t p.lef t
21: tmp.right p.right
22: call DisposeN ode(p)
23: p tmp
24: end if
25: end if
26: end if
27: end if
28: return p
29: end function
if (p == NULL)
p = CreareNod(sir);
else {
ind = strcmp(p->cuvint, sir);
if (ind < 0)
p->right = Insert(p->right, sir);
else
if (ind > 0)
p->left = Insert(p->left, sir);
}
return p;
}
96
VisitInord(p->right);
}
}
if (parent->right == curent)
parent->right = curent->right;
else
parent ->left = curent->right;
return curent;
}
if (p != NULL){
ind = strcmp(p->cuvint, sir);
if (ind < 0)
p->right = DeleteNod(p->right, sir);
else
if (ind > 0)
p->left = DeleteNod(p->left, sir);
else
if (p->left == NULL || p->right == NULL) {
if (p->left != NULL)
tmp = p->left;
else
tmp = p->right;
ElibNod(p);
p = tmp;
}
else{
tmp = p->right;
tmp = LeftmostNod(p, tmp);
tmp->left = p->left;
tmp->right = p->right;
ElibNod(p);
p = tmp;
}
97
}
return p;
}
void main(void) {
char cuvint[100];
char ch;
NOD *p;
rad = NULL;
while (1) {
printf("***********************************\n");
printf("1. Inserare \n");
printf("2. Cautare\n");
printf("3. Stergere\n");
printf("4. Afisare arbore\n");
printf("0. Exit \n");
ch = getch();
if (ch != 0 && ch != 4) {
printf("Cuvint: "); scanf("%s", cuvint);
}
switch (ch) {
case 1: rad = Insert(rad, cuvint); break;
case 2: p = Search(rad, cuvint);
if (!p)
printf("Cuvintul %s nu a fost gasit! \n", cuvint);
else
printf("Cuvintul %s exista in arbore! \n", cuvint);
break;
case 3: rad = DeleteNod(rad, cuvint); break;
case 4: VisitInord(rad); break;
case 0: exit(1);
}
}
}
4.3 Exercitii
1. (a) Sa se realizeze o subrutina ce determina cea mai mare valoare pastrata ntrun
arbore binar de cautare;
(b) Aceeasi cerinta pentru cea mai mica valoare.
2. Sa se realizeze o subrutina ce determina toate nodurile unui arbore binar de cautare cu
proprietatea ca informatia k asociata unui nod verifica relatia a k b, unde a si b
sunt date.
3. (a) Sa se realizeze un algoritm care determina numarul arborilor binari distincti cu
n noduri, unde n este un numar natural dat.
98
Fig. 4.11: Exemplu de arbore binar complet cu 7 varfuri
Observatia 4.4 O expresie va avea maxim 9 nivele de parantezare. Num arul de redu-
ceri ce pot fi efectuate este finit. Liniile de iesire contin maxim 80 caractere. Caracterele
posibile din termenii de iesire sunt tot X, ( si ).
Intrare Iesire
(((XX)X)X) ((XX)(XX))
(((XX)(XX)X) ((XX)((XX)X))
(XX) (XX)
(ACM, Bucuresti, 1996)
5. Prin arbore binar complet ntelegem un arbore binar n care un nod are fie doi descendenti,
fie nu are nici unul.
Un arbore binar complet poate fi reprezentat prin codificarea drumurilor de la radacina
la fiecare frunza utilizand numai valorile 0 si 1: atunci cand coboram n arbore spre
stanga se adauga valoarea 0 iar atunci cand coboram spre dreapta se adauga valoarea
1.
99
Pentru arborele din figura 4.11 drumurile de la radacina la fiecare frunza se codifica
astfel:
ABC : 00
ABDE : 010
ABDF : 011
AG : 1
Concatenand toate drumurile de la radacina catre frunze, arborele anterior poate fi
reprezentat prin secventa ce poate fi interpretata ca reprezentarea n baza 2 a unui
numar (000100111).
Fiind date m si n doua numere naturale, se cere sa se determine daca exista un arbore
binar complet a carui reprezentare va contine exact m cifre 0 si n cifre 1 (0 < m, n
100).
(Timisoara-pregatire, 1996)
6. Scrieti o subrutina nerecursiva care numara nodurile frunza ale unui arbore binar.
7. Realizati o subrutina care sa determine nivelul cu cele mai multe noduri frunze dintrun
arbore binar.
8. Determinati natimea unui arbore binar printro functie nerecursiva.
9. Se considera un dictionar ce este implementat sub forma unei structuri de arbore.
Pentru fiecare cuvant se cunoaste traducerea acestuia precum si probabilitatea lui de
aparitie n texte.
Se cere sa se realizeze un algoritm care sa conduca la o cautare optima a cuvintelor
n dictionar. Se mentioneaza faptul ca probabilitatea de a cauta un cuvant care nu se
gaseste n dictionar este zero.
Datele de iesire constau din afisarea arborelui optimal de cautare compus din cuvintele
introduse.
Spre exemplu, un set de date de intrare poate fi:
5
1 abroad in_strainatate
4 baker brutar
2 calf gamba
1 dice zaruri
2 ear ureche
10. O casa de comenzi este interesata de crearea unui program pe calculator care sa tina
evidenta comenzilor n ordinea datelor la care au fost onorate, iar n cadrul fiecarei date
de livrare, n ordine lexicografica dupa numele produsului. Sa se scrie un program care
foloseste structuri de date de tip arbore.
Intrarea este formata din mai multe linii. Pe fiecare linie avem cate o comanda data
sub forma: [numep rodus] [zi] [luna] [an]. Intrarea se termina cu o linie pe care avem
doar &.
Iesirea este formata prin afisarea comenzilor n ordinea datei la care au fost onorate,
iar n cadrul unei date, n ordine lexicografica n functie de numele comenzii.
Indicatie: Se va crea un arbore de cautare dupa data de livrare si n fiecare nod va fi
un arbore de cautare dupa numele comenzii.
100
11. Se considera o expresie logica formata din n variabile logice reprezentate printro sin-
gura litera si operatorii & (AND), | (OR), ! (NOT) (nu avem paranteze). Expresia este
reprezentata sub forma unui arbore binar.
Se cere sa se realizeze un algoritm care pentru o expresie logica data cerceteaza existenta
unei combinatii de valori logice (true/false), pentru care expresia data ia valoarea logica
true.
De exemplu pentru expresia a|!b|c&d solutia este a = f alse, b = f alse, c = f alse,
d = f alse, iar pentru expresia !a&a nu avem solutie.
101
Capitolul 5
Arbori oarecare
Definitia 5.1 Fiind data o multime M de elemente denumite noduri, vom numi arbore o
submultime finita de noduri astfel nc
at:
legaturi fiu-frate: f iuk - este primul descendent al varfului k, f ratek - urmatorul descen-
dent al tatalui nodului k, ce urmeaza dupa k. Intre descendentii unui varf se presupune
ca definim o relatie de ordine.
Radacina arborelui din figura 5.1 este Rad = 1.
2 3 4
5 6 7 8 9 10
Nod 1 2 3 4 5 6 7 8 9 10
Fiu 2 0 5 7 0 0 0 0 0 0
Frate 0 3 4 0 6 0 8 9 10 0
102
Daca identificam F iu cu Stang si F rate cu Drept, unui arbore oarecare i se poate
asocia un arbore binar.
Pentru reprezentarea unui arbore oarecare, modelul de reprezentare fiu-frate n varianta
de alocare statica poate fi usor extins la o varianta de alocare dinamica, folosind pointeri
pentru legaturile catre primul descendent respectiv pentru urmatorul frate. Astfel un
nod al arborelui poate avea urmatoarea configuratie:
Asemanator cu modelul construit la arbori binari, rad (Nod rad;) este o variabila de
tip pointer la Nod (variabila pastreaza adresa unei zone de memorie). rad desemneaza
adresa radacinii arborelui.
rad
1 NULL
2 3 4 NULL
NULL
5 6 NULL 7 8 9 10 NULL
Fig. 5.2: Exemplu de arbore oarecare cu 10 noduri reprezentat prin legaturi fiu-frate
In figura 5.2 este reprezentat arborele din figura 5.1 prin leg
aturi fiu-frate.
lista descendentilor. In cadrul acestui mod de reprezentare fiecare varf este descris prin
lista descendentilor sai. Pentru memorare se va utiliza un vector cu n componente:
(
0 , varful respectiv nu are descendenti
Ck =
j , j indica adresa (coloana) unde ncepe lista descendentilor varfului k.
Exploatand mai departe aceasta idee, ntr-un nod al arborelui oarecare se poate pastra o
lista cu adresele descendentilor sai, lista fiind reprezentata sub forma unui tablou alocat
103
Table 5.2: Reprezentarea arborelui din figura 5.1 folosind liste cu descendenti.
Nod 1 2 3 4 5 6 7 8 9 10
C 1 0 4 6 0 0 0 0 0 0
2 3 4 5 6 7 8 9 10
L=
2 3 0 5 0 7 8 9 0
static sau dinamic. Oricum modelul se recomanda atunci cand numarul descendentilor
unui nod este limitat superior, sau cand acesta este cunoscut/stabilit n momentul n
care se construieste arborele. Modificarile efectuate asupra arborelui, cum ar fi inserari
de descendenti noi, atunci cand intrarile alocate pentru acesti descendenti sunt deja
alocate nu poate conduce decat la realocarea spatiului de memorie cu un cost reflectat
n complexitatea algoritmului. Astfel daca numarul de descendenti este limitat superior
putem defini
sau
rad
1 3
2 0 3 2 4 4
5 0 6 0 7 0 8 0 9 0 10 0
Fig. 5.3: Exemplu de arbore oarecare cu 10 noduri reprezentat prin liste cu descendenti
In figura 5.3 este reprezentat arborele din figura 5.1 folosind liste cu descendenti.
104
Tata - fiecarui nod k se indica nodul parinte.
Table 5.3: Reprezentarea arborelui din figura 5.1 folosind vectorul tata.
Nod 1 2 3 4 5 6 7 8 9 10
Tata 0 1 1 1 3 3 4 4 4 4
rad
NULL
1 NULL
NULL
2 3 4 NULL
NULL
5 6 NULL 7 8 9 10 NULL
Fig. 5.4: Exemplu de arbore oarecare cu 10 noduri reprezentat prin legaturi fiu-frate-tata
In figura 5.4, reprezentarea din figura 5.2 este mbunatatita prin leg
atura tata.
105
Apreordine: se viziteaza mai ntai radacina, si apoi, n ordine, subarborii sai [93],
[123]. Metoda este identica cu metoda de parcurgere n preordine a arborelui binar
atasat: 1, 2, 3, 5, 6, 4, 7, 8, 9, 10 (vezi algoritmul 35). Parcurgerea n Apreordine este
exemplificata pe arborele oarecare din figura 5.1.
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
/**
* Definitii de tipuri necesare cozii
*/
typedef struct nod {
int id;
float info;
struct nod *down, *next;
}NOD;
106
/**
* Definitii de tipuri pentru reprezentarea arborelui
*/
typedef struct cnod {
TINFO info; //informatia memorata in nod
struct cnod *next; //adresa urmatorului element
}CNOD;
/**
* Functie ce testeaza daca coada e vida
* @return 1 daca coada e vida
* 0 altfel
*/
int isEmpty(void) {
if (prim == NULL)
return 1;
else
return 0;
}
/**
* Functia adauga un element la sfarsitul unei cozi
* unde n reprezinta elementul ce se adauga.
*/
void add(TINFO n) {
CNOD *curent;
/**
* Functia extrage un element din coada. Returneaza elementul scos.
*/
TINFO get(void) {
CNOD *curent;
TINFO n;
if (prim == ultim) {
n = prim->info;
free(ultim);
107
prim = ultim = NULL;
} else {
curent = prim;
n = prim->info;
prim = prim->next;
free(curent);
}
return n;
}
/**
* Functia realizeaza crearea unui arbore oarecare. Se introduce initial radacina,
* apoi descendentii ei, dupa care se introduc descendentii nodurilor de pe
* nivelul 1, dupa care se introduc descendentii nodurilor de pe nivelul 2 s.a.m.d.
*/
NOD* creare(void) {
NOD *p, *c;
p = (NOD *)malloc(sizeof(NOD));
p->next = NULL;
printf("Dati id:"); scanf("%d", &p->id);
add(p);
while (!isEmpty()) {
c = get();
c->down = citListaDescendenti(c);
}
return p;
}
/**
* Functia citeste informatia asociata unui nod.
* @return 1 daca citirea s-a facut corect
* 0 altfel
*/
int citInfo(NOD *pn) {
printf("Id:");
pn->next = NULL;
pn->down = NULL;
return scanf("%d",&pn->id) == 1;
}
/**
* Functia realizeaza citirea listei de descendenti ai nodului up si
* returneaza adresa primului descendent din lista.
*/
NOD* citListaDescendenti(NOD *up) {
NOD *prim, *ultim, *p;
NOD n;
108
p = (NOD *)malloc(sizeof(NOD));
*p = n;
if (prim == NULL)
prim = ultim = p;
else {
ultim->next = p;
ultim = p;
}
add(p);
}
return prim;
}
/**
* Parcurgerea in A-preordine a arborelui cu radacina p.
*/
void aPreordine(NOD *p) {
NOD *desc;
/**
* Parcurgerea in A-postordine a arborelui cu radacina p.
*/
void aPostordine(NOD *p) {
NOD *desc;
desc = p->down;
while (desc != NULL) {
aPostordine(desc);
desc = desc->next;
}
printf("%d ",p->id);
}
void main(void) {
NOD *rad;
rad = creare();
printf("\n Parcurgerea in A-Preordine este:\n");
aPreordine(rad);
printf("\n Parcurgerea in A-Postordine este:\n");
aPostordine(rad);
}
109
5.3 Arbori de acoperire de cost minim
Fie G = (V, E) un graf neorientat si fie c : E R o functie de cost ce asocieaza o valoare
reala fiecarei muchii. Notam cu TG multimea arborilor partiali ai grafului G (un arbore
partial este un graf partial conex si fara cicluri al grafului initial).
Cerinta problemei este aceea de a determina un arbore T TG avand costul cel mai mic
dintre toti arborii ce apartin multimii TG :
Lema 5.2 (Proprietatea taieturii) Fie S o submultime de noduri a lui V si e muchia ce are
costul minim dintre toate muchiile ce au o singur
a extremitate n S. Atunci arborele partial
de cost minim va contine muchia e.
Lema 5.3 (Proprietatea ciclului) Fie C un ciclu si f muchia ce are costul maxim dintre
toate muchiile ce apartin lui C. Atunci arborele partial de cost minim T nu contine muchia
f.
110
Majoritatea algoritmilor ce calculeaza arborele de acoperire de cost minim prezinta aceeasi
tehnica generala de calcul. La nceput se porneste cu o padure de arbori, T 0 = {T10 , T20, . . . , Tn0 }
unde Ti0 = {xi }, i = 1, n este un arbore format dintrun singur nod. La pasul k vom avea
multimea T k compusa din n k arbori: T k = {T1k , T2k , . . . , Tnk
k
}.
k
La fiecare pas, se alege un arbore Ti si o muchie (u , v ) avand costul minim dintre toate
muchiile (u, v) cu proprietatea ca o extremitate apartine multimii de noduri a arborelui Tik
(u Tik ) si cealalta extremitate apartine multimii V \ VTik (v V \ VTik ).
Prin urmare, multimea T k+1 se obtine din multimea T k prin reuniunea arborilor Tik si
Tj (i 6= j), unde u Tik si v Tjk (T k+1 = T k \ {Tik , Tjk } {Tik Tjk }).
k
111
3. Algoritmul lui Kruskal (vezi algoritmul 38)
Algoritmul lui Prim implementat simplu are o complexitate O(n2 )[30] si atinge o com-
plexitate de O(m log n) daca se folosesc heapuri Fibonacci [54], sau pairing heaps [115].
In tabelul 5.4 sunt prezentati mai multi algoritmi dezvoltati dea lungul timpului pentru
determinarea arborelui de acoperire minimal si complexitatile lor. Karger, Klein si Tarjan [79]
pornind de la algoritmul lui Boruvka au realizat un algoritm randomizat pentru determinarea
arborelui de acoperire minimal, avand o complexitate liniara, iar Chazelle [26] a dezvoltat
un algoritm avand complexitatea O(n(m, n)) ((m, n) este inversa functiei lui Ackerman).
Pe de alta parte, Pettie si Ramachandran [105] au propus un algoritm demonstrat ca fiind
optimal, avand complexitatea cuprinsa ntre O(n + m) si O(n(m, n)).
Fie G(V, E) un graf neorientat unde V = {1, 2, ..., n} este multimea nodurilor si E este
multimea muchiilor (E V V ). Pentru reprezentarea grafului se utilizeaza matricea
costurilor C:
0
, daca i = j
ci,j = , daca (i, j) /E
d > 0 , daca (i, j) E
112
5.3.1 Algoritmul lui Boruvka
Algoritmul lui Boruvka [77] a fost descoperit de catre matematicianul ceh Otakar Boruvka
n 1926 [21], si redescoperit apoi de catre alti cercetatori. Dintre acestia, Sollin (1961) este
cel care a mai dat numele algoritmului, acesta fiind cunoscut n literatura de specialitate si
sub numele de algoritmul lui Sollin. Pentru ca acest algoritm sa poata fi aplicat, trebuie ca
muchiile grafului sa aiba costuri distincte (vezi algoritmul 39).
Aplicand algoritmul lui Boruvka, la pasul ntai vor fi selectate muchiile (1, 2), (3, 6), (4, 5),
(4, 7) si (7, 8). In urma operatiilor de reuniune a componentelor conexe pe baza muchiilor
selectate, vor mai ramane trei componente conexe n multimea M.
La pasul al doilea sunt alese muchiile (2, 5) si (1, 3) ce conduc, n urma operatiilor de
reuniune, la o singura componenta conex a.
113
Fig. 5.5: Exemplu de graf ponderat - aplicatie algoritmul lui Boruvka
114
Algoritm 40 Algoritmul Prim (varianta detaliata)
1: function DistantaMinima(n, vizitat, d)
2: min
3: for j 1, n do
4: if (vizitatj 6= 1) (dj < min) then
5: min dj
6: j0 j
7: end if
8: end for
9: if (min = ) then
10: return 1
11: else
12: return j0
13: end if
14: end function
115
Dupa primul pas al ciclului, select
am muchia (1, 5).
1 2 3 4 5 6 7 8
d 14 6 21 5 16
tata 0 1 1 5 1 5 1 1
vizitat 1 0 0 0 1 0 0 0
La pasul al doilea se alege nodul 3 si muchia (1, 3):
1 2 3 4 5 6 7 8
d 14 6 21 5 12 12
tata 0 1 1 5 1 3 1 3
vizitat 1 0 1 0 1 0 0 0
La pasul al treilea avem doua noduri ale c aror distante la noduri din multimea S sunt
egale: 6 si 8. Alegem primul nod - 6 si muchia (3, 6):
1 2 3 4 5 6 7 8
d 14 6 21 5 12 14 6
tata 0 1 1 5 1 3 6 6
vizitat 1 0 1 0 1 1 0 0
Al patrulea nod ales este 8:
1 2 3 4 5 6 7 8
d 14 6 10 5 12 10 6
tata 0 1 1 8 1 3 8 6
vizitat 1 0 1 0 1 1 0 1
La pasul cinci, nodul aflat la distant
a minim
a este 7:
1 2 3 4 5 6 7 8
d 14 6 10 5 12 10 6
tata 0 1 1 8 1 3 8 6
vizitat 1 0 1 0 1 1 1 1
La pasul sase este ales nodul 4:
1 2 3 4 5 6 7 8
d 12 6 10 5 12 10 6
tata 0 4 1 8 1 3 8 6
vizitat 1 0 1 1 1 1 1 1
La final, ultimul nod ales este 2 mpreun
a cu muchia (4, 2).
Trebuie sa remarcam faptul ca algoritmul lui Prim (vezi algoritmul 40) este aproape
identic cu algoritmul lui Dijkstra (vezi algoritmul 62).
Dupa cum am subliniat, implementarea optima se realizeaza folosind niste structuri de
date avansate - heapuri Fibonacci sau pairing heaps (vezi algoritmul 41).
116
Algoritm 41 Algoritmul lui Prim folosind structuri de date avansate
1: procedure Prim3(n, C, u)
2: for fiecare v V do
3: dv
4: end for
5: Q initializeaza coada cu prioritate Q cu multimea vid a
6: for fiecare v V do
7: Qv
8: end for
9: S initializeaz
a multimea S cu multimea vid
a
10: while Q 6= do
11: u deleteM in(Q) extrage nodul de prioritate minim a din Q
12: S S {u}
13: for fiecare muchie e = (u, v) E do
14: if (v
/ S) (c(e) < dv ) then
15: actualizeaza prioritatea lui v: dv c(e)
16: end if
17: end for
18: end while
19: end procedure
O astfel de structura de date pentru multimi disjuncte se mai numeste si structura de date
unionfind (eng. unionfind data structure).
Exemplul 5.6 Pentru o putea opera cu modul de reprezentare ales, cel cu vectori, trebuie
ca elementele multimii sa ia valori naturale n intervalul [1,. . . ,n]. Dac a acest lucru nu
este posibil atunci folosim un vector auxiliar A ce p astreaza valorile elementelor, n cadrul
reprezentarii utilizanduse indicele acestora. S a presupunem c a avem multimea de elemente
A = {a, b, e, x, y, z, u, v} si partitia {a, x, y}, {b, z, v}, {e, u}. Atunci conform modului de
reprezentare descris avem:
117
Algoritm 42 Algoritmi pentru operatiile init, find, merge (varianta ntai)
1: procedure Init(x, u)
2: cx u
3: end procedure
4: function Find(x)
5: return cx
6: end function
7: function Merge(x, y)
8: setx cx
9: sety cy
10: for k 1, n do
11: if (ck = sety) then
12: ck setx
13: end if
14: end for
15: return setx
16: end function
1 2 3 4 5 6 7 8
A a b e x y z u v
C 1 2 3 1 1 2 2 3
a2 = b are semnificatia urmatoare: elementul de pe pozitia 2 are valoarea b . c2 = 2 -
elementul de pe pozitia 2 face parte din multimea de identificator 2. Sau a4 = x - elementul
de pe pozitia 4 are valoarea x , si c4 = 1 - elementul de pe pozitia 4 face parte din multimea
de identificator 1.
118
Algoritm 43 Algoritmi pentru operatiile init, find, merge (varianta a II-a)
1: procedure Init(x, k)
2: Listk new node aloc
a spatiu pentru un nod al listei
3: Listk .data x
4: Listk .next N U LL
5: end procedure
6: function Find(x)
7: for i 1, m do
8: p Listi , reprezentant p
9: while (p 6= N U LL) (p.data 6= x) do
10: p p.next
11: end while
12: if (p 6= N U LL) then
13: return reprezentant
14: end if
15: end for
16: return N U LL
17: end function
18: function Merge(x, y)
19: capx F ind(x)
20: capy F ind(y)
21: curent capx
22: while (curent.next 6= N U LL) do
23: curent curent.next
24: end while
25: curent.next capy
26: return capx
27: end function
Reprezentarea folosind o p
adure de arbori
In cadrul acestei modalitati de reprezentare fiecare multime este pastrata sub forma unui
arbore. Pentru fiecare nod pastram informatia despre parintele sau: tatak reprezinta indicele
nodului parinte al nodului k.
Se poate observa foarte usor (vezi algoritmul 44) faptul ca arborele obtinut n urma unor
operatii repetate Merge poate deveni degenerat (o lista liniara). Pentru a putea sa evitam o
astfel de situatie, reuniunea se poate realiza tinand cont de una din urmatoarele caracteristici:
1. numarul de elemente din fiecare arbore - radacina arborelui ce are mai putine elemente
(notam arborele cu TB ) va deveni descendentul direct al radacinii arborelui ce poseda
mai multe elemente (notat cu TA ). Astfel toate nodurile din arborele TA vor ramane cu
aceeasi adancime, pe cand nodurile din arborele TB vor avea adancimea incrementata
cu 1. De asemenea, arborele rezultat n urma reuniunii va avea cel putin de doua ori
mai multe noduri decat arborele TB .
119
Algoritm 44 Algoritmi pentru operatiile init, find, merge (varianta a III-a)
1: procedure Init(x)
2: tatax x
3: end procedure
4: function Find(x)
5: while (x 6= tatax ) do
6: x tatax
7: end while
8: return x
9: end function
10: function Merge(x, y)
11: radx F ind(x)
12: rady F ind(y)
13: tatarady radx
14: return radx
15: end function
1: function Merge(x, y)
2: radx F ind(x)
3: rady F ind(y)
1: procedure Init(x)
4: size |tataradx | + |tatarady |
2: tatax 1
5: if (|tataradx | > |tatarady |) then
3: end procedure
6: tatarady radx
4: function Find(x)
7: tataradx size
5: while (tatax > 0) do
8: return radx
6: x tatax
9: else
7: end while
10: tataradx rady
8: return x
11: tatarady size
9: end function
12: return rady
13: end if
14: end function
Prin aceasta euristica simpla se urmareste o echilibrare a arborelui rezultat pentru a se
evita cazurile degenerate. Complexitatea timp a procedurii F ind este O(log n).
2. naltimea fiecarui arbore - radacina arborelui ce are naltimea mai mica (notam arborele
cu TB ) va deveni descendentul direct al radacinii arborelui cu naltimea mai mare (notat
cu TA ). Daca naltimile lui TA si TB sunt egale si TA devine subarbore al lui TB atunci
naltimea lui TB creste cu o unitate.
Inaltimea fiecarui subarbore de radacina u se pastreaza ntro variabila noua hu . Pentru
a economisi cantitatea de memorie utilizata, se poate pastra naltimea unui arbore n
vectorul tata: tatax = valoarea naltimii arborelui, atunci cand x reprezinta nodul
radacina al unui arbore.
120
1: function Merge(x, y)
2: radx F ind(x)
1: procedure Init(x) 3: rady F ind(y)
2: tatax x 4: if (hradx > hrady ) then
3: hx 0 5: tatarady radx
4: end procedure 6: return radx
5: function Find(x) 7: else
6: while (tatax 6= x) do 8: tataradx rady
7: x tatax 9: if (hradx = hrady ) then
8: end while 10: hrady hrady + 1
9: return x 11: end if
10: end function 12: return rady
13: end if
14: end function
Cea dea doua tehnica utilizata pentru a reduce complexitatea timp a operatiilor F ind si
Merge este reprezentata de comprimarea drumului. Aceasta consta n a apropia fiecare nod
de radacina arborelui caruia i apartine. Astfel n timpul operatiei F ind(x) se determina mai
ntai radacina arborelui caruia i apartine (rad) si apoi se mai parcurge o data drumul de la
nodul x la radacina, astfel tatay rad, y lantul de la x la rad.
1: function Find(x)
2: yx
3: while (tatay 6= y) do
4: y tatay
5: end while
6: rad y
7: yx
8: while (tatay =6 y) do
9: y tatay
10: tatax rad
11: xy
12: end while
13: return rad
14: end function
121
O verificare eficienta cu privire la apartenenta la acelasi arbore a celor doua extremitati
ale unei muchii se poate face daca vom utiliza o structura de date pentru multimi disjuncte.
Ca implementare, am ales reprezentarea cu padure de arbori. Vom folosi un vector tata
care, pentru fiecare nod, va pastra tat al acestuia n arborele partial caruia i apartine.
Cand sunt preluate ca date de intrare, muchiile vor fi prezentate n ordinea crescatoare a
extremitatilor lor, de forma i j cost, cu i < j.
Exemplul 5.7 Sa consideram graful din figura 5.6. Muchiile grafului si costurile lor sunt
(am ales sa reprezentam matricea A sub forma unei matrice cu m coloane si 3 linii din motive
de spatiu):
A 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
linia 1 1 1 1 2 2 2 3 3 4 4 4 5 6 6 7
linia 2 2 3 5 4 5 6 6 8 5 7 8 6 7 8 8
linia 3 14 6 5 12 16 20 12 12 21 24 10 16 14 6 10
Dupa asezarea n ordine crescatoare a muchiilor dup a cost avem:
A 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
linia 1 1 1 6 4 7 2 3 3 1 6 2 5 2 4 4
linia 2 5 3 8 8 8 4 6 8 2 7 5 6 6 5 7
linia 3 5 6 6 10 10 12 12 12 14 14 16 16 20 21 24
In figura 5.7, sunt ilustrati pasii algoritmului lui Kruskal aplicat pe graful considerat.
La nceput se initializeaza padurea de arbori, fiecare arbore fiind alc atuit dintrun singur
nod, acesta fiind si radacina arborelui. Apoi la fiecare pas se alege o muchie si se verifica
daca extremitatile acesteia fac parte din arbori diferiti. Dac
a raspunsul este afirmativ atunci
muchia respectiva este selectata, altfel se trece la muchia urm atoare.
122
Fig. 5.7: Algoritmului lui Kruskal exemplificat pe graful din figura 5.6
123
Subliniem faptul ca arborii reprezentati n figura 5.7 sunt diferiti de arborii ce con-
duc la obtinerea solutiei problemei: arborii din figur a constituie suportul necesar pentru
reprezentarea structurii de date pentru multimi disjuncte, ce este utilizat a pentru efectuarea
eficienta a operatiilor F ind si Merge. Acest lucru justific a faptul ca desi la pasul al patrulea
selectam muchia (4, 8) pentru arborele partial de cost minim, ea nu se reg aseste n configuratia
(4) (nodul 4 este unit direct cu nodul 6, vezi figura 5.7). Singura leg atura dintre un arbore
suport pentru reprezentarea unei multimi si un arbore folosit pentru obtinerea arborelui partial
de cost minim, este multimea de noduri ce este comun a.
La pasii urmatori, cinci si sase, sunt alese muchiile (7, 8) si respectiv (2, 4): L = {(1, 5), (1, 3),
(6, 8), (4, 8), (7, 8), (2, 4)}.
La pasul al saptelea se evalueaza muchia (3, 6). Nodurile 3 si 6 fac parte din arbori diferiti,
astfel ncat muchia curenta poate fi selectata pentru solutia problemei, L = {(1, 5), (1, 3), (6, 8),
(4, 8), (7, 8), (2, 4), (3, 6)}.
5.4 Exercitii
1. Sa se realizeze o subrutina ce ntoarce numarul de noduri dintrun arbore oarecare.
3. Fie un graf G cu n varfuri si m muchii de cost pozitiv. Alegand un nod, numit nod
central, sa se determine un subarbore al lui G astfel ncat drumurile de la nodul central
la toate celelalte noduri sa aiba lungimea minima.
4. Fiind date n puncte n spatiul R3 determinate prin coordonatele (x, y, z), sa se elaboreze
un algoritm ce determina sfera de raza minima cu centrul ntrunul din punctele date
si care contine n interiorul ei toate cele n puncte.
124
Datele de intrare sunt alcatuite din descrierile mai multor placi electronice. Descrierea
fiecarei placi contine numarul N de componente si numarul M de interconexiuni. O
conexiune se caracterizeaza prin trei valori: doua varfuri, u si v, precum si lungimea
acesteia l.
Datele de iesire constau dintr-un raspuns pentru fiecare multime de date de test, compus
din numarul testului (se ncepe numerotarea cu 1), precum si costul interconectarii de
lungime minima.
5 7
1 2 40 2 3 35 1 5 27 1 4 37 4 5 30 2 4 58 3 4 60
0 0
Cazul 1: [1 5] [4 5] [2 3] [1 2]
Interconectarea de cost minim are valoarea 132.
Fig. 5.8: Descrierea conexiunilor posibile dintre componentele unei placi electronice
6. Realizati o subrutina nerecursiva care sa determine cheia minima dintrun arbore oare-
care.
125
la fiecare punct de supraveghere parcurgand un anumit drum. Evident este de dorit ca
lungimea acestui drum sa fie minima si valoarea maxima dintre lungimile drumurilor
minime pentru fiecare punct de supraveghere n parte sa fie cat mai mica posibila.
Se cere sa se determine punctul de control n care trebuie instalat comandamentul cat
si drumurile ce vor fi parcurse de la comandament la fiecare punct de supraveghere,
astfel ncat aceste drumuri sa aiba valoarea lungimii minima si cel mai lung dintre ele
sa fie cat mai scurt posibil.
126
Capitolul 6
Grafuri orientate
Definitia 6.1 Un graf orientat este o pereche ordonat a G = (V, E), unde V este o
multime de varfuri sau noduri, iar E este o multime de arce.
Definitia 6.2 Un graf partial al unui graf orientat G = (V, E) este un graf orientat
G1 = (V, E1 ) unde E1 E.
Exemplul 6.2 Pentru graful G din exemplul anterior (G = (V, E)), consideram graful
partial G1 = (V, E1 ), cu E1 = {(1, 3), (2, 1), (3, 5), (4, 6), (5, 2), (5, 7), (6, 4), (6, 8)}, si V =
{1, 2, 3, 4, 5, 6, 7, 8}.
Definitia 6.3 Un subgraf al unui graf orientat G = (V, E) este un graf orientat H =
(V1 , E1 ) unde V1 V iar arcele din E1 sunt toate arcele din E ce au ambele extremitati n
multimea V1 (E1 = E|V1 V1 ).
Exemplul 6.3 Fie subgrafurile H1 si H2 ale grafului G din exemplul anterior: H1 = (V1 , E1 ),
unde V1 = {1, 2, 3, 5}, iar E1 = {(1, 2), (1, 3), (2, 1), (3, 5), (5, 2)}, si H2 = (V2 , E2 ), unde
V = {4, 6, 7, 8} si E2 = {(4, 6), (6, 4), (6, 8), (7, 6)}.
Definitia 6.4 Gradul exterior al unui v arf d+ (x) este egal cu numarul arcelor ce au ca ex-
tremitate initiala pe x (d+ (x) = |{(x, u)|(x, u) E, u V }|). Gradul interior al unui varf
d (x) este egal cu numarul arcelor ce au ca extremitate final a pe x (d (x) = |{(u, x)|(u, x)
E, u V }|).
127
Fig. 6.1: Un exemplu de graf orientat
Daca varfurile v0 , v1 , . . . , vp sunt distincte doua cate doua, drumul se numeste elementar.
Exemplul 6.4 L1 = {(6, 8), (7, 6), (7, 5), (3, 5), (1, 3)} si L2 = {(6, 8), (6, 4), (1, 4)} sunt lanturi
de la varful 8 la varful 1, iar D1 = {(1, 3), (3, 5), (5, 7), (7, 6), (6, 8)} si
D2 = {(1, 4), (4, 6), (6, 8)} sunt drumuri elementare de la v arful 1 la v arful 8.
Definitia 6.8 Se numeste circuit hamiltonian un circuit elementar ce trece prin toate
varfurile grafului. Un graf ce admite un circuit hamiltonian se numeste graf hamiltonian
orientat.
Metodele utilizate pentru reprezentarea unui graf neorientat se pot adapta n mod natural
pentru reprezentarea unui graf orientat, nlocuind notiunea de muchie cu cea de arc.
128
Fig. 6.2: Arbore de acoperire n latime pentru graful orientat din figura 6.1
arc al arborelui de acoperire - arcul (u, v) este un arc al arborelui de acoperire n latime.
arc de ntoarcere - arcul (u, v) se numeste arc de ntoarcere daca are sensul contrar unui
drum de la v la u n arborele de acoperire (v u) (se spune ca u este un descendent
al lui v si v este un stramos al lui u).
arc de traversare - arcul (u, v) este un arc de traversare daca v nu este nici descendent
direct, nici stramos al lui u.
In figura 6.2 este prezentat arborele de acoperire n latime corespunzator grafului din
figura 6.1 avand radacina 1: arcele (2, 1), (7, 5), (6, 4) sunt arce de ntoarcere, (5, 2), (7, 6)
sunt arce de traversare, iar celelalte sunt arce ale arborelui de acoperire (de exemplu (1, 3),
(6, 8)).
Vom prezenta schita unui algoritm mai general pentru parcurgerea unui graf (a se vedea
algoritmul 46). Pentru aceasta se utilizeaza doua multimi de noduri, V izitat si Neexplorat,
unde V izitat reprezinta multimea nodurilor vizitate iar Neexplorat (Neexplorat V izitat)
reprezinta multimea nodurilor vizitate dar neexplorate (noduri ce prezinta vecini nca nevizitati).
In urma unei parcurgeri n adancime a unui graf orientat, fiecare arc din multimea arcelor
poate fi ncadrat ntr-unul dintre tipurile urmatoare:
arc al arborelui de acoperire - arcul (u, v) este un arc al arborelui de acoperire daca
call
df s(u) apeleaza df s(v) (df s(u) df s(v)).
arc de naintare - arcul (u, v) este un arc de naintare daca este paralel cu un drum de
la u la v din arborele de acoperire (u v) (nu face parte din arborele de acoperire).
129
Algoritm 46 Algoritm de vizitare a unui graf (model general)
( ParcurgereGraf(u, G)
1: procedure
u - v arful de unde se porneste vizitarea
Input:
G - graful
2: V izitat {u}
3: N eexplorat V izitat
4: while (N eexplorat 6= ) do
5: extrage un nod x din N eexplorat
6: determin a y, urmatorul vecin al lui x ce nu a fost vizitat
7: if (y = N U LL) then
8: elimina x din N eexplorat
9: else
10: if (y
/ V izitat) then
11: V izitat V izitat {y}
12: N eexplorat N eexplorat {y}
13: end if
14: end if
15: end while
16: end procedure
arc de ntoarcere - arcul (u, v) se numeste arc de ntoarcere daca are sensul contrar unui
drum de la v la u din arborele de acoperire (v u).
arc de traversare - arcul (u, v) este un arc de traversare daca df s(v) a fost apelat si s-a
terminat nainte de apelul lui df s(u).
Fig. 6.3: Arbore de acoperire n adancime pentru graful orientat din figura 6.1
Pentru fiecare nod v al unui graf vom introduce doua numere, prenumv si postnumv ,
numere ce depind de ordinea n care sunt ntalnite nodurile acestuia n timpul vizitarii n
adancime: prenumv marcheaza momentul cand este ntalnit nodul v pentru prima oara
130
iar postnumv momentul n care prelucrarea nodului v s-a ncheiat. Variabila counter este
initializata cu valoarea 1:
1: procedure Prenum(v)
2: prenumv counter
3: counter counter + 1
4: end procedure
si
1: procedure Postnum(v)
2: postnumv counter
3: counter counter + 1
4: end procedure
Un arc (u, v) va avea urmatoarele proprietati, n functie de una din cele patru categorii
de arce introduse anterior n care se ncadreaza:
Arborele de acoperire n adancime corespunzator grafului din figura 6.1 ilustreaza aceste
tipuri de arce: arc de naintare (1, 4), arce de ntoarcere - (2, 1), (7, 5), (4, 6) arc de traver-
sare - (5, 2), arce ale arborelui de acoperire - (1, 2), (1, 3), (3, 5), (5, 7), (7, 6), (6, 8), (6, 4) (a
se vedea figura 6.3).
Lema 6.5 Fiind dat un graf orientat G si dou a noduri oarecare u, v G ce apartin aceluiasi
arbore de acoperire n adancime rezultat n urma parcurgerii cu metoda DF S a grafului G
avem:
1. daca (u, v) este un arc al arborelui de acoperire sau un arc de naintare sau un arc de traver-
sare postnumu > postnumv ;
131
2. daca (u, v) este un arc de ntoarcere postnumu < postnumv .
a noduri u, v G avem:
Lema 6.6 Fiind dat un graf orientat G pentru oricare dou
Lema 6.7 Fiind dat un graf neorientat G, dac a noduri oarecare u, v G avem
a pentru dou
relatia prenumu < prenumv < postnumu atunci:
1 2 3 4 5 6 7 8
prenum 1 2 4 8 5 6 7 10
postnum 16 3 15 9 14 13 12 11
Pentru arcul (1, 4) avem prenum1 = 1, postnum1 = 16, prenum4 = 8 si postnum4 = 9.
Deoarece relatia prenumu < prenumv < postnumu este adev arat
a conform lemei 6.7 ar
trebui sa avem prenumu < prenumv < postnumv < postnumu si s a existe un drum de la u
la v, lucruri care sunt adevarate (prenum1 < prenum4 < postnum4 < postnum1 si exista un
drum de la 1 la 4 n arborele de acoperire n ad
ancime).
132
Definitia 6.12 Un graf orientat si care nu posed
a circuite se numeste graf orientat aciclic
(directed acyclic graph - DAG).
Lema 6.9 Intr-un graf orientat aciclic, dac
a prenumu < prenumv si exist
a un drum de la
u la v n G, atunci prenumu < prenumv < postnumv < postnumu .
In practica, exista mai multe situatii n care o multime de activitati sau sarcini, trebuie
organizate ntr-o anumita ordine, ntre acestea existand, de obicei, o multime de restrictii sau
dependente. In activitatea de construire a unei cladiri, anumite activitati nu pot fi ncepute
decat dupa finalizarea altor activitati: spre exemplu, nu se poate ncepe ridicarea peretilor
unei constructii decat dupa turnarea fundatiei si finalizarea structurii de rezistenta, nu se
poate realiza instalatia electrica daca nu au fost ridicate zidurile, s.a.m.d.
Sau daca un student doreste sa si alcatuiasca un plan de studiu individual ce va contine
pe langa cursurile obligatorii, si o serie de cursuri optionale, va trebui sa tina cont de anul de
studiu n care se preda fiecare materie, precum si de cerintele obligatorii ale acestora: un curs
nu poate fi inclus n planul de studiu individual al unui student decat daca acesta a parcurs
si a obtinut creditele la toate materiile anterioare cerute explicit n programa cursului.
( SortTop1(n, V ecin)
1: procedure
n - num
arul de noduri din graf
Input:
V ecin - vector ce contine listele cu vecini ai fiec
arui nod
2: for k 1, n do
3: dminusk 0
4: end for
5: for (u, v) E do
6: dminusv = dminusv + 1
7: end for
8: Q se initializeaz
a coada
9: for k 1, n do
10: if (dminusk = 0) then
11: Qk se insereaza ntro coad
a nodurile cu gradul interior 0
12: end if
13: end for
14: while (Q 6= ) do
15: Qk se extrage din coad a un nod
16: Lk se insereaz a nodul ntr-o list
a
17: w V ecink v ia valoarea capului listei de vecini a nodului k
18: while (w 6= N U LL) do
19: dminusw.nodeIndex dminusw.nodeIndex 1
20: if (dminusw.nodeIndex = 0) then
21: Q w.nodeIndex se insereaza n coada nodul w.nodeIndex
22: end if
23: w w.next se trece la urm atorul vecin
24: end while
25: end while
26: end procedure
Dependenta dintre doua activitati A si B o putem modela prin introducerea a doua noduri
n graf xi si xj , asociate celor doua activitati. Daca activitatea A trebuie realizata naintea
activitatii B, atunci se adauga arcul (xi , xj ).
133
Definitia 6.13 Se numeste sortare topologic a pentru un graf orientat G = (V, E) o or-
donare {x1 , x2 , . . . , xn } a nodurilor grafului astfel nc
at pentru orice arc (xi , xj ) sa avem
i < j.
Prin urmare o sortare topologica presupune aranjarea liniara a varfurilor unui graf astfel
ncat toate arcele sale sa fie orientate de la stanga la dreapta.
Lema 6.10 Daca un graf orientat G admite o sortare topologic
a atunci G este aciclic.
Lema 6.11 Daca un graf orientat G este aciclic atunci el admite o sortare topologica.
Observatia 6.12 Intr-un graf orientat aciclic exist a cel putin un nod al c
arui grad interior
este 0 (graful nu poseda arce care sa aib
a nodul v drept extremitate final
a).
Pornind de la aceasta observatie se schiteaza urmatorul algoritm [78]: ntr-un graf G se
determina un nod v astfel ncat gradul sau interior sa fie zero (d (v) = 0); se adauga acest
nod la o lista ce va contine ordonarea topologica, se sterge nodul din graf mpreuna cu toate
arcele ce l au ca extremitate initiala, si se reia algoritmul pentru graful G = (V , E ), unde
V = V \ {v}, E = E|V V .
Algoritmul 48 se termina n cel mult n pasi (|V | = n). Daca se termina mai devreme,
atunci graful G nu este aciclic (la un moment dat nu mai exista nici un nod v astfel ncat
d (v) = 0).
Fig. 6.5: Sortare topologica cu algoritmul 48 pentru graful orientat din figura 6.4
Exemplul 6.13 Fie graful orientat din figura 6.5. V arful 3 are d (v) = 0. Se adauga acest
nod la lista finala ce va contine nodurile ordonate, se sterge nodul mpreun a cu arcele care l
au drept extremitate initiala. In graful rezultat, v arful 1 are proprietatea ca d (v) = 0. Se
adauga la lista de rezultate, si se elimina din graf mpreun a cu arcele ce pleac a din el. Se
continua procedeul pana cand graful devine vid (ntregul proces poate fi urmarit n figura 6.5).
134
Algoritm 49 Algoritm de sortare topologica a unui graf orientat (a doua varianta)
( SortTop2(n, V ecin)
1: procedure
n - numarul de noduri din graf
Input:
V ecin - matricea de adiacenta a grafului
2: for k 1, n do
3: prenumk 0, postnumk 0
4: vizitatk 0
5: end for
6: for k 1, n do
7: if (vizitatk = 0) then
8: call DF SN um(k, n, V ecin)
9: end if
10: end for
11: se ordoneaz a descresc
ator nodurile dupa postnumk
12: end procedure
Lema 6.14 Un graf orientat G este aciclic dac a si numai daca n urma unei vizitari n
adancime a acestuia nu este ntalnit nici un arc de ntoarcere.
Exemplul 6.15 Sa consideram ca date de intrare pentru algoritmul 49 graful orientat din
figura 6.4. Dupa etapa de initializare (liniile 2 - 5) avem:
1 2 3 4 5 6 7
prenum 0 0 0 0 0 0 0
postnum 0 0 0 0 0 0 0
vizitat 0 0 0 0 0 0 0
Se apeleaza mai ntai DFSNum(1, 7, Vecin). Secventa rezultata de apeluri recursive este
urmatoarea: DFSNum(1, 7, Vecin) DFSNum(2, 7, Vecin) DFSNum(4, 7, Vecin)
DFSNum(5, 7, Vecin) DFSNum(7, 7, Vecin) DFSNum(6, 7, Vecin).
In urma acestei secvente valorile vectorilor prenum si postnum sunt urmatoarele:
1 2 3 4 5 6 7
prenum 1 2 0 3 4 6 5
postnum 12 11 0 10 9 7 8
vizitat 1 1 0 1 1 1 1
Mai ramane nevizitat un singur nod, 3, drept pentru care vom mai avea un apel
DFSNum(3, 7, Vecin) din procedura principal a SortTop2():
1 2 3 4 5 6 7
prenum 1 2 13 3 4 6 5
postnum 12 11 14 10 9 7 8
vizitat 1 1 1 1 1 1 1
Ordonarea descrescatoare a nodurilor multimii V dup
a valorile vectorului postnum, con-
duce la urm
atoarea configuratie:
135
postnum 14 12 11 10 9 8 7
3 1 2 4 5 7 6
Astfel, sortarea topologica a nodurilor grafului obtinut
a n urma aplic
arii algoritmului 49
este: 3, 1, 2, 4, 5, 7, 6.
Fie G = (V, E) un graf orientat unde V = {1, 2, . . . , n} este multimea nodurilor si E este
multimea arcelor (E V V ).
Definitia 6.14 O component a tare conex a a unui graf orientat G este o multime maxi-
mala de varfuri U V , astfel ncat, pentru fiecare pereche de v
arfuri {u, v} (u, v U) exista
atat un drum de la u la v cat si un drum de la v la u. Prin urmare se spune c a varfurile u
si v sunt accesibile unul din celalalt.
Un graf orientat ce are o singura componenta tare conexa astfel ncat U = V este un graf
tare conex. In cazul n care un graf orientat nu este tare conex atunci el se poate descompune
n mai multe componente tare conexe. O astfel de componenta este determinata de un subgraf
al grafului initial.
Definitia 6.15 Graful orientat G se numeste tare conex dac a pentru orice pereche de
varfuri u 6= v, exista un drum de la nodul u la nodul v si un drum de la nodul v la nodul u.
Se poate arata ca relatia de tare conexitate este o relatie de echivalenta. O relatie (notata
cu ) este o relatie de echivalenta daca prezinta proprietatile de reflexivitate, simetrie si
tranzitivitate:
136
reflexivitate: a a;
simetrie: a b b a;
tranzitivitate: a b, b c a c.
O clasa de echivalenta este multimea tuturor elementelor care se afla n relatia ([x] =
{y|x y}). Relatia de echivalenta determina o partitie n clase de echivalenta a multimii
peste care a fost definita. Prin urmare relatia de tare conexitate determina o partitie a
multimii V . Clasele de echivalenta determinate de relatia de tare conexitate sunt componen-
tele tare conexe.
Pentru determinarea componentelor tare conexe exista mai multi algoritmi, dintre care
amintim algoritmul lui Tarjan, algoritmul lui Kosaraju si algoritmul lui Gabow.
Pas 2. Se obtine un nou graf GT = (V, E T ) prin inversarea sensului arcelor grafului G (E T =
{(u, v)|(v, u) E, u, v V }).
Pas 3. Se cauta nodul nevizitat din graful GT ce are cel mai mare numar atribuit n urma
parcurgerii de la Pasul 1. Din acest nod se initiaza parcugerea grafului cu algoritmul
de vizitare n adancime.
Pas 4. Daca mai raman noduri nevizitate atunci se reia Pasul 3, altfel algoritmul se termina.
i nt nump ;
i nt postnum [MAX] ; // Numarul a s o c i a t f i e c a r u i v a r f l a v i z i t a r e a i n p o s t o r d i n e .
137
void d f s ( i nt k ) {
i nt i ;
v i z i t a t [ k ] = TRUE;
for ( i = 0 ; i < n ; i ++)
i f ( ( v i z i t a t [ i ] == FALSE) && ( v e c i n [ k ] [ i ] > 0 ) )
dfs ( i ) ;
nump++;
postnum [ k ] = nump ;
}
void d f s 1 ( i nt k ) {
i nt i ;
v i z i t a t [ k ] = TRUE;
for ( i = 0 ; i < n ; i ++)
i f ( ( v i z i t a t [ i ] == FALSE) && ( v e c i n [ k ] [ i ] > 0 ) )
dfs1 ( i ) ;
p r i n t f ( %d , k ) ;
}
void r e a d I n p u t ( void ) {
i nt i , j ;
p r i n t f ( n = ) ; s c a n f ( %d , &n ) ;
do{
p r i n t f ( nod1 nod2 : ) ; s c a n f ( %d %d , &i , &j ) ;
i f ( i >= 0 )
vecin [ i ] [ j ] = 1;
} while ( i >= 0 ) ;
}
readInput ( ) ;
// prima eta pa
memset ( v i z i t a t , 0 , s i z e o f ( v i z i t a t ) ) ;
nump = 0 ;
for ( i = 0 ; i < n ; i ++)
i f ( v i z i t a t [ i ] == FALSE)
dfs ( i ) ;
// eta pa a doua
memset ( v i z i t a t , 0 , s i z e o f ( v i z i t a t ) ) ;
for ( i = 0 ; i < n ; i ++)
for ( j = i ; j < n ; j ++) {
tmp = v e c i n [ i ] [ j ] ;
vecin [ i ] [ j ] = vecin [ j ] [ i ] ;
v e c i n [ j ] [ i ] = tmp ;
}
k = 0;
while (TRUE) {
maxim = 0 ;
for ( i = 0 ; i < n ; i ++)
i f ( ( v i z i t a t [ i ] == FALSE) && ( maxim < postnum [ i ] ) ) {
maxim = postnum [ i ] ;
nod = i ;
138
}
i f ( maxim == 0 )
break ;
k++;
p r i n t f ( Componenta %d : , k ) ;
d f s 1 ( nod ) ;
p r i n t f ( \n ) ;
}
}
1 2 3 4 5 6 7 8
postnum 8 7 5 6 1 2 3 4
vizitat 1 1 1 1 1 1 1 1
Se construieste graful transpus, GT (a se vedea figura 6.7). Se caut a primul nod u nca
nevizitat (vizitatu = 0), caruia i corespunde cea mai mare valoare postnumu . In acest mod,
se identifica componenta tare conexa compus a numai din nodul 1: {1}.
Urmatorul nod, ca valoarea a vectorului postnum ordonat descresc ator, este 2. Din nodul
2 se parcurg nodurile 3 si 4, rezultand o alt
a component a tare conexa: {2, 3, 4}.
Urmatorul nod nevizitat u ce are valoarea prenumu maxim a este nodul 8. Se identifica
mai ntai componenta tare conexa {7, 8} si apoi {5, 6}.
Analiza algoritmului
Algoritmul lui Kosaraju realizeaza doua parcurgeri ale tuturor elementelor grafului G: prima
data parcurge graful G si a doua oara parcurge graful GT . Prin urmare, complexitatea
timp a algoritmului este (|V | + |E|) n cazul n care graful este reprezentat prin liste de
adiacenta si este patratica n |V |2 (O(|V |2 )) atunci cand graful este reprezentat prin matricea
de adiacenta.
139
Algoritmul initiaza o parcurgere n adancime a nodurilor grafului G pornind de la un nod
oarecare. O componenta tare conexa a grafului G, daca exista, constituie un subarbore al
arborelui de acoperire n adancime, iar radacina acestui subarbore este reprezentantul clasei
de echivalenta.
Prin urmare componentele tare conexe se obtin prin descompunerea arborelui/arborilor
de acoperire n adancime daca eliminam anumite arce ale acestora. Un nod este capul unei
componente tare conexe (sau radacina), daca acesta constituie radacina subarborelui core-
spunzator componentei. Arcul ce are nodul-cap drept extremitate finala este cel ce trebuie
eliminat. Dupa ce determinam toate nodurile-cap, subarborii arborelui/arborilor de acoperire
n adancime ce i au drept radacini, sunt componentele tare conexe.
Algoritmul lui Tarjan are drept scop determinarea nodurilor-cap. Pentru a pastra o ordine
a vizitarii acestora pe parcursul algoritmului, nodurile vor fi adaugate pe o stiva. Ele vor fi
extrase din stiva n momentul n care procedura de vizitare DFS a unui nod este ncheiata: se
determina daca nodul curent este radacina unei componente tare conexe, si, n caz afirmativ,
toate nodurile care au fost vizitate din nodul curent n cadrul parcurgerii n adancime sunt
marcate ca fiind elemente ale componentei tare conexe.
Vom numerota toate nodurile grafului n preordine (altfel spus, acest numar indica pen-
tru nodul curent numarul de noduri vizitate naintea sa), valorile fiind pastrate n vectorul
prenum (cititorul este invitat sa revada si sectiunea despre Muchie critica, cea de-a doua
solutie). Pentru un nod u G definim lowu astfel:
prenumu
lowu = min prenumx , daca [u, x] este arc de ntoarcere sau de traversare si x S
lowy , y descendent direct al lui u
Daca dintr-un varf u G exista un arc de ntoarcere sau de traversare catre un nod v G
n afara subarborelui de vizitare n adancime determinat de u, atunci acest nod v trebuie sa
fi fost vizitat naintea lui u (prenumu > prenumv ). Daca un astfel de nod nu exista atunci
lowu = prenumu . Stiva S pastreaza nodurile grafului, pe masura ce sunt vizitate, ele fiind
adaugate la S. Daca u este un nod-cap (lowu = prenumu ) se extrag din stiva toate nodurile
dintre varful stivei si u inclusiv: acestea formeaza o componenta tare conexa.
Se observa ca algoritmul lui Tarjan (algoritmul 50) seamana foarte mult cu algoritmul 22
de determinare a muchiei critice (varianta a II-a).
1 2 3 4 5 6 7 8
prenum 0 0 0 0 0 0 0 0
vizitat 0 0 0 0 0 0 0 0
Primul element nevizitat este nodul 1 (linia 9), prin urmare se apeleaz
a DFSTarjan(1,
8, Vecin). Rezulta o secventa de apeluri recursive:
DFSNum(1, 8, Vecin) DFSNum(2, 8, Vecin) DFSNum(3, 8, Vecin)
DFSNum(6, 8, Vecin) DFSNum(5, 8, Vecin).
1 2 3 4 5 6 7 8
prenum 1 2 3 0 5 4 0 0
vizitat 1 1 1 0 1 1 0 0
low 1 2 0 0 4 4 0 0
arful stivei fiind n dreapta): {1, 2, 3, 6, 5}.
Stiva contine urmatoarele elemente (v
140
Algoritm 50 Algoritmul lui Tarjan pentru determinarea componentelor tare conexe
( Tarjan(n, V ecin)
1: procedure
n - numarul de noduri din graf
Input:
V ecin - matricea de adiacenta
2: for i 1, n do
3: vizitati 0
4: end for
5: counter 0
6: S
7: for i 1, n do
8: if (vizitati = 0) then
9: call DF ST arjan(i, n, V ecin) vizitarea n ad
ancime a grafului
10: end if
11: end for
12: end procedure
13: procedure DFSTarjan(k, n, V ecin)
14: Sk
15: vizitatk 1
16: counter counter + 1
17: prenumk counter, lowk counter
18: for i 1, n do
19: if (vecink,i = 1) then
20: if (vizitati = 0) then
21: call DF ST arjan(i, n, V ecin)
22: lowk M in(lowk , lowi )
23: else
24: if (i S) then
25: lowk M in(lowk , prenumi )
26: end if
27: end if
28: end if
29: end for
30: if (lowk = prenumk ) then
31: repeat
32: Su
33: Output u
34: until (u = k)
35: end if
36: end procedure
141
DFSNum(3, 8, Vecin) DFSNum(8, 8, Vecin) DFSNum(7, 8, Vecin).
Continutul stivei este {1, 2, 3, 8, 7}.
La nivelul apelului DFSNum(7, 8, Vecin) nu se ia n considerare la calculului valorii lui
low7 nodul 6: desi exista arcul (7, 6), nodul 6 a fost vizitat (vizitati 6= 0) si nu se mai afla
pe stiva (i
/ S). Astfel low7 = min{low7 , prenum8 } = min{7, 6} = 6.
La nivelul lui DFSNum(8, 8, Vecin), low8 = min{low8 , low7 } = min{6, 6} = 6 (linia
22). Avem ca (low8 = prenum8 ) (linia 30) si prin urmare 8 este un nod-cap. Se extrag de pe
stiva toate elementele dintre varful stivei si elementul 8 inclusiv, rezult
and a doua componenta
tare conexa: {7, 8}.
Stiva ramane cu elementele (varful stivei fiind n dreapta) {1, 2, 3}.
1 2 3 4 5 6 7 8
prenum 1 2 3 0 5 4 7 6
vizitat 1 1 1 0 1 1 1 1
low 1 2 0 0 4 4 6 6
In momentul terminarii apelului DFSTarjan(3, 8, Vecin), low3 = 2.
Revenind la nivelul apelului DFSTarjan(2, 8, Vecin), se calculeaz a low2 = min{low2 , low3 } =
min{2, 2} = 2, si se continua cu urmatorul nod nca nevizitat, 4: DFSNum(2, 8, Vecin)
DFSNum(4, 8, Vecin).
Se adauga nodul 4 pe stiva (stiva devine {1, 2, 3, 4}), low4 = prenum4 = 8.
low4 se calculeaza din low4 = min{low4 , prenum3 } = min{8, 3} = 3 (exist a arcul (4, 3),
nodul 3 a fost vizitat - vizitat3 = 1 si 3 S - nodul 3 se afla pe stiv
a).
1 2 3 4 5 6 7 8
prenum 1 2 3 8 5 4 7 6
vizitat 1 1 1 1 1 1 1 1
low 1 2 2 3 4 4 6 6
Ne ntoarcem la nivelul apelului DFSTarjan(2, 8, Vecin), low2 = min{low2 , low4 } =
min{2, 3} = 2. Arcul (2, 5) nu este luat n considerare la calculul lui low2 deoarece nodul 5 a
fost vizitat (vizitat5 = 1) si 5 nu se reg
aseste pe stiv
a. Astfel low2 r
amane cu valoarea 2.
Deoarece low2 = prenum2 , extragem de pe stiv a elementele ce determina cea de-a treia
componenta tare conexa: {4, 3, 2}. Pe stiv a mai r amane un singur element, {1}, ce va
determina ultima componenta tare conex a.
In final, valorile low si prenum sunt urm atoarele:
1 2 3 4 5 6 7 8
prenum 1 2 3 8 5 4 7 6
vizitat 1 1 1 1 1 1 1 1
low 1 2 2 3 4 4 6 6
142
Fig. 6.8: Drumul P n algoritmul lui Gabow pentru graful din figura 6.6
- daca w
/ P , atunci se adauga nodul w la drumul P (P = [v1 , . . . , vk , w]);
- daca nu avem nici un arc neverificat care sa aiba nodul vk ca extremitate initiala, se
marcheaza nodul vk ca apartinand unei componente conexe. Se sterge nodul vk din
graful H si din drumul P , mpreuna cu toate arcele ce l au ca extremitate. Daca
P 6= atunci se continua algoritmul ncercandu-se augmentarea drumului P . In caz
contrar, se ncearca initializarea unui drum nou P n H.
Si acest algoritm are la baza metoda de parcurgere n adancime a unui graf (DFS). In
timpul vizitarii, algoritmul utilizeaza doua stive S si P : S contine toate nodurile ce nu au
fost atribuite nca unei componente tare conexe, n ordinea n care au fost ntalnite, iar P
contine toate nodurile despre care nca nu se poate spune nimic cu privire la apartenenta lor
la componente conexe diferite (contine nodurile drumului P din graful H). Altfel spus, se
observa faptul ca stiva P pastreaza nodurile r
adacina ce formeaza o subsecventa a secventei
nodurilor ce se afla la un moment dat n cealalta stiva S.
In cadrul secventei urmatoare, pentru un nod i adiacent cu nodul curent k, se verifica
daca nu a mai fost vizitat. Daca vizitati = 0 atunci se continua vizitarea n adancime
(call DFSGabow(i, n, Vecin)). Altfel, se verifica daca nodul i nu a fost deja asignat unei
componente tare conexe, n caz afirmativ eliminandu-se din drumul P circuitul {vi , . . . , vk , vi }:
1: if (vizitati = 0) then
2: call DF SGabow(i, n, V ecin)
3: else
4: if (i S) then i
5: while (prenumi < prenumpeek(P )) do
6: P u
7: end while
8: end if
9: end if
La finalul procedurii DFSGabow, se verifica daca nodul curent este identic cu cel aflat n
varful stivei P . In caz afirmativ, se extrag de pe stiva toate nodurile dintre varful curent
143
al stivei S si nodul k, si se marcheaza ca fiind elemente ale componentei tare conexe a carei
radacina este nodul k:
if (k = peek(P )) then
P u
repeat
Su
Output u
until (u = k)
end if
144
6.5 Exercitii
1. (Compilator ) In trecut compilatoarele erau foarte simple. In acele vremuri oamenii
preferau sa includa tot programul ntr-un singur fisier. Daca cineva facea o modificare n
program, trebuia recompilat tot codul sursa. Cresterea lungimii programelor conducea
la timpi de compilare tot mai mari, ceea ce constituia o piedica n ceea ce priveste
cresterea complexitatii algoritmilor implementati.
De aceea programatorii au dezvoltat o tehnica pentru eliminarea compilarilor redun-
dante. Ei au nceput prin a sparge programele n mai multe module mici pe care le
compilau separat. Astfel, pentru orice modificare care se opera ntr-un anumit modul,
se compila numai acesta, si nu ntreaga aplicatie. Fiecare modul contine la nceput lista
celorlaltor module pe care le foloseste.
Modulul A trebuie recompilat numai daca a fost modificat sau are n lista sa un modul
B care a fost recompilat la randul sau. In celelalte cazuri nu este necesara recompilarea
modulului A.
Problema cere sa se realizeze un algoritm si pe baza acestuia un program, care sa decida
ce module trebuie recompilate si care nu. Pentru a avea un timp de recompilare minim,
va trebui sa cautam compilarea unui numar cat mai mic de linii.
Prima linie a datelor de intrare contine numarul de module N (1 N 100). Urmeaza
descrierea modulelor. Prima linie a descrierii contine numele modulului. A doua linie
contine numarul liniilor din codul sursa. A treia linie contine numarul M (0 M < N)
de module de care depinde modulul actual. Linia urmatoare contine numele acestor
module, separate printrun spatiu. Numele unui modul nu depaseste 20 de caractere.
Descrierea modulelor este urmata de mai multe blocuri, cate unul pentru fiecare versiune
a programului. Prima linie a fiecarui bloc contine numarul k (1 k N) de module
ce au suferit modificari de la recompilarea versiunii precedente.
Linia urmatoare contine numele modulelor, separate prin spatiu, n care au survenit
modificari. Dupa ultimul bloc exista o singura linie ce contine doar numarul 0.
Pentru fiecare versiune a programului se scrie o linie ce contine numarul liniilor codului
sursa care au trebuit sa fie recompilate.
Intrare Iesire
3 127
MATH
20
0
MAIN
100
2
MATH IO
IO
7
0
3
MATH IO MAIN
0
(Propusa la CEOI 1997)
145
Sa consideram pentru k = 5 urmatoarele reguli:
(1) 1 2 (2) 1 3 (3) 3 4 (4) 4 5 (5) 1 5
Profesorul cere sa se demonstreze regula 1 5. Demonstratia optima consta n apli-
carea directa a regulii (5).
(2) (1) (3) (4)
Un elev demonstreaza regula (5) astfel: 2 1 3 4 (1 3, 1 2, 3 4, 4 5). Aceasta
(1)
este o demonstratie corecta, ce contine n plus regula 1 2. Profesorul ar fi fost
(2) (3) (4)
multumit cu demonstratia 2 3 4 (1 3, 3 4, 4 5).
Sa se realizeze un algoritm ce utilizeaza drept date de intrare k, x (propozitia care se
considera adevarata), y (propozitia ce trebuie demonstrata), sirul celor k reguli cat si
sirul numerelor de ordine ale regulilor ce constituie demonstratia elevului.
Algoritmul verifica daca sirul numerelor de ordine ale regulilor constituie o demonstratie.
In caz negativ va afisa NU , iar n caz afirmativ va afisa DA , urmat pe linia urmatoare
de demonstratia elevului din care au fost eliminate afirmatiile inutile. Se considera ca
un sir de reguli fara reguli n plus constituie o demonstratie corecta a propozitiei y
pornind de la propozitia x, daca renuntarea la orice regula ar conduce la o secventa
prin care y nu se mai poate deduce din x.
(Marele premiu PACO, 1997)
3. Dupa cum se poate observa prin reclamele de la televizor, multe companii cheltuiesc
foarte multi bani pentru a convinge oamenii ca ofera cele mai bune servicii la cel mai
scazut pret. O companie de telefoane ofera cercuri de apel (calling circles).
Un abonat poate sa faca o lista cu persoanele pe care le suna cel mai frecvent (si care
constituie cercul sau de prieteni). Daca acesta suna pe cineva inclus n aceasta lista,
si persoana respectiva este, de asemenea, abonata la aceeasi companie, va beneficia de
un discount mai mare decat pentru o convorbire telefonica cu cineva din afara listei.
O alta companie a aflat de aceasta initiativa si se ofera sa determine ea lista de
cunostinte cu care un abonat vorbeste cel mai frecvent la telefon.
LibertyBellPhone este o companie noua de telefoane ce se gandeste sa ofere un plan
de apeluri mult mai avantajos decat alte companii. Ea ofera nu numai reduceri pentru
cercul de apel, cat si determina pentru un abonat acest cerc. Iata cum procedeaza:
compania pastreaza numerele tuturor persoanelor participante la fiecare apel telefonic.
In afara unui abonat, cercul sau de apel consta din toate persoanele pe care le suna si
care l suna, direct sau indirect.
De exemplu, daca Ben l suna pe Alexander, Alexander o suna pe Dolly si Dolly l
suna pe Ben, atunci ei toti fac parte din acelasi cerc. Daca Dolly l mai suna si pe
Benedict iar Benedict o suna pe Dolly, atunci Benedict este n acelasi cerc cu Dolly,
Ben si Alexander. In fine, daca Alexander l suna pe Aron dar Aaron nu l suna pe
Alexander, Ben, Dolly sau Benedict, atunci Aaron nu este n cerc.
Sa se realizeze un algoritm ce determina cercurile de apel, cunoscanduse lista apelurilor
telefonice dintre abonati.
(Finala ACM 1995, Calling Circles)
146
(a) Marirea zilei la 30 de ore, si
Optand pentru a doua varianta, Liga Studentilor introduce o platforma program care:
(a) Stabileste care sunt materiile necesare pentru a putea studia o noua materie.
De exemplu, pentru a studia cursul Management este nevoie de cursul Teorie
Economica si de cursul Marketing.
(b) Stabileste care sunt materiile cu adevarat utile dintre toate cele studiate n fac-
ultate. De exemplu, cursul de Masurari electrice nu este util la absolut nimic.
(c) Cere sa se elimine din programa materiile care nu sunt nici folositoare, nici nu
servesc (direct sau indirect) la nvatarea unor materii folositoare.
(d) Cere sa se indice care dintre materii nu pot fi predate n nici o ordine. De ex-
emplu, cursul Mecanica se bazeaza pe cursul Ecuatii Diferentiale, dar cursul
de Ecuatii Diferentiale si preia exemplele din Mecanica. Prin urmare nu ex-
ista nici o ordine n care aceste materii sa fie predate fara a introduce cunostinte
nedemonstrate.
5. Distribuirea cartilor cerute de catre cititorii de la sala de lectura a unei biblioteci este
facuta de catre un robot ce are posibilitatea sa ajunga la orice carte ce poate fi solicitata.
Din pacate, rafturile unde sunt depozitate cartile sunt dispuse astfel ncat robotul nu
poate lua cartile ntrun singur drum. Dupa receptionarea comenzilor de la mai multi
cititori, robotul cunoaste pozitia cartilor n rafturi si drumurile catre acestea. Din
pacate, de la pozitia unei carti, robotul nu se poate deplasa decat spre anumite carti.
Acesta porneste si culege cartile ce sunt accesibile ntrun drum, apoi porneste de la
o alta pozitie de carte si culege acele carti ce sunt accesibile din acel punct si asa mai
departe.
Datele de intrare constau din numarul de carti n precum si numarul m de legaturi
dintre pozitiile acestora (1 n 100, 11 m 10000), urmate de m perechi de
numere naturale, ce semnifica legaturile directe ntre pozitiile cartilor.
Datele de iesire constau din drumurile pe care le face robotul pentru a culege cartile
cerute.
6. Se considera o multime de n elevi dintr-o clasa. Fiecare elev are cunostinte mai avansate
ntr-un anumit domeniu. Pentru ridicarea nivelului clasei, dirigintele vrea sa-i aranjeze
n grupuri astfel ncat toti elevii dintr-un grup sa ajunga sa cunoasca, ntr-o anumita
perioada de timp, toate cunostintele tuturor celorlalti colegi din acelasi grup.
Grupurile de elevi nu si vor schimba cunostintele ntre ele. Se stie ca un elev nu se
poate face nteles de catre oricine. El are o lista de preferinte fata de care el le va
mpartasi cunostintele sale. Relatia de preferinta nu este simetrica.
Sa se determine numarul minim de grupuri n care va fi mpartita clasa precum si
componenta acestora.
147
Fig. 6.9: Pozitiile cartilor ntr-o biblioteca precum si posibilitatile de deplasare ale robotului
148
Capitolul 7
Heap-uri
Un heap (eng. heap = movila) reprezinta o structura de date abstracta organizata sub forma
unei structuri ierarhice (de arbore) si care respecta urmatoarea proprietate: pentru oricare
doua noduri ale arborelui, A si B, astfel ncat tataB = A, avem cheieA cheieB (sau
cheieA cheieB ). Fiecare nod va avea o valoare asociata numita cheie. Se observa faptul ca,
daca este ndeplinita proprietatea ca A, B, tataB = A, cheieA cheieB , atunci ntotdeauna
cheia de valoare maxima se va afla n radacina arborelui.
Structura de date heap poate fi implementata sub mai multe forme: 2 3 heap [116], heap
binar [9], heap binomial [125], heap Fibonacci [54], heap ternar, treap 1 [7], [109].
Principalele operatii definite pentru un heap H sunt:
1. FindMin(H; p) - determina cheia avand cea mai mica valoare si ntoarce o referinta
catre nodul ce o contine;
2. DeleteMin(H) - sterge nodul ce contine cheia de valoare minima si reorganizeaza struc-
tura prin refacerea proprietatii de heap;
3. Insert(H,p) - insereaza nodul p n heap-ul H;
149
Table 7.1: Complexitatea-timp a principalelor operatii ale unui heap
Intr-un mod similar se poate defini un max-heap, drept un arbore binar complet n care
valoarea memorata n orice nod este mai mare sau egal
a dec
at valorile memorate n nodurile
fii ai acestuia.
Aceasta structura poate fi interpretata drept un arbore partial ordonat ce este un arbore
binar complet cu n varfuri.
Reprezentarea cea mai adecvata pentru un heap binar (min-heap) este reprezentarea
secventiala. Pentru n noduri vom utiliza un tablou A cu dimensiunea egala cu numarul
de noduri. a1(reprezinta cheia nodului radacina al arborelui.
(
2i daca 2 i n 2i+1 daca 2 i + 1 n
Lef ti = , Righti =
nu exista daca 2 i > n nu exista daca 2 i + 1 > n
(
2i daca i > 1
T atai =
nu exista daca i = 1
Exemplul 7.1 Fie heap-ul binar din figura 7.1. Acesta poate fi descris de urm
atoarea struc-
tura de date secventiala:
Nod 1 2 3 4 5 6 7 8 9 10
cheie 3 5 8 7 8 8 11 10 18 9
Conform definitiei avem:
150
Nod 1 2 3 4 5 6 7 8 9 10
Left 2 4 6 8 10 - - - - -
Right 3 5 7 9 - - - - - -
Tata - 1 1 2 2 3 3 4 4 5
151
Exemplul 7.2 In figura 7.2 pot fi urm arite operatiile necesare pentru inserarea unui nod
avand cheia asociata de valoare 3. Se adaug a elementul av and valoarea cheii 3, pe ultima
pozitie a heap-ului. Deoarece valoarea cheii ultimului element 3 este mai mic a dec
at valoarea
cheii parintelui sau 8, se interschimb
a valorile cheilor. Se compar a din nou, valoarea cheii
nodului curent (cheia cu valoarea 3) cu valoarea cheii asociat a p
arintelui s
au (nodul 1). Se
interschimba din nou valorile cheilor. La final, heap-ul arat a ca n ultima configuratie din
figura 7.2.
7.1.2 S
tergerea elementului minim
Procedura DeleteMin (a se vedea algoritmul 53) va elimina nodul radacina din heap, ntorcand
valoarea acestuia prin intermediul variabilei minim, si va reorganiza heap-ul.
In linia 6 se pastreaza valoarea radacinii n variabila minim. Instructiunea urmatoare ia
ultimul element si l aduce pe prima pozitie. In linia 8, se decrementeaza numarul de elemente
existente la momentul respectiv n heap. Apoi se pastreaza n variabila j indicele elementului
ce contine cea mai mica valoare a cheii, dintre cheile corespunzatoare descendentilor nodului
curent (liniile 11-15).
In linia 17 se interschimba pozitia parintelui cu pozitia celui mai mic dintre descendentii
sai, daca este necesar. Instructinea de ciclare while (liniile 10 - 22) se paraseste atunci cand
s-a ajuns la o frunza sau cand nu se mai poate nainta n jos n arbore (linia 20).
In figura 7.3 pot fi urmariti pasii necesari pentru stergerea nodului de valoare minima din
heap-ul considerat drept exemplu.
152
Fig. 7.3: Stergerea valorii minime dintr-un heap
Prima metoda de construire a unui heap binar se bazeaza pe urmatoarea tehnica: se pleaca
initial cu heap-ul format dintr-un singur element si se insereaza pe rand, toate elementele n
heap-ul nou creat (a se vedea algoritmul 54).
Exemplul 7.3 Sa construim o structur a de heap din sirul de elemente 10, 7, 9, 5, 7 . . .. Se
pleaca cu heap-ul format dintr-un singur nod, 10. Apoi, se insereaz a elementul cu valoarea 7
n heap. Deoarece 7 > 10, se interschimb a valorile ntre ele (a se vedea figura 7.4). Urmeaza
sa se insereze nodul a carui valoare asociat a este 9 (acest nod va fi descendentul drept al
radacinii, dupa cum se poate observa din figura 7.4). La final, se adaug a nodul a carui cheie
are valoarea 5, si se restabileste proprietatea de heap prin interschimb ari succesive.
Analizand numarul de operatii efectuate de procedura Insert, se observa ca o valoare
nou introdusa poate sa ajunga pana n radacina arborelui. Astfel, numarul de operatii
(interschimbari) n cazul cel mai defavorabil va fi egal cu naltimea arborelui, h. Deoarece
avem un arbore binar complet cu cel mult n noduri, naltimea acestuia este cel mult [log n]
(h [log n]).
Complexitatea timp n cazul cel mai defavorabil al procedurii NewHeap este egala cu suma
timpilor necesari inserarii tuturor celor n valori, n cazul cel mai defavorabil. Vom tine cont
de faptul ca, n cazul unui arbore binar complet, numarul nodurilor de pe nivelul i este 2i ,
cu exceptia ultimului nivel unde numarul de noduri este mai mic.
n [log n] [log n]
X X X
i
T (n) = TInsert (i) i 2 log n 2i = O(n log n) (7.1)
k=1 i=1 i=1
153
Fig. 7.4: Crearea unui heap prin inserari succesive
Cea de-a doua metoda se bazeaza pe ideea de a construi un heap prin combinari (uni-
uni) repetate de heap-uri, strategie cu o eficienta superioara metodei anterioare. Initial, se
porneste cu o padure de arbori, compusi fiecare numai dintr-un singur nod, radacina. La
fiecare pas i, se construieste un heap nou din ai si doua heap-uri de dimensiunile apropiate:
heap-ul avand radacina a2i si cel cu radacina a2i+1 (a se vedea algoritmul 55).
Fie x un nod aflat pe nivelul i (i = 0, h 1, h = log n). Acest nod va suferi cel mult
h i deplasari n cadrul structurii de heap, migrand catre frunzele arborelui. Numarul de
154
Algoritm 56 Algoritm de reorganizare a unui heap
1: procedure Push(A, f irst, last)
2: i f irst
3: while (i last
2 ) do
4: if ((last = 2 i) (a2i < a2i+1 )) then
5: j 2i
6: else
7: j 2i+1
8: end if
9: if (ai > aj ) then
10: ai aj
11: ij
12: else
13: i last
14: end if
15: end while
16: end procedure
155
Algoritm 57 Algoritm de ordonare folosind heap-uri (prima varianta)
1: procedure SortX(A, n)
2: for f iecare x L do
3: call Insert(x, S; S)
4: end for
5: while (S 6= ) do
6: call M in(S; y)
7: ouput {y}
8: call Delete(y, S; S)
9: end while
10: end procedure
/
F u n c t i e pentr u c i t i r e a v a l o r i l o r d a t e l o r de i n t r a r e .
/
i nt r e a d I n p u t ( i nt a ) {
i nt i , n ;
p r i n t f ( n= ) ; s c a n f ( %d , &n ) ;
for ( i = 1 ; i <= n ; i ++) {
p r i n t f ( a[%d]= , i ) ; s c a n f ( %d , &a [ i ] ) ;
}
return n ;
}
156
/
F u n c t i e pentr u a f i s a r e a r e z u l t a t e l o r .
/
void l i s t ( i nt a , i nt n ) {
i nt i ;
/
Reorganizarea unei movile .
@param s t a r t i n d i c e l e p r i m u l u i element a l s e c v e n t e i
@param f i n i s h i n d i c e l e l u l t i m u l u i element a l s e c v e n t e i
@param a v e c t o r u l c e p a s t r e a z a v a l o r i l e m o v i l e i
/
void push ( i nt s t a r t , i nt f i n i s h , i nt a ) {
i nt i , j , aux ;
i = start ;
while ( i <= f i n i s h / 2 ) {
i f ( 2 i == f i n i s h | | a [ 2 i ] < a [ 2 i + 1 ] )
j = 2 i;
else
j = 2 i + 1;
if (a [ j ] < a [ i ]) {
aux = a [ i ] ; a [ i ] = a [ j ] ; a [ j ] = aux ;
i = j;
}
else
return ;
}
}
i nt a [MAXN] ;
// o r g a n i z a r e a unui heap
for ( i = n / 2 ; i > 0 ; i )
push ( i , n , a ) ;
// d e t e r m i n a r e a minimului s i r e o r g a n i z a r e a heapu l u i
j = n;
while ( j > 1 ) {
aux = a [ 1 ] ; a [ 1 ] = a [ j ] ; a [ j ] = aux ;
j ;
push ( 1 , j , a ) ;
}
l i s t (a , n ) ;
}
157
7.3 Aplicatie - Coad
a cu prioritate
Definitia 7.2 O coad a cu prioritate este o structur a de date abstract a formata din ele-
mente ce au asociata o valoare numita cheie sau prioritate si care suport
a urmatoarele opera-
tii:
Insert(Q, X) - insereaza elementul x n coada cu prioritate denumit
a Q;
ExtractMax(Q) - extrage din coada cu prioritate Q, elementul de valoare maxim
a.
Multitasking-ul este o metoda prin intermediul careia mai multe procese utilizeaza n co-
mun resursele calculatorului (inclusiv procesorul). In situatia unui calculator cu un singur
procesor, se spune ca n orice moment ruleaza cel mult un proces, ceea ce nseamna ca proce-
sorul executa instructiunile unui singur proces la un moment dat. Sarcina alegerii procesului
care sa se afle n executie la un moment dat este o problema de planificare. Operatia de a
opri un proces aflat n executie, pentru a-i aloca altui proces, aflat n asteptare, un timp pro-
cesor se numeste schimbare de context (eng. context switch). Realizarea frecventa a acestor
schimbari de context creaza iluzia executiei n paralel a mai multor programe. In cazul unui
calculator cu mai multe procesoare, multitasking-ul permite executia unui numar de procese
mai mare decat numarul de procesoare.
Sistemul de operare este cel care se ocupa de planificarea proceselor, planificare ce se
ncadreaza ntr-una din urmatoarele strategii:
n cazul sistemelor cu multiprogramare, procesul curent se afla n executie pana n
momentul n care realizeaza o operatie ce presupune asteptarea dupa un eveniment
extern (o operatie de I/O), sau pana cand planificatorul de procese forteaza eliberarea
procesorului.
ntr-un sistem de tip time-sharing, fiecare proces va elibera procesorul n mod voluntar,
dupa expirarea cuantei de timp alocate acestuia, sau n urma aparitiei unui eveniment
hardware cum ar fi o ntrerupere.
158
de catre procesul curent sau de catre un altul. In cazul multitasking-ului non-preemtiv,
planificatorul i da controlul unui proces pana cand acesta se termina, sau elibereaza singur
procesorul.
Sistemele de operare bazate pe kernelul Windows NT 4.0 folosesc un planificator de
procese preemtiv cu prioritati. In momentul n care trebuie sa aleaga un proces pentru a-i da
controlul procesorului, planificatorul de procese va alege procesul ce are prioritatea cea mai
mare si este gata de executie, utilizand o strategie de tip round rubin: daca avem trei procese
avand aceeasi prioritate A, B si C, si un proces D de prioritate mai mica, planificatorul va
alege mai ntai procesul A, apoi procesul B, urmat de procesul C, procesului D alocandu-i
procesorul numai n situatia n care celelalte trei procese nu sunt n starea gata de executie (de
exemplu sunt blocate n asteptarea efectuarii unei operatii I/O). Pentru procesele de prioritate
mica, si care nu au fost executate deloc n ultimele, sa zicem, 3 secunde, planificatorul de
procese aplica urmatorul algoritm pentru a evita fenomenul de nfometare: le atribuie o
prioritate temporara foarte mare, ridicandu-le la nceputul cozii de procese, si le aloca o
cuanta de timp mai lunga decat n mod normal.
Sistemele de operare bazate pe un kernel de tip Unix folosesc un planificator bazat pe
mai multe cozi de prioritati ce foloseste aceasi strategie de tip round rubin n cadrul fiecarei
cozi. Procesele noi sunt inserate ntr-o coada corespunzatoare unor prioritati mai mari, si pe
masura ce petrec un timp procesor mai ndelungat, sunt inserate ntr-o coada corespunzatoare
unor prioritati mai mici. Sistemele de operare de generatie mai noua, bazate pe un kernel
Unix, incrementeaza prioritatea unui proces aflat n stare de nfometare (starvation) pana
cand acesta este executat, dupa care prioritatea acestuia este resetata la valoarea pe care
procesul o avea nainte de a ncepe nfometarea.
Vom simula un planificator de procese bazat pe o coada de prioritati: la un moment
dat, planificatorul alege procesul de prioritate minima, i da controlul procesorului pentru o
perioada de timp variabila nsa limitata. Dupa trecerea acelei perioade de timp, prioritatea
procesului este incrementata cu o valoare ce depinde direct proportional de timpul procesor
petrecut de acesta, si este inserat n coada de prioritati pe pozitia corespunzatoare.
In cadrul fisierului task.h definim structura de date Process precum si doua functii ce
au legatura cu prelucrarea informatiilor asociate unui proces:
long p(Process a); - ntoarce prioritatea unui proces;
void execute(int id); - simuleaza executia unui proces pentru o anumita perioada
de timp.
typedef str u ct p r o c e s s {
i nt i d ;
long p r i o r i t y ;
} Process ;
long p ( P r o c e s s a ) ;
void e x e c u t e ( i nt i d ) ;
159
#endif
/
D e f i n i t i a s t r u c t u r i i PriorityQueue
/
typedef str u ct p r i o r i t y q u e u e {
P r o c e s s e l e m e n t s [ MAX SIZE+ 1 ] ;
i nt l a s t ;
} PriorityQueue ;
#endif
160
void init(int p) - creaza un proces cu identificatorul p si prioritatea egala cu momentul
de timp n care a fost creat, pe care l insereaza n coada de prioritati.
void select(void) - la un moment dat, selecteaza procesul aflat n varful cozii, i da con-
trolul procesorului pentru o perioada de timp, actualizeaza prioritatea acestuia si-l insereaza
n coada de prioritati.
Daca dorim ca managementul cozii de prioritati sa fie realizat prin intermediul unei liste
n locul structurii de tip heap, este suficient sa nlocuim linia #include "heap.h" cu linia
#include "list.h".
7.4 Exercitii
1. Sa se implementeze structura de date abstracta coada cu prioritate cu ajutorul:
for ( ; ; ) {
i f ( parent > 0) {
t = a r r [ p a r e n t ] ;
} else {
n;
i f ( n == 0 )
return ;
t = arr [ n ] ;
arr [ n] = arr [ 0 ] ;
}
index = parent ;
2
http://en.wikibooks.org/wiki/Algorithm_Implementation/Sorting/Heapsort
161
c h i l d = index 2 + 1 ;
while ( c h i l d < n ) {
i f ( ( c h i l d + 1 < n ) && ( arr [ child + 1] > arr [ child ] ) ) {
c h i l d ++;
}
162
Pentru a numerota toate cele n pagini ale clasorului, Adriana are nevoie de timbre cu
numerele de la 1 la n.
Exemplu
timbre.in timbre.out
4 3 2
5 3
3
2 1
6 2
Luam subsecventa {1, 2} din al doilea interval si subsecventa {3, 4} din al treilea inter-
val. Obtinem astfel costul minim 3.
(InfoArena, Timbre)
6. Paftenie barbarul a fost capturat de catre dusmanii sai cei mai temuti si aruncat ntr-o
temnita. Temnita este, de fapt, un grid de dimensiune R C. In anumite celule exista
dragoni, unele sunt ocupate de pereti, iar altele sunt libere. Paftenie trebuie sa iasa din
temnita mergand numai prin celule libere (o celula are maxim 4 vecini), si asta stand
cat mai departe de fiorosii dragoni ale caror flacari i pot deteriora vestimentatia (astfel
ncat valoarea minima dintre distantele pana la cel mai apropiat dragon din fiecare din
celulele traseului sau sa fie maxim).
Sa se determine un traseu al lui Paftenie pentru a iesi din temnita astfel ncat distanta
minima pana la cel mai apropiat dragon din fiecare dintre celulele traseului sau sa fie
maxima.
Date de intrare
Pe prima linie a fisierului de intrare barbar.in sunt date doua numere ntregi R si
C, reprezentand numarul liniilor, respectiv al coloanelor temnitei. Pe urmatoarele R
linii se afla cate C caractere, neseparate prin spatii, cu urmatoarele semnificatii: .
celula libera, perete, D dragon, I punctul de plecare al lui Paftenie, O iesirea din
temnita.
Date de iesire
Fisierul de iesire barbar.out va contine pe prima linie un singur numar, reprezentand
valoarea maxima pentru minima dintre distantele pana la cel mai apropiat dragon din
fiecare din celulele traseului. In caz ca nu exista solutie se va afisa valoarea 1.
Restrictii: 1 R, C 1.000.
Se garanteaza ca n temnita exista cel putin un dragon.
Exemplu Pentru datele de intrare
10 10
..........
.I....D...
..........
..D...D...
.*........
D*........
*...D.....
..****....
...O......
..........
163
o solutie posibila este urmatoarea, unde valoarea maxima dintre distantele minime este
2:
..........
.Iooo.D...
....o.....
..D.o.D...
.*..oo....
D*...ooooo
*...D....o
..****...o
...Ooooooo
..........
164
Capitolul 8
Distante n grafuri
Reteaua de drumuri europene, nationale si judetene dintr-o tara, reteaua feroviara reprezentand
infrastructura cailor ferate absolut necesara pentru realizarea transportului de marfuri si
calatori cu trenul, reteaua de fibra optica folosita pentru traficul de date n Internet, reteaua
de transport a energiei electrice folosita pentru alimentarea cu energie electrica a consumato-
rilor casnici si a celor industriali, reteaua de canalizare dintr-un oras folosita pentru evacuarea
deseurilor, reteaua CAT V a unui operator de televiziune prin cablu, reteaua de linii de auto-
buz ce face parte din sistemul public de transport al unui oras sunt exemple tipice de grafuri
cu care interactionam n viata de zi cu zi.
Putem spune ca retelele de transport si cele de comunicatii de date ne influenteaza n
mod direct modul de viata, devenind elemente indispensabile ale omului modern, si astfel,
problemele referitoare la studiul cailor de comunicatie, drumurilor, conexiunilor, capata un
interes special.
De obicei suntem interesati de aspecte precum drumul cel mai scurt sau cel mai lung,
drumul cel mai ieftin sau drumul care se poate parcurge cel mai repede, drumul cel mai
sigur. Teoria grafurilor ne pune la dispozitie mijloacele pentru aflarea raspunsului la multe
astfel de ntrebari.
Definitiile pentru drum, lant, ciclu, circuit au fost prezentate n capitolele anterioare,
mpreuna cu multe alte concepte teoretice. Lungimea unui lant este determinata de numarul
muchiilor sale. In mod analog, lungimea unui drum este egala cu numarul arcelor sale.
Intr-un graf G se introduce o functie de cost ce asocieaza o valoare reala fiecarei muchii
sau arc, dupa caz:
c : E R sau c : V V R
Notam costul unui drum ca fiind suma costurilor arcelor componente. Astfel pentru un
drum D = [v0 , v1 , . . . , vm ], vi V , de lungime m, costul acestuia va fi dat de catre urmatoarea
formula:
m1
X
c(D) = c([vi , vi+1 ]). (8.1)
i=0
165
8.1 Drumul minim de la un v
arf la celelalte v
arfuri
8.1.1 Algoritmul lui Moore
Fie G = (V, E) un graf orientat unde V = {x1 , x2 , . . . , xn } este multimea nodurilor si E este
multimea arcelor (E V V ). Pentru reprezentarea grafului vom utiliza, ca metoda de
reprezentare, listele de adiacenta (liste de vecini).
Fie u un varf al grafului numit surs a. Dorim sa determinam pentru fiecare varf w
V, w 6= u, daca exista, un drum de lungime minima de la u la w. Lungimea unui drum se
defineste ca fiind numarul arcelor ce l compun.
Algoritmul lui Moore (a se vedea algoritmul 59) se bazeaza pe structura algoritmului de
parcurgere n latime al unui graf (Breadth First Search - a se vedea algoritmul 16). In lucrarea
[98], Moore si prezinta algoritmul astfel:
Write 0 on the source. Then look at all the neighbors of the source and write
1 on them. Then look at all the neighbors of nodes with 1 on them and write 2
on them. And so on.
Vom utiliza un vector de distante D, unde dk reprezinta costul drumului minim de la varful u
la varful xk , iar tatak pastreaza varful anterior lui xk , pe drumul de cost minim de la nodul
u la nodul xk .
166
Exemplul 8.1 Fie un graf orientat reprezentat prin intermediul urm
atoarelor liste cu vecini:
1: (2, 4) 7: (8)
2: (3, 6) 8: (6, 7)
3: (2) 9: ()
4: (1, 5) 10: (11)
5: (4, 6, 9) 11: (10)
6: (7, 8)
In urma aplicarii algoritmului lui Moore pentru acest graf si pentru nodul 1 drept sursa,
se obtin urmatoarele valori pentru vectorii D si Tata:
167
costurilor C:
0
, daca i = j
ci,j = , daca (i, j)
/ E, i 6= j
d > 0 , daca (i, j) E
Fie u un varf al grafului numit surs a. Dorim sa determinam pentru fiecare varf w
V, w 6= u, daca exista, un drum de lungime minima de la u la w. Lungimea unui drum se
defineste ca fiind suma costurilor asociate arcelor ce l compun.
Fie p = [x1 , x2 , . . . , xk ] un drum elementar. Se numeste v arf intermediar orice varf u
{x2 , . . . , xk1 } unde u 6= x1 , u 6= xk (v
arful intermediar este diferit de extremitatile drumului
p).
Algoritmul lui Dijkstra [38] utilizeaza metoda generala de elaborare a algoritmilor Greedy,
drumurile de lungime minima fiind generate n ordinea crescatoare a lungimii lor. Vom nota
cu S multimea nodurilor grafului G pentru care se cunoaste (s-a calculat) drumul de lungime
minima de la sursa la un astfel de nod. La nceput, multimea S este compusa doar din
nodul sursa. La fiecare pas, se adauga la multimea S un nod xk V \ S cu proprietatea ca,
drumul de la sursa la acest nod xk , este cel mai mic dintre toate drumurile posibile de la
sursa la noduri din multimea V \ S, cu proprietatea ca, un astfel de drum are drept noduri
intermediare numai elemente din multimea S. Vom utiliza un tablou D, ce va pastra pentru
fiecare nod xk , lungimea drumului cel mai scurt de la sursa ce trece numai prin noduri din
multimea S (a se vedea algoritmul 61).
168
Algoritm 62 Algoritmul lui Dijkstra
1: function DistantaMinima(n, V izitat, D)
2: min
3: for j 1, n do
4: if ((vizitatj = 0) (dj < min)) then
5: min dj
6: j0 j
7: end if
8: end for
9: if (min = ) then
10: return 1
11: else
12: return j0
13: end if
14: end function
169
minim de la u la xj . La nceput,
(
0 , daca nodul j = u sau cu,j =
tataj =
u , n rest (j 6= u si cu,j 6= ).
Un element al acestui vector se poate modifica atunci cand se modifica valoarea ele-
mentului dj astfel ncat dj = dk + ck,j , caz n care tataj = k.
170
Prin urmare
dw + cw,j dk + cw,j < dk + costxk w + cw,j , (8.3)
adica drumul special de la sursa la nodul xj ce trece prin nodul w are lungimea mai mica
decat drumul special compus din drumul de la nodul sursa la nodul xk , drumul de la nodul
xk la nodul w si arcul (w, xj ).
Exemplul 8.3 Sa presupunem ca nodul surs a este nodul 1 pentru graful din figura 8.2. Dupa
etapa de initializare, vom avea urmatoarele valori:
1 2 3 4 5
D 1 21
Tata 0 1 1 1 1
Vizitat 1 0 0 0 0
In cadrul primei iteratii, dupa selectarea nodului 2, se evalueaz
a urm
atoarele distante:
d2 + c2,3 < d3 1 + 8 < +
d2 + c2,4 < d4 1 + < 21
d2 + c2,5 < d5 1 + 4 < +
1 2 3 4 5
D 1 9 21 5
Tata 0 1 2 1 2
Vizitat 1 1 0 0 0
In timpul celei de-a doua iteratii se evalueaz
a urm
atoarele conditii:
d5 + c5,3 < d3 5 + 3 < 9
d5 + c5,4 < d4 5 + < 21
1 2 3 4 5
D 1 8 21 5
Tata 0 1 5 1 2
Vizitat 1 1 0 0 1
Dupa pasul al treilea, vom actualiza lungimea unui singur drum:
d3 + c3,4 < d4 8 + 12 < 21
171
1 2 3 4 5
D 1 8 20 5
Tata 0 1 5 3 2
Vizitat 1 1 1 0 1
La sfasitul ultimei iteratii valorile vectorilor D, Tata, Vizitat sunt urm
atoarele:
1 2 3 4 5
D 1 8 20 5
Tata 0 1 5 3 2
Vizitat 1 1 1 1 1
/
C i t i r e a d a t e l o r de i n t r a r e + i n f i n i t = 1
/
void r e a d I n p u t ( i nt n , i nt c [ ] [MAXN] , i nt u ) {
i nt i , j ;
p r i n t f ( n = ) ; s c a n f ( %d , n ) ;
for ( i = 0 ; i < n ; i ++)
for ( j = 0 ; j < n ; j ++) {
s c a n f ( %d , &c [ i ] [ j ] ) ;
i f ( c [ i ] [ j ] < 0)
c [ i ] [ j ] = MAXINT;
}
p r i n t f ( Nodul i n i t i a l : ) ; s c a n f ( %d , u ) ;
}
i nt minim ( i nt n , i nt d [ ] , char v i z i t a t [ ] ) {
i nt j , j 0 ;
i nt min ;
min = MAXINT;
for ( j = 0 ; j < n ; j ++)
i f ( ! v i z i t a t [ j ] && d [ j ] < min ) {
min = d [ j ] ;
j0 = j ;
}
i f ( min == MAXINT)
return 1;
else
return j 0 ;
}
/
F u n c t i a v e r i f i c a daca a > b + c .
/
172
i nt mai mare ( long a , long b , long c ) {
return ( a > b+c ) ? 1 : 0 ;
}
void d i j k s t r a ( i nt u , i nt n , i nt c [ ] [MAXN] , i nt d [ ] , i nt t a t a [ ] ) {
/ v e c t o r c e p a s t r e a z a s t a r e a unui nod : v i z i t a t sau n e v i z i t a t /
char v i z i t a t [MAXN] ;
i nt i , j , k ;
// i n i t i a l i z a r i
v i z i t a t [ u ] = TRUE;
t a t a [ u ] = 1;
for ( i = 0 ; i < n ; i ++)
i f ( i != u ) {
v i z i t a t [ i ] = FALSE ;
d[ i ] = c [u ][ i ];
tata [ i ] = u ;
}
// p a r t e a p r i n c i p a l a
for ( i = 1 ; i < n ; i ++) {
k = minim ( n , d , v i z i t a t ) ;
i f (k < 0)
break ;
v i z i t a t [ k ] = TRUE;
// a c t u a l i z a r e
for ( j = 0 ; j < n ; j ++)
i f ( ! v i z i t a t [ j ] && mai mare ( d [ j ] , d [ k ] , c [ k ] [ j ] ) ) {
tata [ j ] = k ;
d[ j ] = d[k] + c [k ][ j ];
}
}
}
void printDrum ( i nt k , i nt t a t a [ ] ) {
i f ( t a t a [ k ] >= 0 )
printDrum ( t a t a [ k ] , t a t a ) ;
else
printf ( [ );
p r i n t f ( %d , k ) ;
}
void p r i n t S o l u t i o n ( i nt u , i nt n , i nt d [ ] , i nt t a t a [ ] ) {
i nt i ;
173
i nt c [MAXN] [MAXN] ;
/ no dul f a t a de c a r e s e c a l c u l e a z a d r u m u r i l e de lung ime minima /
i nt u ;
/ t a t a [ k ] p a r i n t e l e v a r f u l u i k i n a r b o r e l e r e z u l t a t /
i nt t a t a [MAXN] ;
/ d i s t a n t a de l a u l a f i e c a r e nod /
i nt d [MAXN] ;
r e a d I n p u t (&n , c , &u ) ;
d i j k s t r a (u , n , c , d , tata ) ;
printSolution (u , n , d , tata ) ;
}
unde n reprezinta numarul de noduri ale grafului G iar m reprezinta numarul de muchii ale
aceluiasi graf. Cele trei operatii ntalnite n cadrul algoritmlui sunt (a se vedea si capitolul
7):
174
2. Insert(Q,p,x) - insereaza nodul p n coada cu prioritate Q, unde x reprezinta priori-
tatea nodului p.
2. Arbore 2 3:
T (n, m) = O(n 1 + n n + m 1) = O(n2 ) (8.6)
3. Heap-uri Fibonacci:
175
Algoritm 64 Algoritmul lui Dijkstra pentru toate perechile de varfuri
1: procedure DijkstraAll(n, C; D)
2: for i 1, n do
3: for j 1, n do
4: if ((i, j) E) then sau (ci,j > 0) (ci,j < )
5: di,j ci,j
6: else
7: di,j
8: end if
9: C (i, j) cu prioritatea di,j
10: end for
11: end for
12: while (C = 6 ) do
13: C (i, j)
14: for k 1, n do
15: if (di,j + cj,k < di,k ) then
16: di,k di,j + cj,k
17: actualizeaza prioritatea perechii (i, k) din C la di,k
18: end if
19: end for
20: for k 1, n do
21: if (ck,i + di,j < dk,j ) then
22: dk,j ck,i + di,j
23: actualizeaza prioritatea perechii (k, j) din C la dk,j
24: end if
25: end for
26: end while
27: end procedure
Prin urmare
(k) (k1) (k1)
c(p) = c(p1 ) + c(p2 ) di,j = di,k + dk,j (8.9)
176
Algoritmul Roy-Floyd-Warshall va construi un sir de matrici D (0) , D (1) , . . . , D (k) , . . . , D (n) .
(k)
Pentru fiecare k, 1 k n, di,j va contine lungimea drumului minim de la nodul xi la nodul
xj ce are drept noduri intermediare numai noduri din multimea {x1 , x2 , . . . , xk }.
Matricea D (0) se va initializa astfel:
0
, daca i = j
(0)
di,j = + , daca (xi , xj )
/ E, i 6= j (8.10)
d > 0 , daca (xi , xj ) E
(un drum ce nu are nici un varf intermediar este determinat de costul legaturii directe dintre
xi si xj ).
Drumul de lungime minima de la xi la xj ce trece numai prin nodurile {x1 , x2 , . . . , xk }
(k) (k1)
fie nu contine nodul xk , caz n care di,j = di,j , fie l contine, si atunci are loc relatia
(k) (k1) (k1) (k)
di,j = di,k + dk,j . Prin urmare, valoarea elementului di,j se va calcula astfel:
(k) (k1) (k1) (k1)
di,j = min{di,j , di,k + dk,j } (8.11)
Desi aceasta ultima relatie sugereaza un algoritm recursiv, o abordare iterativa este mai
eficienta atat n ceea ce priveste complexitatea timp, cat si din punctul de vedere al nece-
sarului de memorie. Se observa ca, matricea D (k) nu mai este necesara de ndata ce matricea
D (k+1) a fost calculata.
(n) (n)
D (n) = (di,j ), di,j = i,j , xi , xj V. (8.12)
177
Algoritm 66 Algoritmul Floyd-Warshall (varianta optimizata)
1: procedure FloydWarshall2(n, C; D)
2: for i 1, n do
3: for j 1, n do
4: di,j ci,j
5: end for
6: end for
7: for k 1, n do
8: for i 1, n do
9: for j 1, n do
10: di,j min {di,j , di,k + dk,j }
11: end for
12: end for
13: end for
14: end procedure
(k) (k)
di,j conform formulei 8.11. Daca pi,j = 0 atunci cel mai scurt drum dintre nodurile xi si xj
este cel direct.
(0)
Valoarea elementelor matricii initiale P (0) este pi,j = 0.
(k)
Valoarea unui element pi,j se va calcula conform formulei urmatoare, tinand cont de
relatia 8.11: (
(k1) (k1) (k1) (k1)
(k) pi,j , daca di,j di,k + dk,j
pi,j = (k1) (k1) (k1) (8.13)
k , daca di,j > di,k + dk,j
Se va calcula secventa de matrici P (0) , P (1) , . . ., P (n) mpreuna cu secventa D (0) , D (1) ,
. . ., D (n) . La final vom avea: P = P (n) , D = D (n) .
Printr-un rationament analog, se poate demonstra ca nu este nevoie sa construim efectiv
sirul de matrici P (0) , P (1) , . . ., P (n) . Algoritmul 67 prezinta varianta extinsa a algoritmului
66, n cadrul acestuia fiind adaugat si suportul pentru determinarea drumului de cost minim
dintre oricare pereche de noduri a grafului.
Exemplul 8.4 Fie graful din figura 8.2. Matricea costurilor asociat
a acestui graf este urmatorul:
0 1 + 21 +
+ 0 8 + 4
C = 3 + 0
12 +
+ + 9 0 +
+ + 3 + 0
Sirul de matrice D k , rezultat al aplic
arii algoritmului Roy-Floyd-Warshall pentru acest
graf, este:
178
Algoritm 67 Algoritmul Floyd-Warshall (reconstruirea drumului optim)
1: procedure FloydWarshall3(n, C; D, P )
2: for i 1, n do
3: for j 1, n do
4: di,j ci,j
5: pi,j 0
6: end for
7: end for
8: for k 1, n do
9: for i 1, n do
10: for j 1, n do
11: if (di,j > di,k + dk,j ) then
12: di,j di,k + dk,j }
13: pi,j k
14: end if
15: end for
16: end for
17: end for
18: end procedure
19: procedure PrintDrum(i, j, P )
20: if (pi,j = 0) then
21: Output {(i,j)}
22: else
23: call P rintDrum(i, pi,j , P )
24: call P rintDrum(pi,j , j, P )
25: end if
26: end procedure
0 1 21 0 1 21
0 8 4
0 8 4
D0 =
3 0 12
D1 =
3 4 0 12
9 0 9 0
3 0 3 0
0 1 9 21 5 0 1 9 21 5
0 8 4
11 0 8 20 4
D2 =
3 4 0 12 8
D3 =
3 4 0 12 8
9 0 12 13 9 0 17
3 0 6 7 3 15 0
0 1 9 21 5 0 1 8 20 5
11 0 8 20 4
10 0 7 19 4
D4 =
3 4 0 12 8
D5 =
3 4 0 12 8
12 13 9 0 17 12 13 9 0 17
6 7 3 15 0 6 7 3 15 0
Inchiderea tranzitiv
a
Definitia 8.1 Fie G = (V, E) un graf orientat, simplu si finit, V = {x1 , x2 , . . . , xn }.
179
un drum de la xi la xj n G}.
Definitia 8.3 Inchiderea tranzitiva a relatiei binare este o relatie binar a cu proprietatea
ca i, j {1, . . . , n}, (i, j) i1 , . . . , im {1, 2, . . . , n} astfel nc
at (i1 , i2 ) , (i2 , i3 )
, . . ., (im1 , im ) si i = i1 , j = im .
180
Algoritm 68 Algoritm de calcul a nchiderii tranzitive
1: procedure InchidereTranzitiva(n, C; D)
2: for i 1, n do
3: for j 1, n do
4: if ((i = j) (ci,j 6= +)) then
5: d0i,j 1
6: else
7: d0i,j 0
8: end if
9: end for
10: end for
11: for k 1, n do
12: for i 1, n do
13: for j 1, n do
14: dki,j dk1 k1 k1
i,j (di,k dk,j )
15: end for
16: end for
17: end for
18: end procedure
intermediare numai din multimea {x1 , . . . , xk1 } sau daca exista un drum de la xi la xk
avand varfurile intermediare numai din multimea {x1 , . . . , xk1 } si daca exista un drum de
la xk la xj avand varfurile intermediare numai din multimea {x1 , . . . , xk1 }:
dki,j = dk1 k1 k1
i,j (di,k dk,j ) (8.18)
181
Exemplul 8.5 Fie graful din figura 8.2, din care am eliminat arcul (1, 2). Matricea cos-
turilor asociata acestui graf modificat este:
0 21
0 8 4
C= 3 0 12
9 0
3 0
1 0 0 1 0 1 0 0 1 0
0 1 1 0 1
0 1 1 0 1
D0 =
1 0 1 1 0
D1 =
1 0 1 1 0
0 0 1 1 0 0 0 1 1 0
0 0 1 0 1 0 0 1 0 1
1 0 0 1 0 1 0 0 1 0
0 1 1 0 1 1 1 1 1 1
D2 =
1 0 1 1 0
D3 =
1 0 1 1 0
0 0 1 1 0 1 0 1 1 0
0 0 1 0 1 1 0 1 1 1
1 0 1 1 0 1 0 1 1 0
1 1 1 1 1 1 1 1 1 1
D4 =
1 0 1 1 0 D5 = 1
0 1 1 0
1 0 1 1 0 1 0 1 1 0
1 0 1 1 1 1 0 1 1 1
8.3 Exercitii
1. (Cutii n-dimensionale) Sa consideram o cutie n-dimensionala, data prin lungimea
fiecarei laturi (o cutie bidimensionala este un dreptunghi, o cutie tridimensionala este
un paralelipiped etc.).
Problema cere analizarea unui grup de k cutii n-dimensionale: sa se elaboreze un algo-
ritm ce determina o secventa de cutii de lungime maxima (b1 , b2 , . . .) din grupul de k
cutii, astfel ncat fiecare cutie bi sa poata fi introdusa n cutia bi+1 .
O cutie D = (d1 , d2 , . . . , dn ) poate fi inclusa ntr-o cutie E = (e1 , e2 , . . . , en ) numai daca
exista o permutare a multimii valorilor laturilor cutiei D astfel ncat lungimea fiecarei
laturi a cutiei D, dupa reorganizare, sa nu depaseasca lungimea laturii corespunzatoare
din cutia E.
De exemplu cutia (2, 6) poate fi introdusa n cutia (7, 3) ((6, 2) (7, 3)), iar cutia
(1, 6, 13) poate fi introdusa n cutia (11, 15, 3) ((6, 13, 1) (11, 15, 3)).
Cutiile egale pot fi introduse una ntr-alta. Masurile laturilor sunt numere reale mai
mici sau egale cu 1000. Numarul de cutii nu depaseste 100, iar numarul de dimensiuni
nu depaseste 20.
(Tuymaada, 1997)
182
Pentru o aprovizionare optima, compania vrea sa investeasca n construirea unui depozit
central din care sa se aprovizioneze toate zonele; va fi, deci, necesar ca acest depozit sa
fie plasat ntr-unul din orase, n asa fel ncat timpul total de livrare din acest depozit
catre toate orasele sa fie minim. Camioanele companiei transporta marfa la depozitele
locale si se ntorc la depozitul central. Timpul de livrare este format din timpul necesar
parcurgerii drumului de la depozitul central la oras si napoi (se presupune ca soferul
va urma de fiecare data calea cea mai rapida, iar n fiecare oras depozitul local se afla
amplasat la intrare) iar timpul de descarcare al camionului la destinatie este de exact 30
de minute. Drumurile ce leaga orasele sunt de aceeasi calitate, dar pot exista drumuri
ce iau mai mult timp ntr-un sens decat n sens invers. Pot fi, de asemenea, drumuri
cu sens unic.
Pentru a simplifica modelul, s-a stabilit pentru fiecare oras o lista cu toate drumurile
ce pleaca din oras spre celelalte orase si cat timp ia parcurgerea fiecarui drum.
(Concurs ACM Zona Pacificul de Sud)
3. Se da reteaua hidrografica a unei tari constituita din multimea raurilor si afluentii lor.
Cele N rauri (2 n 1000) sunt numerotate de la 1 la N. Legatura dintre un rau v
si un afluent al sau u este specificata prin perechea (u, v).
Pentru a se putea face estimari cu privire la potentialul risc de inundatie pe cursul unui
rau trebuie sa se calculeze debitul fiecarui rau n parte.
Debitul unui izvor se defineste ca fiind cantitatea de apa ce trece prin sectiunea izvorului
n unitatea de timp. Debitul raului u la varsare va fi egal cu debitul izvorului raului u
plus suma debitelor afluentilor la varsare n raul u.
Se cere sa se realizeze un algoritm care sa calculeze debitul la varsare al fiecarui rau.
4. Sa se determine drumul de lungime minima necesar deplasarii unui cal pe o tabla de sah
(avand dimensiunile 88), de la un punct de plecare la unui de sosire, stiind ca pe tabla
exista si casute-capcana. Calul n drumul sau nu poate trece printr-o casuta-capcana.
(ACM, Eastern Europe, 1994)
5. Un traducator gaseste o carte scrisa cu litere latine, n care nsa ordinea literelor din
alfabet este schimbata. La sfarsitul cartii, se afla un index de cuvinte complet si necon-
tradictoriu.
Se cere sa se elaboreze un algoritm ce determina ordinea literelor din noul alfabet.
(ONI, 1991)
6. Profesorul Heif realizeaza niste experimente cu o specie de albine din America de Sud
pe care le-a descoperit n timpul unei expeditii n jungla Amazonului. Mierea produsa
de aceste albine este superioara din punct de vedere calitativ mierii produse de albinele
din Europa sau din America de Nord. Din pacate, aceste albine nu se nmultesc n
captivitate. Profesorul Heiff crede ca pozitiile larvelor (albine lucratoare, regina) din
fagure depind de conditiile de mediu, care sunt diferite n laborator fata de padurea
tropicala.
Ca un prim pas pentru a-si verifica teoria, profesorul Heiff doreste sa calculeze distanta
dintre pozitiile larvelor. Pentru aceasta el masoara distanta dintre celulele fagurelui n
care sunt plasate larvele. El a numerotat celulele alegand n mod arbitrat una, careia
i-a atribuit valoarea 1, celelalte celule ramase fiind apoi numerotate circular, n sensul
acelor de ceas, ca n figura.
183
__ __ __ __
__/ \__/ \__/ \__/ \__
__/ \__/ \__/53\__/ \__/ \__
/ \__/ \__/52\__/54\__/ \__/ \
\__/ \__/51\__/31\__/55\__/ \__/
/ \__/50\__/30\__/32\__/56\__/ \
\__/49\__/29\__/15\__/33\__/57\__/
/ \__/28\__/14\__/16\__/34\__/ \
\__/48\__/13\__/ 5\__/17\__/58\__/
/..\__/27\__/ 4\__/ 6\__/35\__/ \
\__/47\__/12\__/ 1\__/18\__/59\__/
/..\__/26\__/ 3\__/ 7\__/36\__/ \
\__/46\__/11\__/ 2\__/19\__/60\__/
/..\__/25\__/10\__/ 8\__/37\__/ \
\__/45\__/24\__/ 9\__/20\__/61\__/
/..\__/44\__/23\__/21\__/38\__/ \
\__/70\__/43\__/22\__/39\__/62\__/
/ \__/69\__/42\__/40\__/63\__/ \
\__/ \__/68\__/41\__/64\__/ \__/
/ \__/ \__/67\__/65\__/ \__/ \
\__/ \__/ \__/66\__/ \__/ \__/
\__/ \__/ \__/ \__/ \__/
\__/ \__/ \__/ \__/
7. Intr-o tara exista N orase, unele dintre ele legate prin autostrazi cu dublu sens. Se
presupune ca din orice oras se poate ajunge n oricare altul. Taxa este aceeasi pentru
orice autostrada (1 Ron), dar trebuie platita din nou la intrarea pe o noua autostrada.
Distanta dintre doua orase este considerata egala cu taxa minima pentru a ajunge
dintr-unul n altul.
Se doreste introducerea unui abonament a carui plata permite circulatia pe autostrazi
fara nici o taxa suplimentara. Costul abonamentului este de 100 de ori mai mare decat
distanta maxima ntre doua perechi de orase.
Sa se scrie un algoritm ce calculeaza costul abonamentului.
(CEOI, 1996)
8. In fiecare noapte, un print intra n subsolurile unui castel unde locuieste o printesa.
Drumul catre printesa este ca un labirint. Sarcina printului este sa se grabeasca si sa
gaseasca drumul prin labirint catre printesa, deoarece trebuie sa se ntoarca n zori.
Acesta are unelte potrivite cu ajutorul carora poate sa sparga numai un singur perete
pentru a-si scurta drumul catre destinatie.
Sa se elaboreze un algoritm care:
184
(a) determina lungimea celui mai scurt drum din labirint din locul n care printul
intra n labirint pana n locul n care traieste printesa;
(b) determina lungimea celui mai scurt drum n conditiile de mai sus, daca se sparge
un perete si numai unul.
5 8
14 10 10 10 10 10 10 9
12 10 10 10 10 10 10 3
5 14 9 14 8 11 14 9
4 10 2 8 3 12 10 1
6 11 14 2 10 2 10 3
1 1 5 8
185
Appendix A
Metoda Backtracking
Metoda Backtracking este una dintre cele mai cunoscute metode de elaborare a algoritmilor.
Metoda se aplica numai n situatia n care nu exista nici o alta modalitate de rezolvare a
problemei propuse deoarece timpul de executie depinde exponential de dimensiunea datelor
de intrare. Se utilizeaza o structura de tip stiva iar metoda poate fi implementata atat
iterativ cat si recursiv. Metoda se aplica acelor probleme n care solutia se poate reprezenta
sub forma unui vector x = (x1 , x2 , . . . , xn ) A1 A2 . . . An , unde multimile Ai , (i = 1, n)
sunt finite si nevide (|Ai| = ni > 0). In plus, pentru fiecare problema n parte este necesar ca
solutia x1 , x2 , . . . , xn sa satisfaca anumite conditii interne (x1 , x2 , . . . , xn ) (vezi algoritmul
70).
186
atunci x2 nu poate fi n. Rezolvarea conduce la urmatoarele variantele: (a, m), (b, m), (c, m), (c, n).
Din cele sase solutii posibile (a, m), (a, n), (b, m), (b, n), (c, m), (c, n), numai acestea patru
ndeplinesc conditiile interne.
Multimea A = A1 A2 ... An se numeste spatiul solutiilor posibile, iar elementele
x A ce satisfac conditiile interne se numesc solutii rezultat. Ne propunem determinarea
tuturor solutiilor rezultat, eventual pentru a alege dintre ele pe aceea ce minimizeaza sau
maximizeaza o functie obiectiv.
Metoda Backtracking evita generarea tuturor solutiilor posibile (toate elementele pro-
dusului cartezian A1 A2 ... An ). Construirea unei solutii se face n mai multi pasi,
elementele vectorului X primind valori pe rand: elementului xk Ak , k = 1, n, i se atribuie
o valoare numai dupa ce au fost atribuite valori pentru componentele x1 A1 , x2 A2 ,
. . ., xk1 Ak1 . Metoda trece la atribuirea unei valori pentru xk+1 Ak+1 doar daca
xk mpreuna cu x1 , x2 , . . . , xk1 verifica conditiile de continuare, notate cu k (x1 , x2 , . . . , xk ).
Daca aceste conditii k (x1 , x2 , . . . , xk ) sunt ndeplinite se trece la cautarea unei valori pentru
elementul xk+1 Ak+1 al solutiei.
Nendeplinirea conditiilor are urmatoarea semnificatie: pentru orice alegere xk+1 Ak+1 ,
. . . , xn An , nu vom putea ajunge la o solutie rezultat n care conditiile interne sa fie
ndeplinite. In aceasta situatie va trebui sa ncercam o alta alegere pentru xk , acest lucru fiind
posibil doar daca nu am epuizat toate valorile disponibile din multimea Ak . Daca multimea
Ak a fost epuizata va trebui sa micsoram valoarea variabilei k cu o unitate (k k 1), si sa
trecem la alegerea unei alte valori pentru elementul xk1 Ak1 . Micsorarea valorii curente
a variabilei k cu o unitate da numele metodei si semnifica faptul ca atunci cand nu putem
avansa, vom urmari napoi secventa curenta din solutie. Faptul ca valorile v1 , v2 , . . . , vk1 ,
ale componentelor x1 , x2 , . . . , xk1 , satisfac conditiile de continuare, nu este suficient pentru
a garanta obtinerea unei solutii ale carei prime k 1 componente coincid cu aceste valori.
O alegere buna a conditiilor de continuare conduce la reducerea numarului de calcule,
fiind de dorit ca aceste conditii de continuare sa fie nu numai necesare, dar si suficiente
pentru obtinerea unei solutii. Conditiile interne devin chiar conditii de continuare pentru
k = n. Orice vector solutie se obtine progresiv ncepand cu prima componenta si deplasandu-
ne catre ultima, cu eventuale reveniri asupra valorilor atribuite anterior. Anumite valori sunt
consumate n urma unor atribuiri sau ncercari de atribuire esuate din cauza nendeplinirii
conditiilor de continuare.
Exista doua variante fata de metoda standard:
dintre solutii se alege cea care minimizeaza sau maximizeaza o functie cost sau o functie
obiectiv data.
187
Pentru k = 2, se verifica x2 = 1, ns a nu se respect a conditiile de continuare (linia 16).
Astfel se trece la urmatoarea valoare, x2 = x1 + 1 = 2 (linia 15). Pentru aceast a configuratie
conditiile de continuare sunt ndeplinite, si se trece la pasul urm ator, k = k + 1 = 3 (linia
24): 1 2
Se verifica valorile 1, 2 si 3 pentru x3 (linia 16), dintre care, numai ultima satisface
conditiile de continuare: 1 2 3
Ajunsi la ultimul pas, k = 4, se verific urma verificarii
a valorile 1, 2, 3 si 4 pentru x4 . In
conditiilor de continuare pentru x4 = 4, se urm areste trecerea la pasul k = k+1 = 5. Deoarece
suntem la ultimul element al vectorului x (linia 21), se obtine prima solutie, ce se si afiseaza:
1 2 3 4
Mai departe, nu mai exista valori netestate pentru x4 (linia 14), si revenim la nivelul
anterior k = k 1 = 3 (linia 28). Aici urm atoarea valoare din domeniul de valori neverificata
este x3 = 4 (linia 15). Solutia partiala respecta conditiile de continuare (linia 16), astfel ncat
se trece la nivelul urmator (linia 24), k = k + 1 = 4: 1 2 4
Pentru x4 sunt verificate valorile, 1, 2, 3, 4 (liniile 1516), dintre acestea numai valoarea
3, conducand la o solutie finala (linia 21): 1 2 4 3 Algoritmul se continu a n acelasi
mod pana se obtin toate solutiile:
(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, 1, 3, 4)
(2, 1, 4, 3)
(2, 3, 1, 4)
(2, 3, 4, 1)
...
#define TRUE 1
#define FALSE 0
#define NN 100
int n;
int a[NN];
/**
* Functia citeste valorile datelor de intrare.
*/
void readInput(void) {
//se citeste numarul de elemente n
printf("n="); scanf("%d", &n);
}
188
Algoritm 71 Algoritm pentru generare permutari (varianta backtracking)
Input: n - num arul de elemente din multimea initial
a
1: function CanContinue(A, k)
2: for i 1, k 1 do
3: if (ai = ak ) then
4: return f alse
5: end if
6: end for
7: return true
8: end function
9: procedure Permutari(n)
10: k1
11: xk 0
12: while (k > 0) do
13: gasit f alse
14: while (xk + 1 n) (gasit 6= true) do
15: xk xk + 1
16: if ((CanContinue(X, k)) = true) then
17: gasit true
18: end if
19: end while
20: if (gasit = true) then
21: if (k = n) then
22: Output {x1 , . . . , xn }
23: else
24: k k+1
25: xk 0
26: end if
27: else
28: k k1
29: end if
30: end while
31: end procedure
/**
* Functia afiseaza solutia calculata a problemei.
*/
void list(void) {
int i;
/**
* Functia de continuare: aici se verifica daca elementul de pe pozitia k
* nu se mai afla in sirul A.
*/
189
int canContinue(int k) {
int i;
void run(void) {
int i;
int gata;
//initializare
i = 1; a[i] = 0;
while (i > 0) {
gata = FALSE;
while ((a[i] + 1 <= n) && !gata) {
/**
* cat timp exista elementul urmator si nu sunt verificate
* conditiile de continuare
*/
if (gata)
//daca s-au verificat conditiile de continuare
if (i == n)
//daca suntem la ultimul element afisam solutia
list();
else { //altfel trecem la elementul urmator
i++;
a[i] = 0;
}
else
//altfel revenim la un element anterior si alegem o alta valoare
i--;
}
}
void main(void) {
read_data();
run();
}
190
m n).
Rezolvare: Spatiul solutiilor este A = m i=1 Ai , Ai = {1, 2, . . . , n}, |Ai | = n. Solutia va
fi obtinuta n vectorul x = (x1 , x2 , . . . , xm ) A1 A2 . . . Am (vezi algoritmul 72).
La pasul k se ncearca atribuirea unui alt element din multimea Ak lui xk . Conditia de
continuare se refera la proprietatea c a elementele vectorului solutie trebuie s a respecte relatia
x1 < x2 < . . . < xk . Acest lucru se obtine foarte usor prin urm atorul procedeu: la trecerea la
pasul k + 1, variabila xk+1 va primi, ca valoare initial a, valoarea curent a a variabilei xk (vezi
linia 15 din algorimul 72).
Pentru n = 5 si m = 3 avem: A = 3i=1 Ai = A1 A2 A3 , unde A1 = A2 = A3 =
{1, 2, 3, 4, 5}. Solutiile obtinute sunt:
(1, 2, 3)
(1, 2, 4)
(1, 2, 5)
(1, 3, 4)
(1, 3, 5)
(1, 4, 5)
(2, 3, 4)
(2, 3, 5)
(2, 4, 5)
(3, 4, 5)
191
#include <stdio.h>
#define TRUE 1
#define FALSE 0
#define NN 100
int n, m;
int a[NN];
/**
* Functia citeste valorile datelor de intrare.
*/
void readInput(void) {
printf("n="); scanf("%d", &n);
printf("m="); scanf("%d", &m);
}
/**
* Functia afiseaza solutia calculata de functia run().
*/
void list(void) {
int i;
void run(void) {
int i;
int gata;
//initializare
i = 1; a[i] = 0;
while (i > 0) {
gata = FALSE;
while ((a[i] + 1 <= n) && !gata) {
/**
* cat timp exista elementul urmator si nu sunt verificate
* conditiile de continuare, treci la elementul urmator
*/
a[i]++;
gata = TRUE;
}
if (gata)
//daca s-au verificat conditiile de continuare
if (i == m)
//daca suntem la ultimul element afisam solutia
list();
else { //altfel trecem la elementul urmator
i++;
192
a[i] = a[i - 1];
}
else
//altfel revenim la un element anterior si alegem o alta valoare
i--;
}
}
void main(void) {
readInput();
run();
}
i = k sau j = l sau |i k| = |j l|
193
Algoritm 73 Algoritm Problema damelor
Input: n - num arul de linii/coloane de pe tabla de sah
1: function CanContinue(A, k)
2: for j 1, k 1 do
3: if (aj = ak ) ((k j) = |ak aj |) then
4: return f alse
5: end if
6: end for
7: return true
8: end function
9: procedure DameBacktracking(n)
10: i1
11: xi 0
12: while (i > 1) do
13: gasit f alse
14: while (xi + 1 n) (gasit 6= true) do
15: xi xi + 1
16: if (CanContinue(X, i) = true) then
17: gasit true
18: end if
19: end while
20: if (gasit = true) then
21: if (i = n) then
22: call Af is Solutie(x1 , . . . , xn )
23: else
24: ii+1
25: xi 0
26: end if
27: else
28: i i1
29: end if
30: end while
31: end procedure
1. xk < m (daca mai exista alte culori neatribuite pentru tara k);
2. pentru i, 1 i < k cu ai,k = 1 avem xi 6= xk + 1.
Daca cele doua conditii anterioare sunt ndeplinite, atunci noua culoare pentru tara k va
caz contrar,
fi xk + 1, si se va trece la pasul k + 1 (stabilirea unei culori pentru tara k + 1). In
tarii k nu i se mai poate atribui o alt a culoare si ne ntoarcem la pasul k 1, corespunzator
alegerii unei noi culori pentru tara k 1. Algoritmul se termin a atunci cand nu mai exista
nici o culoare neverificata pentru tara 1 (vezi algoritmul 74).
194
Algoritm 74 Algoritm Problema colorarii hartilor
A - matricea de adiacent
a/de vecin
atate a t
arilor
Input: n - num arul de tari
m - num arul de culori disponibile
1: function CanContinue(A, X, k)
2: for j 1, k 1 do
3: if (xj = xk ) (aj,k = 1) then
4: return f alse
5: end if
6: end for
7: return true
8: end function
9: procedure ColorBacktracking(A, n, m)
10: k1
11: xk 0
12: while (k > 0) do
13: gasit f alse
14: while (xk + 1 m) (gasit 6= true) do
15: xk xk + 1
16: gasit CanContinue(A, X, k)
17: end while
18: if (gasit = true) then
19: if (k = n) then
20: call Af is Solutie(x1 , . . . , xn )
21: else
22: k k+1
23: xk 0
24: end if
25: else
26: k k1
27: end if
28: end while
29: end procedure
#define FALSE 0
#define TRUE 1
#define NN 100
#define MAX 10000
195
int limit[NN]; //limit[i] numarul maxim de monede de tipul val[i]
int val[NN]; //val[i] valoarea unei monede
int taken[NN]; //cate monede de valoare val[i] au fost luate
int minim; //numarul minim de monede cu care se poate face plata
int keep[NN]; //solutia optima pina la momentul curent
/**
* Se citesc datele de intrare: suma de plata, numarul de
* tipuri de monezi si valoarea fiecarui tip.
*/
void readInput(void) {
int i;
/**
* Se verifica daca sunt satisfacute conditiile de continuare:
* suma partiala sa nu depaseasca valoarea totala de platit.
* @param k - pasul curent
*/
int canContinue(int k) {
int i, suma_tmp;
suma_tmp = 0;
for (i = 0; i <= k; i++)
suma_tmp += val[i] * taken[i];
if ((k == n - 1 && suma_tmp == suma_plata)
|| (k != n - 1 && suma_tmp <= suma_plata))
return TRUE;
else
return FALSE;
}
/**
* Se pastreaza solutia actuala (taken) numai daca este
* mai buna decat cea anterioara (keep).
*/
void final(void) {
int i;
int monezi = 0;
196
for (i = 0; i < n; i++)
keep[i] = taken[i];
}
}
/**
* Se afiseaza solutia optima sau mesajul Nu avem solutie.
*/
void print(void) {
int i, first = 0;
/**
* Implementarea metodei backtracking.
*/
void run(void) {
int i;
int gata; /* are valoarea TRUE cand sunt verificate conditiile
de continuare */
minim = MAX;
i = 0; taken[i] = -1;
while (i >= 0) {
gata = FALSE;
while ((taken[i] + 1 <= limit[i]) && !gata) {
/* cat timp exista elementul urmator si nu sunt verificate conditiile
de continuare */
taken[i]++; //treci la elementul urmator
//verifica conditiile de continuare
if (canContinue(i))
gata = TRUE;
//sau: gata = canContinue(i);
}
if (gata) //daca s-au verificat conditiile de continuare
if (i == n-1)
//daca suntem la ultimul element pastram solutia
final();
else { //altfel trecem la elementul urmator
i++; taken[i] = -1;
197
}
else
//altfel revenim la un element anterior si alegem o alta valoare
i--;
}
}
void main(void) {
readInput();
run();
print();
}
198
Bibliografie
[1] A. Aho, J. Hopcroft, J. Ullman, On finding lowest common ancestors in trees, Proc. 5th
ACM Symp. Theory of Computing (STOC), pp. 253-265, 1973.
[2] A. V. Aho, J. E. Hopcroft, J. D. Ulmann, Data Structures and algorithms, Addison-
Wesley, 1983.
[3] A. V. Aho, J. D. Ulmann, Foundation of Computer Science, Computer Science Press,
1992.
[4] R. K. Ahuja, J. B. Orlin, A Fast and Simple Algorithm for the Maximum Flow Problem,
Operations Research, vol. 37(5), pp. 748759, 1989.
[5] R. Ahuja, T. Magnanti, J. Orlin, Network Flows, Prentice Hall, 1993.
[6] R. Andonie, I. Garbacea, Algoritmi fundamentali, o perspectiv
a C++, Editura Libris,
1995.
[7] C. Aragon, R. Seidel, Randomized Search Trees, in Proc. of 30th IEEE Symposium on
Foundations of Computer Science, pp. 540546, 1989.
[8] A. Atanasiu, Concursuri de informatic
a. Probleme propuse (1994), Editura Petrion,
Bucuresti, 1995.
[9] M.D. Atkinson, J.-R. Sack, N. Santoro, T. Strothotte, Min-max heaps and generalized
priority queues, Programming techniques and Data structures. Comm. ACM, vol. 29(10),
pp. 996-1000, 1986.
[10] M. Augenstein, A. Tenenbaum, Program efficiency and data structures, Proceedings of
the eighth SIGCSE technical symposium on Computer science education, pp. 2127,
1977.
[11] S. Baase, Computer algorithms. Introduction to Design and Analysis, Addison-Wesley,
1992.
[12] P. Bazavan, Elemente de Teoria Algoritmilor, Editura Sitech, Craiova, 2007.
[13] R. Bellman, On a routing problem, Quarterly Applied Mathematics, XVI(1), pp. 87-90,
1958.
[14] M. A. Bender, M. Farach-Colton, The LCA problem revisited, Proceedings of the 4th
Latin American Symposium on Theoretical Informatics, LNCS, vol. 1776, Springer-
Verlag, pp. 88-94, 2000.
[15] J. Bentley, R. Sedgewick, Fast Algorithms for Sorting and Searching Strings, Proceedings
of the 8th Annual ACM-SIAM Symposium on Discrete Algorithms, 1997.
199
[16] J. Bentley, R. Sedgewick, Ternary Search Trees, Dr. Dobbs Journal, 1998.
[20] O. Berkman, U. Vishkin, Recursive Star-Tree Parallel Data Structure, SIAM Journal on
Computing, vol.22(2), pp.221-242, 1993.
[21] O. Boruvka, O jistem problemu minim alnm (About a certain minimal problem), Acta
Societ. Scient. Natur. Moravicae, 3, pp. 37-58, 1926.
[22] R. P. Brent, An improved Monte Carlo factorization algorithm, BIT Numerical Mathe-
matics, Springer, vol. 20(2), pp. 176184, 1980.
[26] B. Chazelle, A minimum spanning tree algorithm with inverse-Ackerman type complexity,
J. ACM, 47, pp. 1028-1047, 2000.
[27] D. Cherition, R. E. Tarjan, Finding minimum spanning trees, SIAM Journal on Com-
puting, vol. 5, pp. 724-741, 1976.
[28] J. Cheriyan, K. Mehlhorn, Algorithms for dense graphs and networks on the random
access computer, Algorithmica, vol. 15, pp. 521-549, 1996.
[29] B. V. Cherkassky,
Algorithm for construction of maximal flows in networks with com-
plexity of O(V 2 E), Mathematical Methods of Solution of Economical Problems, vol.
7, pp. 112125, 1977.
[32] C. Croitoru, Tehnici de baza n optimizarea combinatorie, Editura Univ. Al. I. Cuza Iasi,
Iasi, 1992.
[33] G. B. Dantzig, Linear programming and extensions, University Press, Princeton, 1962.
200
[36] R. B. Dial, Algorithm 360: shortest-path forest with topological ordering [H], Communi-
cations of the ACM, vol. 12:11, pp. 632633, 1969.
[38] E. W. Dijkstra, A note on two problems in connections with graphs, Numerische Math-
ematik, 1, pp. 269-271, 1959.
[39] Y. Dinitz, Algorithm for solution of a problem of maximum flow in a network with power
estimation, Doklady Akademii nauk SSSR, vol. 11, pp. 1277-1280, 1970.
[40] Y. Dinitz, Dinitz Algorithm: The Original Version and Evens Version, in Oded Goldre-
ich, Arnold L. Rosenberg, and Alan L. Selman, Theoretical Computer Science: Essays
in Memory of Shimon Even, Springer, pp. 218-240, 2006.
[41] J. Edmonds, Paths, Trees and Flowers, Canadian J. Math, vol. 17, pp. 449-467, 1965.
[44] J. Farey, On a Curious Property of Vulgar Fractions, London, Edinburgh and Dublin
Phil. Mag. 47, 385, 1816.
[45] J. Feng, G. Li, J. Wang, L. Zhou, Finding and ranking compact connected trees for
effective keyword proximity search in XML documents, Information Systems, vol. 35(2),
pp. 186203, 2010.
[46] L. R. Ford, Network flow theory, Technical Report P-923, RAND, Santa Monica, CA,
1956.
[47] L. R. Ford, Jr., D. R. Fulkerson, Maximal Flow Through a Network, Canadian Journal
of Mathematics, 8, pp. 399404, 1956.
[48] L. R. Ford, Jr., D. R. Fulkerson, A Simple Algorithm for Finding Maximal Network
Flows and an Application to the Hitchcock Problem, Canadian Journal of Mathematics,
9, pp. 210218, 1957.
[49] L. R. Ford, Jr., D. R. Fulkerson, Flows in Networks, Princeton University Press, Prince-
ton, 1962.
[50] C. L. Foster, The Design and Analysis of Algorithms, Springer Verlag, 1992.
[52] E. Fredkin, Trie Memory, Communications of the ACM, 3:(9), pp. 490, 1960.
[53] M. Fredman, R. Sedgewick, R. Sleator, R. Tarjan, The pairing heap: A new form of
self-adjusting heap, Algorithmica, 1, pp. 111129, 1986.
[54] M.L. Fredman, R.E. Tarjan, Fibonacci heaps and their use in improved network opti-
mization algorithms, Journal of the ACM, vol. 34, pp. 596-615, 1987.
201
[55] H. N. Gabow, R. E. Tarjan, A linear-time algorithm for a special case of disjoint set
union, Proceedings of the 15th ACM Symposium on Theory of Computing (STOC), pp.
246-251, 1983.
[56] H. N. Gabow, Path-based depth-first search for strong and biconnected components, In-
formation Processing Letters, pp. 107-114, 2000.
[57] D. Gale, L. S. Shapley, College Admissions and the Stability of Marriage, American
Mathematical Monthly, vol. 69, pp. 914, 1962.
5
[58] Z. Galil, An O(V 3 E f rac23 ) algorithm for the maximal flow problem, Acta Informatica,
vol. 14, pp. 221242, 1980.
[59] Z. Galil, A. Naamad, An O(EV (log V )2 ) algorithm for the maximal flow problem, Jour-
nal of Computer and System Sciences, vol. 21(2), pp. 203217, 1980.
[62] A. V. Goldberg, R. E. Tarjan, A new approach to the maximum flow problem, in Pro-
ceedings of the 18th ACM Symposium on Theory of Computing, ACM, pp. 136146,
1986.
Tardos, R. E. Tarjan, Network flow algorithms, Algorithms and
[63] A. V. Goldberg, E.
Combinatorics, vol. 9. in B. Korte, L. Lovsz, H. J. Prmel, A. Schrijver, Editors, Paths,
Flows, and VLSI-Layout, Springer-Verlag, Berlin, pp.101-164, 1990.
[65] D. Gries, The Science of Programming, Springer Verlag, Heidelberg, NewYork, 1981.
[66] L. Guo, F. Shao, C. Botev, J. Shanmugasundaram, XRANK: ranked keyword search over
XML documents, in Proceedings of the 2003 ACM SIGMOD International Conference
on Management of Data, pp. 1627, 2003.
[67] D. Gusfield, Algorithms on Strings, Trees, and Sequences, Cambridge University Press,
1997.
[69] D. Harel, R. E. Tarjan, Fast algorithms for finding nearest common ancestors, SIAM
Journal on Computing, vol. 13(2), pp. 338-355, 1984.
[70] T. E. Harris, F. S. Ross, Fundamentals of a Method for Evaluating Rail Net Capacities,
Research Memorandum RM-1573, The RAND Corporation, Santa Monica, California,
1955.
202
5
[72] J. E. Hopcroft, R. Karp, An n 2 algorithm for maximum matchings in bipartite graphs,
SIAM Journal on Computing, vol. 2(4), pp.225-231, 1973.
[76] V. Jarnk, O jistem problemu minimalnm (About a certain minimal problem), Prace
Moravske Prrodovedecke Spolecnosti, 6, pp. 5763, 1930.
[77] D. Jungnickel, Graphs, Networks and Algorithms, Algorithms and Computation in Math-
ematics, vol. 5, 3rd Edition, Springer, 2008.
[78] A. B. Kahn, Topological sorting of large networks, Communications of the ACM, vol.
5(11), pp. 558-562, 1962.
[80] A. V. Karzanov, Determining the maximum flow in the network by the method of pre-
flows, Doklady Akademii nauk SSSR 15, pp. 434-437, 1974.
[82] D. C. Kozen, The Design and Analysis of Algorithms. Texts and Monographs in Com-
puter Science, Springer, 1993.
[83] D. Konig, Theorie der endlichen und unendlichen Graphen, Leipzig: Akademische Ver-
lagsgesellschaft, 1936. Translated from German by Richard McCoart, Theory of finite
and infinite graphs, Birkhauser, 1990.
[88] J. B. Kruskal, On the shortest spanning subtree of a graph and the traveling salesman
problem, Proc. of the American Mathematical Society, 7, pp. 48-50, 1956.
203
[90] H. W. Kuhn, The Hungarian Method for the assignment problem, Naval Research Lo-
gistics Quarterly, vol. 2, pp. 83-97, 1955.
[91] H. W. Kuhn, Variants of the Hungarian method for assignment problems, Naval Research
Logistics Quarterly, vol. 3, pp. 253-258, 1956.
[95] K. Mehlhorn, Data Structures and Algorithms: Graph Algorithms and NP-Completeness,
Springer Verlag, 1984.
p
[96] S. Micali, V. V. Vazirani, An O( |V | |E|) algorithm for finding maximum matching
in general graphs, Proc. 21st IEEE Symp. Foundations of Computer Science, pp. 17-27,
1980.
[98] E. F. Moore, The shortest path through a maze, in Proceedings of International Sympo-
sium on the Theory of Switching, Part II, pp. 285-292, 1959. (prezentat la simpozion la
Universitatea Harvard in aprilie 1957)
[99] R. Motwani, Average-case analysis of algorithms for matchings and related problems,
Journal of the ACM, vol. 41(6), pp. 1329-1356, 1994.
[100] J. Munkres, Algorithms for the Assignment and Transportation Problems, Journal of
the Society for Industrial and Applied Mathematics, vol. 5(1), pp. 32-38, 1957.
[101] G. Nivasch, Cycle detection using a stack, Information Processing Letters, vol. 90(3),
pp. 135140, 2004.
[103] S. Pettie, A faster all-pairs shortest path algorithm for real-weighted sparse graphs, in
Proceedings of 29th International Colloquium on Automata, Languages, and Program-
ming (ICALP02), LNCS Vol. 2380, pp. 85-97, 2002.
[104] S. Pettie, V. Ramachandran, Computing shortest paths with comparisons and additions,
in Proceedings of the 13th Annual ACM-SIAM Symposium on Discrete Algorithms
(SODA02), SIAM, pp. 267-276, 2002.
[106] R. C. Prim, Shortest connection networks and some generalizations, Bell System Tech-
nical Journal, 36, pp. 1389-1401, 1957.
204
[107] B. Schieber, U. Vishkin, On finding lowest common ancestors: Simplification and par-
allelization, SIAM J. Comput., vol. 17, pp. 1253-1262, 1988.
[108] A. Schrijver, On the history of the transportation and maximum flowproblems, Mathe-
matical Programming, vol. 91, issue 3, pp. 437-445, 2002.
[109] R. Seidel, C. Aragon, Randomized Search Trees, Algorithmica, vol. 16, pp. 464497,
1996.
[110] M. Sharir, A strong-connectivity algorithm and its applications in data fow analysis,
Computers and Mathematics with Applications, vol. 7(1), pp. 6772, 1981.
[111] Y. Shiloach, U. Vishkin, An O(n2 log n) parallel max-flow algorithm, Journal of Algo-
rithms, vol. 3(2), pp. 128-146, 1982.
[112] S. Skiena, The Algorithm Design Manual, 2nd Edition, Springer, 2008.
[113] D. D. Sleator, An O(EV log V ) algorithm for maximum network flow, Technical Report,
STAN-CS-80-831, 1980.
[114] D. D. Sleator, R. E. Tarjan, A data structure for dynamic trees, Journal of Computer
Sciences, vol. 26, pp. 362391, 1983.
[115] J. Stasko, J. Vitter, Pairing heaps: Experiments and analysis, Communications of the
ACM, vol. 30(3), pp. 234-249, 1987.
[116] T. Takaoka, Theory of 2-3 heaps, Discrete Applied Mathematics, Vol. 126(1), 5th An-
nual International Computing and Combinatories Conference (COCOON99), pp. 115
128, 2003.
[117] R. Tarjan, Depthfirst search and linear graph algorithms, SIAM Journal on Computing,
vol. 1(2), pp. 146160, 1972.
[118] R. E. Tarjan, Edge-disjoint spanning trees and depth-first search, Algorithmica, vol.
6(2), pp. 171-185, 1976.
[119] R. E. Tarjan, Applications of path compression on balanced trees, Journal of the ACM,
vol. 26(4), pp. 690-715, 1979.
[120] R. E. Tarjan, A simple version of Karzanovs blocking flow algorithm, Operation Re-
search Letters, vol. 2, pp.265268, 1984.
[123] I. Tutunea, Algoritmi, logica si programare, Reprografia Universitatii din Craiova, 1993.
205
[126] J. Vuillemin, A unifying look at data structures, Communications of the ACM, vol.
23:4, pp. 229-239, 1980.
[127] G. A. Waissi, A new Karzanov-type O(n3 ) max-flow algorithm, Mathematical and Com-
puter Modelling, vol. 16(2), pp.6572, 1992.
[129] J. W. J. Williams, Algorithm 232 - Heapsort, Communications of the ACM, vol. 7(6),
pp. 347-348, 1964.
[130] Y. Xu, Y. Papakonstantinou, Efficient keyword search for smallest LCAs in XML
databases, in Proceedings of the 2005 ACM SIGMOD International Conference on Man-
agement of Data, pp. 527538, 2005.
206