Documente Academic
Documente Profesional
Documente Cultură
1 Introducere n algoritmi
1.1 Limbajul pseudocod . . . . . .
1.2 Exemple . . . . . . . . . . . . .
1.3 Elemente de analiza algoritmilor
1.3.1 Metoda substitutiei . . .
1.3.2 Schimbarea de variabila
1.3.3 Metoda iterativa . . . .
1.3.4 Teorema master . . . . .
1.4 Exercitii . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3
4
12
16
20
21
21
22
25
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
28
28
33
36
40
40
44
45
49
50
55
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
58
58
59
62
66
68
70
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
80
81
82
84
90
98
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5 Arbori oarecare
5.1 Moduri de reprezentare . . . . . . . . . . . . . . .
5.2 Metode de parcurgere . . . . . . . . . . . . . . . .
5.3 Arbori de acoperire de cost minim . . . . . . . . .
5.3.1 Algoritmul lui Boruvka . . . . . . . . . . .
5.3.2 Algoritmul lui Prim . . . . . . . . . . . . .
5.3.3 Structuri de date pentru multimi disjuncte
5.3.4 Algoritmul lui Kruskal . . . . . . . . . . .
5.4 Exercitii . . . . . . . . . . . . . . . . . . . . . . .
6 Grafuri orientate
6.1 Notiuni de baza . . . . . . . . .
6.2 Parcurgerea grafurilor . . . . .
6.3 Sortarea topologica . . . . . . .
6.4 Componente tare conexe . . . .
6.4.1 Algoritmul lui Kosaraju
6.4.2 Algoritmul lui Tarjan . .
6.4.3 Algoritmul lui Gabow .
6.5 Exercitii . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
102
. 102
. 105
. 110
. 113
. 113
. 116
. 121
. 124
.
.
.
.
.
.
.
.
127
. 127
. 129
. 132
. 136
. 137
. 139
. 142
. 145
7 Heap-uri
7.1 Heap-uri binare (Min-heapuri sau Max -heapuri)
7.1.1 Inserarea unui element . . . . . . . . . .
7.1.2 Stergerea elementului minim . . . . . . .
7.1.3 Crearea unui heap . . . . . . . . . . . .
7.2 Ordonare prin metoda HeapSort . . . . . . . . .
7.3 Aplicatie - Coada cu prioritate . . . . . . . . . .
7.4 Exercitii . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
149
. 149
. 151
. 152
. 153
. 155
. 158
. 161
8 Distante n grafuri
8.1 Drumul minim de la un varf la celelalte varfuri .
8.1.1 Algoritmul lui Moore . . . . . . . . . . .
8.1.2 Algoritmul lui Dijkstra . . . . . . . . . .
8.2 Drumuri minime ntre toate perechile de varfuri
8.2.1 Algoritmul lui Roy-Floyd-Warshall . . .
8.3 Exercitii . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
A Metoda Backtracking
165
166
166
167
175
175
182
186
Capitolul 1
Introducere n algoritmi
Definitia 1.1 Algoritmul constituie o reprezentare finit
a a unei metode de calcul ce permite rezolvarea unei anumite probleme. Se poate spune c
a un algoritm reprezint
a o secventa
finita de operatii, ordonata si complet definit
a, care, pornind de la datele de intrare, produce
rezultate.
Termenul de algoritm i este atribuit matematicianului persan Abu Jafar Mohammed
ibn Musa al-Khowarizmi (sec. VIII-IX), care a scris o carte de matematica cunoscuta n traducere latina sub titlul de Algorithmi de numero indorum, iar apoi ca Liber algorithmi ,
unde termenul de algorithm provine de la al-Khowarizmi , ceea ce literal nseamna din
orasul Khowarizm. Matematicienii din Evul Mediu ntelegeau prin algoritm o regula (sau
o multime de reguli) pe baza careia se efectuau calcule aritmetice: de exemplu n secolul al
XVI-lea, algoritmii se foloseau la nmultiri sau njumatatiri de numere.
Fiecare propozitie ce face parte din descrierea unui algoritm este, de fapt, o comanda ce
trebuie executata de cineva, acesta putand fi o persoana sau o masina de calcul. De altfel,
un algoritm poate fi descris cu ajutorul oricarui limbaj, de la limbajul natural si pana la
limbajul de asamblare al unui calculator. Denumim limbaj algoritmic un limbaj al carui scop
este acela de a descrie algoritmi.
Algoritmul specifica succesiuni posibile de transformari ale datelor. Un tip de date poate
fi caracterizat printr-o multime de valori ce reprezinta domeniul tipului de date si o multime
de operatii definite peste acest domeniu. Tipurile de date pot fi organizate n urmatoarele
categorii:
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.
Un algoritm trebuie sa posede urmatoarele tr
as
aturi caracteristice:
1. claritate - la fiecare pas trebuie sa specifice operatia pe care urmeaza sa o efectueze
algoritmul asupra datelor de intrare;
2. corectitudine - rezultatele trebuie sa fie corecte;
1.1
Limbajul pseudocod
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 >
1: if (a mod 2 = 0) then
2:
Output {Numarul este par }
3: else
4:
Output {Numarul este impar }
5: end if
[
3: else
4:
<instructiune2>
]
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 matematic
a 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
acut
a pentru nicio valoare a lui x R sau
C. Spunem, n acest caz, ca ecuatia este incompatibil
a. Dac
a 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
Input {a, b}
if (a = 0) then
if (b = 0) then
Output { Ecuatie compatibil nedeterminata }
else
Output { Ecuatie incompatibila }
end if
else
x ab
Output { Solutia este:, x }
end if
1: while <expresie-booleana> do
2:
<instructiune>
3: end while
sum 0
i1
while (i n) do
sum sum + i
i i+1
end while
Input {a, b}
if ((a > 0) (b > 0)) then
cat 0
rest a
while (rest b) do
rest rest b
cat cat + 1
end while
Output {cat, rest}
else
Output {Numerele trebuie sa fie strict pozitive!}
end if
pe instructiunile while si repeat sunt mult mai potrivite n cazul n care conditia de terminare 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
2: for i 1, n do
Step < p > do
3:
sum sum + i
2:
<instructiune>
4: end for
3: 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
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:
Expresie1 > Expresie2: <instructiune> nu se executa niciodata;
Expresie1 = Expresie2: <instructiune> se executa exact o data;
Expresie1 < Expresie2: <instructiune> se executa de Expresie2 Expresie1 + 1 ori.
Exemplul 1.3 Un caz destul de frecvent nt
alnit n practic
a este cel n care se cere determinarea elementului de valoare maxim
a, respectiv minim
a dintr-un sir de elemente. Acest
7
Input {N, x1 , x2 , . . . , xN }
min x1
for i 2, N do
if (min > xi ) then
min xi
end if
end for
Output { min }
1: repeat
2:
<instructiune>
3: until <expresie-booleana>
sum 0
i1
repeat
sum sum + i
i i+1
until (i > n)
=
=
=
=
...
=
1
1 + 2 = S1 + 2
(1 + 2) + 3 = S2 + 3
(1 + 2 + 3) + 4 = S3 + 4
(1 + 2 + . . . + n 1) + n = Sn1 + n
Algoritm 4 Algoritm pentru calculul sumei primelor n numere naturale (prima varianta)
1:
2:
3:
4:
5:
6:
7:
8:
Input {N }
S0
i1
repeat
S S+i
ii+1
until (i > N )
Output {S}
Observatia 1.5
until.
1. Algoritmul 4 utilizeaz
a enuntul repetitiv cu test final, repeat . . .
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
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)
1. cmmdc(a, 0) = a.
10
3. Intre cel mai mic multiplu comun si cel mai mare divizor comun exist
a urmatoarea
relatie:
ab
.
(1.1)
cmmmc(a, b) =
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
0 r1 < b
0 r 2 < r1
0 r 3 < r2
0 rk+2 < rk+1
0 rn < rn1
0 rn+1 < rn
Aceste mpartiri nu se constituie ntr-un proces infinit, deoarece secventa de numere naturale 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.
Algoritm 6 Algoritmul lui Euclid pentru calculul cmmdc a doua numere
1: procedure cmmdc(x, y; b)
2:
ax
3:
by
4:
if (b = 0) then
5:
ba
6:
else
7:
r a mod b
8:
while (r 6= 0) do
9:
ab
10:
br
11:
r a mod b
12:
end while
13:
end if
14: end procedure
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, trebuie 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
1.2
Exemple
Input {N }
if (n < 2) then
Output {Numarul nu este prim.}
else
i2
prim true
while ((i n 1) (prim = true)) do
if (n mod i = 0) then
prim f alse
else
i i+1
end if
end while
if (prim = true) then
Output {Numarul este prim.}
else
Output {Numarul nu este prim.}
end if
end if
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 mbun
at
atit
a cu i n/2, deoarece ntre jum
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 asem
an
ator este valabil
12
corespunde
corespunde
corespunde
..
.
k
|{z}
corespunde
Sir1
n
2
n
3
n
4
..
.
n
k
|{z}
Sir2
Se observa faptul ca primul sir (Sir1 ) este compus din elemente cu valori consecutive,
iar al doilea sir (Sir2 ) nu respecta aceast
a proprietate. Numerele dintr-o pereche (p, np ) sunt
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
Input {N }
if (n < 2) then
Output {Numarul nu este prim.}
else
i2
prim true
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;
. . . an
a1 . . .
a ...a
| l {znm+i}
subsecventa pentru care se determin
a cifra maxim
a
Input {n, a1 , . . . , an , k}
m nk
l0
for i 1, m do
l l+1
jl
max 0
while (j n m + i) do
if (max < aj ) then
max aj
lj
end if
j j+1
end while
Output {max}
end for
ni1
X
j=1
j=
(n i) (n i 1)
2
ori.
Din i = 2, n 2 rezulta ca
n2 ni1
X
X
i=2
j=1
j =
n2
X
(n i) (n i 1)
i=2
n2
1X 2
=
(i + (1 2n)i + n2 n)
2 i=2
n2
n2
X
1X 2
=
[
i + (1 2n)
i + (n2 n) (n 3)]
2 i=2
i=2
15
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
Input {n}
for i 2, n 2 do
for j i + 1, n 1 do
for k j + 1, n do
if (k k = i i + j j) then
Output {i, j, k}
end if
end for
end for
end for
return
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
Input {n}
for i 2, n 1 do
for j i + 1, n do
s ii+j j
k s
if ((k k = s) (k n)) then
Output {i, j, k}
end if
end for
end for
return
1.3
(n i) =
n1
X
i=1
(n i) (n 1) =
n1
X
i=1
i (n 1) =
n(n 1)
n+1
2
Gradul de dificultate al unei probleme P poate fi pus in evidenta prin timpul de executie al
algoritmului corespunzator si/sau prin spatiul de memorie necesar. Timpul de executie n
cazul cel mai defavorabil ne da durata maxima de executie a unui algoritm. Timpul mediu
de executie se obtine prin nsumarea timpilor de executie pentru toate multimile de date de
intrare urmata de raportarea la numarul acestora.
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 num
arul 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.
Fiind data o problema P , o functie T (n) se spune ca este o margine superioar
a daca exista
un algoritm A ce rezolva problema P iar timpul de executie n cazul cel mai defavorabil al
algoritmului A este cel mult T (n).
Fiind data o problema P , o functie T (n) se spune ca este o margine pentru cazul mediu
daca exista un algoritm A ce rezolva problema P iar timpul mediu de executie al algoritmului
A este cel mult T (n).
Fiind data o problema P , o functie T (n) se spune ca este o margine inferioar
a daca orice
algoritm A ce rezolva problema P va avea cel putin un timp T (n) pentru unele date de intrare
de dimensiune n, atunci cand n tinde la infinit (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)).
Observatia 1.13 Vom prezenta cateva propriet
ati ale lui O():
O(f (n) + g(n)) = O(max(f (n), g(n))).
f (n)
g(n)
= 0.
f (n) = O(n ), d d.
17
Teorema 1.17
Exemplul 1.18
Deoarece
c
g(n)
lim
= 0
n f (n)
(1.2)
< 4
=
= ( )n
4
4
n/2
2n
n!
(x )
x
x
x
. . x}
| .{z
(1.3)
n/2ori
rezulta ca
xn
=0
n n!
lim
log n o(n).
(1.4)
1
log x
(log x)
x ln 2
= lim
=
lim
=0
x x
x (x)
x 1
lim
18
2:
A(i)
3: end for
i=1
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 .
T (n) = c1 n + c2 n +
n
X
i=1
c3 i +
n
X
c4 i + c5 n + c6 = c1 n + c2 n + c3
i=1
n
X
i + c4
i=1
n
X
i + c5 n + c6
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
(1.6)
s0
for i 1, n do
s s + ai
bi si
end for
return
(1.7)
1.3.1
Metoda substitutiei
Metoda substitutiei presupune mai ntai ghicirea (estimarea) formei solutiei pentru relatia
de recurenta si apoi demonstrarea prin inductie matematica a corectitudinii solutiei alese.
Metoda poate fi aplicata n cazul unor ecuatii pentru care forma solutiei poate fi estimata.
Sa consideram urmatoarea relatie de recurenta:
(
1
, daca n = 1
T (n) =
(1.8)
n
2T ( 2 ) + n , n rest
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 inegalitatea:
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
(1.10)
1.3.2
Schimbarea de variabil
a
T (2m ) = 2T (2 2 ) + log 2m = 2T (2 2 ) + m
(1.11)
m
)+m
2
(1.12)
Se observa ca aceasta relatie are o forma similara cu cea din formula (1.8).
Solutia relatiei (1.12) este:
S(m) = O(m log m) T (n) = T (2m ) = S(m) = O(m log m) = O(log n log log n)
(1.13)
1.3.3
Metoda iterativ
a
Metoda iterativa este mult mai eficienta deoarece nu presupune ghicirea solutiei, varianta ce
conduce deseori la rezultate gresite si timp pierdut. De exemplu, sa consideram urmatoarea
formula de recurenta:
(
1
, daca n = 1
T (n) =
(1.14)
T ( n2 ) + 1 , n rest
Pentru rezolvarea acestei recurente vom substitui succesiv pe n cu n2 :
n
T (n) = T ( ) + 1
2
n
= T( ) + 2
4
n
= T( ) + 3
8
...
n
= T( k) + k
2
21
(1.15)
n
2
n
T (n) = 2T ( ) + n
2
n
n
n
= 2(2T ( ) + ) + n = 4T ( ) + 2n
2
2
4
n
n
n
= 4(2T ( ) + ) + 2n = 8T ( ) + 3n
8
4
8
...
n
= 2k T ( k ) + kn
2
Deoarece substitutia se opreste atunci cand
n
2k
(1.17)
1.3.4
(1.16)
(1.18)
Teorema master
Metoda master pune la dispozitie o varianta de rezolvare a unor recurente avand forma
urmatoare
n
T (n) = aT ( ) + f (n)
(1.19)
b
unde a 1, b > 1 sunt constante, iar f (n) este o functie asimptotic pozitiva. Formula de
recurenta (1.19) descrie timpul de executie al unui algoritm ce presupune descompunerea unei
probleme de dimensiune n n a subprobleme, fiecare avand dimensiunea datelor de intrare nb .
f (n) reprezinta costul asociat mpartirii datelor de intrare cat si costul combinarii rezultatelor
celor a subprobleme.
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:
1. daca f (n) = O(nlogb a ) pentru o constant
a > 0, atunci T (n) = (nlogb a );
2. daca f (n) = (nlogb a logk n), atunci T (n) = (nlogb a logk+1 n) (de obicei k = 0);
3. daca f (n) = (nlogb a+ ) pentru o constant
a > 0, si dac
a af ( nb ) cf (n) pentru o
constanta c < 1 si oricare numar n suficient de mare, atunci T (n) = (f (n)).
Corolarul 1.22 Pentru o functie f de tip polinomial unde f (n) = cnk avem:
1. daca a > bk atunci T (n) = O(nlogb a );
2. daca a = bk atunci T (n) = O(nk log n);
22
1
O(n 2 ) obtinem: T (n) = ( n).
Fie T (n) = 3T ( n4 ) + n log n. Aplic
and Teorema master pentru a = 3, b = 4, f (n) =
log4 3+
(n
) obtinem: T (n) = (n log n).
Fie T (n) = 16T ( n4 ) n log n. Nu se poate aplica Teorema master deoarece f (n) =
n log n nu este o functie asimptotic pozitiv
a.
Exemplul 1.24 Analiza actiunilor
Deschiderea unei actiuni la o anumit
a dat
a calendaristic
a se calculeaz
a drept numarul
maxim de zile consecutive (pana la acea dat
a) n care pretul actiunii a fost mai mic sau egal
cu pretul din ziua respectiva. Fie pk pretul unei actiuni n ziua k iar dk deschiderea calculata
n aceeasi zi.
In continuare se prezinta un exemplu de calcul al deschiderii unei actiuni pentru un numar
de 7 zile:
p0
9
d0
1
p1
6
d1
1
p2
3
d2
1
p3
4
d3
2
p4
2
d4
1
p5
5
d5
4
p6
7
d6
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:
push(x:
size():
isEmpty():
isFull():
init(capacitate:integer) - initializeaz
a stiva.
Algoritm 12 Algoritm de calcul al deschiderii unei actiuni (a doua varianta)
1: procedure ComputeSpan2(p, n; d)
2:
call init(S)
3:
for k 0, n 1 do
4:
ind 1
5:
while (isEmpty(S) = f alse) (ind = 1) do
6:
if (pk ppeek(S)) then
7:
call pop(S)
8:
else
9:
ind 0
10:
end if
11:
end while
12:
if (ind = 1) then
13:
h 1
14:
else
15:
h peek(S)
16:
end if
17:
dk k h
18:
call push(S, k)
19:
end for
20:
return
21: end procedure
24
1.4
Exercitii
(1.20)
100y
3+4z
100y
3+4z
x [1,
100y
3+4z
100 y
3 + 4z
(1.21)
].
Astfel, pentru fiecare pereche de numere (z, x) vom calcula pe y astfel: y = 100 3x
4xz.
De asemenea, n 1.21 daca luam x = 0 obtinem multimea de solutii (0, 100, z), z N.
25
Input {n}
j2
while (j n) do
if (n mod j = 0) then
k0
while (n mod j = 0) do
k k+1
n n div j
end while
Output {j k }
end if
j j +1
end while
n
;
log n
(c) T (n) = 3T ( n3 ) + n;
(d) T (n) = 3T ( n3 ) + n2 .
9. Sa se calculeze timpul mediu de lucru T (n) al algoritmului 14.
Algoritm 14 Algoritm Calcul5
1: for i 1, n do
2:
jn
3:
while (j 1) do
4:
...
5:
j 2j
6:
end while
7: end for
26
T (n) = 3 T ( n2 ) + c n, n = 2k > 1.
12. Sa se rezolve ecuatia recurenta T (n) = n T 2 ( n2 ), n = 2k , T (1) = 6.
13. Sa se arate ca ex = 1 + x + (x2 ) unde x .
14. Sa se determine primele n elemente ale sirurilor ak si bk date prin urmatoarele relatii
de recurenta:
ak + 3
5ak + 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 );
27
Capitolul 2
Grafuri. Grafuri neorientate
2.1
Notiuni de baz
a
Fie V o multime finita si nevida avand n elemente (V = {x1 , x2 , . . . , xn }). Fie E o multime
finita astfel ncat E V V (unde V V este produsul cartezian al multimii V cu ea nsasi)
si E = {(x, y)|x, y V } (E este multimea perechilor (x, y) cu proprietatea ca x si y apartin
multimii V ).
Definitia 2.1 Se numeste graf o pereche ordonat
a G = (V, E).
Elementele xi V se numesc noduri sau v
arfuri. Elementele multimii E sunt arce
sau muchii. O muchie (xk , xl ) E se mai noteaz
a si cu [xk , xl ].
|V | se numeste ordinul grafului G si reprezinta numarul varfurilor acestuia, iar |E| se
numeste dimensiunea grafului G si reprezinta numarul muchiilor/ arcelor grafului G.
Graful = (, ) se numeste graful vid.
Daca ntr-o pereche [xk , xl ] nu tinem cont de ordine atunci graful este neorientat iar
perechea reprezinta o muchie ([xk , xl ] = [xl , xk ]). Daca se introduce un sens fiecarei muchii
atunci aceasta devine arc iar graful se numeste orientat ([xk , xl ] 6= [xl , xk ]). Muchiile ce au
aceleasi varfuri se spune ca sunt paralele. O muchie de forma [u, u] se numeste bucl
a.
Un graf G se numeste simplu daca oricare doua varfuri ale sale sunt extremitati pentru
cel mult o muchie. Un graf este finit daca multimile V si E sunt finite.
In continuare vom considera un graf neorientat, simplu si finit. Daca [x, y] E vom
spune ca x si y sunt adiacente, iar muchia [x, y] este incidenta cu varfurile x si y. x si y se
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.
Definitia 2.2 Se numeste graf complet de ordinul n, si se noteaz
a cu Kn , un graf cu proprietatea ca oricare doua varfuri distincte ale sale sunt adiacente (x V, y V, x 6= y
[x, y] E).
Exemplul 2.2 Sa consideram grafurile din figura 2.1.
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));
b) K5 este graful complet cu 5 varfuri. V
arfurile 3 si 5 sunt adiacente (vezi figura 2.1 b)).
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 extremit
ati n multimea V1
(E1 = E|V1 V1 = {[x, y]|[x, y] E, x, y V1 }).
Se spune ca graful H este indus sau generat de submultimea de varfuri V1 si se noteaza
H = G|V1 sau H = [V1 ]G .
Iata alte cateva notatii des ntalnite:
- G V1 = subgraful ce se obtine din G prin eliminarea submultimii de varfuri V1 (V1
V );
- G x = subgraful G {x};
- < E1 >G = graful partial al lui G generat de E1 ;
29
- G E1 =< E \ E1 >G ;
- G e = G {e}, graful partial obtinut prin eliminarea unei muchii e.
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 v
arf izolat.
Notam (G) = min{dG (u)|u G} si (G) = max{dG (u)|u G}.
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:
n
X
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.
Definitia 2.6 Se numeste secventa
grafic
a un sir de numere naturale d1 , d2 , . . . , dn cu
proprietatea ca ele reprezinta gradele v
arfurilor unui graf neorientat.
Corolarul 2.6 Pentru ca o secventa de numere naturale d1 , d2 , . . . , dn s
a fie secventa grafica,
este necesar ca:
1. k = 1, n, dk n 1;
Pn
2.
ar par.
k=1 dk este un num
Daca varfurile v0 , v1 , . . . , vm sunt distincte doua cate doua, lantul se numeste elementar
(vi 6= vj , i, j = 0, m).
Definitia 2.8 Un lant L pentru care v0 = vm se numeste ciclu.
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.
Definitia 2.10 Un lant L ce contine fiecare muchie exact o singur
a dat
a se numeste lant
eulerian. Daca v0 = vm si lantul este eulerian atunci ciclul se numeste ciclu eulerian.
Un graf ce contine un ciclu eulerian se numeste graf eulerian.
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.
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 .
Definitia 2.16 Se numeste izomorfism de la graful G la graful G o functie bijectiva :
V (G) V (G ) astfel ncat [u, v] E(G) ([u, v]) E(G ).
Daca exista un izomorfism de la graful G la graful G atunci se spune c
a G este izomorf
cu G si not
am acest lucru astfel: G G .
Definitia 2.17 Un graf etichetat este un graf n care fiecare muchie si v
arf poate avea
asociata o eticheta.
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;
adaugarea sau stergerea unui nod sau a unei muchii/arc;
translatarea dintrun mod de reprezentare n altul. De foarte multe ori modul de
reprezentare sub forma caruia au fost introduse datele, difera de modul de reprezentare
optim recomandat pentru un anumit algoritm. In aceasta situatie este indicata transformarea primului mod de reprezentare n modul de reprezentare optim;
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
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).
6. Reuniunea si intersectia a doua grafuri se definesc astfel:
daca V1 = V2 atunci: G1 G2 = (V1 , E1 E2 ), G1 G2 = (V1 , E1 E2 );
7. Definim suma grafurilor G1 si G2 ca fiind graful complementar al reuniunii complementarelor celor doua grafuri: G1 G2 = (Gc1 Gc2 )c .
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 .
E = {[(u1 , u2), (v1 , u2)], [(u1 , u2 ), (u1, v2 )], [(u1 , v2 ), (v1 , v2 )], [(u1 , v2 ), (u1, w2 )],
[(u1 , w2), (v1 , w2 )], [(v1 , u2 ), (v1 , v2 )], [(v1 , v2 ), (v1 , w2 )]} (vezi figura 2.11).
9. Se numeste radacina patrata a grafului G, un graf neorientat H cu proprietatea ca
H 2 = G (H 2 = H H).
10. Operatia de compunere a doua grafuri neorientate G1 = (V1 , E1 ) si G2 = (V2 , E2 ),
notata cu G1 [G2 ], se realizeaza astfel: varful (x1 , y1 ) este adiacent cu varful (x2 , y2) n
graful rezultat daca varful x1 este adiacent cu varful x2 n graful G1 sau (x1 = x2 si
varful y1 este adiacent cu varful y2 n graful G2 ) (vezi figura 2.12).
35
Fig. 2.12: Graful rezultat n urma compunerii grafurilor G1 si G2 din figura 2.11
2.3
Moduri de reprezentare
0
1
1
A=
0
0
0
1
0
0
0
1
0
0
0
1
0
0
0
1
0
0
0
36
1
0
0
0
0
1
0
0
0
1
1
0
0
0
1
0
0
0
0
1
0
0
1
1
0
0
0
0
1
1
0
0
0
0
0
0
sau
0
ci,j = +
d>0
0
ci,j =
d>0
, daca xi = xj
, daca [xi , xj ]
/E
, daca [xi , xj ] E
(2.2)
, daca xi = xj
, daca [xi , xj ]
/E
, daca [xi , xj ] E.
(2.3)
0
35
20
C=
35
0
50
22
20
15
50
22
44
15
0
10
27
44
10
0
27
(2,
(1,
(1,
(1,
3, 4)
5)
5)
6)
5:
6:
7:
8:
(2, 3, 7)
(4, 7, 8)
(5, 6)
(6)
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:
List2,j
4
8
5
10
6
13
7
16
8
18
1 5 1 5 1 6 2 3 7 4 7 8 5 6 6
5 0 7 0 9 0 11 12 0 14 15 0 17 0 0
NULL
5
6
3
NULL
1
1
2
7
8
NULL
NULL
NULL
NULL
NULL
NULL
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;
2.4
Matricea de adiacent
a
O(n)
O(1)
O(n)
Liste de vecini
O(d(xi ))
O(d(xi ))
O(d(xi ))
Lista de muchii
O(m)
O(m)
O(m)
Parcurgerea grafurilor
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 algoritmul 16):
se viziteaza mai ntai varful de pornire (sa l notam k);
urmeaza, n ordine, toti vecinii nca nevizitati ai nodului k;
se continua cu vecinii nca nevizitati ai acestora, s.a.m.d.
Pentru graful considerat n figura 2.13 ordinea de parcurgere este urmatoarea: 1, 2, 3, 4,
5, 6, 7, 8. Daca consideram muchiile folosite n timpul parcurgerilor (muchiile prin intermediul
carora s-a naintat n graf) se obtine un arbore/p
adure de parcurgere/vizitare/acoperire. In
figura 2.16 este reprezentat arborele de acoperire n l
atime rezultat n urma parcurgerii grafului din exemplu.
Algoritmul de parcurgere utilizeaza o structura de date de tip coad
a n care vor fi memorate nodurile vizitate, dar care nu au fost nca prelucrate (nu au fost cercetati vecinii lor).
Reamintim ca numarul de noduri din multimea V este n. Vectorul vizitat pastreaza situatia
vizitarii nodurilor
grafului G, astfel:
(
1 , daca nodul k a fost vizitat
vizitatk =
0 , daca nodul k nu a fost vizitat.
Implementarea n limbajul C a algoritmului 16 este urmatoarea:
40
i nt n ;
char v e c i n [MAXN] [MAXN] ;
i nt coada [MAXN] ;
i nt f i r s t ;
i nt l a s t ;
//
//
//
//
//
Numarul de n o d u r i d i n g r a f
M a t r i c e a de a d i a c e n t a
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 n d i c e l e p r i m u l u i element d i n coada
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 headerele 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
2
http://msdn.microsoft.com/en-us/library/1fdeehz6.aspx
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.
Vizitarea ncepe de la nodul 0 (bfs(0)).
Subrutina vizitare(), n principiu, afiseaza valoarea etichetei nodului trimis ca argument, nsa poate efectua si alte prelucrari necesare asupra nodului curent, n functie de
cerintele specifice ale algoritmului.
Algoritmul BFS este utilizat de catre metoda Branch-and-Bound ca metoda de explorare
a spatiului solutiilor, cat si n cadrul problemelor de determinare a distantei minime de la
un varf la toate celelalte varfuri, n cazul n care lungimea unui drum dintre doua noduri se
considera ca fiind egala cu numarul de noduri intermediare ale acestuia.
2.4.2
Parcurgerea D (D - Depth)
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 viziteaza varful de pornire (notat cu k);
urmeaza, primul vecin nca nevizitat al acestuia;
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 categorii:
1. muchie a arborelui de acoperire - muchia [u, v] este o muchie a arborelui de acoperire
daca dfs(u) apeleaza direct dfs(v) sau invers;
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
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
//
//
//
//
M a t r i c e a de a d i a c e n t a
Vecto 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 nu
Numarul de v a r f u r i d i n g r a f
Nodul d i n c a r e s e p o r n e s t e v i z i t a r e a
/
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 ) ;
}
void main ( void ) {
readInput ( ) ;
d f s ( no di ) ;
}
Matricea de adiacent
a
2
O(n )
O(n2 )
O(n2 )
Liste de vecini
O(n + m)
O(n + m)
O(n + m)
48
Lista de muchii
O(n + m2 )
O(n + m2 )
O(n + m2 )
2.5
Componente conexe
m
[
i=1
Vi , Vi Vj = , i, j = 1, m, i 6= j
(2.4)
m
[
i=1
Ei , Ei Ej = , i, j = 1, m, i 6= j
(2.5)
n
- num
arul de noduri din graf
V ecin - matricea de adiacent
a a grafului
for i 1, n do
vizitati 0
end for
cmp conex nr 0
for i 1, n do
if (vizitati = 0) then
cmp conex nr cmp conex nr + 1
call DF S(i, n, V ecin)
determinarea componentei conexe ce contine nodul i
end if
end for
end procedure
Input:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
49
2.6
Muchie critic
a
- num
arul de noduri din graf
n
Input:
m
- num
arul de muchii din graf
vizitarea n ad
ancime a grafului
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
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;
}
void main ( void ) {
i nt i , j ;
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;
}
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
lowk min {lowk , prenumi}.
Implementarea n limbajul C a algoritmului 22 este urmatoarea:
52
n
- num
arul de noduri din graf
V ecin - matricea de adiacent
a
for i 1, n do
vizitati 0
end for
counter 0
call DF S critic(1, n, V ecin)
end procedure
procedure DFS critic(k, n, V ecin)
vizitatk 1
counter counter + 1
prenumk counter, lowk counter
for i 1, n do
if (vecink,i = 1) then
if (vizitati = 0) then
tatai k
call DF S critic(i, n, V ecin)
lowk M in(lowk , lowi )
if (prenumk < lowi ) then
Output {Muchia (k,i) este critica}
end if
else
if (tatak 6= i) then
lowk M in(lowk , prenumi )
end if
end if
end if
end for
end procedure
Input:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
vizitarea n ad
ancime a grafului
//
//
//
//
//
//
M a t r i c e a de a d i a c e n t a
Vecto 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 nu
Numarul de n o d u r i d i n g r a f
P a s t r e a z a t a t a l f i e c a r u i nod i n a r b o r e l e de a c o p e r i r e
i n a dincime g e n e r a t de metoda DFS .
prenum [ k ] numer o ta r ea i n p r e o r d i n e
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
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 );
}
void main ( void ) {
p r i n t f ( \n ) ;
readInput ( ) ;
critic ();
}
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 reprezint
a valoarea sa la numerotarea n preordine, prenum, iar numarul din stanga reprezint
a valoarea calculat
a low). Conditia prenumu <
lowv , unde [u, v] este o muchie a grafului, este ndeplinit
a 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
2.7
Exercitii
1. Un graf neorientat cu n noduri, G = (V, E) se numeste graf scorpion daca poseda trei
noduri speciale:
(a) acul - dG (u) = 1 si este legat de coada;
(b) coada - dG (u) = 2 si este legata de ac si corp;
(c) corpul - dG (u) = n 2 fiind legat de toate nodurile din graf cu exceptia acului.
Nu exista alte restrictii cu privire la nodurile grafului.
Sa se realizeze un algoritm care determina daca un graf este graf scorpion folosind O(n)
ntrebari de forma Exista o muchie ntre nodurile u si v? .
2. Se considera n bazine dispuse circular si lipite unul de altul n ordinea numerelor de
ordine. Se da lista celor m perechi de bazine ce trebuie unite prin canale. Un numar
de bazine poate sa apara n mai multe perechi. Canalele pot fi realizate n interiorul
sau n exteriorul cercului, cu conditia ca ele sa nu se intersecteze.
Daca problema are solutie, vor fi afisate doua liste: cea a perechilor de bazine unite
prin canale interioare si cea a perechilor de bazine unite prin canale exterioare. Daca
problema nu are solutie, atunci se va raporta acest lucru.
In cazul n care problema are solutie sa se determine toate posibilitatile de unire prin
canale exterioare sau interioare.
(ONI, 1993)
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
Grafuri euleriene si hamiltoniene
3.1
Grafuri Euleriene
In anul 1736, matematicianul Leonhard Euler publica o lucrare asupra problemei podurilor
din Konigsberg1 (a se vedea figura 3.1) n care se prezinta un studiu teoretic asupra lanturilor
si ciclurilor Euleriene.
58
Fig. 3.2: Doua figuri geometrice ce pot fi desenate folosind o singura linie
3.1.1
Pornind de la Teorema 3.1 construim un ciclu eulerian. Se considera drept punct de plecare
un varf oarecare al grafului. La fiecare pas se alege pentru varful curent u, un varf adiacent
59
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 eulerian.
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 .
Algoritm 24 Algoritm de determinare a unui ciclu eulerian ntrun graf conex
1: function EulerRec(G = (V, E))
2:
if (IsEulerian(G) = f alse) then
3:
return
4:
end if
5:
call F indCycle(G; C)
6:
if (E(G) \ E(C) = ) then
7:
return C
8:
end if
9:
G G E(C)
10:
call F indComponents(G ; k, G11 , G12 , . . . , G1k )
11:
for i 1, k do
12:
Ci1 EulerRec(G1i )
13:
end for
14:
call M ergeCycles(C, C11 , . . . , Ck1 ; C0 )
15:
return C0
16: end function
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
3.1.2
Algoritmul lui Rosenstiehl [51] construieste un lant L care la final va deveni un ciclu eulerian,
folosind pentru aceasta o stiva drept structura de date auxiliara.
Se porneste de la un nod oarecare. Se nainteaza atata timp cat este posibil: pentru
nodul curent u se cauta o muchie incidenta cu el, si care sa nu mai fi fost parcursa la unul
din pasii anteriori. Daca exista o astfel de muchie (e = [u, v]), atunci se salveaza pe stiva
nodul u, iar nodul v devine nodul curent. In momentul n care nodul curent u nu mai are
muchii nevizitate, se adauga lantului L, si se extrage de pe stiva nodul anterior (din care s-a
ajuns n u) (a se vedea algoritmul 25). Vectorul vizit are drept scop sa pastreze situatia
muchiilor: (
1 , daca e E a fost parcursa
vizite =
0 , daca e E nu a fost nca parcursa.
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:
S = [1, 2, 3, 4, 5, 3, 9], L = [1], u = 1.
La pasul urmator, se extrage valoarea 9 de pe stiv
a si astfel u devine 9. La sf
arsitul acestuia
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
6:
while (S 6= ) do
7:
Su
8:
while (e = [u, v] E) (vizite = 0)) do
9:
vizite 1
10:
Su
11:
uv
12:
end while
13:
Lu
14:
end while
15:
return L
16: end function
Se insereaz
a pe stiv
a nodul u
Se extrage din stiv
a nodul curent
Se marcheaz
a muchia e ca fiind utilizat
a
Se salveaz
a pe stiv
a nodul curent u
Nodul curent devine nodul v
Se adaug
a la lista L nodul curent
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:
P as 20 : S = [], L = [1, 9, 8, 10, 6, 7, 8, 2, 10, 4, 6, 5, 7, 9, 3, 5, 4, 3, 2, 1], u = 1.
Ciclul eulerian obtinut se afla n lista L.
In continuare se prezinta implementarea n limbajul C++ a algoritmului lui Rosenstiehl :
Listing 3.1: rosenstiehl.cpp
#include
#include
#include
#include
#include
#include
#include
<v e c t o r >
<s t a c k >
<l i s t >
<i o s t r e a m>
<f s t r e a m>
<iomanip>
<c o n i o . h>
u s i n g namespace s t d ;
#define INPUT FILE g r a f 1 . t x t
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
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 ) ;
}
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 existente n limbajul C++, mai exact structuri de date implementate cu ajutorul template-urilor
din cadrul librariei Standard Template Library - STL2 3 :
stiva - stack 4. Am folosit o stiva de numere ntregi: stack<int> s;.
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>();.
vector - vector 6 . Matricea de adiacenta am implementat-o sub forma unui vector de
vectori cu elemente numere ntregi:
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 ;
...
M a t r i c e ma ;
ma = M a t r i c e ( n , L i n i e ( n , 0 ) ) ;
Dupa cum se poate observa din programul anterior, pentru reprezentarea interna a
grafului s-a folosit matricea de adiacent
a.
Parcurgerea elementelor listei se realizeaza cu ajutorul unui iterator - iterator 7 . Declararea
unui obiect de tip list<int>::iterator se poate face astfel: list<int>::iterator
it;.
2
http://en.wikipedia.org/wiki/Standard_Template_Library,
http://www.sgi.com/tech/stl/
4
http://www.sgi.com/tech/stl/stack.html
5
http://www.sgi.com/tech/stl/List.html
6
http://www.sgi.com/tech/stl/Vector.html
7
http://www.sgi.com/tech/stl/Iterators.html
3
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].
3.1.3
Un alt algoritm pentru determinarea unui ciclu eulerian este algoritmul lui Fleury.
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 .
Pasul k
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Pasul k
20
21
22
23
24
25
26
Muchia aleas
a
[u1 u4 ]
[u4 u10 ]
[u10 u3 ]
[u3 u2 ]
[u2 u6 ]
[u6 u1 ]
[u1 u5 ]
[u5 u6 ]
[u6 u7 ]
[u7 u2 ]
[u2 u8 ]
[u8 u3 ]
[u3 u9 ]
[u9 u8 ]
[u8 u7 ]
[u7 u13 ]
[u13 u5 ]
[u5 u12 ]
[u12 u4 ]
Muchia aleas
a
[u4 u11 ]
[u11 u10 ]
[u10 u9 ]
[u9 u13 ]
[u13 u11 ]
[u11 u12 ]
[u12 u1 ]
Lk
L1 = [u1 , [u1 u4 ], u4 ]
L2 = [u1 , [u1 u4 ], u4 , [u4 u10 ], u10 ]
L3 = [u1 , [u1 u4 ], u4 , [u4 u10 ], u10 , [u10 u3 ], u3 ]
L4 = [. . . , [u10 u3 ], u3 , [u3 , u2 ], u2 ]
L5 = [. . . , u2 , [u2 u6 ], u6 ]
L6 = [. . . , u6 , [u6 u1 ], u1 ]
L7 = [. . . , u1 , [u1 u5 ], u5 ]
L8 = [. . . , u5 , [u5 u6 ], u6 ]
L9 = [. . . , u6 , [u6 u7 ], u7 ]
L10 = [. . . , u7 , [u7 u2 ], u2 ]
L11 = [. . . , u2 , [u2 u8 ], u8 ]
L12 = [. . . , u8 , [u8 u3 ], u3 ]
L13 = [. . . , u3 , [u3 u9 ], u9 ]
L14 = [. . . , u9 , [u9 u8 ], u8 ]
L15 = [. . . , u8 , [u8 u7 ], u7 ]
L16 = [. . . , u7 , [u7 u13 ], u13 ]
L17 = [. . . , u13 , [u13 u5 ], u5 ]
L18 = [. . . , u5 , [u5 u12 ], u12 ]
L19 = [. . . , u12 , [u12 u4 ], u4 ]
Lk
L20 = [. . . , u4 , [u4 u11 ], u11 ]
L21 = [. . . , u11 , [u11 u10 ], u10 ]
L22 = [. . . , u10 , [u10 u9 ], u9 ]
L23 = [. . . , u9 , [u9 u13 ], u13 ]
L24 = [. . . , u13 , [u13 u11 ], u11 ]
L25 = [. . . , u11 , [u11 u12 ], u12 ]
L26 = [. . . , u12 , [u12 u1 ], u1 ]
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.
Lema 3.8 Fie G = (V, E) un graf neorientat si fie u si v dou
a v
arfuri neadiacente ale grafului
(u, v V , u 6= v, [u, v]
/ E), astfel nc
at dG (u) + dG (v) n. Atunci G este hamiltonian
G + [u, v] este 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 hamiltonian 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 hamiltonian 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.
Prin urmare lema este demonstrata.
Teorema 3.9 (Dirac, 1952) Un graf G = (V, E) (|V | 3) este hamiltonian daca u V
at jum
atate din numarul de
avem dG (u) n2 (orice varf al grafului are gradul mai mare dec
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 dou
a v
arfuri distincte,
neadiacente, ale grafului suma gradelor lor este mai mare dec
at num
arul de v
arfuri din graf ).
Teorema 3.11 (Chvatal, 1972) Fie un graf G = (V, E) (|V | 3) si d1 , d2 , . . . , dn o
secventa grafica. Daca este satisfacut
a relatia
k a.i. dk k
n
dnk n k
2
68
2 3
4 4
4 5 6
4 2 2
2 3
4 3
4 5 6
4 4 5
7 8
3 4
1
3
3 7 2
3 3 4
4 5
4 4
8 6
4 5
3.2.1
n
dnk n k.
2
Problema comisvoiajorului
, daca xi = xj
0
ai,j = +
(3.1)
, daca [xi , xj ]
/E.
(3.2)
0 14 6 5
14 0 12 16
6 0
12 0 21
A=
5 16 21 0
20 12 16
24
12 10
20
12
16
0
14
6
24
14
0
10
12
10
10
0
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
// numarul maxim de n o d u r i d i n g r a f
72
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 ;
cost optim = cost ;
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 ) ;
}
/
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 ) ;
}
14
0
-1
12
16
20
-1
-1
6
-1
0
-1
-1
12
-1
12
-1
12
-1
0
21
-1
24
10
5
16
-1
21
0
16
-1
-1
-1
20
12
-1
16
0
14
6
-1
-1
-1
24
-1
14
0
10
-1
-1
12
10
-1
6
10
0
http://en.wikipedia.org/wiki/Arithmetic_overflow
75
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 ] ] ;
}
}
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
Arbori. Arbori binari
Definitia 4.1 Fiind data o multime M de elemente denumite noduri, vom numi arbore o
submultime finita de noduri astfel nc
at:
1. exista un nod cu destinatie speciala, numit r
ad
acina arborelui;
2. celelalte noduri sunt repartizate n n multimi distincte, disjuncte dou
a c
ate dou
a, A1 , A2 , . . . , An ,
fiecare multime Ai constituind la randul ei un arbore.
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:
1. G este un arbore liber.
2. Oricare doua varfuri din G sunt conectate printr-un drum elementar unic.
3. G este conex, dar, daca eliminam o muchie oarecare din E, graful obtinut nu mai este conex.
4. G este conex, si |E| = |V | 1.
6. G este aciclic, dar daca adaugam o muchie oarecare n E, graful obtinut contine un ciclu.
Daca se alege un varf sau nod drept r
ad
acina 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
10
12
11
13
14
15
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 ad
ancimea 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 p
adure.
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.
4.1
Arbori binari
81
10
12
11
4.1.1
Moduri de reprezentare
5
4
1. expresii cu paranteze expresia ncepe cu radacina si dupa fiecare varf k urmeaza expresiile 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
Stang
Drept
1 2
2 3
8 5
3 4
0 0
4 0
5 6 7
6 0 0
7 0 0
8 9
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:
typedef struct nod {
TipOarecare data;
struct nod* stang;
struct nod* drept;
}Nod;
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.
3. reprezentarea tip tata fiecarui varf k i se indica tatal nodului respectiv.
(
i , daca nodul i este parintele nodului k
tatak =
0 , nu exista
Nod
Tata
1 2
0 1
3 4 5
2 3 2
6 7
5 5
8 9
1 8
(
2i
T atai =
nu exista
(
2i
Stangi =
nu exista
4.1.2
, i2
, i=1
, 2in
, 2i>n
(
2i+1
, 2i+1 n
Drepti =
nu exist
a , 2i+1>n
Metode de parcurgere
Exista mai multe moduri de parcurgere a unui arbore binar. Indiferent de metoda de parcurgere aleasa se parcurge mai ntai subarborele stang si apoi subarborele drept. Dupa momentul
n care un nod k este vizitat fata de subarborii sai stang si drept, avem:
parcurgere n preordine (radacin
a, subarbore st
ang, subarbore drept).
In urma parcurgerii n preordine a arborelui din figura 4.3 rezulta urmatoarea ordine
pentru noduri: 1, 2, 3, 4, 5, 6, 7, 8, 9.
In figura 4.6 este prezentata parcurgerea n preordine a unui arbore binar, descrisa n
mai multi pasi, conform descrierii recursive a acestei metode.
Vom da exemplu de o procedura nerecursiva de vizitare n preordine (vezi algoritmul
28). Se pleaca de la radacina si se merge spre stanga atata timp cat este posibil (liniile
58). Daca nu se mai poate merge spre stanga se ncearca continuarea algorimului
din descendentul drept, daca este posibil. Daca nu se poate face un pas spre dreapta
(descendentul drept nu exista linia 10), se va urca n arbore cate un nivel (liniile
1222), pana cand se ajunge n situatia de a se veni din descendentul stang (linia 19)
si se reia algoritmul trecand n descendentul drept, daca exista (linia 25). In momentul
n care sa ajuns n nodul radacina venind din dreapta, algoritmul se ncheie. Datorita
faptului ca atunci cand se urca n arbore trebuie sa stim din ce descendent venim, vom
utiliza si vectorul tata.
Aceasta procedura poate fi utilizata si pentru celelalte moduri de parcurgere, modificarile
necesare referinduse la momentul cand trebuie vizitat nodul curent (prin apelul procedurii V izit - instructiunea call V izit(k)).
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.
85
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);
}
Metoda de parcurgere n inordine este ilustrata de algoritmul 29 [93], [123]. Vom utiliza
o stiva S unde se vor introduce nodurile din care se coboara spre stanga (liniile 69).
Atunci cand nu se mai poate merge spre stanga se ncearca continuarea algoritmului din
descendentul drept al nodului curent. Daca acesta nu exista, se reface drumul napoi
spre radacina, compus numai din descendentii stangi (liniile 1118). Parcurgerea se
termina n momentul n care stiva este vida (vezi algoritmul 29).
Implementarea n limbajul C a algoritmului 29 este:
#include <stdio.h>
#define MAX 100
/** stang[k] - descendentul stang al nodului k; 0 daca nu exista */
char stang[MAX];
/** drept[k] - descendentul drept al nodului k */
char drept[MAX];
/** Numarul de noduri din arbore */
int n;
/** Nodul radacina */
int rad;
void vizit(int nod) {
printf("%2d ", nod);
}
void readInput(void) {
87
S.push(k)
verificare dac
a stiva este vid
a
S.pop(k)
int k;
printf("n = "); scanf("%d", &n);
printf("rad = "); scanf("%d", &rad);
for (k = 1; k <= n; k++) {
printf("stang[%d] = ", k); scanf("%d", &stang[k]);
}
for (k = 1; k <= n; k++) {
printf("drept[%d] = ", k); scanf("%d", &drept[k]);
}
}
void inord(int rad) {
int i, cap;
int q;
int stack[MAX];
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
5
6
4.2
Arbori binari de c
autare
90
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;
3. nodul ce urmeaza a fi sters (notat A) are doi descendenti:
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
<stdio.h>
<string.h>
<stdlib.h>
<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
char *cuvint;
struct nod *left,*right;
} NOD;
NOD *rad;
NOD* Search(NOD *p, char *sir) {
int ind;
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;
}
NOD* CreareNod(char *sir) {
NOD *p;
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;
}
NOD* Insert(NOD *p, char *sir) {
int ind;
95
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;
}
void VisitInord(NOD *p) {
if (p != NULL) {
VisitInord(p->left);
printf("[%s] ", p->cuvint);
96
VisitInord(p->right);
}
}
NOD* LeftmostNod(NOD* parent, NOD* curent) {
while (curent->left != NULL) {
parent = curent;
curent = curent->left;
}
if (parent->right == curent)
parent->right = curent->right;
else
parent ->left = curent->right;
return curent;
}
NOD* ElibNod(NOD *p) {
free(p->cuvint);
free(p);
return NULL;
}
NOD* DeleteNod(NOD* p, char *sir) {
NOD *tmp;
int ind;
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
1.
Exercitii
(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.
Iesire
((XX)(XX))
((XX)((XX)X))
(XX)
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
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
4
2
1
2
abroad in_strainatate
baker brutar
calf gamba
dice zaruri
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 singura 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.
12. Se considera o expresie matematica reprezentata printr-un arbore binar. Operatorii
corespund operatiilor matematice uzuale +, , , /, si ? (pentru ridicare la putere).
Nu avem paranteze si toti operatorii sunt operatori binari. Operanzii sunt identificati
printro singura litera.
Se cere sa se realizeze un algoritm ce creaza structura de date corespunzatoare unei
expresii date si sa evalueaze expresia pentru o multime de valori ale operanzilor.
Pentru expresia a + b c unde a = 2, b = 3 si c = 1 avem valoarea 1, iar n urma
evaluarii expresiei a b/c?a cu a = 2, b = 9 si c = 3 obtinem valoarea 1.
101
Capitolul 5
Arbori oarecare
Reamintim definitia unui arbore oarecare:
Definitia 5.1 Fiind data o multime M de elemente denumite noduri, vom numi arbore o
submultime finita de noduri astfel nc
at:
1. exista un nod cu destinatie speciala, numit r
ad
acina arborelui;
2. celelalte noduri sunt repartizate n n multimi disjuncte dou
a c
ate dou
a, A1 , A2 , , An , fiecare
multime Ai constituind la randul ei un arbore.
5.1
Moduri de reprezentare
10
3 4
5 7
4 0
5 6 7
0 0 0
6 0 8
102
8 9 10
0 0 0
9 10 0
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
NULL
NULL
5
NULL
6
NULL
NULL
NULL
8
NULL
NULL
10
NULL
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.
Listele de descendenti se pastreaza prin intermediul unei matrice L cu 2 linii si N 1
coloane:
L1,k - un descendent al varfului a carui lista contine coloana k a matricei date.
(
0 , daca descendentul respectiv este ultimul
L2,k =
j , j indica coloana unde se afla urmatorul descendent
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
C
1 0 4
2 3 4
L=
2 3 0
4
6
5
0
5 6
5 0
6
0
7
7
7
0
8
8
8
0
9
9
9 10
0 0
10
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
#define NMAX 100
typedef struct nod {
TipOarecare data;
struct nod* children[NMAX];
}Nod;
sau
typedef struct nod {
TipOarecare data;
int no_of_children;
struct nod** children;
}Nod;
10
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
1 2 3
0 1 1
4 5
1 3
6 7 8
3 4 4
9 10
4 4
rad
data;
fiu;
tata;
frate;
NULL
1
NULL
NULL
2
NULL
NULL
5
NULL
NULL
NULL
10
NULL
NULL
NULL
NULL
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.
5.2
Metode de parcurgere
Pentru parcurgerea unui arbore oarecare se pot folosi metodele generale pentru parcurgerea arborelui binar asociat. Arborele binar asociat unui arbore oarecare se obtine n urma
realizarii corespondentei F iu Stang si F rate Drept.
In plus fata de acestea, avem si doua metode de parcurgere pe care putem sa le numim
specifice unui arbore oarecare. Acestea se pot clasifica dupa momentul n care se realizeaza
vizitarea nodului parinte astfel:
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.
Algoritm 35 Algoritm de parcurgere n A-preordine
1: procedure APreordine(Rad, F iu, F rate)
2:
k rad
3:
q0
4:
S
5:
while (q = 0) do
6:
if (k 6= 0) then
7:
call V izit(k)
8:
Sk
9:
k f iuk
else
10:
11:
if (S = ) then
12:
q1
13:
else
14:
Sk
15:
k f ratek
16:
end if
17:
end if
18:
end while
19:
return
20: end procedure
S.push(k)
verificare dac
a stiva este vid
a
S.pop(k)
106
/**
* Definitii de tipuri pentru reprezentarea arborelui
*/
typedef struct cnod {
TINFO info; //informatia memorata in nod
struct cnod *next; //adresa urmatorului element
}CNOD;
CNOD *prim = NULL, *ultim = NULL;
NOD* citListaDescendenti(NOD *up);
/**
* 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;
curent = (CNOD *) malloc(sizeof(CNOD));
curent->info = n;
curent->next = NULL;
if (!prim) {
prim = ultim = curent;
} else {
ultim->next = curent;
ultim = 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
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;
printf("%d ", p->id);
desc = p->down;
while (desc != NULL) {
aPreordine(desc);
desc = desc->next;
}
}
/**
* 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
T {e} \ {f }.
Deoarece c(e) < c(f ) vom avea ca c(T ) = c(T ) + c(e) c(f ) c(T ) < c(T ) adica am
| {z }
<0
obtinut un arbore de acoperire T ce are costul mai mic decat T , contradictie cu faptul ca
T este un arbore de acoperire de cost minim.
Definitia 5.2 Se numeste t
aietur
a a grafului G o partitie de dou
a submultimi a multimii
nodurilor V , notata astfel: < S, T > (unde S T = V si S T = ).
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.
singur nod
L
while (|P| > 1) do
for T P do
alege e muchia de cost minim de la T la G \ T
L e adaug
a muchia e la lista de muchii alese ce vor forma arborele de acoperire
de cost minim
end for
adaug
a toate muchiile selectate n cadrul f or-ului anterior la P
end while
end procedure
111
Algoritmul lui Prim implementat simplu are o complexitate O(n2 )[30] si atinge o complexitate 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)).
Table 5.4: Algoritmi pentru determinarea arborelui de acoperire minim
Anul
1975
1976
1984
1986
1997
2000
2002
Complexitate
E log log V
E log log V
E log V, E + V log V
E log log V
E(V ) log (V )
E(V )
optimal
Autori
Yao
Cheriton-Tarjan
Friedman-Tarjan
Gabow-Galil-Spencer-Tarjan
Chazelle
Chazelle [26]
Pettie-Ramachandran [105]
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:
, daca i = j
0
ci,j =
, daca (i, j)
/E
5.3.1
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).
Algoritm 39 Algoritmul lui Boruvka
1: procedure Boruvka2(G, C, n; L)
2:
for i 1, n do
3:
Vi {i}
4:
end for
5:
L , M {V1 , . . . , Vn }
6:
while (|T | < n 1) do
7:
for U M do
8:
fie (u, v) muchia pentru care se obtine valoarea minim
a min{c(u , v )|(u , v ) E, u
9:
10:
11:
12:
13:
14:
15:
16:
/ V \ U}
U, v
determin
a componenta U ce contine pe v
L (u, v)
end for
for U M do
reuneste multimile ce contin pe u si v, U si U
end for
end while
end procedure
(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.
5.3.2
Algoritmul a fost descoperit mai ntai de V. Jarnik (1930) [76], si apoi independent de Prim
(1957) [106] si Djikstra (1959) [38].
Se porneste cu o multime S formata dintr-un singur nod (S = {v0 }). La fiecare pas se
alege muchia de cost minim ce are numai o extremitate n multimea S. Procesul se ncheie
dupa n 1 pasi, rezultand un graf partial aciclic. Din teorema 4.1 rezulta faptul ca acest
graf partial aciclic cu n 1 muchii este un arbore (de acoperire).
Conform Proprietatii taieturii, toate muchiile alese apartin arborelui partial de cost minim
de unde rezulta ca acest arbore de acoperire este un arbore partial minim.
Vom utiliza trei vectori de dimensiune n, unde n reprezinta numarul de varfuri al grafului
(vezi algoritmul 40):
113
(
1 , daca nodul k S
vizitatk =
0 , daca nodul k V \ S
d - pentru un nod k
/ S, dk va contine distanta minima de la k la un nod j S. La
nceput, dj = cv0 ,j . Pentru un nod k ales la un moment dat, dj (j S) se modifica
numai daca ck,j < dj astfel dj = ck,j .
tata - contine pentru fiecare nod k
/ S nodul j S astfel ncat ck,j = min{ck,i|i S}.
La nceput,
(
0 , daca nodul k = v0
tatak =
v0 , daca nodul k 6= v0
In momentul n care se modifica dj , se va modifica si valoarea lui tataj = k.
114
= 1. La nceput,
4 5 6
7
5
1 1 1
1
0 0 0
0
dup
a etapa de initializare avem:
8
1
0
115
al ciclului, select
am muchia (1, 5).
3 4 5 6
7 8
6 21 5 16
1 5 1 5
1 1
0 0 1 0
0 0
La pasul al doilea se
1 2 3
d
14 6
tata 0 1 1
vizitat 1 0 1
este
4
10
8
0
8:
5 6
5 12
1 3
1 1
7
10
8
0
8
6
6
1
distant
a
6
7
12 10
3
8
1
1
minim
a este 7:
8
6
6
1
4:
6
12
3
1
8
6
6
1
7
10
8
1
5.3.3
procedure Init(x, u)
cx u
end procedure
function Find(x)
return cx
end function
function Merge(x, y)
setx cx
sety cy
for k 1, n do
if (ck = sety) then
ck setx
end if
end for
return setx
end function
1
2
3
A a b e
C 1
2
3
4
5
x y
1
1
6
7
z u
2
2
8
v
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.
Reprezentarea folosind liste nl
antuite
In cadrul acestei metode fiecare multime este reprezentata printro lista simplu nlantuita,
elementul reprezentant fiind elementul aflat n capul listei. Un nod al listei va avea un camp
ce pastreaza informatia cu privire la un element al unei multimi, si un camp ce contine
legatura catre urmatorul nod al listei.
Vom folosi un vector de adrese (List), ce contine adresa primului element din fiecare lista
(Listi ).
Procedura Init aloca un nod si initializeaza campurile acestuia. Adresa nodului alocat
este pastrata n Listk (vezi algoritmul 43).
Functia F ind cauta un element printre elementele pastrate de fiecare lista n parte. Se
parcurge vectorul List si se obtine reprezentantul fiecarei submultimi pastrate. Se parcurge
lista (cautare liniara) si se cauta un element cu valoarea x. Daca elementul este gasit atunci
se ntoarce capul listei n care a fost gasit. Daca pana la sfarsit nu a fost identificat elementul
x, atunci functia returneaza valoarea NULL.
Functia Merge reuneste doua liste: practic se adauga o lista la sfarsitul celeilalte. Pentru
a realiza aceasta operatie pe langa adresa primului element avem nevoie de adresa ultimului
element. Acest lucru se poate obtine indirect prin parcurgerea listei de la primul la ultimul
element, fie direct daca n momentul crearii pastram pentru fiecare lista nca o variabila ce
contine adresa ultimului element (last).
118
procedure Init(x, k)
Listk new node
Listk .data x
Listk .next N U LL
end procedure
function Find(x)
for i 1, m do
p Listi , reprezentant p
while (p 6= N U LL) (p.data 6= x) do
p p.next
end while
if (p 6= N U LL) then
return reprezentant
end if
end for
return N U LL
end function
function Merge(x, y)
capx F ind(x)
capy F ind(y)
curent capx
while (curent.next 6= N U LL) do
curent curent.next
end while
curent.next capy
return capx
end function
aloc
a spatiu pentru un nod al listei
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
procedure Init(x)
tatax x
end procedure
function Find(x)
while (x 6= tatax ) do
x tatax
end while
return x
end function
function Merge(x, y)
radx F ind(x)
rady F ind(y)
tatarady radx
return radx
end function
1:
2:
3:
4:
5:
6:
7:
8:
9:
1: function Merge(x, y)
2:
radx F ind(x)
3:
rady F ind(y)
4:
size |tataradx | + |tatarady |
5:
if (|tataradx | > |tatarady |) then
6:
tatarady radx
7:
tataradx size
8:
return radx
9:
else
10:
tataradx rady
11:
tatarady size
12:
return rady
13:
end if
14: end function
procedure Init(x)
tatax 1
end procedure
function Find(x)
while (tatax > 0) do
x tatax
end while
return x
end function
120
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
1: function Merge(x, y)
2:
radx F ind(x)
3:
rady F ind(y)
4:
if (hradx > hrady ) then
5:
tatarady radx
6:
return radx
7:
else
8:
tataradx rady
9:
if (hradx = hrady ) then
10:
hrady hrady + 1
11:
end if
12:
return rady
13:
end if
14: end function
procedure Init(x)
tatax x
hx 0
end procedure
function Find(x)
while (tatax 6= x) do
x tatax
end while
return x
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
end while
5:
6:
rad y
7:
yx
8:
while (tatay =
6 y) do
9:
y tatay
tatax rad
10:
11:
xy
12:
end while
13:
return rad
14: end function
5.3.4
Algoritmul lui Kruskal [88] este o ilustrare foarte buna a metodei generale Greedy (vezi
algoritmul 45). La nceput se pleaca cu o padure de arbori, fiecare arbore fiind alcatuit
dintrun singur nod. La fiecare pas se alege o muchie: daca cele doua extremitati (noduri)
fac parte din acelasi arbore atunci nu se va adauga muchia curenta la arborele partial respectiv
deoarece ar conduce la un ciclu, ceea ce ar strica proprietatea de graf aciclic. Altfel, daca cele
doua noduri fac parte din arbori partiali distincti, se adauga muchia curenta la multimea de
muchii alese anterior, iar cei doi arbori partiali devin unul singur. La fiecare pas, numarul
de arbori partiali scade cu 1. Dupa n 1 alegeri padurea initiala compusa din n arbori sa
transformat ntrun singur arbore.
Pentru a pastra muchiile grafului vom utiliza o matrice A cu m coloane si 3 linii: primele
doua linii contin extremitatile muchiilor, iar linia a 3-a costul muchiei respective. Pentru a se
aplica o strategie Greedy, datele de intrare muchiile se vor ordona crescator n functie de
costul asociat. Deoarece graful partial respectiv este un arbore cu n noduri el va avea n 1
muchii.
121
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 r
aspunsul 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
de cost minim, L = {(1, 5), (1, 3), (6, 8), (4, 8)}. In urma reuniunii arborilor corespunzatori
celor doua noduri se obtine configuratia din figura 5.7 (4).
123
Subliniem faptul ca arborii reprezentati n figura 5.7 sunt diferiti de arborii ce conduc 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 c
a 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
atur
a 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 selectat
a pentru solutia problemei, L = {(1, 5), (1, 3), (6, 8),
(4, 8), (7, 8), (2, 4), (3, 6)}.
5.4
Exercitii
Iesire
DA
1 2 3 4
2 5 6
3 7
4
5
6
7
(Timisoara-pregatire, 1996)
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.
5. Se considera procesul de proiectare a unei placi electronice cu N componente (1 N
100). Pentru fiecare componenta electronica C se cunoaste numarul de interconexiuni.
Se considera graful determinat de multimea pinilor si multimea interconectarilor posibile ale tuturor componentelor, precum si lungimile lor. Dintre toate modalitatile de
interconectare posibile se cere cea corespunzatoare arborelui de acoperire minim (interconectarea pentru care suma tuturor circuitelor imprimate are lungimea minima).
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.
Exemplul 5.8 Pentru datele de intrare
5 7
1 2 40
0 0
2 3 35
1 5 27
1 4 37
4 5 30
2 4 58
3 4 60
Fig. 5.8: Descrierea conexiunilor posibile dintre componentele unei placi electronice
6. Realizati o subrutina nerecursiva care sa determine cheia minima dintrun arbore oarecare.
7. Determinati naltimea unui arbore oarecare printro functie nerecursiva.
8. Pentru asigurarea securitatii activitatilor dintrun combinat chimic sa apelat la o companie de pompieri. Desfasurarea activitatilor companiei presupune stabilirea locurilor
de instalare a comandamentului precum si a posturilor de supraveghere. Pentru aceasta
sunt disponibile n cadrul combinatului n puncte de control. Pentru fiecare pereche de
puncte de control se cunoaste daca exista o legatura directa ntre ele si, n caz afirmativ,
distanta dintre ele. Se cunoaste, de asemenea, faptul ca ntre oricare doua puncte de
control exista un drum direct sau indirect.
Odata stabilite amplasamentele comandamentului si ale punctelor de supraveghere n
cate unul dintre cele n puncte de control este posibil sa se ajunga de la comandament
125
126
Capitolul 6
Grafuri orientate
6.1
Notiuni de baz
a
127
128
Fig. 6.2: Arbore de acoperire n latime pentru graful orientat din figura 6.1
6.2
Parcurgerea grafurilor
In urma vizitarii unui graf neorientat, de obicei nu rezulta numai un arbore de acoperire ci o
padure de arbori de acoperire.
In urma unei parcurgeri n latime a unui graf orientat, se ntalnesc urmatoarele tipuri de
arce:
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
u - v
arful de unde se porneste vizitarea
G - graful
V izitat {u}
N eexplorat V izitat
while (N eexplorat 6= ) do
extrage un nod x din N eexplorat
determin
a y, urm
atorul vecin al lui x ce nu a fost vizitat
if (y = N U LL) then
elimin
a x din N eexplorat
else
if (y
/ V izitat) then
V izitat V izitat {y}
N eexplorat N eexplorat {y}
end if
end if
end while
end procedure
Input:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
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:
1. arc al arborelui de acoperire: prenumu < prenumv si postnumu > postnumv ;
2. arc de naintare: prenumu < prenumv si postnumu > postnumv ;
3. arc de ntoarcere: prenumu > prenumv si postnumu < postnumv ;
4. arc de traversare: prenumu > prenumv si postnumu > postnumv .
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 traversare - (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).
Algoritm 47 Algoritm de vizitare n adancime pentru un graf orientat
1: procedure DFSNum(k, n, V ecin)
exista un drum de la u la v n G.
3 4
4 8
15 9
5
6 7 8
5
6 7 10
14 13 12 11
6.3
Sortarea topologic
a
132
n
- num
arul de noduri din graf
V ecin - vector ce contine listele cu vecini ai fiec
arui nod
for k 1, n do
dminusk 0
end for
for (u, v) E do
dminusv = dminusv + 1
end for
Q
se initializeaz
a coada
for k 1, n do
if (dminusk = 0) then
Qk
se insereaz
a ntro coad
a nodurile cu gradul interior 0
end if
end for
while (Q 6= ) do
Qk
se extrage din coad
a un nod
Lk
se insereaz
a nodul ntr-o list
a
w V ecink
v ia valoarea capului listei de vecini a nodului k
while (w 6= N U LL) do
dminusw.nodeIndex dminusw.nodeIndex 1
if (dminusw.nodeIndex = 0) then
Q w.nodeIndex
se insereaz
a n coad
a nodul w.nodeIndex
end if
w w.next
se trece la urm
atorul vecin
end while
end while
end procedure
Input:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
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
Fig. 6.5: Sortare topologica cu algoritmul 48 pentru graful orientat din figura 6.4
n
- num
arul de noduri din graf
V ecin - matricea de adiacent
a a grafului
for k 1, n do
prenumk 0, postnumk 0
vizitatk 0
end for
for k 1, n do
if (vizitatk = 0) then
call DF SN um(k, n, V ecin)
end if
end for
se ordoneaz
a descresc
ator nodurile dup
a postnumk
end procedure
Input:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
2
0
0
0
3
0
0
0
4
0
0
0
5
0
0
0
6
0
0
0
7
0
0
0
3 4 5
0 3 4
0 10 9
0 1 1
6
6
7
1
7
5
8
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
prenum 1 2
postnum 12 11
vizitat
1 1
3 4 5
13 3 4
14 10 9
1 1 1
6
6
7
1
7
5
8
1
135
postnum 14 12
3 1
11 10 9
2 4 5
8 7
7 6
6.4
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 maximala 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 componentele tare conexe.
Pentru determinarea componentelor tare conexe exista mai multi algoritmi, dintre care
amintim algoritmul lui Tarjan, algoritmul lui Kosaraju si algoritmul lui Gabow.
6.4.1
Algoritmul lui Kosaraju-Sharir a fost prezentat de Aho, Hopcroft si Ullman n lucrarea lor [2],
fiind preluat dintr-un manuscris al lui S. Rao Kosaraju (M. Sharir l-a prezentat n lucrarea
[110]).
Algoritmul foloseste graful transpus GT asociat grafului initial G ([11], [23], [30]), fiind
compus din urmatorii pasi:
Pas 1. Se realizeaza parcugerea grafului cu algoritmul de vizitare n adancime, pornind de la
un nod arbitrar. In timpul vizitarii, se realizeaza numerotarea n postordine a nodurilor
n cadrul vectorului postnum.
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.
Fiecare arbore rezultat n urma parcurgerii de la Pasul 3 constituie o component
a tare conexa
a grafului G.
In continuare se prezinta implementarea n limbajul C a algoritmului anterior:
Listing 6.1: kosaraju.c
#include <s t d i o . h>
#include <mem. h>
#define MAX 100
#define TRUE 1
#define FALSE 0
char v e c i n [MAX] [MAX] ;
char v i z i t a t [MAX] ;
i nt n ;
// M a t r i c e a de a d i a c e n t a
// Vecto 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 .
// Numarul de v a r f u r i d i n g r a f
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 ) ;
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 ) ;
2 3
7 5
1 1
4 5 6
6 1 2
1 1 1
7 8
3 4
1 1
6.4.2
Algoritmul lui Tarjan [117] este considerat drept o mbunatatire a algoritmului lui Kosaraju
prin aceea ca nu mai este nevoie de parcurgerea grafului G de doua ori.
139
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).
Exemplul 6.18 La nceput valorile vectorilor vizitat si prenum sunt urm
atoarele:
1
prenum 0
vizitat 0
2 3 4
0 0 0
0 0 0
5 6
0 0
0 0
7 8
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
prenum 1
vizitat 1
low
1
2
2
1
2
3
3
1
0
4
0
0
0
5
5
1
4
6
4
1
4
7
0
0
0
8
0
0
0
n
- num
arul de noduri din graf
V ecin - matricea de adiacent
a
for i 1, n do
vizitati 0
end for
counter 0
S
for i 1, n do
if (vizitati = 0) then
call DF ST arjan(i, n, V ecin)
end if
end for
end procedure
procedure DFSTarjan(k, n, V ecin)
Sk
vizitatk 1
counter counter + 1
prenumk counter, lowk counter
for i 1, n do
if (vecink,i = 1) then
if (vizitati = 0) then
call DF ST arjan(i, n, V ecin)
lowk M in(lowk , lowi )
else
if (i S) then
lowk M in(lowk , prenumi )
end if
end if
end if
end for
if (lowk = prenumk ) then
repeat
Su
Output u
until (u = k)
end if
end procedure
Input:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
vizitarea n ad
ancime a grafului
141
2
2
1
2
3
3
1
0
4
0
0
0
5
5
1
4
6
4
1
4
7
7
1
6
8
6
1
6
6.4.3
2
2
1
2
3
3
1
2
4
8
1
3
5
5
1
4
6
4
1
4
7
7
1
6
8
6
1
6
Algoritmul a fost introdus de catre Joseph Cheriyan and Kurt Mehlhorn n 1996 [28] si apoi
independent de catre Harold Gabow n 1999 [56].
Algoritmul construieste un graf H [56] ce reprezinta o contractie a grafului original G: unul
sau mai multe noduri din G pot sa corespunda unui nod din H. La nceput, se initializeaza
H = G. Se ncepe construirea unui drum P alegandu-se un nod oarecare v H.
La fiecare pas al algoritmului se ncearca sa se augmenteze drumul P = P {w} =
[v1 , . . . , vk , w] prin parcurgerea tuturor arcelor (vk , w):
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 w P , fie w = vj . Contractam circuitul {vj , vj+1 , . . . , vk , w} n graful H: n
locul multimii de varfuri {vj , vj+1, . . . , vk } va ramane doar reprezentantul acesteia, e.g.
nodul ce are valoarea minima n urma numerotarii n preordine;
- 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
ad
acin
a 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
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
Iesire
127
(5) 1 5
Profesorul cere sa se demonstreze regula 1 5. Demonstratia optima consta n aplicarea directa a regulii (5).
(2)
(1)
(3)
(4)
(3)
(4)
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
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 structura prin refacerea proprietatii de heap;
3. Insert(H,p) - insereaza nodul p n heap-ul H;
4. Delete(H,p) - sterge nodul p din heap-ul H;
5. DecreaseKey(H,p,v) - modifica valoarea cheii nodului p din heap-ul H, atribuindu-i
valoarea v si reorganizeaza aceasta structura, pastrandu-i proprietatea de heap;
6. MergeHeaps(H1 , H2 , H) - unifica doua heap-uri, desemnate prin H1 si H2 , ntr-una
noua H, ce va contine elementele celor doua heap-uri.
Tabelul 7.1 ([30]) ilustreaza complexitatea-timp a acestor operatii pentru diferite tipuri
de implementari ale structurii de heap.
Structura de date de tip heap prezinta multiple aplicatii dintre care amintim algoritmul
de ordonare heapsort a unei secvente de elemente, diferiti algoritmi din teoria grafurilor
(algoritmi pentru determinarea drumurilor de cost minim) sau algoritmi de selectie (pentru
determinarea valorii minime, maxime, mediane dintr-un vector).
7.1
Definitia 7.1 Un min-heap este un arbore binar complet n care valoarea memorata n
orice nod al sau este mai mica sau egal
a dec
at valorile memorate n nodurile fii ai acestuia.
1
http://acs.lbl.gov/~aragon/treaps.html
149
Heap binar
(1)
(log n)
(log n)
(log n)
(log n)
(n)
Heap binomial
O(log n)
(log n)
O(log n)
(log n)
(log n)
O(log n)
Heap Fibonacci
(1)
O(log n)
(1)
O(log n)
(1)
(1)
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 structura de date secventiala:
Nod 1 2
cheie 3 5
3 4 5
8 7 8
6 7
8 11
8 9 10
10 18 9
150
Nod 1 2
Left 2 4
Right 3 5
Tata - 1
7.1.1
3
6
7
1
4
8
9
2
5 6 7
10 - - - 2 3 3
8 9
- - 4 4
10
5
10:
ai a i
adaug
a elementul cel nou pe ultima pozitie
interschimb
a valorile lui ai si a i
2i
11:
i
12:
end while
13: end procedure
151
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).
Algoritm 53 Algoritm de stergere a elementului minim dintr-un heap
1: procedure DeleteMin(A, last, N ; minim)
2:
if (last = 0) then
3:
Output { Heap vid! }
4:
return
5:
end if
6:
minim a1
7:
a1 alast
8:
last last 1
9:
i1
10:
while (i last
2 ) do
11:
if ((2 i = last) (a2i < a2i+1 )) then
12:
j 2i
13:
else
14:
j 2i+1
15:
end if
16:
if (ai > aj ) then
17:
ai aj
18:
ij
19:
else
20:
return
21:
end if
22:
end while
23: end procedure
In figura 7.3 pot fi urmariti pasii necesari pentru stergerea nodului de valoare minima din
heap-ul considerat drept exemplu.
152
7.1.3
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.
T (n) =
n
X
k=1
[log n]
TInsert (i)
X
i=1
[log n]
i
i 2 log n
153
X
i=1
2i = O(n log n)
(7.1)
Cea de-a doua metoda se bazeaza pe ideea de a construi un heap prin combinari (uniuni) 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).
Algoritm 55 Algoritm de creare a unui heap (a doua varianta)
1: procedure NewHeap2(A, n)
2:
for i n2 , 1 ST EP 1 do
3:
call P ush(A, i, n)
4:
end for
5: end procedure
7.2
h1
X
i=0
j=hi
2 (h i) =
h
X
j=1
j2
hj
h
X
h
X
j
j
=
2 j n
< 2n = O(n)
2
2j
j=1
j=1
h
(7.2)
Metoda de ordonare HeapSort [129] are la baza algoritmul general prezentat n subrutina
SortX (a se vedea algoritmul 57). Vom presupune ca elementele sirului initial se afla ntr-o
structura de date de tip lista L, iar rezultatul va fi obtinut n structura de date de tip heap
S. Initial structura S nu contine nici un element.
Subrutina Insert(x, S; S) va adauga elementul x structurii de date S. Subrutina
155
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] ;
void main ( void ) {
i nt n = r e a d I n p u t ( a ) ;
i nt i , j , aux ;
// 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
159
#endif
7.4
Exercitii
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 ) &&
c h i l d ++;
}
}
arr [ index ] = t ;
}
Ordonati crescator urmatoarea secventa de numere, pe baza functiei anterioare, utilizand numai o coala de hartie si un creion: 5, 100, 25, 20, 10, 5, 7, 80, 90, 1.
4. Sa se elaboreze un algoritm de timp O(n lg k) pentru a interclasa k liste ordonate,
unde n este numarul total de elemente din listele de intrare.
5. Adriana este o mare colectionara de timbre. In fiecare zi se duce la magazinul de pe
strada ei pentru a-si mari colectia. Intr-o zi, vanzatorul (nimeni altul decat Balaurul
Arhirel) s-a gandit sa-i faca o surpriza. A scos dintr-un dulap vechi niste timbre foarte
valoroase pe care erau scrise cu fir de aur si de argint numere naturale. Stiind ca fetita
nu are bani prea multi, Balaurul i-a spus urmatoarele: Eu pot s
a mpart timbrele n m
intervale de forma [1, . . . , mi ]. Tu poti s
a iei din orice interval o singur
a subsecventa
de maxim k elemente. Desigur, dac
a ai ales o subsecvent
a din intervalul i vei plati o
anumita suma . . .
Adriana s-a gandit ca ar fi frumos sa-si numeroteze toate cele n pagini ale clasorului
ei cu astfel de timbre. Fiind si o fetita pofticioasa si-a zis: Tare as vrea s
a mananc o
nghetata din banii pe care i am la mine, dar nu stiu dac
a o s
a-mi ajung
a sa platesc
timbrele. Cum sa fac?
Fiind cunoscute cele m intervale, precum si costurile acestora, ajutati-o pe Adriana sa
cumpere timbrele necesare numerotarii clasorului, platind o suma cat mai mica.
Date de intrare
Pe prima linie a fisierului timbre.in se afla n m k. n reprezinta numarul de pagini ale
clasorului, m reprezinta numarul de intervale, iar k lungimea maxima a unei subsecvente.
Pe urmatoarele m linii se afla doua numere separate printr-un spatiu, mi ci , unde mi
reprezinta marginea superioara a intervalului i, iar ci costul acestuia.
Date de iesire
Pe prima linie a fisierului timbre.out se va afla Smin, reprezentand suma minima pe
care trebuie sa o plateasca Adriana pentru a cumpara timbrele necesare numerotarii
clasorului.
Restrictii: 0 < N < 1001, 0 < M < 10001, 0 < K < 1001, 0 < mi < 100000,
0 < ci < 10000.
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
4 3 2
5 3
2 1
6 2
timbre.out
3
Luam subsecventa {1, 2} din al doilea interval si subsecventa {3, 4} din al treilea interval. 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.
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
..........
(preONI 2005, Barbar)
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 consumatorilor 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 autobuz 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
min c(s,t ).
s,t Ms,t
165
(8.2)
8.1
Drumul minim de la un v
arf la celelalte v
arfuri
8.1.1
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 .
Algoritm 59 Algoritmul lui Moore (prima versiune)
1: procedure Moore1(k, n, V ecin; D, T ata)
- nodul surs
a
k
Input:
n
- num
arul de noduri din graf
166
(2, 4)
(3, 6)
(2)
(1, 5)
(4, 6, 9)
(7, 8)
7: (8)
8: (6, 7)
9: ()
10: (11)
11: (10)
In urma aplicarii algoritmului lui Moore pentru acest graf si pentru nodul 1 drept sursa,
se obtin urmatoarele valori pentru vectorii D si Tata:
In urma aplicarii algoritmului lui Moore pentru acest graf si av
and nodul 1 drept sursa,
se obtin urmatoarele valori pentru vectorii d si tata:
D
Tata
1
0
2
1
1
3
2
2
4
1
1
5
2
4
6
2
2
7
3
6
8
3
6
9
3
5
10
11
a
k - nodul surs
Input:
n - num
arul de noduri din graf
C - matricea costurilor
2:
for i 1, n do
3:
di +
4:
end for
5:
dk 0
6:
Qk
7:
while (Q 6= ) do
8:
Qk
9:
for fiecare vecin (v = xi ) al lui xk do
10:
if (dk + ck,i < di ) then
11:
di dk + ck,i , tatai k
12:
Qi
13:
end if
14:
end for
15:
end while
16: end procedure
8.1.2
costurilor C:
ci,j
, daca i = j
0
=
, daca (i, j)
/ E, i 6= j
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 elementului dj astfel ncat dj = dk + ck,j , caz n care tataj = k.
Observatia 8.2 Algoritmul lui Dijkstra prezint
a asem
an
ari cu algoritmul de cautare n
latime, vectorul T ata pastrand la sfarsitul procesului de calcul al distantelor un arbore al
drumurilor minime ce are drept radacin
a nodul surs
a.
Demonstrarea corectitudinii algoritmului
Prin urmare
dw + cw,j dk + cw,j < dk + costxk
+ 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 ).
0
1
2
1
1
0
1
0
4
21
1
0
1
0
0
1
2
1
1
1
3
9
2
0
4
21
1
0
5
5
2
0
0
1
2
1
1
1
3
8
5
0
4
21
1
0
5
5
2
1
1
D
Tata
Vizitat
0
1
2
1
1
1
3
8
5
1
4
20
3
0
5
5
2
1
0
1
2
1
1
1
3
8
5
1
4
20
3
1
5
5
2
1
MAXN 100
TRUE 1
FALSE 0
MAXINT 30000
/
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
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 ) ;
}
(8.4)
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):
1. DeleteMin(Q) - sterge nodul ce contine cheia de valoare minima si reorganizeaza structura prin refacerea proprietatii de coad
a cu prioritate.
174
2. Arbore 2 3:
(8.5)
(8.6)
(8.7)
3. Heap-uri Fibonacci:
8.2
8.2.1
(k1)
= di,k ,
(k1)
= dk,j .
di,k
dk,j
(k)
Prin urmare
(k)
(k1)
(k1)
+ dk,j
(8.9)
, daca i = j
0
(0)
(8.10)
di,j = +
, daca (xi , xj )
/ E, i 6= j
(k1)
di,j = min{di,j
(k1)
, di,k
(k1)
+ 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 necesarului de memorie. Se observa ca, matricea D (k) nu mai este necesara de ndata ce matricea
D (k+1) a fost calculata.
(n)
(n)
(8.12)
(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)
, daca di,j di,k + dk,j
pi,j
(k)
(8.13)
pi,j =
(k1)
(k1)
(k1)
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
12 +
C = 3 + 0
+ + 9
0 +
+ + 3 + 0
Sirul de matrice D k , rezultat al aplic
arii algoritmului Roy-Floyd-Warshall pentru acest
graf, este:
178
procedure FloydWarshall3(n, C; D, P )
for i 1, n do
for j 1, n do
di,j ci,j
pi,j 0
end for
end for
for k 1, n do
for i 1, n do
for j 1, n do
if (di,j > di,k + dk,j ) then
di,j di,k + dk,j }
pi,j k
end if
end for
end for
end for
end procedure
procedure PrintDrum(i, j, P )
if (pi,j = 0) then
Output {(i,j)}
else
call P rintDrum(i, pi,j , P )
call P rintDrum(pi,j , j, P )
end if
end procedure
D0
D2
D4 =
0 1 21
0 8 4
3 0 12
9 0
3 0
0 1 9
0 8
3 4 0
9
3
21 5
4
12 8
0
0
0 1 9
11 0 8
3 4 0
12 13 9
6 7 3
21 5
20 4
12 8
0 17
15 0
D1
D3
D5 =
0 1 21
0 8 4
3 4 0 12
9 0
3 0
0 1 9
11 0 8
3 4 0
12 13 9
6 7 3
21 5
20 4
12 8
0 17
15 0
0 1 8
10 0 7
3 4 0
12 13 9
6 7 3
20 5
19 4
12 8
0 17
15 0
Inchiderea tranzitiv
a
Definitia 8.1 Fie G = (V, E) un graf orientat, simplu si finit, V = {x1 , x2 , . . . , xn }.
Inchiderea tranzitiv
a a grafului G este un graf G+ = (V, E + ), unde E + = {(xi , xj )|
179
un drum de la xi la xj n G}.
Definitia 8.2 O relatie binara peste o multime A = {1, 2, . . . , n} se defineste ca fiind o
submultime a produsului cartezian A A ( A A). (a, b) se mai poate scrie ab.
Definitia 8.3 Inchiderea tranzitiva a relatiei binare este o relatie binar
a cu proprietatea
(8.14)
nN
k
serii de matrici R0 , R1 , . . . , Rn , unde un element ri,j
Rk se calculeaza dupa formula:
(
k1
k1
k1
1 , daca ri,j
= 1 sau (ri,k
= 1 rk,j
= 1)
k
ri,j
=
(8.16)
0 , altfel .
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 }:
k1
k1
dki,j = dk1
i,j (di,k dk,j )
181
(8.18)
Exemplul 8.5 Fie graful din figura 8.2, din care am eliminat arcul (1, 2). Matricea costurilor asociata acestui graf modificat este:
0 21
0 8 4
C=
3 0 12
9 0
3 0
0
1
0
0
0
0
1
1
1
1
1
0
1
1
0
0
1
0
0
1
1 0
0 1
D2 =
1 0
0 0
0 0
1
1
D4 =
1
1
1
0
1
1
1
1
0
1
0
0
0
1
0
1
1
0
1
1
1
1
1
0
1
0
1
1 0
1 1
1 0
1 0
1 1
D0
8.3
1
0
1
0
0
0
1
0
0
0
0
1
1
1
1
1
0
1
1
0
0
1
0
0
1
1 0
1 1
D3 =
1 0
1 0
1 0
D5 = 1
1
1
0
1
1
1
1
0
1
0
0
0
1
1
1
1
1
1
1
1
1
1
0
1
0
1
1 0
1 1
1 0
1 0
1 1
D1
1
0
1
0
0
Exercitii
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
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 necontradictoriu.
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.
Castelul are o forma dreptunghiulara cu m n camere (1 n, m 100). Fiecare
camera poate avea pereti spre E, S, V si N codificati cu un numar ntre 1 si 14: 1, 2,
4, 8. Se stie ca nu exista camere fara nici un perete (codul > 0) si nici camere care sa
aiba pereti n toate directiile (codul < 15).
Lungimea celui mai scurt drum se defineste ca fiind numarul de camere dintre sursa si
destinatie inclusiv.
Datele de intrare constau din M linii ce contin fiecare N numere ntregi reprezentand
codificarea peretilor din fiecare camera, urmate de coordonatele printului si ale printesei.
De exemplu, pentru datele de intrare
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).
Algoritm 70 Algoritm Backtracking (varianta generala)
1: procedure Backtracking(n, A)
2:
k1
3:
xk prima valoare din afara domeniului de valori
4:
while (k > 0) do
5:
gasit f alse
6:
while ( valori neverif icate pentru xk ) (gasit 6= true) do
7:
xk urmatoarea valoare neverif icata
8:
if (k (x1 , x2 , . . . , xk ) = true) then
9:
gasit true
10:
end if
11:
end while
12:
if (gasit = true) then
13:
if (k = n) then
14:
call Af is Solutie(x1 , . . . , xn )
15:
else
16:
k k+1
17:
xk prima valoare din afara domeniului de valori
18:
end if
19:
else
20:
k k1
21:
end if
22:
end while
23: end procedure
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 produsului 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 deplasandune 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:
solutiile pot avea numar variabil de componente;
dintre solutii se alege cea care minimizeaza sau maximizeaza o functie cost sau o functie
obiectiv data.
Exemplul A.1 Enuntul problemei: Se cere s
a se genereze permut
ari de n elemente.
n
Rezolvare: Spatiul solutiilor este A = i=1 Ai , Ai = {1, 2, . . . , n}, |Ai | = n. Solutia va
fi obtinuta n vectorul x = (x1 , x2 , . . . , xn ) A1 A2 . . . An .
La pasul k se ncearca atribuirea unui alt element din multimea Ak lui xk . Vom trece la
pasul k + 1 doar daca sunt ndeplinite conditiile de continuare: (x1 , x2 , . . . , xk ) nu contine
elemente care se repeta. Deoarece aceast
a verificare se face la fiecare pas, este suficient sa
verificam daca valoarea elementului xk nu se reg
aseste printre valorile x1 , x2 , . . . , xk1 (vezi
functia CanContinue() din algoritmul 71).
Exemplul A.2 Fie n = 4. Atunci Ai va fi compus din elementele Ai = {1, 2, 3, 4}.
Pentru k = 1, x1 va primi valuarea 1 (vezi linia 15): 1
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
urma verificarii
Ajunsi la ultimul pas, k = 4, se verific
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 respect
a 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)
...
O varianta de implementare a algoritmului 71 n limbajul C este:
#include <stdio.h>
#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
/**
* Functia afiseaza solutia calculata a problemei.
*/
void list(void) {
int i;
for (i = 1; i <= n; i++)
printf("%d ", a[i]);
printf("\n");
}
/**
* 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;
for (i = 1; i <= k - 1; i++)
if (a[k] == a[i])
return FALSE;
return TRUE;
}
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]++;
//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)
//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();
}
m n).
Rezolvare: Spatiul solutiilor este A = m
ia va
i=1 Ai , Ai = {1, 2, . . . , n}, |Ai | = n. Solut
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)
Algoritm 72 Algoritm pentru generare combinari (varianta backtracking)
1: procedure Combinari(n, m)
2:
k1
3:
xk 0
4:
while (k > 0) do
5:
gasit f alse
6:
while (xk + 1 n) (gasit 6= true) do
7:
xk xk + 1
8:
gasit true
9:
end while
10:
if (gasit = true) then
11:
if (k = m) then
12:
Output {x1 , . . . , xm }
13:
else
14:
k k+1
15:
xk xk1
16:
end if
17:
else
18:
k k1
19:
end if
20:
end while
21: end procedure
#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;
for (i = 1; i <= m; i++)
printf("%d ", a[i]);
printf("\n");
}
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
|i k| =
6 |xi (xk + 1)| (nu sunt pe aceeasi diagonal
a).
Daca xk < n si k sunt adevarate, dama de pe linia k va fi pozitionat
a pe coloana xk + 1
a/de vecin
atate a t
arilor
A - matricea de adiacent
Input: n - num
arul de t
ari
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
195
int
int
int
int
int
limit[NN];
val[NN];
taken[NN];
minim;
keep[NN];
/**
* Se citesc datele de intrare: suma de plata, numarul de
* tipuri de monezi si valoarea fiecarui tip.
*/
void readInput(void) {
int i;
printf("Suma= "); scanf("%d", &suma_plata);
printf("Numarul de tipuri de monede: "); scanf("%d", &n);
for (i = 0; i < n; i++) {
printf("val[%d]=", i); scanf("%d", &val[i]);
limit[i] = suma_plata / val[i];
}
}
/**
* Se verifica daca
* suma partiala sa
* @param k - pasul
*/
int canContinue(int
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;
//se numara cate monede sunt in solutie
for (i = 0; i < n; monezi += taken[i], i++);
if (monezi < minim) {
minim = monezi;
196
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, AddisonWesley, 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, SpringerVerlag, 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.
[17] C. Bereanu, Algoritmica Grafurilor, Editura Sitech, Craiova, 2006.
[18] C. Berge, Graphes et hypergraphes, Dunod, Paris 1970.
[19] O. Berkman, D. Breslauer, Z. Galil, B. Schieber, si U. Vishkin, Highly parallelizable
problems, in Proceedings of the 21st Annual ACM Symposium on Theory of Computing,
pp. 309-319, 1989.
[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 Mathematics, Springer, vol. 20(2), pp. 176184, 1980.
[23] D. D. Burdescu, Analiza complexit
atii algoritmilor, Editura Albastra, ClujNapoca,
1998.
[24] D. D. Burdescu, M. Brezovan, M. Cosulschi, Structuri de date arborescente cu aplicatii
n Pascal si C, Reprografia Universitatii din Craiova, 2000.
[25] D. D. Burdescu, Liste, arbori, grafuri, Editura Sitech, Craiova, 2005.
[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 Computing, 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 complexity of O(V 2 E), Mathematical Methods of Solution of Economical Problems, vol.
7, pp. 112125, 1977.
[30] T. H. Cormen, C. E. Leiserson, R. L. Rivest, Introducere n Algoritmi, Computer Libris
Agora, ClujNapoca, 1999.
[31] M. Cosulschi, M. Gabroveanu, Algoritmi o abordare pragmatic
a, Editia a 2-a, Universitaria, Craiova, 2004.
[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.
[34] S. Dasgupta, C. H. Papadimitriou, U. V. Vazirani, Algorithms, McGrawHill, 2006.
[35] C. Demetrescu, G. F. Italiano, Engineering Shortest Path Algorithms, WEA, Springer,
LNCS 3059, pp. 191198, 2004.
200
[36] R. B. Dial, Algorithm 360: shortest-path forest with topological ordering [H], Communications of the ACM, vol. 12:11, pp. 632633, 1969.
[37] Dictionarul explicativ al limbii rom
ane, Academia Romana, Institutul de Lingvistica
Iorgu Iordan, Editura Univers Enciclopedic, 1998.
[38] E. W. Dijkstra, A note on two problems in connections with graphs, Numerische Mathematik, 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 Goldreich, 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.
[42] J. Edmonds, R. M. Karp, Theoretical improvements in algorithmic efficiency for network
flow problems, Journal of the ACM, vol. 19(2), pp. 248-264, 1972.
[43] J. Egervary, Matrixok kombinatorius tulajdons
agair
ol, (in Hungarian), Matematikai es
Fizikai Lapok, vol. 38 pp. 16-28, 1931.
[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, Princeton, 1962.
[50] C. L. Foster, The Design and Analysis of Algorithms, Springer Verlag, 1992.
[51] J.-C. Fournier, Graph Theory and Applications, Wiley-Blackwell, 2009.
[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 optimization 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, Information 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, Journal of Computer and System Sciences, vol. 21(2), pp. 203217, 1980.
[60] C. Giumale, Introducere n analiza algoritmilor, Editura Polirom, Iasi, 2004.
[61] A. V. Goldberg, A new max-flow algorithm, Technical Report MIT/LCS/TM-291, Laboratory for Computer Science, MIT, 1985.
[62] A. V. Goldberg, R. E. Tarjan, A new approach to the maximum flow problem, in Proceedings 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.
[64] R. L. Graham, D. E. Knuth, O. Patashnik, Concrete Mathematics: A Foundation for
Computer Science, 2nd Edition, Addison-Wesley, 1994.
[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.
[68] G. H. Hardy, E. M. Wright, An Introduction to the Theory of Numbers, 5th Edition,
Oxford University Press, 1979.
[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.
[71] I. N. Herstein, I. Kaplansky, Matters mathematical, 2nd Edition, Chelsea Publishing
Company, 1978.
202
203
[90] H. W. Kuhn, The Hungarian Method for the assignment problem, Naval Research Logistics 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.
[92] Y. Li , C. Yu , H. V. Jagadish, Schema-free XQuery, in Proceedings of the Thirtieth
International Conference on Very Large Databases (VLDB), pp. 7283, 2004.
[93] L. Livovschi, H. Georgescu, Analiza si sinteza algoritmilor, Ed. Stiintifica si Enciclopedica, Bucuresti, 1986.
[94] V. M. Malhotra, M. P. Kumar, S. N. Maheshwari, An O(|V |3 ) algorithm for finding
maximum flows in networks, Information Processing Letters, 7(6), pp.277278, 1978.
[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.
[97] V. Mitrana, Provocarea algoritmilor, Editura Agni, Bucuresti, 1994.
[98] E. F. Moore, The shortest path through a maze, in Proceedings of International Symposium 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.
[102] I. Odagescu, F. Furtuna, Metode si tehnici de programare, Computer Libris Agora,
ClujNapoca, 1998.
[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 Programming (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.
[105] S. Pettie, V. Ramachandran, An optimal minimum spanning tree algorithm, Journal of
ACM, 49:1634, 2002.
[106] R. C. Prim, Shortest connection networks and some generalizations, Bell System Technical Journal, 36, pp. 1389-1401, 1957.
204
[107] B. Schieber, U. Vishkin, On finding lowest common ancestors: Simplification and parallelization, SIAM J. Comput., vol. 17, pp. 1253-1262, 1988.
[108] A. Schrijver, On the history of the transportation and maximum flowproblems, Mathematical 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 Algorithms, 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 Annual 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 Research Letters, vol. 2, pp.265268, 1984.
[121] I. Tomescu, Grafuri si programare liniar
a, Ed. Didactica si Pedagogica, Bucuresti, 1975.
[122] I. Tomescu, Probleme de combinatoric
a si teoria grafurilor, Ed. Didactica si Pedagogica,
Bucuresti, 1981.
[123] I. Tutunea, Algoritmi, logica si programare, Reprografia Universitatii din Craiova, 1993.
[124] I. Tutunea, S. Pescarus, Algoritmi si programe Pascal. (Culegere de probleme), Reprografia Universitatii din Craiova, 1994.
[125] J. Vuillemin, A data structure for manipulating priority queues, Communications of
the ACM, vol. 21:4, pp. 309315, 1978.
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 Computer Modelling, vol. 16(2), pp.6572, 1992.
[128] H. S. Wilf, Algorithms and Complexity, Internet edition, 1996.
[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 Management of Data, pp. 527538, 2005.
[131] D. Zaharie, Introducere n proiectarea si analiza algoritmilor, Eubeea, Timisoara, 2008.
206