Documente Academic
Documente Profesional
Documente Cultură
1 Introducere n algoritmi
3
1.1 Limbajul pseudocod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.3 Exercitii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2 Elemente de analiza algoritmilor
2.1 Metoda substitutiei . . . . . . .
2.2 Schimbarea de variabila . . . .
2.3 Metoda iterativa . . . . . . . .
2.4 Teorema master . . . . . . . . .
2.5 Exercitii . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
21
25
25
26
27
28
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
30
30
35
38
42
42
46
47
51
52
57
.
.
.
.
.
.
60
60
61
64
68
70
72
.
.
.
.
83
84
85
87
93
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
6 Arbori oarecare
6.1 Moduri de reprezentare . . . . . . . . . . . . . . .
6.2 Metode de parcurgere . . . . . . . . . . . . . . . .
6.3 Arbori de acoperire de cost minim . . . . . . . . .
6.3.1 Algoritmul lui Boruvka . . . . . . . . . . .
6.3.2 Algoritmul lui Prim . . . . . . . . . . . . .
6.3.3 Structuri de date pentru multimi disjuncte
6.3.4 Algoritmul lui Kruskal . . . . . . . . . . .
6.4 Exercitii . . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
104
. 104
. 107
. 112
. 115
. 115
. 118
. 123
. 126
7 Grafuri orientate
7.1 Notiuni de baza . . . . . . . . .
7.2 Parcurgerea grafurilor . . . . .
7.3 Sortarea topologica . . . . . . .
7.4 Componente tare conexe . . . .
7.4.1 Algoritmul lui Kosaraju
7.4.2 Algoritmul lui Tarjan . .
7.4.3 Algoritmul lui Gabow .
7.5 Exercitii . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
150
. 151
. 151
. 152
. 160
. 162
. 164
. 167
. 168
. 170
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
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.1.3 Algoritmul lui BellmanKalaba . . . . .
8.2 Drumuri minime ntre toate perechile de varfuri
8.2.1 Algoritmul lui Roy-Floyd-Warshall . . .
8.3 Circuitul Hamiltonian . . . . . . . . . . . . . .
8.4 Graf pe mai multe niveluri . . . . . . . . . . . .
8.5 Exercitii . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
128
128
130
133
137
138
141
144
145
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
181
181
183
185
186
187
190
Capitolul 1
Introducere n algoritmi
Definitia 1.1 Algoritmul constituie o reprezentare finita a unei metode de calcul ce permite rezolvarea unei anumite probleme. Se poate spune ca un algoritm reprezint
a o secvent
a
finita de operatii, ordonata si complet definita, 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 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 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 trasaturi 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;
3. generalitate - algoritmul trebuie sa ofere solutia nu numai pentru o singura problema
ci pentru o ntreaga clasa de probleme;
3
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. 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
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
2. dac
a a 6= 0, ecuatia are o singura solutie, x1 = ab R.
Av
and acest algoritm drept model sa se realizeze un algoritm pentru rezolvarea ecuatiei de
gradul al II-lea, ax2 + bx + c = 0, a, b, c R.
Enunturi repetitive
Enunturile repetitive permit descrierea unor prelucrari ce trebuie efectuate n mod repetat,
n functie de pozitia conditiei de continuare existand doua variante de structuri repetitive:
structura repetitiva conditionata anterior si structura repetitiva conditionata posterior.
Structura repetitiva conditionata anterior while (sau instructiune de ciclare cu test initial)
are sintaxa:
1:
2:
3:
4:
5:
6:
1: while <expresie-booleana> do
2:
<instructiune>
3: end while
sum 0
i1
while (i n) do
sum sum + i
ii+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
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 lui p, 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
negative.
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 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 aflarea
elementului maxim, respectiv minim dintr-un sir de elemente. Acest sir de elemente poate fi
pastrat ntr-o structura de date elementara, cunoscuta sub numele de vector. Un vector este
un caz particular de matrice avand o singura linie si n coloane.
1: repeat
2:
<instructiune>
3: until <expresie-booleana>
sum 0
i1
repeat
sum sum + i
ii+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
1. Algoritmul 4 utilizeaz
a enuntul repetitiv cu test final, repeat . . . until.
Intotdeauna
variabilele trebuie initializate corect din punctul de vedere al algoritmului
8
Algorithm 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}
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 la definire, urmat de lista parametrilor actuali. Aceasta 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.
2. dac
a cmmdc(a, b) = 1 atunci se spune ca numerele a si b sunt prime ntre ele.
3. Intre
cel mai mic multiplu comun si cel mai mare divizor comun exista urmatoarea
relatie:
ab
cmmmc(a, b) =
cmmdc(a, b)
Metoda de calcul pentru a obtine cel mai mare divizor comun a doua numere a si b prin
mp
artiri succesive, cunoscuta sub numele de algoritmul lui Euclid, se poate descrie astfel:
Se mparte a la b si se retine restul r. Daca r este nul atunci cel mai mare divizor comun
caz contrar, se mparte b la r si se pastreaz
este b. In
a noul rest. Calculele se continu
a,
folosindu-se ca demp
artit vechiul mp
artitor iar ca mp
artitor ultimul rest obtinut, pan
a
cand restul obtinut devine nul.
10
0 r1 < b
0 r2 < r 1
0 r3 < r 2
0 rk+2 < rk+1
0 rn < rn1
0 rn+1 < rn
Aceste mp
artiri nu se constituie ntr-un proces infinit, deoarece secventa de numere naturale r1 , r2 , . . . , rn+1 , . . . este descresc
atoare (r1 > r2 > . . . > rn > . . .) si marginit
a inferior
de 0. Rezulta ca p N astfel nc
at rp 6= 0, si rp+1 = 0.
Algorithm 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 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 0
(linia 8).
Functiile au aceeasi sintaxa ca si procedurile:
1: function <nume>(<parametri>)
2:
<instructiune>
3: end function
11
1.2
Exemple
Exemplul 1.9 S
a se verifice printr-un algoritm daca un numar natural este prim sau nu.
Definitia 1.3 Un numar natural k 2 se numeste prim daca singurii sai divizori naturali
sunt 1 si k.
Pe baza acestei definitii se poate concluziona ca un numar natural k este prim daca nu are
nici un divizor propriu n intervalul [2, k 1].
Algorithm 7 Algoritm pentru verificarea daca un numar este prim
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
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
ii+1
end if
end while
if (prim = true) then
Output {Numarul este prim.}
else
Output {Numarul nu este prim.}
end if
end if
Dac
a n este numar prim, corpul enuntului de ciclare while se va executa de n 2 ori
(vezi algoritmul 7).
Conditia i n 1 poate fi mbun
at
atit
a cu i n/2, deoarece ntre jumatatea numarului
si n nu mai exista nici un alt divizor, pentru orice valoare a lui n.
Dac
a 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. Similar pentru 3, 4, 5, . . .. Astfel
se formeaz
a doua siruri:
corespunde n
2
2
corespunde n
3
3
corespunde n
4
4
..
..
.
.
n
corespunde
k
|{z}
k
|{z}
Sir1
Sir2
Se observa faptul ca sirul 1 este compus din elemente cu valori consecutive, iar sirul 2
nu respect
a aceast
a proprietate. Numerele dintro pereche (p, np ) sunt legate ntre ele astfel:
12
Input {N }
if (n < 2) then
Output {Numarul nu este prim.}
else
i2
prim true
Exemplul 1.10 Se spune ca un vector este simetric daca primul element este egal cu ultimul,
al doilea cu penultimul, etc. Algoritmul urmator verifica daca un vector de numere ntregi
este simetric.
Vom utiliza doua variabile de indici, ce pornesc, una din stanga, si cealalt
a din dreapta.
At
ata timp cat variabila din stanga este mai mica dec
at variabila din dreapta iar elementele
din vector corespunz
atoare variabilelor de indiciere sunt egale, se incrementeaz
a variabila din
stanga si se decrementeaz
a variabila din dreapta. Dupa par
asirea instructiunii de ciclare, se
face o verificare pentru determinarea conditiei care nu a mai fost ndeplinit
a si a condus la
iesirea din bucla. Daca i j atunci conditia (i < j) nu a mai fost ndeplinit
a. Acest lucru
conduce la concluzia ca cealalt
a conditie, (xi = xj ) a fost ndeplinit
a tot timpul (i < j, i, j =
1, n). Deci sirul este simetric n aceast
a situatie.
Exemplul 1.11 Se da urmatorul sir de numere naturale: 1, 1, 2, 1, 2, 3, 1, 2, 3, 4, . . .. Pentru
un numar natural n dat sa se determine cel de-al n-lea element al sirului. De exemplu, cel
de-al 5-lea element al sirului prezentat este 2.
Dup
a cum se observa din constructia sirului, acesta este format din concatenarea mai
multor secvente de numere naturale consecutive, fiecare secvent
a ncep
and cu valoarea 1.
Lungimea fiec
arei subsecvente este cu o unitate mai mare dec
at lungimea subsecventei anterioare: astfel prima subsecvent
a are lungimea 1, a doua subsecvent
a are lungimea 2, a treia
cadrul algoritmului 10 se va ncerca decrementarea varisubsecvent
a are lungimea 3, etc. In
abilei index cu lungimea cate unui sir n ntregime (index index k), atata timp cat este
13
Input {N }
k1
index N
while (k < index) do
index index k
k k+1
end while
Output {Cel de-al n-lea element este, index}
Aplic
and de doua ori ipoteza de inductie avem:
(a21 + b21 ) (a22 + b22 ) . . . (a2n + b2n ) (a2n+1 + b2n+1 ) = (u2 + v 2 ) (a2n+1 + b2n+1 ) = d2 + e2 (1.3)
|
{z
}
u2 +v 2
Input {N, a1 , . . . , aN , b1 , . . . , bN }
d a1
e b1
for i 2, N do
u = d ai + e bi
v = d bi e ai
du
ev
end for
Output {d, e}
Exemplul 1.13 Se da un numar de 9 monede de acelasi tip. Printre ele s-a strecurat o
moned
a falsa: ea este mai grea sau mai usoar
a dec
at celelalte. Avem la dispozitie o balant
a
far
a greut
ati. Se cere un algoritm care sa determine moneda falsa, stiind ca putem folosi
cantarul doar pentru 3 operatii de cant
arire.
Fie a1 , a2 , . . . , a9 cele 9 monede. Vom mp
arti monedele n trei grupe egale: A = {a1 , a2 , a3 },
B = {a4 , a5 , a6 } si C = {a7 , a8 , a9 }. Pentru a determina moneda falsa avem nevoie de doua
informatii:
1. n ce grupa se afla moneda falsa;
2. cum este moneda falsa (mai grea sau mai usoar
a fat
a de restul).
Notam cu g() greutatea unei monede sau a unei multimi de monede. Vom compara grupurile
A si B cu ajutorul balantei.
1. g(A) = g(B).
aceast
In
a situatie moneda falsa se afla n cea dea treia grupa, C = {a7 , a8 , a9 }.
Compar
am monedele a7 si a8 , rezult
and una din situatiile:
(a) g(a7 ) = g(a8 ) a9 este moneda falsa.
(b) g(a7 ) < g(a8 ).
Compar
am monedele a7 cu a9 :
i. g(a7 ) = g(a9 ) a8 este moneda falsa si este mai grea dec
at restul monedelor.
ii. g(a7 ) > g(a9 ) a8 > a7 > a9 (imposibil deoarece doua monede trebuie sa fie
egale).
iii. g(a7 ) < g(a9 ) a7 este moneda falsa si este mai usoar
a dec
at restul monedelor.
15
Algorithm 12 Algoritm pentru calculul numarului de zerouri ale unui produs de numere
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
Input {N, a1 , . . . , aN }
exp2 0
exp5 0
for i 1, N do
while ((ai 6= 0) (ai mod 2 = 0)) do
exp2 exp2 + 1
ai ai /2
end while
while ((ai 6= 0) (ai mod 5 = 0)) do
exp5 exp5 + 1
ai ai /5
end while
end for
if (exp2 < exp5) then
min exp2
else
min exp5
end if
Output {min}
Input {n, a1 , . . . , an , k}
mnk
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
reprezinta limita superioara a indexului pana la care se poate alege un element la pasul
curent;
a1 . . .
al . . . anm+i
. . . an
|
{z
}
subsecventa pentru care se determin
a cifra maxim
a
ni1
X
j=1
j=
(n i) (n i 1)
2
ori.
P
Pni1
P
Pn2 2
(ni)(ni1)
1
Din i = 2, n 2 rezulta c
a n2
j = n2
=
i=2
j=1
i=2
i=2 (i + (1 2n)i +
2
2
P
P
n2
n2
n2 n) = 12 [ i=2 i2 + (1 2n) i=2 i + (n2 n) (n 3)]
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
sii+jj
k b sc
if ((k k = s) (k n)) then
Output {i, j, k}
end if
end for
end for
return
1.3
Exercitii
4. (a) Pentru un numar natural n dat (n < 32000), sa se determine ultima cifra nenula
a lui n!.
(b) Pentru un numar natural n dat (n < 1000), sa se determine numarul de cifre ale
lui n!.
5. Se da un numar natural n (0 n 106 ).
(a) Se calculeaza suma factorialelor cifrelor numarului n si se obtine un nou numar.
(b) Se calculeaza suma cuburilor cifrelor numarului n.
Dupa aceasta procedeul se repeta aplicandu-se de fiecare data numai una din cele doua
operatii (a) sau (b) (cea cu care s-a nceput). Sa se afiseze sirul de numere astfel
obtinut pana cand elementele lui ncep sa se repete. Se cere lungimea listei pana la
prima repetitie si pozitia elementului care se repeta primul.
6. O secvent
a Farey[45] de ordinul n este un sir de numere rationale
n, (a, b) = 1. Secventele Farey de ordinul 1, 2 si 3 sunt:
a
b
unde 0 a b
0 1
F1 = { , }
1 1
0 1 1
F2 = { , , }
1 2 1
0 1 1 2 1
F3 = { , , , , }
1 3 2 3 1
Pentru un numar natural n (n < 216 ) sa se determine elementele secventei Farey de
ordinul n.
Intrare
7
19
Iesire
(0,1),(1,7),(1,6),(1,5),(1,4),(2,7),(1,3),(2,5),(3,7),(1,2),(4,7),(3,5),
(2,3),(5,7),(3,4),(5,6),(6,7),(1,1)
Indicatie Fie ab11 , ab22 , ab33 trei elemente consecutive ale unei secvente Farey de ordinul n.
Atunci avem relatiile ([67]):
b1 a2 a1 b2 = 1
a1 + a3
a2
=
b2
b1 + b3
(1.4)
(1.5)
Daca ab11 si ab33 sunt doua elemente consecutive ale unei secvente Farey de ordinul n,
3
este un element al secventei Farey de ordinul n + 1.
atunci ab11 +a
+b3
Fie ab11 si ab22 doua elemente consecutive ale unei secvente Farey de ordinul n. Elementul
+a3
urmator ce apartine secventei, ab33 , se poate determina astfel: ab22 = ab11 +b
. k N astfel
3
ncat ka2 = a1 + a3 si kb2 = b1 + b3 a3 = ka2 a1 si b3 = kb2 b1 . Valoarea lui k
trebuie aleasa astfel ncat fractia ab33 sa fie cat mai apropiata de ab22 . Astfel k trebuie sa
1
.
fie cat mai mare, nsa respectand restrictia k n+b
b2
7. Fiind date doua numere ntregi X si Y , sa se determine cea mai mica baza pentru Y
astfel ncat X si Y sa reprezinte aceeasi valoare.
De exemplu numerele 12 si 5 nu sunt egale n baza 10 (1210 6= 510 ), nsa ele sunt egale
daca le consideram ca fiind reprezentate n bazele 3 si respectiv 6: 123 = 510 , 56 = 510 .
Bazele n care se vor considera reprezentate numerele au valoarea maxima 36.
Intrare
12 5
10 A
12 34
123 456
10 2
Iesire
123 = 56
1010 = A11
1217 = 345
no solution
102 = 23
(ACM, 1995)
20
Capitolul 2
Elemente de analiza algoritmilor
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 algoritmului. 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 2.1 Timpul de executie n cazul cel mai defavorabil al unui algoritm A este o
functie TA : N N unde TA (n) reprezint
a numarul maxim de instructiuni executate de
catre A n cazul unor date de intrare de dimensiune n.
Definitia 2.2 Timpul mediu de executie al unui algoritm A este o functie TAmed : N N
unde TAmed (n) reprezint
a numarul mediu de instructiuni executate de catre 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 2.3 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 }.
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 2.1 Vom prezenta cateva propriet
ati ale lui O():
O(f (n) + g(n)) = O(max(f (n), g(n))).
De exemplu pentru functia f (n) = 7 n5 n2 + 3 log n aplicand regula valorii maxime
vom avea: O(f (n)) = O(max(7 n5 , n2 , 3 log n)) adica O(f (n)) = O(n5 ).
O(loga n) = O(logb n)
21
f (n)
g(n)
= 0.
Exemplul 2.6
Deoarece
c
g(n) (f (n)), c > 0
g(n)
lim
= 0
g(n) o(f (n))
n f (n)
f (n) o(g(n))
(2.1)
=
= ( )n
4
4
n/2
2n
n!
(x )
x
x
x
. . x}
| .{z
(2.2)
dn/2eori
rezult
a ca
xn
=0
n n!
lim
22
(2.3)
log n o(n).
1
log x
(log x)0
x ln 2
= lim
lim
= lim
=0
x x
x (x)0
x 1
t=t
n
X
1 = t n = O(n)
(2.4)
i=1
1: i l
2: while (ai < x) do
3:
...
4:
ii+1
5: end while
Timpul de executie pentru secventa de cod ce contine doua instructiuni de ciclare imbricate
este O(n m).
Exemplul 2.8 S
a consider
am un alt exemplu: se da P
un sir a de n numere reale, si se doreste
calcularea elementelor unui sir b astfel nc
at bi = 1i ij=1 aj , pentru i = 1, n.
1: for i 1, n do
2:
s0
3:
for j 1, i do
4:
s s + aj
5:
end for
6:
bi si
7: end for
8: return
Dac
a notam cu o constant
a cx timpul necesar pentru efectuarea unei operatii atomice,
vom obtine: costul efectu
arii 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=1
i + c4
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
De obicei nu se efectueaza o analiza asa de detaliata a algoritmului, dar se ncearc
a o evaluare a blocurilor principale cum ar fi instructiunile de ciclare, atribuindule direct valori
corespunz
atoare complexitatii timp, atunci cand este posibil:
T (n) = O(n2 ) + O(n) + O(1) = O(n2 )
24
(2.5)
s0
for i 1, n do
s s + ai
bi si
end for
return
Pentru aceast
a varianta de lucru, complexitatea timp este
T (n) = O(1)(linia 1) + O(n)(liniile 24) + O(1)(linia 6) = O(n)
(2.6)
urma analizei de complexitate, putem concluziona ca cea de-a doua varianta a algoritmului
In
ruleaz
a ntrun timp liniar.
2.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) =
(2.7)
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 ca
T (n) cn log n
(2.8)
Presupunem mai ntai ca inecuatia (2.8) are loc pentru k < n, inclusiv pentru n2 , adica
T ( n2 ) c n2 log ( n2 ).
Vom demonstra ca inecuatia (2.8) este ndeplinita si pentru n: T (n) = 2T ( n2 ) + n.
n
n
n
T (n) 2(c log ) + n = cn log + n
2
2
2
= cn log n cn log 2 + n = cn log n cn + n
cn log n
(2.9)
2.2
Schimbarea de variabil
a
Sa consideram ecuatia T (n) = 2T ( n) + log n. Daca nlocuim n cu 2m obtinem:
m
T (2m ) = 2T (2 2 ) + log 2m = 2T (2 2 ) + m
(2.10)
(2.12)
2.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 formula
de recurenta
(
1
, daca n = 1
T (n) =
(2.13)
n
T ( 2 ) + 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
n
Prin urmare atunci cand 1 = 2k k = log n, vom avea
T (n) = 1 + k = 1 + log n T (n) = O(log n).
Fie o alta formula de recurenta:
(
1
, daca n = 1
T (n) =
n
2T ( 2 ) + n , n rest
Inlocuind succesiv pe n cu
(2.14)
(2.15)
n
2
(2.16)
(2.17)
2.4
Teorema master
1
O(n 2 ) obtinem: T (n) = ( n).
Fie T (n) = 3T ( n4 ) + n log n. Aplicand Teorema master pentru a = 3, b = 4, f (n) =
(nlog4 3+ ) 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.
27
2.5
Exercitii
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 ) +
n
2
28
29
Capitolul 3
Grafuri. Grafuri neorientate
3.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 3.1 Se numeste graf o pereche ordonata 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
30
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 3.1 Un graf neorientat, simplu, finit poate fi utilizat drept model de reprezentare
al unei relatii simetrice peste o multime.
Definitia 3.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 3.2 S
a consider
am grafurile din figura 3.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 3.1 a));
b) K5 este graful complet cu 5 varfuri. Varfurile 3 si 5 sunt adiacente (vezi figura 3.1 b)).
Fig. 3.2: a) Un exemplu de graf neorientat cu 5 varfuri. b) Subgraf al grafului din figura 3.1 b). c) Graf
partial al grafului din figura 3.1 b).
Definitia 3.3 Un graf partial al unui graf dat G = (V, E) este un graf G1 = (V, E1 ) unde
E1 E.
Definitia 3.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 ;
31
- G E1 =< E \ E1 >G ;
- G e = G {e}, graful partial obtinut prin eliminarea unei muchii e.
Exemplul 3.3 Graful partial din figura 3.2 c) se obtine din graful 3.1 b) prin stergerea
muchiilor [2, 4], [2, 5], [3, 5], [4, 5]. Subgraful din figura 3.2 b) este indus de multimea V1 =
{1, 3, 4, 5} din graful complet K5 (H = K5 |V1 ).
Definitia 3.5 Gradul unui varf este egal cu numarul muchiilor incidente cu varful x si se
noteaz
a cu d(x) (d(x) = |{[x, u]|[x, u] E, u V }|). Un varf 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 3.4 Graful lui Petersen din figura 3.3 este un exemplu de graf trivalent sau cubic
(toate varfurile grafului au acelasi grad, 3).
Propozitia 3.5 Pentru un graf G = (V, E), V = {x1 , x2 , . . . , xn }, |E| = m, avem urmatoarea
relatie:
n
X
d(xk ) = 2m.
(3.1)
k=1
Astfel n orice graf G exista un numar par de varfuri al caror grad este un numar impar.
Definitia 3.6 Se numeste secventa
grafic
a un sir de numere naturale d1 , d2 , . . . , dn cu
proprietatea ca ele reprezint
a gradele varfurilor unui graf neorientat.
Corolarul 3.6 Pentru ca o secvent
a de numere naturale d1 , d2 , . . . , dn sa fie secvent
a grafic
a,
este necesar ca:
1. k = 1, n, dk n 1;
Pn
ar par.
2.
k=1 dk este un num
Definitia 3.7 Un lant L = [v0 , v1 , . . . , vm ] este o succesiune de varfuri cu proprietatea ca
oricare doua varfuri vecine sunt adiacente ([vi , vi+1 ] E, i = 0, m 1). Varfurile v0 si vm
se numesc extremit
atile lantului, iar m reprezint
a lungimea lantului.
32
Daca varfurile v0 , v1 , . . . , vm sunt distincte doua cate doua, lantul se numeste elementar
(vi 6= vj , i, j = 0, m).
Definitia 3.8 Un lant L pentru care v0 = vm se numeste ciclu.
Definitia 3.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 3.10 Un lant L ce contine fiecare muchie exact o singura data 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 3.7 [1, 2, 3, 1, 4] este un exemplu de lant n graful din figura 3.2 c). Varfurile 1
si 4 sunt extremit
atile lantului. Lantul nu este elementar deoarece varful 1 se nt
alneste 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 3.2 a). [4, 5, 1, 2, 4, 3] este un
acest graf nu exista nici un ciclu eulerian.
lant eulerian, precum si [2, 4, 3, 2, 1, 5, 4]. In
Definitia 3.11 Un graf se numeste conex daca pentru orice pereche de varfuri x si y exist
a
un lant de la x la y (x, y V, x y).
Se numeste component
a conex
a un subgraf conex maximal, adica un subgraf conex n
care nici un varf din subgraf nu este adiacent cu unul din afara lui prin intermediul unei
muchii apartin
and grafului initial.
Definitia 3.12 O muchie e E se numeste muchie critic
a dac
a prin eliminarea acesteia
o component
a conex
a se mparte n doua sau mai multe componente conexe.
Exemplul 3.8 Graful din figura 3.4 are doua componente conexe: {1, 2, 3, 4, 5, 6} si {7, 8, 9}.
Muchia [2, 5] este muchie critica.
Definitia 3.13 Un graf planar este un graf ce poate fi reprezentat n plan astfel nc
at
muchiile sale sa nu se intersecteze doua cate doua.
Definitia 3.14 Un graf G = (V, E) se numeste graf bipartit dac
a exista o partitie a
multimii nodurilor {V1 , V2 } (V = V1 V2 , V1 V2 = , V1 , V2 6= ) astfel nc
at orice muchie din
E va avea o extremitate n multimea V1 si cealalt
a extremitate n multimea V2 ([x, y] E
avem x V1 si y V2 ).
33
Propozitia 3.9 Un graf este bipartit daca si numai daca nu contine cicluri de lungime
impar
a.
Definitia 3.15 Un graf bipartit este complet daca x V1 , y V2 , [x, y] E (G =
(V, E), V = V1 V2 , V1 V2 = ). Daca |V1 | = p, |V2 | = q atunci graful bipartit complet se
noteaz
a Kp,q .
figura 3.5 a) este ilustrat un graf bipartit (V1 = {1, 2, 3}, V2 = {4, 5}),
Exemplul 3.10 In
iar n cazul b) avem un graf bipartit complet, K2,3 .
Definitia 3.16 Se numeste izomorfism de la graful G la graful G0 o functie bijectiva :
V (G) V (G0 ) astfel nc
at [u, v] E(G) ([u, v]) E(G0 ).
Dac
a exista un izomorfism de la graful G la graful G0 atunci se spune ca G este izomorf
cu G0 si notam acest lucru astfel: G u G0 .
Definitia 3.17 Un graf etichetat este un graf n care fiecare muchie si varf poate avea
asociat
a 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.
34
3.2
Operatii pe grafuri
In figura 3.6 b) este reprezentat graful complementar Gc al grafului G = (V, E), din
figura 3.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 3.6 a), prin insertia varfului 6 pe muchia [1, 5] se obtine
graful din figura 3.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. 3.7: a) Graful obtinut prin inserarea varfului 6 n graful din figura 3.6 a). b) Graful obtinut prin
contractia muchiei [3, 4] n graful din figura 3.6 a).
In figura 3.7 b) este reprezentat graful obtinut prin contractia muchiei [3, 4] din graful
G = (V, E) (vezi figura 3.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 3.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}.
35
Fig. 3.8: Figura b) prezinta graful reprezentativ al muchiilor grafului din figura a).
Fig. 3.9: a) Un exemplu de graf neorientat. b) Graful total al grafului din figura a).
Fig. 3.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 .
37
Fig. 3.12: Graful rezultat n urma compunerii grafurilor G1 si G2 din figura 3.11
3.3
Moduri de reprezentare
1 , daca [xi , xj ] E
0 , daca [xi , xj ]
/E
0
1
1
A=
0
0
0
1
0
0
0
1
0
0
0
1
0
0
0
1
0
0
0
38
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
ai,j
, daca xi = xj
0
= +
, daca [xi , xj ]
/E
(3.2)
ai,j
, daca xi = xj
0
=
, daca [xi , xj ]
/E
(3.3)
sau
0
35
20
A=
35
0
50
22
20
15
50
22
44
15
0
10
27
44
10
0
27
Notatia (3.2) este folosita n aplicatii n care se cere un drum de lungime minima iar
(3.3) este folosita n aplicatii n care se cere un drum de lungime maxima ntre doua
noduri.
Matricea costurilor va ocupa un spatiu de memorie mai mare decat cel ocupat de
matricea de adiacenta: pentru reprezentarea unui element al matricii de adiacenta este
suficient un bit, pe cand pentru reprezentarea unui element al matricii costurilor se
foloseste un byte, un ntreg (lung) sau un numar real.
39
(2,
(1,
(1,
(1,
3, 4)
5)
5)
6)
5:
6:
7:
8:
(2, 3, 7)
(4, 7, 8)
(5, 6)
(6)
al varfului xk .
List1,j - indicele varfului ce se afla n lista de vecini a varfului xk .
List2,j
de vecini a nodului xk .
Exemplul 3.14 Pentru exemplul din figura 3.13 avem urmatoarea configuratie pentru
cei doi vectori considerati:
Nod
Cap
List =
1
1
2
4
3
6
4
8
5
10
6
13
7
16
8
18
2 3 4 1 5 1 5 1 6 2 3 7 4 7 8 5 6 6
2 3 0 5 0 7 0 9 0 11 12 0 14 15 0 17 0 0
40
NULL
5
6
3
NULL
1
1
2
7
8
NULL
NULL
NULL
NULL
NULL
NULL
1 1 1 2 3 4 5 6 6
M=
2 3 4 5 5 6 7 7 8
Vom sintetiza complexitatea timp pentru diverse operatii folosind fiecare dintre modurile
de reprezentare prezentate:
Gradul unui nod xi
(xi , xj ) E?
Urmatorul vecin al lui xi
3.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
3.4.1
Parcurgerea n l
atime (BF-Breadth-First)
Metoda de parcurgere n latime viziteaza nodurile grafului n felul urmator (vezi algoritmul
17):
se viziteaza mai ntai varful de pornire k;
urmeaza n ordine toti vecinii nca nevizitati ai acestuia;
se continua cu vecinii nca nevizitati ai acestora, s.a.m.d.
43
int vida;
/**
* Initializarea cozii circulare.
*/
void initQueue(void) {
vida = TRUE;
first = 0;
last = MAX;
}
/**
* Intoarce pentru pozitia i, urmatoarea pozitie din coada.
*/
int next(int i) {
return (i + 1) % MAX;
}
/**
* Insereaza elementul a carui valoare este pastrata in variabila k in coada.
*/
void enQueue(int k) {
last = next(last);
coada[last] = k;
if (vida)
vida = FALSE;
}
/**
* Extrage un element din coada.
*/
int deQueue(void) {
int k = coada[first];
first = next(first);
if (first == next(last))
vida = TRUE;
return k;
}
/**
* Parcurge in latime graful pornind de la nodul de inceput k.
*/
void bfs(int k) {
int i;
char vizitat[MAX];
memset(vizitat, 0, sizeof(vizitat));
vizitat[k] = 1;
enQueue(k);
printf("%d ", k);
while (!vida) {
k = deQueue();
44
In exemplul anterior functia de citire readInput() este foarte simpla: se citeste mai
ntai numarul de noduri al grafului, si apoi se citeste matricea de adiacenta a grafului.
Variabilele n si vecin sunt declarate drept variabile globale. Deoarece matricea vecin
este simetrica si pentru a reduce numarul de citiri, se vor solicita numai valorile aflate
deasupra diagonalei principale:
void readInput(void) {
int i, j;
printf("n = "); scanf("%d",&n);
for (i = 0; i < n - 1; i++)
for (j = i + 1; j < n; j++) {
printf("a[%d,%d] = ", i, j);
scanf("%d", &vecin[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 are urmatoarea semnatura2
1
2
http://msdn.microsoft.com/en-us/library/1fdeehz6.aspx
http://en.wikibooks.org/wiki/C_Programming/Strings#The_memset_function
45
3.4.2
Parcurgerea D (D - Depth)
3.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 k;
urmeaza, n ordine, 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.
Pentru graful considerat (vezi figura 3.13), n urma parcurgerii n adancime, vom obtine
nodurile n ordinea urmatoare: 1, 2, 5, 3, 7, 6, 4, 8 (vezi figura 3.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 adancime.
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 df s(u) apeleaza direct df s(v) sau invers;
2. muchie de ntoarcere - muchia [u, v] este o muchie de ntoarcere daca df s(u) apeleaza
indirect df s(v) (x V a.i. df s(u) df s(x) df s(v)) sau invers.
47
- nodul vizitat
k
Input:
n
- numarul de noduri din graf
Implementarea algoritmului se poate realiza fie n varianta recursiva (vezi algoritmul 19),
fie n varianta nerecursiva (vezi algoritmul 20). Ca si la algoritmul de parcurgere n latime,
vectorul vizitat
( pastreaza situatia vizitarii nodurilor grafului G:
1 , daca nodul k a fost vizitat
vizitatk =
0 , n caz contrar.
Subrutina DFS (algoritmul 19) ncepe cu marcarea nodului curent ca fiind vizitat (vizitatk
1) si apelul procedurii V izitare (call V izitare(k)). Apoi se cauta primul vecin nevizitat i
al varfului curent k ((vizitati = 0) (vecink,i = 1)) si se reia secventa de operatii pentru
acesta, printrun apel recursiv (call DF S(i)). Enuntul repetitiv for i 1, n poate fi optimizat astfel ncat sa nu se mai verifice toate varfurile grafului, ci numai nodurile adiacente
cu nodul curent: n aceasta situatie reprezentarea cu liste de adiacenta este optima din punct
48
Algoritmul 20 utilizeaza o 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 20.1320.25 nu este optima deoarece, de fiecare data cand se revine la un nod
parinte, se verifica ntreaga multime V pentru a identifica un vecin nevizitat al acestuia. In
vederea reducerii numarului de verificari, se va utiliza reprezentarea prin liste de adiacenta,
si se salveaza pe stiva, pe langa valoarea nodului curent k, valoarea vecinului nevizitat gasit,
astfel ncat, la revenire, sa se continue cautarea urmatorului vecin nevizitat al nodului curent
k pornind de la acest nod. Daca nu mai exista nici un vecin nevizitat al varfului k (linia
10), atunci se revine la parintele nodului curent aflat 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 aici varianta recursiva pentru usurinta de programare si eleganta ei. Prezentam
n continuare implementarea n limbajul C a algoritmului 19:
#include <stdio.h>
49
#include <mem.h>
#define MAX 100
// Matricea de adiacenta
char vecin[MAX][MAX];
// Vector ce pastreaza starea unui nod: vizitat sau nevizitat
char vizitat[MAX];
// Numarul de varfuri din graf
int n;
// Nodul din care se porneste vizitarea
int nodi;
/**
* Se citesc numarul de noduri precum si matricea de adiacenta.
*/
void readInput(void) {
int i, j;
printf("n = "); scanf("%d",&n);
for (i = 0; i < n - 1; i++)
for (j = i + 1; j < n; j++) {
printf("a[%d,%d] = ", i, j);
scanf("%d", &vecin[i][j]);
vecin[j][i] = vecin[i][j];
}
printf(Nodul initial = ); scanf(%d,&nodi);
memset(vizitat, 0, sizeof(vizitat));
}
/**
* Parcurgerea in adancime pornind din nodul k.
*/
void dfs(int k) {
int i;
vizitat[k] = 1;
printf("%d ",k);
for (i = 0; i < n; i++)
if (vizitat[i] == 0 && vecin[k][i] == 1)
dfs(i);
}
void main(void) {
readInput();
dfs(nodi);
}
3.5
Matricea de adiacenta
O(n2 )
O(n2 )
O(n2 )
Liste de vecini
O(n + m)
O(n + m)
O(n + m)
Lista de muchii
O(n + m2 )
O(n + m2 )
O(n + m2 )
Componente conexe
Se poate defini pe multimea varfurilor unui graf neorientat G o relatie astfel: xy daca
x = y sau exista n G un lant de la x la y.
Se demonstreaza foarte usor ca relatia este o relatie de echivalenta. Se cunoaste faptul ca
o relatie de echivalenta determina pe multimea pe care este definita o partitie. Componentele
conexe vor fi elementele acestei partitii formate din varfurile ce sunt echivalente ntre ele
(conform relatiei de echivalenta).
Cu alte cuvinte, exista o partitie a multimii varfurilor V
V =
m
[
Vi , Vi Vj = , i, j = 1, m, i 6= j
i=1
m
[
Ei , Ei Ej = , i, j = 1, m, i 6= j
i=1
3.6
Muchie critic
a
Reamintim faptul ca ntrun graf neorientat G = (V, E), o muchie critica este acea muchie
care prin eliminarea ei conduce la cresterea numarului de componente conexe ale grafului.
Se cere sa se determine toate muchiile critice ale unui graf dat. In continuare sunt prezentate doua variante de rezolvare.
Solutia I
Pentru simplitate, vom trata situatia n care graful considerat este conex (daca graful nu
este conex se determina numarul de componente conexe si se aplica algoritmul pentru fiecare
componenta conexa n parte). Se elimina, pe rand, fiecare muchie a grafului si apoi se verifica
daca graful rezultat mai este conex (vezi algoritmul 22).
Algorithm 22 Algoritm de determinare a muchiei critice (prima varianta)
1: procedure MuchieCriticaI(n, m, V ecin)
Subrutina Conex verifica daca graful identificat prin matricea de adiacenta V ecin este
conex. Subrutina ntoarce valoarea 1 n cazul n care graful considerat este conex si 0 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.
Implementarea n limbajul C:
#include <stdio.h>
#include <mem.h>
#define MAX 100
52
#define TRUE 1
#define FALSE 0
char vecin[MAX][MAX];
char vizitat[MAX];
int n;
/**
* Se citesc numarul de noduri precum si matricea de adiacenta.
*/
void readInput(void) {
int i, j;
printf("n = "); scanf("%d", &n);
for (i = 0; i < n - 1; i++)
for (j = i + 1; j < n; j++) {
printf("a[%d,%d] = ", i, j);
scanf("%d", &vecin[i][j]);
vecin[j][i] = vecin[i][j];
}
}
/**
* Parcurgerea in adancime a grafului pornind din nodul de start k.
*/
void dfs(int k) {
int i;
vizitat[k] = TRUE;
printf("%d ", k);
for (i = 0; i < n; i++)
if ((vizitat[i] == FALSE) && (vecin[k][i] == 1))
dfs(i);
}
/**
* Functia verifica daca graful este conex.
*/
int conex(void) {
int i;
memset(vizitat, 0, sizeof(vizitat));
dfs(0);
for (i = 0; i < n; i++)
if (vizitat[i] == FALSE) {
return FALSE;
}
return TRUE;
}
void main(void) {
int i, j;
53
readInput();
for (i = 1; i < n; i++)
for (j = 0; j < i; j++)
if (vecin[i][j] == 1) {
vecin[i][j] = 0;
vecin[j][i] = 0;
if (conex() == FALSE) {
printf("Muchia (%d,%d) este critica! \n",i,j);
}
vecin[i][j] = 1;
vecin[j][i] = 1;
}
}
prenumu , si
lowu = min prenumx , daca [u, x] este muchie de ntoarcere, si
lowy
, y descendent direct al lui u.
Daca prenumu lowv , v descendent direct al lui u, atunci nseamna ca v sau un
descendent al lui v are 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 (vezi algoritmul
23). 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, M uchieCriticaII() si DF S 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 }. Pentru un nod i vecin cu nodul curent
k, deja vizitat, avem o muchie de ntoarcere (nodului k i sa atribuit deja un numar n
preordine): astfel valoarea lui lowk se calculeaza dupa formula lowk min {lowk , prenumi }.
Implementarea n limbajul C a algoritmului 23 este:
54
n
- numarul 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:
#include <stdio.h>
#include <mem.h>
#define MAX 100
/**
* Numarul de noduri din graf
*/
int n;
/**
* Matricea de adiacenta
*/
char vecin[MAX][MAX];
/**
* Vector ce pastreaza starea unui nod: vizitat sau nevizitat.
*/
char vizitat[MAX];
/**
55
56
memset(vizitat, 0, sizeof(vizitat));
counter = 0;
dfs(0);
}
void main(void) {
printf("\n");
readInput();
critic();
}
Exemplul 3.19 Pentru graful din figura 3.13, valorile prenum si low sunt cele din figura
3.19 (valoarea din partea dreapt
a a unui varf 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, complexitateatimp 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.
3.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.
57
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 un
anumit fel de cultura (de exemplu este specializat numai pe cositul lucernei).
Terenul agricol se modeleaza printro matrice n m. 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 dea lungul a doua elemente
vecine.
Se cere sa se determine numarul de muncitori care pot sa coseasca o cultura data.
59
Capitolul 4
Grafuri euleriene si hamiltoniene
4.1
Grafuri Euleriene
In anul 1736, matematicianul Leonhard Euler publica o lucrare asupra problemei podurilor
din Konigsberg (figura 4.1)1 n care se prezinta un studiu teoretic asupra lanturilor si ciclurilor
Euleriene.
60
Fig. 4.2: Doua figuri geometrice ce pot fi desenate folosind o singura linie
Teorema 4.2 (Euler) Un graf G conex are un lant eulerian daca si numai daca exista exact
doua varfuri n G al caror grad sa fie impar.
Demonstratie: Se demonstreaza la fel ca la teorema 4.1.
Fie G = (V, E) un graf conex ce are exact doua varfuri de grad impar, u si
v. Sa considera un nod w
/ V mpreuna cu muchiile [u, w] si [v, w]. Fie G0 = (V 0 , E 0 ),
V 0 = V {w}, E 0 = E {[u, w], [v, w]}, graful obtinut din G prin adaugarea nodului w si a
muchiilor [u, w], [v, w]. Atunci dG0 (u) este par, u V 0 exista un ciclu eulerian C n G0 .
Daca eliminam muchiile [u, w] si [v, w] obtinem un lant eulerian n G.
Corolarul 4.3 Un graf G conex (|V | 3) este eulerian daca si numai daca multimea muchiilor sale poate fi descompus
a n cicluri disjuncte.
Teorema 4.4 Pentru un graf conex cu 2k varfuri de grad impar, k 2, exista k lanturi
n G ale caror multimi de muchii formeaz
a o partitie a lui E, si din care cel mult unul are
lungime impar
a.
4.1.1
Pornind de la Teorema 4.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
61
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.
In algoritmul 25 este prezentata functia EulerRec pentru determinarea unui ciclu eulerian.
62
Fig. 4.4: Grafurile partiale obtinute n urma primelor doua etape ale algoritmului 25
Exemplul 4.5 S
a aplicam algoritmul 25 pentru graful din figura 4.3. Presupunem ca primul
element (elementul de start) este varful u0 = 1. Procedura F indCycle construieste lantul
63
4.1.2
Algoritmul lui Rosenstiehl [53] 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 sa ajuns
n u) (vezi algoritmul
26). 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 4.6 S
a aplicam algoritmul 26 pentru graful din figura 4.3. Sa presupunem ca
varful u de la care porneste algoritmul este varful 1. La sfarsitul 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 stiva si astfel u devine 9. La sfarsitul acestuia
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.
64
Algorithm 26 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
continuare, deoarece nu mai exista muchii nevizitate, se vor extrage elementele aflate pe
In
stiva S (8, 10, 6, 7, 8, 2, 10, 4, 6, 5, 7, 9, 3, 5, 4, 3, 2, 1), cate unul la fiecare pas, 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 pastrat n lista L.
Prezentam implementarea n limbajul C + + a algoritmului lui Rosenstiehl:
#include
#include
#include
#include
#include
#include
#include
<vector>
<stack>
<list>
<iostream>
<fstream>
<iomanip>
<conio.h>
65
void main(void) {
Matrice ma;
list<int> l = list<int>();
int n = readInput(ma);
rosenstiehl(n, ma, 0, l);
print(l);
}
Continutul fisierului de intrare graf1.txt corespunzator grafului din figura 4.6 este urmatorul:
66
6
0
1
1
1
1
0
1
0
1
1
1
0
1
1
0
1
0
1
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 vector<int> Linie;
typedef vector<Linie> Matrice;
...
Matrice ma;
ma = Matrice(n, Linie(n, 0));
Dupa cum se poate observa din aplicatie, pentru reprezentarea interna a grafului am
ales matricea de adiacent
a.
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
3
67
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:
ma[u][v] = 0;
ma[v][u] = 0;
4.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 nealeasa 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
7
http://www.sgi.com/tech/stl/Iterators.html
68
Gk = G {e1 , e2 , . . . , ek }
Lk+1 [Lk , ek+1 , vk+1 ]
k k+1
end while
return Lk
end function
Exemplul 4.7 S
a aplicam algoritmul 27 pentru graful din figura 4.7. La nceput L0 = [u1 ].
Apoi se intra n ciclul while (liniile 4 8): tabelul urmator indica muchia ce a fost aleas
a 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
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 ]
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 ]
69
Pasul k
20
21
22
23
24
25
26
Muchia aleas
a
[u4 u11 ]
[u11 u10 ]
[u10 u9 ]
[u9 u13 ]
[u13 u11 ]
[u11 u12 ]
[u12 u1 ]
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 ]
4.2
Grafuri Hamiltoniene
Definitia 4.2 Se numeste lant Hamilton sau lant hamiltonian un lant L ce trece o
singura data prin toate varfurile unui graf.
Definitia 4.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 4.8 Fie G = (V, E) un graf neorientat si fie u si v doua varfuri 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, G + [u, v]
este hamiltonian.
Presupunem ca G + [u, v] este un graf hamiltonian. Atunci exista un ciclu hamiltonian n G + [u, v] pe care l notam cu C.
1. daca [u, v]
/ C atunci C este un ciclu hamiltonian si n 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 4.9 (Dirac, 1952) Un graf G = (V, E) (|V | 3) este hamiltonian daca u V
at jumatate din numarul de
avem dG (u) n2 (orice varf al grafului are gradul mai mare dec
varfuri din graf ).
70
Teorema 4.10 (Ore, 1961) Un graf G = (V, E) (|V | 3) este hamiltonian daca u, v V
avem dG (u) + dG (v) n unde u 6= v, [u, v]
/ E (pentru oricare doua varfuri dinstincte,
neadiacente, ale grafului suma gradelor lor este mai mare dec
at numarul de varfuri din graf ).
Teorema 4.11 (Chvatal, 1972) Fie un graf G = (V, E) (|V | 3) si d1 , d2 , . . . , dn o
secvent
a grafic
a. Daca este satisfacut
a relatia
k a.i. dk k
n
dnk n k
2
Graful din figura 4.6 are mai multe cicluri hamiltoniene: de exemplu C1 = [1, 5, 2, 3,
6, 4, 1] si C2 = [1, 3, 6, 4, 2, 5, 1].
Nod
dG
1
4
2
4
3
4
4 5
4 2
6
2
n
6
= = 3.
2
2
1
3
2
4
3
3
4 5
4 4
6
5
7
3
8
4
1
3
3 7
3 3
2
4
4
4
5
4
8 6
4 5
4.2.1
n
dnk n k.
2
Problema comisvoiajorului
Un comis-voiajor trebuie sa viziteze n orase etichetate prin 1, . . . , n. Pentru a simplifica problema, acesta va pleca ntotdeauna din orasul numarul 1 si se va ntoarce tot n 1, trec
and prin
fiecare oras o singura data (cu exceptia orasului 1 care va fi vizitat de doua ori). Cunoscand
distantele ntre orase, sa se determine o parcurgere (ciclu hamiltonian) de cost minim.
Pentru a reprezenta drumurile directe dintre orase (existenta unei legaturi directe ntre
acestea) utilizam matricea costurilor A = (aij ), i, j = 1, n:
, daca xi = xj
0
(4.1)
ai,j = +
, daca [xi , xj ]
/E.
(4.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
Graful respectiv are mai multe cicluri hamiltoniene:
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].
73
20
12
16
0
14
6
24
14
0
10
12
10
10
0
C4 = [1, 3, 6, 7, 8, 4, 2, 5, 1].
Programul corespunzator scris n limbajul C este urmatorul:
#include <stdio.h>
#include <memory.h>
#define NMAX 100
74
Fig. 4.9: Rezultatul rularii programului pentru determinarea ciclului hamiltonian optim
75
return 0;
}
/**
* Functia afiseaza solutia calculata a problemei.
*/
void AfisareSolutie(int n) {
int i;
printf("Costul ciclului optim: %ld \n", cost_optim);
printf("Ciclu hamiltonian: [");
for (i = 1; i < n+1; i++)
printf("%d, ", x_optim[i]);
printf("%d]\n", x_optim[n+1]);
}
/**
* Functia de continuare: aici se verifica daca elementul de pe pozitia k
* nu se mai afla in sirul A.
*/
int CanContinue(int n, Matrice a, int k, long cost, Vector vizitat, Vector x) {
if ((vizitat[x[k]] == 1) || (mai_mare(cost, a[x[k-1]][x[k]], cost_optim))
|| ((k != n+1) && (x[k] == 1)))
return 0;
else
return 1;
}
/**
* Functia salveaza solutia optima (circuitul hamiltonian de cost minim)
* determinata/gasita pana in momentul curent.
*/
void EvaluareSolutie(int n, long cost, Vector x) {
int i;
cost_optim = cost;
for (i = 1; i <= n+1; i++)
x_optim[i] = x[i];
AfisareSolutie(n);
}
/**
* Metoda backtracking implementata.
*/
void ComisVoiajorBacktracking(int n, Matrice a) {
Vector x;
Vector vizitat;
int k, gasit;
long cost;
memset(vizitat, 0, sizeof(vizitat));
x[1] = 1;
76
cost = 0;
k = 2; x[k] = 1;
while (k > 1) {
gasit = 0;
while ((x[k] + 1 <= n) && (gasit == 0)) {
x[k] = x[k] + 1;
gasit = CanContinue(n, a, k, cost, vizitat, x);
}
if (gasit == 1) {
if (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;
cost = cost - a[x[k-1]][x[k]];
}
}
}
void main(void) {
Matrice a;
int n = readInput(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
77
Folosim o functie (mai mare()) cu scopul de a realiza verificarea cost + axk1 ,xk +1 >
costoptim :
int mai_mare(long a, long b, long c) {
if (a + b > c)
return 1;
else
return 0;
}
http://en.wikipedia.org/wiki/Arithmetic_overflow
78
}
if (gasit == 1) {
if (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;
cost = cost - a[x[k-1]][x[k]];
}
}
}
adica la pasul k = k 1 = 9 1 = 8.
79
80
Fig. 4.10: Pasii efectuati de algoritm pentru determinarea ciclului hamiltonian asociat grafului din figura 4.8
81
Fig. 4.11: Pasii efectuati de algoritm pentru determinarea ciclului hamiltonian asociat grafului din figura 4.8
(continuare)
82
Capitolul 5
Arbori. Arbori binari
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 special
a, numit rad
acina arborelui;
2. celelalte noduri sunt repartizate n n multimi distincte, disjuncte doua cate doua, 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 varf al unui arbore cu radacina constituie radacina unui subarbore compus din varful
respectiv si toti descendentii sai.
Se observa ca un arbore impune o structura de organizare ierarhica asupra elementelor
unei multimi.
Definitia 5.2 Se numeste arbore liber (free tree) un graf G = (V, E) neorientat, conex
si aciclic.
Teorema 5.1 (Propriet
atile arborilor liberi) Fie G = (V, E) un graf neorientat. Urmatoarele
afirmatii sunt adevarate:
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.
5. G este aciclic, si |E| = |V | 1.
6. G este aciclic, dar daca adaug
am o muchie oarecare n E, graful obtinut contine un ciclu.
Daca se alege un varf sau nod drept rad
acina arborelui, atunci un arbore liber devine
arbore cu rad
acin
a. In general vom folosi termenul de arbore n loc de termenul corect arbore
cu rad
acin
a.
Un v
arf 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.
83
10
12
11
13
14
15
Definitia 5.3 Ad
ancimea unui varf este data de lungimea drumului de la rad
acin
a la acel
altimea unui varf se determina ca fiind lungimea celui mai lung drum dintre acel varf
varf. In
altimea rad
si un varf terminal. In
acinii determina n
altimea arborelui. Nivelul unui varf se
calculeaz
a ca diferenta dintre n
altimea arborelui si adancimea acestui varf.
Toate varfurile unui arbore ce au aceeasi adancime se spune ca sunt pe acelasi nivel. Un
arbore oarecare cu radacina este n-ar daca fiecare varf are pana la n descendenti directi.
O multime de arbori disjuncti formeaza o padure.
Din punct de vedere grafic, un arbore se poate reprezenta descriind nodurile cu ajutorul
unor cercuri sau patrate n care se afla informatia aferenta, iar relatia de descendenta prin
legaturile ce unesc nodurile cu fii lor.
5.1
Arbori binari
10
12
11
5.1.1
Moduri de reprezentare
1
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 N U LL. 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 5.3 avem:
1(2(3(0, 4(0, 0)), 5(6(0, 0), 7(0, 0))), 8(0, 9(0, 0)))
2. forma standard se indica radacina arborelui, iar pentru fiecare varf k se precizeaza
descendentul sau stang si/sau drept.
(
k , daca nodul k este descendentul stang al nodului i
Stangi =
0 , nu exista
(
k , daca nodul k este descendentul drept al nodului i
Drepti =
0 , nu exista
Rad = 1
Pentru arborele din figura 5.3 avem urmatoarea reprezentare:
Nod
Stang
Drept
1 2
2 3
8 5
3 4 5
0 0 6
4 0 7
6 7 8
0 0 0
0 0 9
9
0
0
85
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;
Nod
Tata
1 2 3
0 1 2
4 5 6
3 2 5
7 8 9
5 1 8
5.1.2
Metode de parcurgere
Exista mai multe moduri de parcurgere a unui arbore binar. Indiferent de metoda de vizitare
aleasa se parcurge mai ntai subarborele stang si apoi subarborele drept. Dupa momentul n
care un nod k este vizitat fata de subarborele sa stang si drept, avem:
parcurgere n preordine (rad
acin
a, subarbore stang, subarbore drept).
In urma parcurgerii n preordine a arborelui din figura 5.3 rezulta urmatoarea ordine
pentru noduri: 1, 2, 3, 4, 5, 6, 7, 8, 9.
Fig. 5.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.
In figura 5.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
29). 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
87
(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.
Algorithm 29 Algoritm de parcurgere n preordine
1: procedure Preordine(Rad, Stang, Drept, T ata)
2:
k rad
3:
q0
4:
while (q = 0) do
5:
while (stangk 6= 0) do
6:
call V izit(k)
7:
k stangk
8:
end while
9:
call V izit(k)
10:
while (dreptk = 0) (q = 0) do
11:
f ound 0
12:
while (q = 0) (f ound = 0) do
13:
jk
14:
k tatak
15:
if (k = 0) then
16:
q1
17:
else
18:
if (j = stangk ) then
19:
f ound 1
20:
end if
21:
end if
22:
end while
23:
end while
24:
if (q = 0) then
25:
k dreptk
26:
end if
27:
end while
28:
return
29: end procedure
88
char drept[MAX];
/** tata[k] - parintele nodului k */
char tata[MAX];
/** numarul de noduri din arbore */
int n;
/** nodul radacina */
int rad;
void vizit(int nod) {
printf("%2d ", nod);
}
void readInput(void) {
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]);
}
for (k = 1; k <= n; k++) {
printf("tata[%d] = ", k); scanf("%d", &tata[k]);
}
}
void preord(int rad) {
int i, j;
int q, found;
i = rad; q = 0;
while (q == 0) {
while (stang[i] != 0) {
vizit(i);
i = stang[i];
}
vizit(i);
while (drept[i] == 0 && q == 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;
89
}
}
if (q == 0)
i = drept[i];
}
}
void main(void){
readInput();
preord(rad);
}
. S.push(k)
. S.pop(k)
90
91
}
}
if (q == 0)
i = drept[i];
}
}
void main(void) {
readInput();
inord(rad);
}
5
6
vizitam pentru prima data, n cazul metodei de vizitare n inordine vom marca un nod n
momentul n care l vizitam pentru prima data daca este frunza, respectiv a doua oara daca
este un nod interior, iar la postordine vom marca un nod n momentul n care l vizitam
pentru ultima oara.
Arborele asociat unei expresii aritmetice
Oricarei expresii aritmetice i se poate asocia un arbore binar astfel:
fiecarui operator i corespunde un nod neterminal avand drept subarbori stang si drept
cei doi operanzi corespunzatori.
fiecare nod terminal este etichetat cu o variabila sau o constanta.
Arborele din figura 5.8 corespunde expresiei matematice: 2 ((a + b) (c + b)). In urma
vizitarii n postordine a acestui arbore se obtine forma polonez
a postfixat
a asociata expresiei
anterioare: 2ab + cb + . Pentru a obtine valoarea expresiei respective, arborele poate fi
parcurs n postordine.
5.2
Arbori binari de c
autare
94
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 35):
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 34); acesta va fi sters n mod fizic, dar la un pas
ulterior;
toate informatiile legate de datele continute n nodul B vor fi copiate n nodul A;
subarborele drept al nodului B se va lega fie ca descendent drept al nodului A
(daca nodul B este descendent direct al nodului A), fie ca descendent stang al
tatalui nodului B;
95
<stdio.h>
<string.h>
<stdlib.h>
<conio.h>
96
Fig. 5.10: Arbore binar de cautare. Stergerea unui nod. a) Nod fara descendenti b) Nod cu un singur
descendent c) Nod cu doi descendenti.
97
98
99
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;
}
}
return p;
}
void main(void) {
100
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);
}
}
}
Exercitii:
1. Sa consideram o secventa de intrare compusa din termeni definiti recursiv astfel:
(a) X este un termen;
(b) daca A si B sunt termeni, atunci (A B) este un termen;
(c) orice termen se construieste cu ajutorul regulilor (i) si (ii).
Un termen este transformat cu ajutorul urmatoarei reguli de rescriere: sablonul (((X A) B) C)
unde A, B si C sunt termeni, este nlocuit cu ((A C)(B C)).
Reducerea unui termen nseamna aplicarea acestei reguli. Un termen se numeste redus
daca nu contine nici un subtermen care sa poata fi redus. Un termen poate fi considerat
ca subtermen pentru el nsusi.
Sa se realizeze un algoritm care determina pentru un termen de intrare, termenul redus
corespunzator. Un termen contine doar simbolurile X, ( si ), fara spatii ntre ele.
Lungimea unui termen de intrare este 100.
101
Observatia 5.4 O expresie va avea maxim 9 nivele de parantezare. Numarul de reduceri ce pot fi efectuate este finit. Liniile de iesire contin maxim 80 caractere. Caracterele
posibile din termenii de iesire sunt tot X,( si ).
Intrare
(((XX)X)X)
(((XX)(XX)X)
(XX)
Iesire
((XX)(XX))
((XX)((XX)X))
(XX)
2. 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.
Pentru arborele din figura 5.11 drumurile de
astfel:
ABC :
ABDE :
ABDF :
AG :
3. Scrieti o subrutina nerecursiva care numara nodurile frunza ale unui arbore binar.
4. Realizati o subrutina care sa determine nivelul cu cele mai multe noduri frunze dintrun
arbore binar.
5. Determinati natimea unui arbore binar printro functie nerecursiva.
102
abroad in_strainatate
baker brutar
calf gamba
dice zaruri
ear ureche
7. 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.
8. 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.
9. 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.
103
Capitolul 6
Arbori oarecare
Reamintim definitia unui arbore oarecare:
Definitia 6.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 special
a, numit rad
acina arborelui;
2. celelalte noduri sunt repartizate n n multimi disjuncte doua cate doua, A1 , A2 , , An , fiecare
multime Ai constituind la randul ei un arbore.
6.1
Moduri de reprezentare
10
4 5 6 7
7 0 0 0
0 6 0 8
104
8 9
0 0
9 10
10
0
0
NULL
NULL
NULL
5
NULL
6
NULL
NULL
NULL
8
NULL
NULL
10
NULL
NULL
Fig. 6.2: Exemplu de arbore oarecare cu 10 noduri reprezentat prin legaturi fiu-frate
In figura 6.2 este reprezentat arborele din figura 6.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 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
105
Table 6.2: Reprezentarea arborelui din figura 6.1 folosind liste cu descendenti.
Nod
C
1 2 3
1 0 4
2 3 4
L=
2 3 0
4 5 6
6 0 0
5 6 7
5 0 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. 6.3: Exemplu de arbore oarecare cu 10 noduri reprezentat prin liste cu descendenti
In figura 6.3 este reprezentat arborele din figura 6.1 folosind liste cu descendenti.
106
4 5 6
1 3 3
7 8 9
4 4 4
10
4
rad
data;
fiu;
tata;
frate;
NULL
1
NULL
NULL
2
NULL
NULL
5
NULL
NULL
NULL
10
NULL
NULL
NULL
NULL
NULL
Fig. 6.4: Exemplu de arbore oarecare cu 10 noduri reprezentat prin legaturi fiu-frate-tata
In figura 6.4, reprezentarea din figura 6.2 este mbunatatita prin leg
atura tata.
6.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:
107
Apreordine: se viziteaza mai ntai radacina, si apoi, n ordine, subarborii sai [93],
[136]. 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 36). Parcurgerea n Apreordine este
exemplificata pe arborele oarecare din figura 6.1.
Algorithm 36 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
10:
else
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)
. S.pop(k)
108
/**
* 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);
109
110
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);
}
111
6.3
obtinut un arbore de acoperire T 00 ce are costul mai mic decat T 0 , contradictie cu faptul ca
T 0 este un arbore de acoperire de cost minim.
unde Ti1 = {xi } este un arbore format dintrun singur nod. La pasul k vom avea T k =
{T1k , T2k , . . . , Tnk }.
Se alege un arbore Tik si muchia (u0 , v 0 ) de cost minim dintre toate muchiile (u, v) avand
proprietatea ca o extremitate apartine arborelui Tik (u0 Tik ) si cealalta apartine lui V \ VTik
(v 0 V \ VTik ).
T k+1 se obtine din T k prin reuniunea arborilor Tik si Tjk (i 6= j), unde u0 Tik si v 0 Tjk .
La pasul n 1, multimea T n1 = {T1n1 } va fi compusa dintrun singur element, acesta
fiind arborele de acoperire de cost minim.
Algoritmii cei mai cunoscuti pentru determinarea arborilor de acoperire de cost minim
sunt:
1. Algoritmul lui Boruvka (vezi algoritmul 37)
Algorithm 37 Algoritmul lui Boruvka (varianta schematica)
1: procedure Boruvka1(G, C, n; L)
2:
initializeaza padurea de arbori P compus
a din n arbori, fiecare arbore fiind compus dintrun
3:
4:
5:
6:
7:
8:
9:
10:
11:
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 . adauga muchia e la lista de muchii alese ce vor forma arborele de acoperire
de cost minim
end for
adauga toate muchiile selectate n cadrul f or-ului anterior la P
end while
end procedure
Algoritmul lui Prim implementat simplu are o complexitate O(n2 )[35] si atinge o complexitate de O(m log n) daca se folosesc heapuri Fibonacci [56], sau pairing heaps [128].
In tabelul 6.4 sunt prezentati mai multi algoritmi dezvoltati dea lungul timpului pentru
determinarea arborelui de acoperire minimal si complexitatile lor. Karger, Klein si Tarjan [82]
pornind de la algoritmul lui Boruvka au realizat un algoritm randomizat pentru determinarea
arborelui de acoperire minimal, avand o complexitate liniara, iar Chazelle [32] a dezvoltat
un algoritm avand complexitatea O(n(m, n)) ((m, n) este inversa functiei lui Ackerman).
Pe de alta parte, Pettie si Ramachandran [110] au propus un algoritm demonstrat ca fiind
optimal, avand complexitatea cuprinsa ntre O(n + m) si O(n(m, n)).
Table 6.4: Algoritmi pentru determinarea arborelui de acoperire minim
Anul
Complexitate
1975
E log log V
1976
E log log V
1984 E log V, E + V log V
1986
E log log V
1997
E(V ) log (V )
2000
E(V )
2002
optimal
Autori
Yao
Cheriton-Tarjan
Friedman-Tarjan
Gabow-Galil-Spencer-Tarjan
Chazelle
Chazelle [32]
Pettie-Ramachandran [110]
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
114
6.3.1
Algoritmul lui Boruvka [78] a fost descoperit de catre matematicianul ceh Otakar Boruvka
n 1926 [26], 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 40).
Algorithm 40 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 minima min{c(u0 , v 0 )|(u0 , v 0 ) E, u0
9:
10:
11:
12:
13:
14:
15:
16:
U, v 0
/ V \ U}
determina componenta U 0 ce contine pe v
L (u, v)
end for
for U M do
reuneste multimile ce contin pe u si v, U si U 0
end for
end while
end procedure
Exemplul 6.4 S
a consider
am graful din figura 6.5:
G = (V, E), V = {1, 2, 3, 4, 5, 6, 7, 8}
Aplicand algoritmul lui Boruvka, la pasul nt
ai vor fi selectate muchiile (1, 2), (3, 6), (4, 5),
(4, 7) si (7, 8). In urma operatiilor de reuniune a componentelor conexe pe baza muchiilor
selectate, vor mai ram
ane 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 component
a conex
a.
6.3.2
Algoritmul a fost descoperit mai ntai de V. Jarnik (1930) [77], si apoi independent de Prim
(1957) [113] si Djikstra (1959) [44].
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 5.1 rezulta faptul ca acest
graf partial aciclic cu n 1 muchii este un arbore (de acoperire).
Conform Propriet
atii 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 41):
115
116
= 1. La nceput,
4 5 6
7
5
1 1 1
1
0 0 0
0
1
0
117
Dup
a primul pas
1 2
d
14
tata 0 1
vizitat 1 0
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
La pasul al treilea avem doua noduri ale caror distante la noduri din multimea S sunt
egale: 6 si 8. Alegem primul nod - 6 si muchia (3, 6):
1 2 3 4 5 6
7 8
d
14 6 21 5 12 14 6
tata 0 1 1 5 1 3
6 6
vizitat 1 0 1 0 1 1
0 0
Al patrulea nod ales
1 2 3
d
14 6
tata 0 1 1
vizitat 1 0 1
este
4
10
8
0
8:
5
5
1
1
6
12
3
1
7
10
8
0
8
6
6
1
distant
a
6
7
12 10
3
8
1
1
minima este 7:
8
6
6
1
4:
6
12
3
1
8
6
6
1
7
10
8
1
6.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
A
C
1
a
1
2
b
2
3
e
3
4
x
1
5
y
1
6
z
2
7
u
2
8
v
3
a2 =0 b0 are semnificatia urmatoare: elementul de pe pozitia 2 are valoarea 0 b0 . c2 = 2 elementul de pe pozitia 2 face parte din multimea de identificator 2. Sau a4 =0 x0 - elementul
de pe pozitia 4 are valoarea 0 x0 , 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 44).
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 N U LL.
Functia M erge 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).
120
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
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 45) faptul ca arborele obtinut n urma unor
operatii repetate M erge 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. num
arul 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 .
121
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
122
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 (tataradx = tatarady ) 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
M erge este reprezentata de comprimarea drumului. Aceasta consta n a apropia fiecare nod
de radacina arborelui caruia i apartine. Astfel n timpul operatiei F ind(x) se determina mai
ntai radacina arborelui caruia i apartine (rad) si apoi se mai parcurge o data drumul de la
nodul x la radacina, astfel tatay rad, y lantul de la x la rad.
1: function Find(x)
2:
yx
3:
while (tatay 6= y) do
4:
y tatay
5:
end while
6:
rad y
7:
yx
8:
while (tatay =
6 y) do
9:
y tatay
10:
tatax rad
11:
xy
12:
end while
13:
return rad
14: end function
6.3.4
Algoritmul lui Kruskal [90] este o ilustrare foarte buna a metodei generale Greedy (vezi
algoritmul 46). 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.
123
Exemplul 6.7 S
a consider
am graful din figura 6.6. Muchiile grafului si costurile lor sunt
(am ales sa reprezent
am 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
1 1 1 1 2
2
2
3 3
4 4
4 5
6
6
7
2 2 3 5 4
5
6
6 8
5 7
8 6
7
8
8
3 14 6 5 12 16 20 12 12 21 24 10 16 14 6 10
Dup
a asezarea n ordine cresc
atoare a muchiilor dupa cost avem:
A 1 2 3 4
5
6 7
8 9 10 11 12 13 14 15
1 1 1 6 4
7
2 3
3 1
6
2 5
2
4 4
2 5 3 8 8
8
4 6
8 2
7
5 6
6
5 7
3 5 6 6 10 10 12 12 12 14 14 16 16 20 21 24
figura 6.7, sunt ilustrati pasii algoritmului lui Kruskal aplicat pe graful considerat. La
In
nceput se initializeaz
a padurea de arbori, fiecare arbore fiind alcatuit dintrun singur nod.
La pasul nt
ai evaluam muchia (1, 5), si deoarece cele doua extremit
ati fac parte din arbori
distincti, vom selecta aceast
a muchie. Reunim arborii din care fac parte cele doua extremit
ati,
L = {(1, 5)} (vezi figura 6.7 (1)).
124
Fig. 6.7: Algoritmului lui Kruskal exemplificat pe graful din figura 6.6
La pasul al doilea, luam n considerare muchia (1, 3) si multimea muchilor selectate devine
L = {(1, 5), (1, 3)} (vezi figura 6.7 (2)).
La pasul al patrulea, avem muchia (4, 8) avand costul 10. 4 face parte din arborele de
rad
acin
a 4 iar 8 face parte din arborele de rad
acin
a 6. Deoarece extremit
atile muchiei sunt
amplasate n arbori distincti select
am muchia curent
a pentru arborele partial de cost minim,
urma reuniunii arborilor corespunz
L = {(1, 5), (1, 3), (6, 8), (4, 8)}. In
atori celor doua noduri
se obtine configuratia din figura 6.7 (4).
Subliniem faptul ca arborii reprezentati n figura 6.7 sunt diferiti de arborii ce conduc la obtinerea solutiei problemei: arborii din figura constituie suportul necesar pentru
reprezentarea structurii de date pentru multimi disjuncte, ce este utilizata pentru efectu125
area eficienta a operatiilor F ind si M erge. Acest lucru justifica faptul ca desi la pasul al
patrulea select
am 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). 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 (2, 4): L = {(1, 5), (1, 3), (6, 8),
(4, 8), (7, 8), (2, 4)}.
La pasul al saptelea avem muchia (3, 6). Nodurile 3 si 6 fac parte din arbori diferiti, astfel
nc
at muchia curent
a poate fi selectat
a pentru solutia problemei, L = {(1, 5), (1, 3), (6, 8),
(4, 8), (7, 8), (2, 4), (3, 6)}.
6.4
Exercitii
Iesire
DA
1 2 3 4
2 5 6
3 7
4
5
6
7
(Timisoara-pregatire, 1996)
2. 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.
3. 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.
4. 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).
Datele de intrare sunt compune 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.
126
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 6.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. 6.8: Descrierea conexiunilor posibile dintre componentele unei placi electronice
5. Scrieti o subrutina nerecursiva care sa determine cheia minima dintrun arbore oarecare.
6. Determinati naltimea unui arbore oarecare printro functie nerecursiva.
7. 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
la fiecare punct de supraveghere parcurgand un anumit drum. Evident este de dorit ca
lungimea acestui drum sa fie minima si valoarea maxima dintre lungimile drumurilor
minime pentru fiecare punct de supraveghere n parte sa fie cat mai mica posibila.
Se cere sa se determine punctul de control n care trebuie instalat comandamentul cat
si drumurile ce vor fi parcurse de la comandament la fiecare punct de supraveghere,
astfel ncat aceste drumuri sa aiba valoarea lungimii minima si cel mai lung dintre ele
sa fie cat mai scurt posibil.
127
Capitolul 7
Grafuri orientate
7.1
Notiuni de baz
a
128
129
Fig. 7.2: Arbore de acoperire n latime pentru graful orientat din figura 7.1
7.2
Parcurgerea grafurilor
De obicei nsa, n urma vizitarii unui graf neorientat 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 7.2 este prezentat arborele de acoperire n latime corespunzator grafului din
figura 7.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 (vezi algoritmul 47). Pentru aceasta se utilizeaza doua multimi de noduri, V izitat si N eexplorat,
unde V izitat reprezinta multimea nodurilor vizitate iar N eexplorat (N eexplorat 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 ntrunul 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 n arborele de acoperire (u v).
130
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 n arborele de acoperire (v u).
arc de traversare - arcul (u, v) este un arc de traversare daca df s(v) a fost apelat si sa
terminat nainte de apelul lui df s(u).
Fig. 7.3: Arbore de acoperire n adancime pentru graful orientat din figura 7.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
131
iar postnumv momentul n care prelucrarea nodului v sa 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 7.1 ilustreaza aceste
tipuri de arce: arc de naintare (1, 4), arce de ntoarcere (2, 1), (7, 5), arc de traversare
(5, 2), arce ale arborelui de acoperire (1, 2), (3, 5), (7, 6) (vezi figura 7.3).
Algorithm 48 Algoritm de vizitare n adancime pentru un graf orientat
1: procedure DFSNum(k, n, V ecin)
Lema 7.5 Fiind dat un graf neorientat G si doua noduri oarecare u, v G ce apartin
aceluiasi arbore de acoperire n adancime rezultat n urma parcurgerii cu metoda DF S a
grafului G avem:
1. daca (u, v) este un arc al arborelui de acoperire sau un arc de naintare sau un arc de traversare postnumu > postnumv ;
2. daca (u, v) este un arc de ntoarcere postnumu < postnumv .
132
Lema 7.6 Fiind dat un graf neorientat G pentru oricare doua noduri u, v G avem:
1. u este un descendent al lui v n padurea de arbori de acoperire rezultati n urma vizitarii n
adancime a grafului G intervalul [prenumu , postnumu ] este inclus n intervalul [prenumv , postnumv ];
2. nu exista nici o leg
atur
a ntre u si v n padurea de arbori de acoperire rezultati n urma
vizitarii n adancime a grafului G intervalele [prenumu , postnumu ] si [prenumv , postnumv ]
sunt disjuncte;
3. situatii prenumu < prenumv < postnumu < postnumv sau prenumv < prenumu < postnumv <
postnumu nu sunt posibile.
Lema 7.7 Fiind dat un graf neorientat G, daca pentru doua noduri oarecare u, v G avem
relatia prenumu < prenumv < postnumu atunci:
prenumu < prenumv < postnumv < postnumu .
exista un drum de la u la v n G.
urma apel
Exemplul 7.8 In
arii procedurii DF SN um(1, 8, V ecin) (vezi algoritmul 48), secventa
de apeluri recursive ale procedurii DF SN um este ilustrat
a prin arborele de acoperire n
adancime din figura 7.3.
Numerotarea nodurilor n preordine si postordine rezultat
a n urma vizitarii este urmatoarea:
1
prenum 1
postnum 16
2
2
3
3
4
15
4
8
9
5
5
14
6
6
13
7
7
12
8
10
11
7.3
Sortarea topologic
a
133
Lema 7.9 Intrun
graf orientat aciclic, daca prenumu < prenumv si exista un drum de la
u la v n G, atunci prenumu < prenumv < postnumv < postnumu .
In practica, exista mai multe situatii n care o multime de activitati sau sarcini, trebuie
organizate ntro anumita ordine, ntre acestea existand, de obicei, o multime de restrictii
sau dependente. In activitatea de construire a unei cladiri, anumite activitati nu pot fi
ncepute decat dupa finalizarea altor activitati: spre exemplu, nu se poate ncepe ridicarea
peretilor decat dupa turnarea fundatiei si finalizarea structurii de rezistenta, nu se poate realiza instalatia electrica daca nu au fost ridicate zidurile, s.a.m.d. Sau daca un student doreste
sa si alcatuiasca un plan de studiu individual ce va contine pe langa cursurile obligatorii, si
o serie de cursuri optionale, va trebui sa tina cont de anul de studiu n care se preda fiecare
materie, precum si de cerintele obligatorii ale acestora: un curs nu poate fi inclus n planul de
studiu individual al unui student decat daca acesta a parcurs si a obtinut creditele la toate
materiile anterioare cerute explicit n programa cursului.
Algorithm 49 Algoritm de sortare topologica a unui graf orientat (prima varianta)
1: procedure
( SortTop1(n, V ecin)
n
- numarul de noduri din graf
V ecin - vector ce contine listele cu vecini ai fiecarui 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 insereaza ntro coada nodurile cu gradul interior 0
end if
end for
while (Q 6= ) do
Qk
. se extrage din coada un nod
Lk
. se insereaza nodul ntr-o lista
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 insereaza n coada nodul w.nodeIndex
end if
w w.next
. se trece la urmatorul 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 ).
Definitia 7.12 Se numeste sortare topologic
a pentru un graf orientat G = (V, E) o ordonare {x1 , x2 , . . . , xn } a nodurilor grafului astfel nc
at pentru orice arc (xi , xj ) s
a avem
134
i < j.
Prin urmare o sortare topologic
a presupune aranjarea liniara a varfurilor unui graf astfel
ncat toate arcele sale sa fie orientate de la stanga la dreapta.
Lema 7.10 Daca un graf orientat G admite o sortare topologic
a atunci G este aciclic.
Lema 7.11 Daca un graf orientat G este aciclic atunci el admite o sortare topologic
a.
Fig. 7.5: Sortare topologica cu algoritmul 49 pentru graful orientat din figura 7.4
135
n
- numarul 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 ordoneaza descrescator nodurile dupa postnumk
end procedure
Input:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
Lema 7.14 Un graf orientat G este aciclic daca si numai daca n urma unei vizitari n
adancime a acestuia nu este nt
alnit nici un arc de ntoarcere.
Ideea algoritmului 50 o reprezinta lema 7.14[132].
Exemplul 7.15 Sa consider
am ca date de intrare pentru algoritmul 50 graful orientat din
figura 7.4. Dupa etapa de initializare (liniile 2 - 5) avem:
1
prenum 0
postnum 0
vizitat 0
2
0
0
0
3
0
0
0
4
0
0
0
5
0
0
0
6
0
0
0
7
0
0
0
Se apeleaz
a mai nt
ai DF SN um(1, 7, V ecin). Secventa rezultat
a de apeluri recursive este
urmatoarea: DF SN um(1, 7, V ecin) DF SN um(2, 7, V ecin) DF SN um(4, 7, V ecin)
DF SN um(5, 7, V ecin) DF SN um(7, 7, V ecin) DF SN um(6, 7, V ecin).
urma acestei secvente valorile vectorilor prenum si postnum sunt urmatoarele:
In
1
prenum 1
postnum 12
vizitat
1
2
2
11
1
3
0
0
0
4
3
10
1
5
4
9
1
6
6
7
1
7
5
8
1
Mai ramane nevizitat un singur nod, 3, drept pentru care vom mai avea un apel DF SN um(3, 7, V ecin)
din procedura principal
a SortT op2:
1
prenum 1
postnum 12
vizitat
1
2
2
11
1
3
13
14
1
4
3
10
1
5
4
9
1
6
6
7
1
7
5
8
1
Ordonarea descresc
atoare a nodurilor multimii V dup
a valorile vectorului postnum conduce la urmatoarea asezare:
postnum 14 12 11 10 9 8 7
3
1 2
4 5 7 6
136
7.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 7.13 O component
a tare conex
a a unui graful orientat G este o multime maximala de varfuri U V , astfel nc
at, pentru fiecare pereche de varfuri {u, v} (u, v U ) exista
atat un drum de la u la v cat si un drum de la v la u. Astfel se spune ca varfurile u si v
sunt accesibile unul din cel
alalt.
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 7.14 Graful orientat G se numeste tare conex daca 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 echivalent
a daca prezinta proprietatile de reflexivitate, simetrie si
tranzitivitate:
reflexivitate: a a;
137
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.
7.4.1
Algoritmul lui KosarajuSharir a fost prezentat de Aho, Hopcroft si Ullman n lucrarea lor [3],
fiind preluat dintrun manuscris al lui S. Rao Kosaraju (M. Sharir la prezentat n lucrarea
[126]).
Algoritmul foloseste graful transpus GT asociat grafului initial G ([11], [29], [35]), 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 conex
a
a grafului G.
Prezentam implementarea n limbajul C a algoritmului anterior:
#include <stdio.h>
#include <mem.h>
#define MAX 100
#define TRUE 1
#define FALSE 0
/**
* Matricea de adiacenta
*/
char vecin[MAX][MAX];
/**
* Vector ce pastreaza starea unui nod: vizitat sau nevizitat.
*/
char vizitat[MAX];
/**
138
139
if (vizitat[i] == FALSE)
dfs(i);
// etapa a doua
memset(vizitat, 0, sizeof(vizitat));
for (i = 0; i < n; i++)
for (j = i; j < n; j++) {
tmp = vecin[i][j];
vecin[i][j] = vecin[j][i];
vecin[j][i] = tmp;
}
k = 0;
while (TRUE) {
maxim = 0;
for (i = 0; i < n; i++)
if ((vizitat[i] == FALSE) && (maxim < postnum[i])) {
maxim = postnum[i];
nod = i;
}
if (maxim == 0)
break;
k++;
printf("Componenta %d : ", k);
dfs1(nod);
printf("\n");
}
}
Exemplul 7.17 Dupa parcurgerea n adancime a grafului 7.6, valorile vectorilor vizitat si
postnum sunt urmatoarele:
1
postnum 8
vizitat 1
2
7
1
3
5
1
4 5
6 1
1 1
6
2
1
7
3
1
8
4
1
Urm
atorul nod, ca valoarea a vectorului postnum ordonat descresc
ator, este 2. Din nodul
2 se parcurg nodurile 3 si 4, rezult
and o alta component
a tare conex
a: {2, 3, 4}.
Urm
atorul nod nevizitat u ce are valoarea prenumu maxim
a este nodul 8. Se identifica
astfel mai nt
ai componenta tare conex
a {7, 8} si apoi {5, 6}.
Analiza algoritmului
Algoritmul lui Kosaraju realizeaza doua parcurgeri ale tuturor elementelor grafului G: prima
data parcurge graful G si a doua oara parcurge graful GT . Prin urmare complexitatea timp a
algoritmului este (|V | + |E|) n cazul n care graful este reprezentat prin liste de adiacenta si
este patratica n |V |2 (O(|V |2 )) atunci cand graful este reprezentat prin matricea de adiacenta.
7.4.2
Algoritmul lui Tarjan [131] este considerat drept o mbunatatire a algoritmului lui Kosaraju
prin aceea ca nu mai este nevoie de parcurgea graful G de doua ori.
Algoritmul initiaza o parcurgere n adancime a nodurilor grafului G pornind de la un nod
oarecare. O componenta tare conexa a grafului G, daca exista, constituie un subarbore al
arborelui de acoperire n adancime, iar radacina acestui subarbore este reprezentantul clasei
de echivalenta.
Prin urmare componentele tare conexe se obtin prin descompunerea arborelui/arborilor
de acoperire n adancime daca eliminam anumite arce ale acestora. Un nod este capul unei
componente tare conexe (sau rad
acina), daca acesta constituie radacina subarborelui corespunzator componentei. Arcul ce are nodulcap drept extremitate finala este cel ce trebuie
eliminat. Dupa ce determinam toate nodurilecap, subarborii arborelui/arborilor de acoperire
n adancime ce i au drept radacini sunt componentele tare conexe.
Algoritmul lui Tarjan are drept scop determinarea nodurilorcap. Astfel pentru a pastra
o ordine a vizitarii acestora pe parcursul algoritmului, nodurile vor fi adaugate pe o stiva.
Ele vor fi extrase din stiva n momentul n care procedura de vizitare DF S a unui nod este
ncheiata: astfel se determina daca nodul curent este radacina unei componente tare conexe,
si, n caz afirmativ, toate nodurile care au fost vizitate din nodul curent n cadrul parcurgerii
n adancime sunt marcate ca fiind elemente ale componentei tare conexe.
Vom numerota toate nodurile grafului n preordine (altfel spus acest numar indica pentru nodul curent numarul de noduri vizitate naintea sa), valorile fiind pastrate n vectorul
prenum (cititorul este invitat sa revada si sectiunea despre Muchie critica, cea de-a doua
solutie). Pentru un nod u G definim lowu astfel:
prenumu
lowu = min prenumx , daca [u, x] este arc de ntoarcere sau de traversare si x S
lowy
, y descendent direct al lui u
Daca dintrun 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 nodcap (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 51) seamana foarte mult cu algoritmul 23
de determinare a muchiei critice (varianta a II-a).
141
n
- numarul 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:
2
0
0
3 4
0 0
0 0
5
0
0
6
0
0
7
0
0
8
0
0
Primul element nevizitat este nodul 1 (linia 9), prin urmare se apeleaz
a DF ST arjan(1, 8, V ecin).
Rezult
a o secvent
a de apeluri recursive:
DF SN um(1, 8, V ecin) DF SN um(2, 8, V ecin) DF SN um(3, 8, V ecin) DF SN um(6, 8, V ecin)
DF SN um(5, 8, V ecin).
142
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
Stiva contine urmatoarele elemente (varful stivei fiind n dreapta): {1, 2, 3, 6, 5}.
low5 = min{prenum6 , low5 } = min{4, 5} = 4 (deoarece desi vizitat6 = 1, avem 6 S,
adica nodul 6 se afla pe stiva). Astfel n momentul terminarii apelului DF ST arjan(5, 8, V ecin),
low5 = 4.
Acum la nivelul lui DF ST arjan(6, 8, V ecin), n urma revenirii din DF ST arjan(5, 8, V ecin),
se calculeaz
a low6 = min{low6 , low5 } = min{4, 4} = 4 (linia 22).
Nodul 6 este un nodcap (low6 = prenum6 ) (vezi linia 30), si prin urmare se extrag de pe
stiva toate elementele dintre varful stivei si elementul 6 inclusiv, rezult
and prima component
a
tare conex
a: {5, 6}. Stiva ram
ane cu elementele (varful stivei fiind n dreapta) {1, 2, 3}.
Din nodul 3 se continu
a cu vizitarea nodului 8, nc
a nevizitat:
DF SN um(3, 8, V ecin) DF SN um(8, 8, V ecin) DF SN um(7, 8, V ecin).
Continutul stivei este {1, 2, 3, 8, 7}.
La nivelul apelului DF SN um(7, 8, V ecin) nu se ia n considerare la calculului valorii lui
low7 nodul 6: desi exista arcul (7, 6), nodul 6 a fost vizitat (vizitati 6= 0) si nu se mai afla
pe stiva (i
/ S). Astfel low7 = min{low7 , prenum8 } = min{7, 6} = 6.
La nivelul lui DF SN um(8, 8, V ecin), low8 = min{low8 , low7 } = min{6, 6} = 6 (linia 22).
Avem ca (low8 = prenum8 ) (linia 30) si prin urmare 8 este un nodcap. Se extrag de pe stiva
toate elementele dintre varful stivei si elementul 8 inclusiv, rezult
and a doua component
a tare
conex
a: {7, 8}.
Stiva ram
ane cu elementele (varful stivei fiind n dreapta) {1, 2, 3}.
1 2 3 4 5 6 7 8
prenum 1 2 3 0 5 4 7 6
vizitat 1 1 1 0 1 1 1 1
low
1 2 0 0 4 4 6 6
momentul terminarii apelului DF ST arjan(3, 8, V ecin), low3 = 2.
In
Revenind la nivelul apelului DF ST arjan(2, 8, V ecin), se calculeaz
a low2 = min{low2 , low3 } =
min{2, 2} = 2, si se continu
a cu urmatorul nod nc
a nevizitat, 4: DF SN um(2, 8, V ecin)
DF SN um(4, 8, V ecin).
Se adauga nodul 4 pe stiva (stiva devine {1, 2, 3, 4}), low4 = prenum4 = 8.
low4 se calculeaz
a din low4 = min{low4 , prenum3 } = min{8, 3} = 3 (exista arcul (4, 3),
nodul 3 a fost vizitat - vizitat3 = 1 si 3 S - nodul 3 se afla pe stiva).
1 2 3 4 5 6 7 8
prenum 1 2 3 8 5 4 7 6
vizitat 1 1 1 1 1 1 1 1
low
1 2 2 3 4 4 6 6
Ne ntoarcem la nivelul apelului DF ST arjan(2, 8, V ecin), low2 = min{low2 , low4 } =
min{2, 3} = 2. Arcul (2, 5) nu este luat n considerare la calculul lui low2 deoarece nodul 5 a
fost vizitat (vizitat5 = 1) si 5 nu se reg
aseste pe stiva. Astfel low2 r
am
ane cu valoarea 2.
Deoarece low2 = prenum2 , extragem de pe stiva elementele ce determina cea de-a treia
component
a tare conex
a: {4, 3, 2}. Pe stiva mai ram
ane un singur element, {1}, ce va
determina ultima component
a tare conex
a.
143
7.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 [34] si apoi
independent de catre Harold Gabow n 1999 [59].
Algoritmul construieste un graf H [59] 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 aleganduse un nod oarecare v H.
Fig. 7.8: Drumul P n algoritmul lui Gabow pentru graful din figura 7.6
.i
La finalul procedurii DF SGabow, 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
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
7.5
Exercitii
contine numarul liniilor din codul sursa. A treia linie contine numarul M (0 M < N )
de module de care depinde modulul actual. Linia urmatoare contine numele acestor
module, separate printrun spatiu. Numele unui modul nu depaseste 20 de caractere.
Descrierea modulelor este urmata de mai multe blocuri, cate unul pentru fiecare versiune
a programului. Prima linie a fiecarui bloc contine numarul k (1 k N ) de module
ce au suferit modificari de la recompilarea versiunii precedente.
Linia urmatoare contine numele modulelor, separate prin spatiu, n care au survenit
modificari. Dupa ultimul bloc exista o singura linie ce contine doar numarul 0.
Pentru fiecare versiune a programului se scrie o linie ce contine numarul liniilor codului
146
Iesire
127
(4) 4 5
(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.
147
Fig. 7.9: Pozitiile cartilor ntro biblioteca precum si posibilitatile de deplasare ale robotului
6. Se considera o multime de n elevi dintro clasa. Fiecare elev are cunostinte mai avansate
ntrun anumit domeniu. Pentru ridicarea nivelului clasei, dirigintele vrea sai aranjeze
n grupuri astfel ncat toti elevii dintrun grup sa ajunga sa cunoasca, ntro anumita
perioada de timp, toate cunostintele tuturor celorlalti colegi din acelasi grup.
Grupurile de elevi nu si vor schimba cunostintele ntre ele. Se stie ca un elev nu se
poate face nteles de catre oricine. El are o lista de preferinte fata de care el le va
mpartasi cunostintele sale. Relatia de preferinta nu este simetrica.
Sa se determine numarul minim de grupuri n care va fi mpartita clasa precum si
componenta acestora.
149
Capitolul 8
Distante n grafuri
Reteaua de drumuri europene, nationale si judetene dintro 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 dintrun 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.
Intrun 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 ] de lungime m, costul acestuia va fi dat de urmatoarea formula:
c(D) =
m1
X
c((vi , vi+1 ))
i=0
min c(s,t )
s,t Ms,t
150
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 listele de adiacenta
(listele cu vecini).
Fie u un varf al grafului numit sursa. 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 (algoritmul 53) se bazeaza pe structura algoritmului de parcurgere
n latime al unui graf (breadth first search vezi algoritmul 17). In lucrarea [103], 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 nota cu dk costul drumului minim de la varful u la varful xk , iar tatak varful anterior
lui xk , pe drumul de cost minim de la u la xk .
Algorithm 53 Algoritmul lui Moore
1: procedure Moore1(k, n, V ecin; d, tata)
Input:
k
n
V ecin
d
tata
- nodul sursa
- numarul de noduri din graf
- vector ce contine capetele listelor de vecini
Exemplul 8.1 Fie un graf orientat definit de catre urmatoarele liste de vecini:
151
1:
2:
3:
4:
5:
6:
(2, 4)
(3, 6)
(2)
(1, 5)
(4, 6, 9)
(7, 8)
7: (8)
8: (6, 7)
9: ()
10: (11)
11: (10)
urma aplicarii algoritmului lui Moore pentru acest graf si avand nodul 1 drept sursa,
In
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
Urm
arind valorile afisate, putem spune ca drumul de lungime minima de la nodul 1 la
nodul 9 are costul 3 si este compus din nodurile [1, 4, 5, 9].
Algoritmul 54 calculeaza lungimea drumurilor minime de la un nod sursa la toate nodurile
accesibile din acesta, n cazul n care lungimea unui drum se defineste ca fiind suma costurilor
asociate arcelor ce-l compun.
Algorithm 54 Algoritm lui Moore (a doua varianta)
1: procedure Moore2(k, n, C; d, tata)
k - nodul sursa
Input:
n - numarul 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
Fie G = (V, E) un graf orientat unde V = {1, 2, . . . , n} este multimea nodurilor si E este
multimea arcelor (E V V ). Pentru reprezentarea grafului se utilizeaza matricea costurilor
C:
, daca i = j
0
ci,j =
, daca (i, j)
/ E, i 6= j
152
(
1 , daca nodul k S
vizitatk =
0 , daca nodul k V \ S
Un element al acestui vector se poate modifica atunci cand se modifica dj , caz n care
tataj = k.
Algorithm 56 Algoritmul lui Dijkstra
1: function DistantaMinima(n, vizitat, d)
2:
min
3:
for j 1, n do
4:
if (vizitatj 6= 1) (dj < min) then
5:
min dj
6:
j0 j
7:
end if
8:
end for
9:
if (min = ) then
10:
return 1
11:
else
12:
return j0
13:
end if
14: end function
15: procedure Dijkstra2(n, C, u)
16:
vizitatu 1
17:
du 0, tatau 0
18:
for i 1, n do
19:
if (i 6= u) then
20:
vizitati 0
21:
di cu,i , tatai u
22:
end if
23:
end for
24:
while (true) do
25:
k DistantaM inima(n, vizitat, d)
26:
if (k < 0) then
27:
break
. forteaza iesirea dintr-o instructiune de ciclare
28:
else
29:
vizitatk 1
30:
for i 1, n do
31:
if (vizitati 6= 1) (dk + ck,i < di ) then
32:
tatai k
33:
di dk + ck,i
34:
end if
35:
end for
36:
end if
37:
end while
38: end procedure
154
j
x
u
k
0
1
2
1
1
0
1
0
4
21
1
0
1
0
155
Dup
a prima iteratie avem:
d2 + c2,3 < d3 1 + 8 < +
d2 + c2,4 < d4 1 + < 21
d2 + c2,5 < d5 1 + 4 < +
1
d
tata
vizitat
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
Dup
a pasul al treilea avem:
d3 + c3,4 < d4 8 + 12 < 21
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
156
#include <stdio.h>
#include <values.h>
#define MAX 100
#define TRUE 1
#define FALSE 0
/* numarul de noduri din graf */
int n;
/* matricea costurilor */
int c[MAX][MAX];
/* vector care pastreaza starea unui nod: vizitat sau nevizitat */
char vizitat[MAX];
/* nodul fata de care se calculeaza drumurile de lungime minima */
int u;
/* tata[i] parintele varfului i in arborele rezultat */
int tata[MAX];
/* distanta de la fiecare nod la u */
int d[MAX];
/**
* Citirea datelor de intrare - +infinit = -1
*/
void readInput(void) {
int i, j;
printf("n = "); scanf("%d", &n);
for (i = 0; i < n; i++)
for (j = 0; j < n; j++) {
scanf("%d", &c[i][j]);
if (c[i][j] < 0)
c[i][j] = MAXINT;
}
printf("Nodul initial : "); scanf("%d", &u);
}
int minim(int* d) {
int j, j0;
int min;
min = MAXINT;
for (j = 0; j < n; j++)
if (!vizitat[j] && d[j] < min) {
min = d[j];
j0 = j;
}
if (min == MAXINT)
return -1;
else
return j0;
}
157
/**
* Functia verifica daca a>b+c; variabilele sunt de tip long
* pentru ca daca suma depaseste 32767 se face trunchiere.
*/
int mai_mare(long a, long b, long c) {
return (a > b+c) ? 1 : 0;
}
void dijkstra(int u) {
int i, k;
//initializari
vizitat[u] = TRUE;
tata[u] = -1;
for (i = 0; i < n; i++)
if (i != u) {
vizitat[i] = FALSE;
d[i] = c[u][i];
tata[i] = u;
}
//partea principala
while (TRUE) {
k = minim(d);
if (k < 0)
break;
vizitat[k] = TRUE;
//actualizare
for (i = 0; i < n; i++)
if (!vizitat[i] && mai_mare(d[i],d[k],c[k][i])) {
tata[i] = k;
d[i] = d[k] + c[k][i];
}
}
}
void printSolution(void) {
int i;
for (i = 0; i < n; i++)
if (i != i0)
if (d[i] == MAXINT)
printf("Nu exista drum de la varful %d la
varful %d. \n", i0, i);
else
printf("Distanta de la varful %d la varful %d
este %d. \n", i0, i, d[i]);
}
void main(void) {
readInput();
dijkstra(u);
printSolution();
158
(8.1)
unde n = numarul de noduri ale grafului G iar m = numarul de muchii ale aceluiasi graf.
Cele trei operatii ntalnite n cadrul algoritmlui sunt (vezi si capitolul ??):
1. DeleteM in(Q) - sterge nodul ce contine cheia de valoare minima si reorganizeaza structura prin refacerea proprietatii de coad
a cu prioritate.
2. Insert(Q, p, x) - insereaza nodul p n coada cu prioritate Q.
3. DecreaseKey(Q, p, v) - modifica valoarea prioritatii nodului p din coada cu prioritate
Q, atribuindui valoarea v si reorganizeaza aceasta structura.
Astfel pentru diferite implementari ale cozii cu prioritate Q, complexitatea algoritmului
va fi [35]:
1. Lista liniara dublu nlantuita neordonata:
T (n, m) = O(n 1 + n n + m 1) = O(n2 )
159
2. Arbore 2 3:
T (n, m) = O(n 1 + n n + m 1) = O(n2 )
3. Heap-uri Fibonacci:
T (n, m) = O(n 1 + n n + m 1) = O(n2 )
8.1.3
, daca i = j
0
ci,j =
, daca (i, j)
/ E, i 6= j
(k1)
= min {ci,j + j
j=1,n
(1)
= ci,t .
, daca i = t.
160
(k)
(b) next(k) = (nexti )i=1,n , next(k) nodul spre care exista arce de lungime minima
din nodul i
(k)
= i , i = 1, n.
t - nodul destinatie
Input:
n - numarul de noduri din graf
C - matricea costurilor
2:
for i 1, n do
3:
for j 1, n do
4:
vi,j ci,j
5:
end for
6:
end for
7:
for i 1, n do
(1)
8:
i ci,t
9:
end for
10:
k1
11:
repeat
12:
k k+1
13:
for i 1, n do
14:
min
15:
for j 1, n do
(k1)
16:
if (min > ci,j + j
) then
17:
(k1)
min ci,j + j
(k)
18:
nexti j
19:
end if
20:
end for
(k)
21:
i min
22:
end for
23:
until (V (k) = V (k1) )
24: end procedure
Dupa ncheierea etapei computationale, valorile drumurilor se obtin din coloana V k iar
componenta drumurilor de valoare minima de la fiecare nod spre nodul t, se poate determina
din vectorii nextk .
Exemplul 8.4 S
a consider
am graful din figura 8.3. Matricea costurilor asociat
a acestui graf
este:
161
C=
5
0
7
7
14
0
8
12
0
12
9
0
29
16
0
Num
arul de noduri al grafului este 7 (n = 7), iar varful catre care se calculeaz
a drumurile
de cost minim de la toate celelalte varfuri este t = 7.
Pentru determinarea valorilor vectorului V (2) se efectueaz
a urmatoarele calcule:
(2)
(1)
1 : a1,j + j
a1,2 + 21 = 5 +
a1,3 + 31 = 6 +
a1,7 + 71 = 29 + 0 = 29
(2)
(1)
2 : a2,j + j
a2,4 + 41 = 7 +
a2,6 + 61 = 7 + 16 = 23
(1)
(2)
3 : a3,j + j
a3,2 + 21 = 7 +
a3,4 + 41 = 14 +
a3,5 + 51 = 8 + =
(2)
(1)
4 : a4,j + j
a4,5 + 51 = 12 +
a4,6 + 61 = 12 + 16 = 28
(2)
(1)
a5,6 + 61 = 9 + 16 = 25
(2)
(1)
a6,7 + 71 = 16 + 0 = 16
5 : a5,j + j
6 : a6,j + j
(2)
(2)
7 : cazul i = t 7
1
2
3
4
5
6
7
V (1)
29
16
0
V (2)
29
23
28
25
16
0
V (3)
28
23
30
28
25
16
0
= 0
V (4)
28
23
30
Deoarece V (3) = V (4) , calculele se opresc la pasul 4.
28
25
16
0
8.2
Pentru a determina drumurile minime ntre toate perechile de varfuri, putem alege un
algoritm ce determina drumurile minime de sursa unica (drumurile minime de la acel varf la
toate celelalte), si pe care sal aplicam pentru fiecare varf al grafului.
Un astfel de algoritm este algoritmul lui Dijkstra, algoritm ce a cunoscut multiple mbunatatiri fata de varianta orginala, ce se bazeaza pe utilizarea unor structuri de date avansate (de exemplu heap-uri Fibonacci), menite sai optimizeze timpul de executie. Astfel complexitatea
timp n cazul cel mai defavorabil pentru calculul celor mai scurte drumuri pornind dintrun
varf al unui graf cu n noduri si m arce este O(m + n log n). Daca aplicam acest algoritm
pentru toate cele n varfuri vom obtine un timp de lucru de O(mn + n2 log n).
Algorithm 59 Algoritmul lui Dijkstra pentru toate perechile de varfuri
1: procedure DijkstraAll(n, C; D)
2:
for i 1, n do
3:
for j 1, n do
4:
if ((i, j) E) then
5:
di,j ci,j
6:
else
7:
di,j
8:
end if
9:
C (i, j) cu prioritatea di,j
10:
end for
11:
end for
12:
while (C 6= ) do
13:
C (i, j)
14:
for k 1, n do
15:
if (di,j + cj,k < di,k ) then
16:
di,k di,j + cj,k
17:
actualizeaza prioritatea lui (i, k) din C la di,k
18:
end if
19:
end for
20:
for k 1, n do
21:
if (ck,i + di,j < dk,j ) then
22:
dk,j ck,i + di,j
23:
actualizeaza prioritatea lui (k, j) din C la dk,j
24:
end if
25:
end for
26:
end while
27: end procedure
163
8.2.1
, daca i = j
0
0
(8.2)
di,j = +
, daca (xi , xj )
/ E, i 6= j
0
1 + 21 +
+ 0
8 + 4
3
+
0
12
+
C=
+ + 9
0 +
+ + 3 + 0
164
S
irul de matrice Dk , rezultat al aplicarii algoritmului Roy-Floyd-Warshall pentru acest
graf, este:
165
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
0 1 21
0 8 4
3 4 0 12
9 0
3 0
D5
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 )|
un drum de la xi la xj n G}.
Inchiderea tranzitiva a unui graf orientat prezinta multiple aplicatii drept subproblema n
cazul unor probleme computationale cum ar fi: construirea unui automat de parsare utilizat
la realizarea unui compilator, evaluarea interogarilor recursive realizate asupra unei baze de
date sau analiza elementelor accesibile ntro retea de tranzitie asociata unui sistem paralel
sau distribuit.
Prima varianta de a determina nchiderea tranzitiva presupune atribuirea unui cost unitar
(= 1) arcelor din E urmata de aplicarea algoritmului RoyFloydWarshall. Vom obtine o
matrice n care, di,j = + daca nu exista un drum de la xi la xj sau di,j = c < n daca exista.
In cadrul celei de-a doua variante de calcul (vezi algoritmul 62), se nlocuiesc operatiile
min cu (sau logic) si respectiv + cu (si logic).
dki,j = 1 daca exista un drum de la xi la xj n graful G avand varfurile intermediare numai
din multimea {x1 , . . . , xk }.
(
1 , daca i = j sau (xi , xj ) E
(8.3)
dki,j =
0 , daca i 6= j si (xi , xj )
/E
k1
k1
dki,j = dk1
i,j (di,k dk,j )
Exemplul 8.6 Fie graful din figura 8.2, din care am eliminat arcul (1, 2). Matricea costurilor asociat
a acestui graf modificat este:
0 21
0 8 4
C=
3 0 12
9 0
3 0
166
D0
D2
D4
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
0
0
0
1
0
0
0
0
1
1
1
1
1
0
1
1
0
0
1
0
0
1
1
1
1
1
1
0
1
0
0
0
1
1
1
1
1
1
1
1
1
1
0
1
0
0
1
D1
D3
D5
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
1
1
1
1
0
1
0
0
0
0
1
1
1
1
1
1
1
1
1
0
1
0
0
1
1
1
1
1
1
0
1
0
0
0
1
1
1
1
1
1
1
1
1
1
0
1
0
0
1
Circuitul Hamiltonian
, daca i = j
0
ci,j = + , daca (xi , xj )
(8.4)
/ E, i 6= j
2kn
di, = ci,1 .
Algorithm 63 Algoritm de calcul al circuitului Hamiltonian
1: procedure CircuitHamiltonian(n, C; cost, D)
2:
for i 1, n do
3:
di,0 ci,1
4:
end for
5:
for k 1, n 1 do
6:
for i 1, n do
7:
optim +
8:
for j 1, n do
9:
if (j 6= i) (optim > ci,j + dj,k1 ) then
10:
optim ci,j + dj,k1
11:
end if
12:
end for
13:
di,k optim
14:
end for
15:
end for
16:
cost d1,n1
17: end procedure
Din motive de implementare vom modifica putin definitia termenului di,S : di,k = lungimea
drumului minim de la nodul xi la nodul x1 ce trece prin exact k v
arfuri intermediare distincte
ntre ele si diferite de xi .
Atunci vom avea relatia:
di,k = min {ci,j + dj,k1 }, i, j S, i 6= j
jS
(8.5)
8.4
Definitia 8.2 Se numeste graf pe mai multe niveluri un graf orientat G = (V, E) n
care:
168
min
n
{cj,k + di+1
k } cu dt = 0
(8.6)
Aceste relatii presupun utilizarea metodei programarii dinamice, mai exact metoda nainte
(vezi algoritmul 64).
Exemplul 8.7 Fie graful pe mai multe niveluri din figura 8.4. Numarul de varfuri este
n = 12.
urma aplicarii algoritmului prezentat pentru acest graf si avand varfurile 1 drept sursa,
In
respectiv 12 destinatie, se obtin urmatoarele valori pentru vectorii d si next:
d
next
1
11
4
2
10
5
3
8
6
4
9
6
5
8
9
6
2
9
8
7
12
9
1
12
10
169
11
2
12
12
0
0
>+0
>+2
>+0
>+
>+2
> 1 + 0 d9 = 1
>+1
>+
>+2
Prin urmare drumul de lungime minima de la 1 la 12 are costul 11 si trece prin nodurile
[1, 4, 6, 9, 12].
Complexitatea timp a algoritmului prezentat (algoritmul 64) este O(n2 ), ea devenind
O(n + m) daca se utilizeaza reprezentarea grafului prin liste de adiacenta.
8.5
Exercitii
caedru (vezi figura 8.5). In figura 8.6 este ilustrata o solutie a acestei probleme.
2. Cutii ndimensionale Sa consideram o cutie ndimensionala, data prin lungimea fiecarei
laturi (o cutie bidimensionala este un dreptunghi, o cutie tridimensionala este un paralelipiped, etc.).
Problema cere analizarea unui grup de k cutii ndimensionale: trebuie gasita o secventa
de lungime maxima (b1 , b2 , . . .) din grupul de k cutii astfel ncat fiecare cutie bi sa poata
fi introdusa n cutia bi+1 .
Cutia D = (d1 , d2 , . . . , dn ) poate fi introdusa n cutia E = (e1 , e2 , . . . , en ) numai daca
laturile cutiei D pot fi combinate n asa fel cu laturile cutiei E, astfel ncat lungimea
fiecarei laturi a cutiei D sa nu depaseasca lungimea laturii corespunzatoare din cutia
E. De exemplu cutia (2, 6) poate fi introdusa n cutia (7, 3).
Cutiile egale pot fi introduse una ntralta. 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)
companiei.
Pentru o aprovizionare optima, compania vrea sa investeasca n construirea unui depozit
central din care sa se aprovizioneze toate zonele; va fi deci necesar ca acest depozit sa
fie plasat ntr-unul din orase n asa fel ncat timpul total de livrare din acest depozit
n toate orasele sa fie minim. Camioanele companiei transporta marfa la depozitele
locale si se ntorc la depozitul central. Timpul de livrare este format din timpul necesar
parcurgerii drumului de la depozitul central la oras si napoi (se presupune ca soferul
va urma de fiecare data calea cea mai rapida, iar n fiecare oras depozitul local se afla
amplasat la intrare) si timpul de descarcare al camionului la destinatie, ce este de exact
30 minute. Drumurile ce leaga orasele sunt de aceeasi calitate, dar pot exista drumuri
ce iau mai mult timp ntr-un sens decat n sens invers. Pot fi, de asemenea, drumuri
cu sens unic.
Pentru a simplifica modelul, s-a stabilit pentru fiecare oras o lista cu toate drumurile
ce pleaca din oras spre celelalte orase si cat timp ia parcurgerea fiecarui drum.
(Concurs ACM Zona Pacificul de Sud)
4. 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 inundaie 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.
5. Sa se determine drumul de lungime minima necesar deplasarii unui cal pe o tabla de
sah (avand dimensiunile 8 8) de la un punct de plecare la unui de sosire, stiind ca pe
tabla exista si gauri. Calul poate fi mutat numai n casutele fara gauri.
(ACM, Eastern Europe, 1994)
6. 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 determine ordinea literelor din noul alfabet.
(ONI, 1991)
printr-un oficiu intermediar necesita un timp mare (necesar pentru sortarea corespondentei
n functie de directia spre care trebuie trimisa mai departe), se alege ca masura a
calitatii noului sistem de comunicatii numarul de oficii (inclusiv dispeceratul si oficiul
de destinatie) prin care o scrisoare trebuie sa treaca pentru a ajunge de la dispecerat
la destinatie. Conducerea postei doreste ca dintre toate sistemele de comunicatie ce se
pot forma prin suprimarea unor legaturi existente si care respecta conditiile:
O scrisoare poate ajunge de la dispecerat la orice oficiu postal
Drumul pe care o scrisoare poate sa-l urmeze de la dispecerat la orice oficiu postal
este unic
sa-l aleaga pe cel cu masura calitatii (definita mai sus) minima.
In fisierul de intrare se gasesc mai multe seturi de date; un set de date are pe prima
linie numaarul n de oficii. Fiecare oficiu este identificat printr-un numar ntre 1 si n,
oficiul 1 fiind dispeceratul. Pe urmatoarele n linii sunt date (ntr-o ordine arbitrara),
pentru fiecare k (1 k n), oficiile cu care oficiul k comunica direct printr-o legatura.
In fisierul de iesire trebuie sa apara:
Numarul de legaturi ce raman n urma operatiei de suprimare.
Numarul maxim de oficii (inclusiv dispeceratul si oficiul de destinatie) prin care
o scrisoare trebuie sa treaca pentru a ajunge de la dispecerat la destinatie.
Legaturile ramase, prezentate sub forma a n linii, pe fiecare linie afisandu-se
numarul unui oficiu urmat de numerele de identificare ale oficiilor cu care acesta
este legat direct.
Intrare
4
2 3 4
1 3
1 2 4
1 3
5
2 4
1 3
2 4 5
1 3 5
3 4
Iesire
Numarul de legaturi ramase: 3
Numarul maxim de oficii prin care trece o scrisoare: 2
Legaturi ramase:
1:
2 3 4
2:
1
3:
1
4:
1
Numarul de legaturi ramase: 4
173
9. Profesorul Heif realizeaza niste experimente cu o specie de albine din America de Sud
pe care lea 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 diferenta
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 si
numerotando cu 1, apoi celelalte celule ramase sunt numerotate circular, n sensul
acelor de ceas, ca n figura.
__
__
__
__
__/ \__/ \__/ \__/ \__
__/ \__/ \__/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\__/ \__/ \__/
\__/ \__/ \__/ \__/ \__/
\__/ \__/ \__/ \__/
10. Intro 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
dintrunul 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)
11. 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 asi scurta drumul catre destinatie.
(a) gasiti lungimea celui mai scurt drum din labirint din locul n care printul intra n
labirint pana n locul n care traieste printesa;
(b) gasiti lungimea celui mai scurt drum daca se sparge un perete.
Castelul este de 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 se 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
175
Appendix A
Probleme. Algoritmi. Complexitate
Vom considera o problem
a computational
a ca fiind o aplicatie
P : I O
(A.1)
unde I reprezinta multimea intrarilor problemei, multimea instantelor problemei, iar O reprezinta
multimea iesirilor, raspunsurilor, solutiilor. Aceasta problema P pentru fiecare intrare i I
ofera o iesire P(i) O.
Definitia A.1 Daca O = {da, nu} atunci P se va numi problem
a de decizie, P(i)
{da, nu} va fi raspunsul la ntrebarea pusa de P, iar forma uzuala de prezentare a pro-blemei va fi:
P Intrare:
i I.
Intrebare: . . ..
Exemplul A.1 S
a consider
am urmatoarea problem
a:
NrCompus Intrare:
n N, n 2.
176
(A.2)
(A.3)
(A.4)
(A.5)
A.0.1
(A.6)
Daca P este o problema de decizie, atunci ea este o problema (din) NP daca limbajul
L = P 1 (da) satisface L NP.
NP este mnemonic pentru Nedeterminist Polinomial.
Nu este bine sa asimilam NP cu nepolinomial deoarece
textP NP. Justificarea este imediata: Daca L P, atunci exista A : {da, nu, nedecidabil}
algoritm ce decide L n timp polinomial. Consideram A0 : {da, nu, nedecidabil},
satisfacand A0 (x, x) = A(x) pentru orice x . Se vede usor ca L este verificat de A0 n
timp polinomial.
Din definitiile de mai sus ar fi fost mai normal sa folosim notatia VP (verificabil polinomial ). Sintagma Nedeterminist Polinomial se justifica daca am considera algoritmi nedeterministi n care, dupa fiecare pas, este posibil sa se execute unul dintre pasii specificati
dintro multime finita de pasi succesori. Un astfel de algoritm accepta un cuvant de intrare
daca este posibila o executie care sa conduca la rezultatul da. Se poate arata ca aceasta
definitie a acceptarii nedeterministe este echivalenta cu cea de verificare data mai sus, n
care certificatul este utilizat pentru efectuarea alegerilor corecte ale passilor urmatori ai unei
executii a algoritmului nedeterminist.
NP noteaza clasa problemelor de decizie pentru care raspunsurile afirmative au certificate
care pot fi folosite pentru a demonstra succint (n timp polinomial) corectitudinea lor.
178
Intuitiv, NP este clasa tuturor problemelor de decizie pentru care se poate verifica un
raspuns pozitiv (da) rapid daca ni se da o solutie.
De exemplu, pentru problema M axCut D un raspuns afirmativ are drept certificat o
partitie (S , T ) a multimii varfurilor grafului (iar proprietatea ca suma lungimilor muchiilor
cu o extremitate n S si cealalta n T nu depaseste pragul k se face n timp polinomial).
Prin urmare M axCut D NP.
Daca am considera urmatoarea problema de decizie:
UnDrum Intrare:
G un graf, a, b varfuri ale lui G.
Intrebare: Exista un drum unic de la a la b n G?
Cum s-ar putea justifica un raspuns da la o problema UnDrum? Daca prezentam un
drum anume drept certificat, el nu ne asigura ca nu mai exista s altele. O demonstratie
succinta pare a nu exista. Prin urmare nu se cunoaste daca U nDrum NP.
Definitia A.12 Clasa de complexitate coNP:
co NP = {L | \ L NP}.
O problema de decizie P co NP daca
L = P 1 (da) co NP (echivalent, L = P 1 (nu) NP).
Exemplu de problema din co NP:
NeHam Intrare:
G un graf.
Intrebare: Este adevarat ca n G nu exista un circuit
care sa treaca exact o data prin fiecare varf al sau?
Observatia A.4 P NP co NP.
A.0.2
Reduceri polinomiale
0
la L. Atunci, A = A F este un algoritm polinomial care decide L . (x , A (x) = da
A(F (x)) = da F (x) L x L0 ; A0 este polinomial deoarece multimea polinoamelor
este nchis
a la operatia de compunere).
2. Relatia este tranzitiv
a: L1 L2 , L2 L3 L1 L3 .
Definitia A.14 Limbajul L este NPdificil (engl. NP-hard) daca L0 NP are loc
L0 L.
Definitia A.15 Limbajul L este NPcomplet daca L NP si L este NPdificil.
Terminologia se transfera pentru probleme de decizie.
Spunem ca problema de decizie P1 se reduce polinomial la problema de decizie P2 , si
notam P1 P2 , daca L1 = P11 (da) L2 = P21 (da).
179
(A.7)
(A.8)
(A.9)
Appendix B
Liste. Stive. Cozi
Definitia B.1 Lista liniara reprezint
a o multime de elemente omogene (de acelasi tip) cu
urmatoarele caracteristici:
- elementele verifica o relatie liniara de succesiune, adica fiecare element are un singur succesor
si un singur predecesor (cu exceptia primului si ultimului element)
- fiecare element are o anumita pozitie n lista
Un tip special de lista este lista vida (far
a nici un element).
B.1
Stiva
Un tip abstract de date este definit printro multime de valori si operatiile asociate definite peste aceasta
multime de valori. Operatiile trebuie sa fie binedefinite si sa fie independente de orice implementare particulara.
181
Pe de alta parte, stiva este o structura de date de tip container deoarece ea depoziteaza
elemente de un anumit tip. Pentru a putea sa operam asupra unei colectii de elemente
pastrate ntro stiva se definesc urmatoarele operatii, n afara de operatiile fundamentale
push si pop:
push(x:object) adauga obiectul x n varful stivei.
pop():object elimina si ntoarce obiectul din varful stivei; daca stiva este goala avem
o situatie ce genereaza o exceptie.
peek():object ntoarce valoarea obiectului din varful stivei fara al extrage.
size():integer ntoarce numarul de obiecte din stiva.
isEmpty():boolean ntoarce true daca stiva nu contine nici un obiect.
isFull():
, daca x 12
,n rest
adauga o parantez
a pe stiva, iar
1: S ch
extrage o parantez
a din stiva.
Exemplul B.2 Se da o expresie aritmetica ce contine operanzi, operatori si paranteze. Se
doreste sa se verifice daca aceast
a expresie este parantezat
a corect: n orice moment al pars
arii
expresiei numarul de paranteze deschise este mai mare sau egal cu numarul de paranteze
nchise, pentru ca la final cele doua valori sa fie egale.
algoritmul 67 vom utiliza o funtie getN extT oken() ce ntoarce valoarea urmatorului
In
element atomic (token) al expresiei ce este analizata.
Se parcurge secventa de token-uri: daca valoarea token-ului curent este parantez
a deschisa
0 0
0 0
( ( ) atunci se salveaz
a pe stiva, daca valoarea este parantez
a nchis
a ( ) ) atunci se extrage
de pe stiva o parantez
a deschisa, iar daca are alta valoare se ignora.
Dac
a se doreste extragerea unui element de pe stiva (a unei paranteze deschise) iar aceasta
este vida nseamn
a ca numarul de paranteze nchise este mai mare la un moment dat dec
at
numarul de paranteze deschise, ceea ce nu este bine din punctul de vedere al corectitudinii
expresiei. Astfel expresia aritmetica (2 (6 + 7)/(4 6 2))/(19 + 1/(7 3)). este corecta
din acest punct de vedere, pe cand expresia ((7 4/3)/2)) ((4 + 5)) 2 7). nu este corect
a.
(Pentru a delimita n mod clar o expresie aritmetica, presupunem ca aceasta se termina cu
un punct - .)
182
B.2
Coada
O coada completa (dequeue) este o lista liniara n care operatiile de inserare si stergere
sunt realizate la ambele capete ale listei.
O coada simpla se poate implementa prin intermediul unei liste liniare simplu nlantuita,
fiind nevoie de doua variabile referinta pentru a indica elementul din fata (head ), respectiv
elementul din spate (rear ).
De asemenea, asemanator cu stiva, coada este o structura de date de tip container deoarece
ea pastreaza elemente de acelasi tip. Definim urmatoarele operatii pentru a manipula elementele unei colectii pastrata ntro coada:
enqueue(x:object) insereaza obiectul x la sfarsitul cozii.
dequeue():
184
call init(S)
token getN extT oken()
while (token 6=0 .0 ) do
if (token =0 (0 ) then
call push(S, 0 (0 )
else
if (token =0 )0 ) then
if (isEmpty(S) = true) then
call pop(S)
else
Output Expresie incorecta: #paranteze deschise #paranteze inchise
return
end if
end if
end if
token getN extT oken()
end while
if (isEmpty(S) = true) then
Output Parantezele deschise se inchid corect cu parantezele inchise
else
Output Expresie incorecta: #paranteze deschise #paranteze inchise
end if
Fig. B.3: Structura de date coada implementata static - pozitia primului element este fixa, si pozitia ultimului
element este variabila.
B.2.1
Pentru implementarea statica a structurii de coada vom utiliza urmatoarele variabile (vezi
figura B.3 si algoritmul 68):
capacitate pastreaza numarul de elemente maxim ce poate fi stocat n aceasta structura.
last indicele ultimului element adaugat n coada.
coada vector alocat static cu un anumit numar de elemente.
Operatiile enQueue(), f ront(), isF ull() si isEmpty() au o complexitate O(1), iar operatia
deQueue() are o complexitate de O(n) deoarece la fiecare extragere a unui element ntregul
vector coada este deplasat spre stanga cu o pozitie (vezi algoritmul 68).
185
B.2.2
Coada circular
a - implementare static
a
Daca dorim ca si operatia de extragere a primului element din coada, deQueue(), sa aiba o
complexitate O(1), vom utiliza doua variabile, f irst si last, unde prima indica varful cozii iar
186
cea dea doua spatele cozii. Se observa ca fata si spatele cozii migreaz
a spre dreapta, dupa
un numar de operatii de inserare si extragere, lasand un numar de locasuri libere n dreapta.
Ideea naturala este de a utiliza si celulele ramase libere din partea dreapta a tabloului: atunci
cand ajunge la limita din stanga, last continua cu prima valoare din partea stanga.
Aceasta varianta de implementare induce o problema noua: trebuie sa facem diferenta
dintre o coada plina (spatele cozii ajunge din urma capul cozii) si o coada goala (atunci cand
capul cozii ajunge din urma spatele cozii). Exista mai multe metode pentru rezolvarea acestei
situatii: se pastreaza tot timpul o celula neocupata, se utilizeaza nca o variabila ce indica
starea cozii (plina, goala sau nici una dintre aceste doua stari), se utilizeaza doua variabile
ce pastreaza tot timpul numarul de inserari si numarul de extrageri si cu ajutorul acestora
se calculeaza numarul curent de elemente aflate n coada.
Vom implementa cea dea doua tehnica descrisa mai sus, structura de date bazanduse
pe urmatoarele variabile:
f irst indicele primului element din coada.
last indicele ultimului element adaugat n coada.
empty variabila booleana ce primeste valoarea true atunci cand coada nu contine nici
un element.
coada vector alocat static cu M AX elemente.
La nceput, atunci cand coada nu contine nici un element, vom avea f irst = 0 si last =
M AX 1 (M AX - numarul maxim de elemente).
B.3
Liste
Conform definitiei B.1, o lista este o colectie de elemente ntre care este stabilita o anumita
ordine. Avem mai multe forme sub care poate fi ntalnita o lista:
list
a liniara simplu nl
antuit
a fiecare nod contine o referinta catre elementul urmator
(next). Ultimul element nu are element succesor asa cum se poate observa n figura
B.4.
list
a circular
a simplu nl
antuita elementul succesor al ultimului element al listei este
primul element (vezi figura B.5).
187
list
a liniara dublu nl
antuit
a fiecare nod contine o referinta catre elementul precedent
(prec) si catre elementul urmator (next). Primul element nu are element precedent, iar
ultimul element nu are element succesor (vezi figura B.6).
188
list
a circular
a dublu nl
antuit
a elementul succesor al ultimului element al listei este
primul element, iar elementul predecesor al primului element al listei devine ultimul
element al listei (vezi figura B.7).
189
Appendix C
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).
Algorithm 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 C.1 Enuntul problemei: Se cere sa 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 obtinut
a n vectorul x = (x1 , x2 , . . . , xn ) A1 A2 . . . An .
La pasul k se ncearc
a 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 repet
a. 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 C.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
191
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 urmator, 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 verifica valorile 1, 2, 3 si 4 pentru x4 . In
conditiilor de continuare pentru x4 = 4, se urmareste trecerea la pasul k = k+1 = 5. Deoarece
suntem la ultimul element al vectorului x (linia 21), se obtine prima solutie, ce se si afiseaz
a:
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 urmatoarea valoare din domeniul de valori neverificat
a
este x3 = 4 (linia 15). Solutia partiala respect
a conditiile de continuare (linia 16), astfel nc
at
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, conduc
and la o solutie finala (linia 21): 1 2 4 3 Algoritmul se continu
a n acelasi
mod pan
a 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);
}
192
/**
* 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.
*/
193
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 obtinut
a n vectorul x = (x1 , x2 , . . . , xm ) A1 A2 . . . Am (vezi algoritmul 72).
La pasul k se ncearc
a atribuirea unui alt element din multimea Ak lui xk . Conditia de
continuare se refer
a la proprietatea ca elementele vectorului solutie trebuie sa respecte relatia
x1 < x2 < . . . < xk . Acest lucru se obtine foarte usor prin urmatorul 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)
continuare prezent
In
am o implementare a algoritmului 72 n limbajul C:
195
#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++;
196
atate a t
arilor
A - matricea de adiacenta/de vecin
Input: n - numarul de tari
199
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;
200
201
}
else
//altfel revenim la un element anterior si alegem o alta valoare
i--;
}
}
void main(void) {
readInput();
run();
print();
}
202
Bibliografie
[1] W. Ackermann, Zum Hilbertschen Aufbau der reellen Zahlen, Journal Mathematische
Annalen, Springer, vol. 99:1, pp. 118133, 1928.
[2] 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.
[3] A. V. Aho, J. E. Hopcroft, J. D. Ulmann, Data Structures and algorithms, AddisonWesley, 1983.
[4] A. V. Aho, J. D. Ulmann, Foundation of Computer Science, Computer Science Press,
1992.
[5] R. Andonie, I. Garbacea, Algoritmi fundamentali, o perspectiv
a C++, Editura Libris,
1995.
[6] A. W. Appel, G. J. Jacobson, The worlds fastest Scrabble program, Communications of
the ACM, vol. 31:5, pp. 572578, 1988.
[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 informatica. 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] Gh. Barbu, I. Vaduva, M. Bolosteanu, Bazele informaticii, Editura Tehnica, Bucuresti,
1997.
[13] T. Balanescu, S. Gavrila, H. Georgescu, M. Gheorghe, L. Sofonea, I. Vaduva, Pascal si
Turbo Pascal, Editura. Tehnica, Bucuresti, 1992.
[14] P. Bazavan, Elemente de Teoria Algoritmilor, Editura Sitech, Craiova, 2007.
[15] R. Bellman, Dynamic Programming, Princeton University Press, 1957, Dover paperback
edition, 2003.
203
[16] R. Bellman, On a routing problem, Quarterly Applied Mathematics, XVI(1), pp. 87-90,
1958.
[17] 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.
[18] J. Bentley, R. Sedgewick, Fast Algorithms for Sorting and Searching Strings, Proceedings
of the 8th Annual ACM-SIAM Symposium on Discrete Algorithms, 1997.
[19] J. Bentley, R. Sedgewick, Ternary Search Trees, Dr. Dobbs Journal, 1998.
[20] J. Bentley, Programming Pearls, 2nd Edition, Addison-Wesley, 2000.
[21] C. Bereanu, Algoritmica Grafurilor, Editura Sitech, Craiova, 2006.
[22] 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.
[23] O. Berkman, U. Vishkin, Recursive Star-Tree Parallel Data Structure, SIAM Journal on
Computing, vol.22(2), pp.221-242, 1993.
[24] M. de Berg, M. van Kreveld, M. Overmars, O. Schwartzkopf, Computational Geometry:
Algorithms and Applications, Springer, 2000.
[25] A. Binstock, J. Rex, Practical algorithms for programmers, 8th Edition, Addison-Wesley,
1999.
[26] O. Boruvka, O jistem problemu minimalnm (About a certain minimal problem), Acta
Societ. Scient. Natur. Moravicae, 3, pp. 37-58, 1926.
[27] G. Brassard, P. Bratley, Algorithmics. Theory and Practice, Prentice Hall, 1988.
[28] R. P. Brent, An improved Monte Carlo factorization algorithm, BIT Numerical Mathematics, Springer, vol. 20(2), pp. 176184, 1980.
[29] D. D. Burdescu, Analiza complexit
atii algoritmilor, Editura Albastra, ClujNapoca,
1998.
[30] D. D. Burdescu, M. Brezovan, M. Cosulschi, Structuri de date arborescente cu aplicatii
n Pascal si C, Reprografia Universitatii din Craiova, 2000.
[31] D. D. Burdescu, Liste, arbori, grafuri, Editura Sitech, Craiova, 2005.
[32] B. Chazelle, A minimum spanning tree algorithm with inverse-Ackerman type complexity,
J. ACM, 47, pp. 1028-1047, 2000.
[33] D. Cherition, R. E. Tarjan, Finding minimum spanning trees, SIAM Journal on Computing, vol. 5, pp. 724-741, 1976.
[34] J. Cheriyan, K. Mehlhorn, Algorithms for dense graphs and networks on the random
access computer, Algorithmica, vol. 15, pp. 521-549, 1996.
204
[73] E. Horowitz, Fundamentals of Data Structures in C++, Computer Science Press, 1995.
[74] V. Hristidis , N. Koudas , Y. Papakonstantinou , D. Srivastava, Keyword Proximity
Search in XML Trees, IEEE Transactions on Knowledge and Data Engineering, vol.
18(4), pp. 525539, 2006.
[75] D. A. Huffman, A Method for the Construction of Minimum-Redundancy Codes, Proceedings of the I.R.E., 1952, pp. 1098-1102.
[76] C. Ionescu, I. Zsako, Structuri arborescente, Editura Tehnica, Bucuresti, 1990.
[77] V. Jarnk, O jistem problemu minimalnm (About a certain minimal problem), Prace
Moravske Prrodovedecke Spolecnosti, 6, pp. 5763, 1930.
[78] D. Jungnickel, Graphs, Networks and Algorithms, Algorithms and Computation in Mathematics, vol. 5, 3rd Edition, Springer, 2008.
[79] A. B. Kahn, Topological sorting of large networks, Communications of the ACM, vol.
5(11), pp. 558-562, 1962.
[80] A. Karatsuba, Y. Ofman, Multiplication of multiple numbers by mean of automata,
Dokadly Akad. Nauk SSSR 145, no. 2, 1962.
[81] A. A. Karatsuba, The complexity of computations, Proc. Steklov Inst. Math., Vol. 211,
pp. 169183, 1995.
[82] D. Karger, P. Klein, R. Tarjan, A randomized linear-time algorithm to find minimum
spanning trees, Journal of ACM, 42, pp. 321328, 1995.
[83] B. W. Kernigham, D. M. Ritchie, The C Programming Language, 2nd Edition, Prentice
Hall, 1988.
[84] B. W. Kernigham, R. Pike, The Practice of Programming, Addison-Wesley, 1999.
[85] D. C. Kozen, The Design and Analysis of Algorithms. Texts and Monographs in Computer Science, Springer, 1993.
[86] J. Kleinberg, E. Tardos, Algorithm Design, Addison-Wesley, 2005.
[87] D. E. Knuth, Arta program
arii calculatoarelor, vol. 1 Algoritmi fundamentali, Teora,
Bucuresti, 1999.
[88] D. E. Knuth, Arta program
arii calculatoarelor, vol. 2 Algoritmi seminumerici, Teora,
Bucuresti, 2000.
[89] D. E. Knuth, Arta program
arii calculatoarelor, vol. 3 Sortare si Cautare, Teora, Bucuresti, 2001.
[90] J. B. Kruskal, On the shortest spanning subtree of a graph and the traveling salesman
problem, Proc. of the American Mathematical Society, 7, pp. 48-50, 1956.
[91] C. Levcopoulos, O. Petersson, Heapsort - Adapted for Presorted Files, in Proceedings of
the Workshop on Algorithms and Data Structures, LNCS, vol. 382, Springer-Verlag, pp.
499-509, 1989.
207
209
[129] V. Strassen, Gaussian elimination is not optimal, Numerische Mathematik, vol. 14, pp.
354-356, 1969.
[130] 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.
[131] R. Tarjan, Depthfirst search and linear graph algorithms, SIAM Journal on Computing,
vol. 1(2), pp. 146160, 1972.
[132] R. E. Tarjan, Edge-disjoint spanning trees and depth-first search, Algorithmica, vol.
6(2), pp. 171-185, 1976.
[133] R. E. Tarjan, Applications of path compression on balanced trees, Journal of the ACM,
vol. 26(4), pp. 690-715, 1979.
[134] I. Tomescu, Grafuri si programare liniara, Ed. Didactica si Pedagogica, Bucuresti, 1975.
[135] S. Tudor, Tehnici de programare, L&S Infomat, Bucuresti, 1996.
[136] I. Tutunea, Algoritmi, logic
a si programare, Reprografia Universitatii din Craiova, 1993.
[137] I. Tutunea, S. Pescarus, Algoritmi si programe Pascal. (Culegere de probleme), Reprografia Universitatii din Craiova, 1994.
[138] J. Vuillemin, A data structure for manipulating priority queues, Communications of
the ACM, vol. 21:4, pp. 309315, 1978.
[139] J. Vuillemin, A unifying look at data structures, Communications of the ACM, vol.
23:4, pp. 229-239, 1980.
[140] M. A. Weiss, Data Structures and Algorithm Analysis, Benjamin Cummings, 1992.
[141] M. A. Weiss, Data structures and problem solving using Java, Pearson Addison Wesley,
2006.
[142] D. Wells, The Penguin Dictionary of Curious and Interesting Numbers, Middlesex,
Penguin Books, 1986.
[143] H. S. Wilf, Algorithms and Complexity, Internet edition, 1996.
[144] J. W. J. Williams, Algorithm 232 - Heapsort, Communications of the ACM, vol. 7(6),
pp. 347-348, 1964.
[145] S. Winograd, A new algorithm for inner product, IEEE Trans. Computers, C-17, pp.
693694, 1968.
[146] N. Wirth, Algorithms and Data Structures, Prentice Hall, 1986.
[147] 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.
[148] D. Zaharie, Introducere n proiectarea si analiza algoritmilor, Eubeea, Timisoara, 2008.
210