1 Introducere n algoritmi 3
1.1 Limbajul pseudocod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2 Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.3 Exercit ii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2 Elemente de analiza algoritmilor 23
2.1 Metoda substitut iei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.2 Schimbarea de variabila . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.3 Metoda iterativa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.4 Teorema master . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.5 Exercit ii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3 Grafuri. Grafuri neorientate 34
3.1 Not iuni de baza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3.2 Operat ii pe grafuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.3 Moduri de reprezentare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
3.4 Parcurgerea grafurilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
3.4.1 Parcurgerea n lat ime (BF-Breadth-First) . . . . . . . . . . . . . . . . . 46
3.4.2 Parcurgerea D (D - Depth) . . . . . . . . . . . . . . . . . . . . . . . . 50
3.4.3 Parcurgerea n adancime (DFS-Depth First Search) . . . . . . . . . . . 51
3.5 Componente conexe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.6 Muchie critica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
3.7 Exercit ii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
4 Grafuri euleriene si hamiltoniene 64
4.1 Grafuri Euleriene . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
4.1.1 Algoritm pentru determinarea unui ciclu eulerian . . . . . . . . . . . . 65
4.1.2 Algoritmul lui Rosenstiehl . . . . . . . . . . . . . . . . . . . . . . . . . 68
4.1.3 Algoritmul lui Fleury . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
4.2 Grafuri Hamiltoniene . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
4.2.1 Problema comisvoiajorului . . . . . . . . . . . . . . . . . . . . . . . . 76
5 Arbori. Arbori binari 87
5.1 Arbori binari . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
5.1.1 Moduri de reprezentare . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
5.1.2 Metode de parcurgere . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
5.2 Arbori binari de cautare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
5.3 Exercit ii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
1
6 Arbori oarecare 109
6.1 Moduri de reprezentare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
6.2 Metode de parcurgere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
6.3 Arbori de acoperire de cost minim . . . . . . . . . . . . . . . . . . . . . . . . . 117
6.3.1 Algoritmul lui Boruvka . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
6.3.2 Algoritmul lui Prim . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
6.3.3 Structuri de date pentru mult imi disjuncte . . . . . . . . . . . . . . . . 123
6.3.4 Algoritmul lui Kruskal . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
6.4 Exercit ii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
7 Grafuri orientate 134
7.1 Not iuni de baza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
7.2 Parcurgerea grafurilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
7.3 Sortarea topologica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
7.4 Componente tare conexe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
7.4.1 Algoritmul lui Kosaraju . . . . . . . . . . . . . . . . . . . . . . . . . . 144
7.4.2 Algoritmul lui Tarjan . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
7.4.3 Algoritmul lui Gabow . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
7.5 Exercit ii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
8 Distant e n grafuri 156
8.1 Drumul minim de la un varf la celelalte varfuri . . . . . . . . . . . . . . . . . . 157
8.1.1 Algoritmul lui Moore . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
8.1.2 Algoritmul lui Dijkstra . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
8.2 Drumuri minime ntre toate perechile de varfuri . . . . . . . . . . . . . . . . . 166
8.2.1 Algoritmul lui Roy-Floyd-Warshall . . . . . . . . . . . . . . . . . . . . 166
8.3 Exercit ii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
9 Fluxuri n ret ele de transport 174
9.1 Ret ea de transport. Flux. Taietura . . . . . . . . . . . . . . . . . . . . . . . . 174
9.2 Graf rezidual. Drum de ameliorare. Flux maximtaietura minima . . . . . . . 178
9.3 Metoda Ford-Fulkerson . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
9.3.1 Algoritmul Ford-Fulkerson (varianta) . . . . . . . . . . . . . . . . . . . 182
9.4 Exercit ii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
A Probleme. Algoritmi. Complexitate 188
A.0.1 Vericare n timp polinomial . . . . . . . . . . . . . . . . . . . . . . . . 190
A.0.2 Reduceri polinomiale . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
B Metoda Backtracking 193
2
Capitolul 1
Introducere n algoritmi
Denit ia 1.1 Algoritmul constituie o reprezentare nita a unei metode de calcul ce per-
mite rezolvarea unei anumite probleme. Se poate spune ca un algoritm reprezinta o secvent a
nita de operat ii, ordonata si complet denita, 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 nt elegeau prin algoritm o regula (sau o mult ime de reguli)
pe baza careia se efectuau calcule aritmetice: de exemplu n secolul al XVI-lea, algoritmii se
foloseau la nmult iri sau njumatat iri de numere.
Fiecare propozit ie ce face parte din descrierea unui algoritm este de fapt o comanda ce
trebuie executata de cineva, acesta putand o persoana sau o masina de calcul. De altfel,
un algoritm poate 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 specica succesiuni posibile de transformari ale datelor. Un tip de date poate
caracterizat printr-o mult ime de valori ce reprezinta domeniul tipului de date si o mult ime
de operat ii denite peste acest domeniu. Tipurile de date pot organizate n urmatoarele
categorii:
1. tipuri de date elementare (de exemplu tipul ntreg, tipul real ) - valorile sunt unitat i
atomice de informat ie;
2. tipuri de date structurate (de exemplu tipul tablou, tipul nregistrare) - valorile sunt
structuri relativ simple rezultate n urma combinat iei 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 ecare pas trebuie sa specice operat ia pe care urmeaza sa o efectueze
algoritmul asupra datelor de intrare;
2. corectitudine - rezultatele trebuie sa e corecte;
3
3. generalitate - algoritmul trebuie sa ofere solut ia nu numai pentru o singura problema
ci pentru o ntreaga clasa de probleme;
4. nitudine - algoritmul trebuie sa se termine ntr-un timp nit;
5. ecient a - un algoritm poate utilizat numai n situat ia n care resursele de calcul
necesare acestuia sunt n cantitat i rezonabile, si nu depasesc cu mult posibilitat ile cal-
culatoarelor la un moment dat.
Un program reprezinta implementarea unui algoritm ntr-un limbaj de programare.
Studiul algoritmilor cuprinde mai multe aspecte:
elaborare - activitatea de concepere a unui algoritm are si un caracter creativ, din
aceasta cauza neind posibila deprinderea numai pe cale mecanica. Pentru a facilita
obt inerea unei solut ii la o problema concreta se recomanda folosirea tehnicilor generale
de elaborare a algoritmilor la care se adaugan mod hotarator intuit ia programatorului;
exprimare - implementarea unui algoritm intr-un limbaj de programare se poate face
utilizand mai multe stiluri: programare structurata, programare orientata pe obiecte
etc.;
validare - vericarea corectitudinii algoritmului prin metode formale;
analiza - stabilirea unor criterii pentru evaluarea ecient ei unui algoritm pentru a-i
putea compara si clasica.
Un model de reprezentare al memoriei unei masini de calcul este acela al unei structuri
liniare compusa din celule, ecare celula ind identicata printr-o adresa si putand pastra o
valoare corespunzatoare unui anumit tip de data. Accesul la celule este facilitat de variabile.
O variabila se caracterizeaza prin:
un identicator - un nume ce refera variabila;
o adresa - desemneaza o locat ie de memorie;
un tip de date - descrie tipul valorilor memorate n celula de memorie asociata.
1.1 Limbajul pseudocod
Limbajul natural nu permite o descriere sucient de riguroasa a algoritmilor, de aceea, pentru
reprezentarea acestora se folosesc alte modalitat i de descriere precum:
scheme logice;
limbajul pseudocod.
In cazul n care pasul de incrementare este 1, atunci el se poate omite din enunt ul repetitiv
for, variabila contor ind incrementata automat la ecare repetit ie cu valoarea 1. Daca pasul
de incrementare p are valoarea 1 atunci avem urmatoarele situat ii posibile pentru construct ia
for:
Expresie1 > Expresie2: <instructiune> nu se executa niciodata;
Expresie1 = Expresie2: <instructiune> se executa exact o data;
Expresie1 < Expresie2: <instructiune> se executa de Expresie2Expresie1+1 ori.
Exemplul 1.3 Un caz destul de frecvent ntalnit n practica este cel n care se cere aarea
elementului maxim, respectiv minim dintr-un sir de elemente. Acest sir de elemente poate
7
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.
Prezentam n continuare algoritmul pentru determinarea elementului de valoare minima
ce face parte dintr-un sir de numere naturale (vezi algoritmul 3).
Algoritm 3 Algoritm pentru calculul elementului de valoare minima dintrun sir
1: Input {N, x
1
, x
2
, ..., x
N
}
2: min x
1
3: for i 2, N do
4: if (min > x
i
) then
5: min x
i
6: end if
7: end for
8: Output { min }
In acest algoritm se observa utilizarea enunt ului repetitiv for, ntalnit, n general, n
situat iile n care se cunoaste apriori numarul de pasi pe care trebuie sa-l realizeze instruct iunea
repetitiva.
Mai ntai se init ializeaza variabila min cu x
1
, presupunand ca elementul de valoare
minima este primul element din cadrul vectorului X (linia 2).
In continuare vom com-
para valoarea variabilei min cu valoarea ecarui element din sir (liniile 37): daca vom gasi
un element a carui valoare este mai mica decat cea a minimului calculat pana la momentul
curent (linia 4), vom ret ine noua valoare n variabila min (linia 5).
Structura repetitiva condit ionata posterior repeat. . .until prezinta urmatoarea sintaxa:
1: repeat
2: <instructiune>
3: until <expresie-booleana>
1: sum 0
2: i 1
3: repeat
4: sum sum + i
5: i i + 1
6: until (i > n)
Atata timp cat <expresie-booleana> este falsa, se executa <instructiune>. Se observa
faptul ca instruct iunile dintre repeat si until se vor executa cel put in o data.
Exemplul 1.4 Algoritmul 4 prezentat n continuare calculeaza suma primelor n numere
naturale, S = 1 + 2 + 3 +. . . +n.
Notam cu S
n
suma primelor n numere naturale (S
n
= 1 + 2 + 3 + . . . + n). Aceasta se
poate calcula ntr-o maniera incrementala astfel:
S
1
= 1
S
2
= 1 + 2 = S
1
+ 2
S
3
= (1 + 2) + 3 = S
2
+ 3
S
4
= (1 + 2 + 3) + 4 = S
3
+ 4
. . .
S
n
= (1 + 2 +. . . +n 1) +n = S
n1
+n
Observat ia 1.5 1. Algoritmul 4 utilizeaza enunt ul repetitiv cu test nal, repeat ...until.
8
Algoritm 4 Algoritm pentru calculul sumei primelor n numere naturale (prima varianta)
1: Input {N}
2: S 0
3: i 1
4: repeat
5: S S + i
6: i i + 1
7: until (i > N)
8: Output {S}
2. Enunt urile S 0 si i 1 au rolul de a atribui valori init iale variabilelor S si i.
Intotdeauna variabilele trebuie init ializate corect din punctul de vedere al algoritmului
de calcul. Variabila ce ret ine rezultatul unui proces de nsumari succesive se va init ializa
ntotdeauna cu 0, valoare ce nu va inuent a rezultatul nal. O variabila ce pastreaza
rezultatul unei sume sau al unui produs se va init ializa cu elementul neutru fat a de
operat ia respectiva.
3. Atribuirile i = i +1 si S = S +i (liniile 5 si 6) sunt lipsite de sens din punct de vedere
algebric.
Insa atunci cand ne referim la un sistem de calcul electronic, aceste operat ii
se refera la valoarea curenta si la cea anterioara a unei variabile. De asemenea, trebuie
sa se faca o distinct ie clara ntre operat ia de atribuire si cea de egalitate: operat ia de
atribuire () face ca valoarea curenta a variabilei din stanga operatorului de atribuire
sa e init ializata cu valoarea expresiei din dreapta acestuia (n expresia k i + 2
variabila k primeste valoarea rezultata n urma evaluarii expresiei i + 2), pe cand
operat ia de egalitate verica daca valoarea elementului din stanga operatorului = (sau
a expresiei aate n partea stanga a operatorului) este egala cu valoarea expresiei din
dreapta acestuia (de exemplu rezultatul evaluarii expresiei k = i + 2) are valoarea de
adevar true daca valoarea variabilei k este egala cu valoarea rezultata n urma evaluarii
expresiei i + 2 si are valoarea de adevar false n caz contrar).
Prin urmare, n cazul expresiei i i +1, valoarea curenta a variabilei i devine valoarea
anterioara a aceleiasi variabile incrementata cu o unitate.
Suma primelor n numere naturale se constituie n suma termenilor unei progresii arit-
metice ce se poate calcula direct cu formula sumei, astfel 1 + 2 + . . . + n =
n(n+1)
2
. Avand
n vedere aceasta identitate, algoritmul de calcul a sumei primelor n numere naturale se
simplica foarte mult (vezi algoritmul 5).
Algoritm 5 Algoritm pentru calculul sumei primelor n numere naturale (a doua varianta)
1: Input {N}
2: S n (n + 1)/2
3: Output {S}
Proceduri si funct ii
Procedurile sunt subrutine ale caror instruct iuni se executa ori de cate ori acestea sunt apelate
prin numele lor.
9
Apelarea procedurilor se face n unitatea de program apelanta prin numele procedurii,
primit la denire, urmat de lista parametrilor actuali. Aceasta trebuie sa corespunda ca
numar si tip cu lista parametrilor formali, n situat ia n care exista o lista de parametri
formali n antetul subrutinei. Denit ia unei proceduri are urmatoarea sintaxa:
1: procedure <nume>(<lista parametri formali>)
2: <instructiune>
3: end procedure
lista parametri formali (opt ionala) simbolizeaza o lista de identicatori (parametri) ce
permite transferul datelor ntre subrutina apelanta si subrutina apelata. Acesti parametri se
specica prin nume (identicator) urmat, eventual, de tipul de data al parametrului. Mai
mult i parametri de acelasi tip pot grupat i, folosindu-se drept separator virgula.
De fapt, lista parametrilor formali poate descrisa mai detaliat astfel:
1: procedure <nume>(<PFI; PFO>)
2: end procedure
unde
PFI = lista parametrilor formali de intrare.
PFO = lista parametrilor formali de iesire.
Enunt ul de apel al unei proceduri are urmatoarea sintaxa:
1: CALL <nume>(PAI; PAO)
PAI - lista parametrilor actuali de intrare, ce corespund parametrilor formali de intrare.
Corespondent a se face de la stanga la dreapta si trebuie sa e de acelasi tip cu parametrii
formali.
PAO - lista parametrilor actuali de iesire.
Exemplul 1.6 Sa se realizeze un algoritm care determina cel mai mare divizor comun a
doua numere naturale.
Reamintim cateva rezultate teoretice ce vor folositoare pentru nt elegerea procesului de
calcul al algoritmului ce va prezentat.
Teorema 1.7 (Teorema mp art irii cu rest) Pentru doua numere ntregi a si b, cu b =
0, doua numere ntregi q si r, unice, astfel ncat:
a = b q +r, 0 r < |b|
unde a se numeste dempart itul, b mpart itorul, q catul iar r restul.
Denit ia 1.2 Cel mai mare divizor comun (notat cmmdc) a doua numere naturale a si b
este un numar natural d cu proprietat ile:
1. d|a, d|b (d este un divizor comun al lui a si b)
2. d
N astfel ncat d
|a, d
|b avem d
n
2
3
corespunde
n
3
4
corespunde
n
4
.
.
.
.
.
.
k
..
corespunde
n
k
..
Sir
1
Sir
2
Se observa faptul ca primul sir (Sir
1
) este compus din elemente cu valori consecutive,
iar al doilea sir (Sir
2
) nu respecta aceasta proprietate. Numerele dintro pereche (p,
n
p
) sunt
12
legate ntre ele astfel: n momentul n care am vericat faptul ca numarul p nu este divizor
al lui n, implicit am dedus ca nici n/p nu este un divizor al lui n. Prin urmare nu mai este
nevoie sa vericam si valoarea n/p. Primul sir este strict crescator iar cel de-al doilea este
strict descrescator. Elementele din primul sir devin mai mari decat cele din al doilea atunci
cand k n/k k
2
n k
n) (prim = true)) do
8: if (n mod i = 0) then
9: prim false
10: else
11: i i + 1
12: end if
13: end while
14: if (prim = true) then
15: Output {Numarul este prim.}
16: else
17: Output {Numarul nu este prim.}
18: end if
19: end if
Exemplul 1.10 Sa se aproximeze, cu o precizie data, limita sirului de numere reale a
n
,
n 0:
a
n
=
n
k=0
1
k!
. (1.2)
Analizand structura termenului general al sirului a
n
,
a
n
=
1
0!
+
1
1!
+
1
2!
+. . . +
1
(n 1)!
+
1
n!
(1.3)
se observa ca avem relat ia:
a
n+1
a
n
=
1
(n + 1)!
.
Se noteaza t
n
=
1
n!
. Vom calcula termenul a
n
pana cand diferent a dintre doi termeni consecu-
tivi ai sirului este mai mica decat :
|a
n
a
n1
| <
1
n!
< t
n
< . (1.4)
Pe de alta parte avem urmatoarea relat ie ntre doi termeni consecutivi ai sirului t
n
t
n+1
=
t
n
n + 1
, (1.5)
pe care o vom folosi pentru a reduce numarul de calcule efectuate de algoritmul 9.
13
Algoritm 9 Algoritm pentru calculul limitei unui sir de numere cu aproximare
1: Input {}
2: a 1, t 1, k 1
3: while (t ) do
4: a a + t
5: t
t
k+1
6: k k + 1
7: end while
8: a a + t
9: Output {a, k}
Exemplul 1.11 Se spune ca un vector este simetric daca primul element este egal cu ultimul,
al doilea cu penultimul etc. Algoritmul urmator verica daca un vector de numere ntregi este
simetric.
Vom utiliza doua variabile de indici, ce pornesc, una din stanga, si cealalta din dreapta.
Atata timp cat variabila din stanga este mai mica decat variabila din dreapta iar elementele
din vector corespunzatoare variabilelor de indiciere sunt egale, se incrementeaza variabila din
stanga si se decrementeaza variabila din dreapta. Dupa parasirea instruct iunii de ciclare, se
face o vericare pentru determinarea condit iei care nu a mai fost ndeplinita si a condus la
iesirea din bucla. Daca i j atunci condit ia (i < j) nu a mai fost ndeplinita. Acest lucru
conduce la concluzia ca cealalta condit ie, (x
i
= x
j
) a fost ndeplinita tot timpul (i < j, i, j =
1, n). Deci sirul este simetric n aceasta situat ie.
Algoritm 10 Algoritm pentru vericarea simetriei unui vector
1: Input {N, x
1
, x
2
, ..., x
N
}
2: i 1, j N
3: while ((i < j) (x
i
= x
j
)) do
4: i i + 1
5: j j 1
6: end while
7: if (i j) then
8: Output {Vectorul este simetric.}
9: else
10: Output {Vectorul nu este simetric.}
11: end if
Exemplul 1.12 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.
Dupa cum se observa din construct ia sirului, acesta este format din concatenarea mai
multor secvent e de numere naturale consecutive, ecare secvent a ncepand cu valoarea 1.
Lungimea ecarei subsecvent e este cu o unitate mai mare decat lungimea subsecvent ei ante-
rioare: astfel prima subsecvent a are lungimea 1, a doua subsecvent a are lungimea 2, a treia
subsecvent a are lungimea 3, etc.
In cadrul algoritmului 11 se va ncerca decrementarea vari-
abilei index cu lungimea cate unui sir n ntregime (index index k), atata timp cat este
posibil acest lucru. Valoarea cu care ramane variabila index reprezinta cel de-al n-lea element
al sirului.
Se poate optimiza algoritmul 11 astfel ncat sa renunt am la enunt ul de ciclare cu test
init ial while, procesul de calcul efectuand astfel mai put ine calcule (exercit iu).
14
Algoritm 11 Algoritm pentru determinarea celui deal nlea element
1: Input {N}
2: k 1
3: index N
4: while (k < index) do
5: index index k
6: k k + 1
7: end while
8: Output {Cel de-al n-lea element este, index}
Exemplul 1.13 Se dau doua siruri de numere naturale, a
1
, a
2
, . . . , a
n
si b
1
, b
2
, . . . , b
n
(a
i
, b
i
N). Sa se determine doua numere naturale d si e (d, e N) astfel ncat:
(a
2
1
+b
2
1
) (a
2
2
+b
2
2
) . . . (a
2
n
+b
2
n
) = d
2
+e
2
Vom demonstra prin induct ie matematica faptul ca
a
i
, b
i
N, i = 1, n, d, e N astfel ncat (a
2
1
+b
2
1
) (a
2
2
+b
2
2
) . . . (a
2
n
+b
2
n
) = d
2
+e
2
(1.6)
Fie n = 2. Ar trebui sa determinam valorile necunoscutelor d si e astfel ncat sa avem
(a
2
1
+b
2
1
) (a
2
2
+b
2
2
) = d
2
+e
2
.
Daca vom considera
_
d = a
1
a
2
+b
1
b
2
e = a
1
b
2
b
1
a
2
atunci, dupa efectuarea calculelor obt inem
d
2
+e
2
= (a
1
a
2
+b
1
b
2
)
2
+(a
1
b
2
b
1
a
2
)
2
= a
2
1
a
2
2
+b
2
1
b
2
2
+2a
1
a
2
b
1
b
2
+a
2
1
b
2
2
+b
2
1
a
2
2
2a
1
a
2
b
1
b
2
=
a
2
1
a
2
2
+b
2
1
b
2
2
+a
2
1
b
2
2
+b
2
1
a
2
2
= (a
2
1
+b
2
1
) (a
2
2
+b
2
2
),
si relat ia anterioara devine adevarata.
Presupunem ca relat ia (1.6) este adevarata k n.
Vom arata ca ea este adevarata si pentru n + 1:
a
i
, b
i
N, i = 1, n + 1, d, e N astfel ncat (a
2
1
+b
2
1
) (a
2
2
+b
2
2
) . . . (a
2
n+1
+b
2
n+1
) = d
2
+e
2
(1.7)
Aplicand de doua ori ipoteza de induct ie avem:
(a
2
1
+b
2
1
) (a
2
2
+b
2
2
) . . . (a
2
n
+b
2
n
)
. .
u
2
+v
2
(a
2
n+1
+b
2
n+1
) = (u
2
+v
2
) (a
2
n+1
+b
2
n+1
) = d
2
+e
2
(1.8)
unde e = ua
n+1
+vb
n+1
si e = ub
n+1
va
n+1
.
Algoritmul 12 se bazeaza pe modelul de construct ie pentru d si e, prezentat n demonstrat ia
anterioara.
Exemplul 1.14 Se da un numar de 9 monede de acelasi tip. Printre ele s-a strecurat o
moneda falsa: ea este mai grea sau mai usoara decat celelalte. Avem la dispozit ie o balant a
fara greutat i. Se cere un algoritm care sa determine moneda falsa, stiind ca putem folosi
cantarul doar pentru 3 operat ii de cantarire.
Fie a
1
, a
2
, . . . , a
9
cele 9 monede. Vommpart i monedele n trei grupe egale: A = {a
1
, a
2
, a
3
},
B = {a
4
, a
5
, a
6
} si C = {a
7
, a
8
, a
9
}. Pentru a determina moneda falsa avem nevoie de doua
informat ii:
15
Algoritm 12 Algoritm pentru calculul elementelor unei expresii
1: Input {N, a
1
, . . . , a
N
, b
1
, . . . , b
N
}
2: d a
1
3: e b
1
4: for i 2, N do
5: u = d a
i
+ e b
i
6: v = d b
i
e a
i
7: d u
8: e v
9: end for
10: Output {d, e}
1. n ce grupa se aa moneda falsa;
2. cum este moneda falsa (mai grea sau mai usoara fat a de restul).
Notam cu g() greutatea unei monede sau a unei mult imi de monede. Vom compara grupurile
A si B cu ajutorul balant ei. Avem urmatoarele cazuri:
1. g(A) = g(B).
In continuare comparam grupa A cu grupa C si vom obt ine una din situat iile urmatoare:
(a) g(A) = g(C) moneda falsa se aa n grupa B si este mai grea.
Mai departe comparam moneda a
4
cu moneda a
5
:
i. g(a
4
) = g(a
5
) a
6
este moneda falsa.
ii. g(a
4
) < g(a
5
) a
5
este moneda falsa (deoarece este mai grea).
iii. g(a
4
) > g(a
5
) a
4
este moneda falsa.
(b) g(A) < g(C)
g(A)<g(B)
g(B) = g(C) moneda falsa se aa n grupa A si este mai
usoara.
In instruct iunea for (linia 4) pentru ecare pozit ie libera din numarul rezultat, se va
alege cifra maxima dintre cele disponibile;
linia 5 - se trece la pozit ia urmatoare: l reprezinta indexul din sirul A al cifrei aleasa
la pasul anterior (i 1) si cont ine pozit ia pe care a fost gasita cifra maxim a. Cifra
maxima pentru pasul curent va cautata n intervalul l . . . n m+ i, unde n m+ i
reprezinta limita superioara a indexului pana la care se poate alege un element la pasul
curent;
a
1
. . . a
l
. . . a
nm+i
. .
subsecvent a pentru care se determin a cifra maxim a
. . . a
n
liniile 6, 7 - se init ializeaza variabila de ciclare j cu l si valoarea maxima cu 0; Deoarece
a
i
0, i = 1, n, se poate init ializa variabila max (unde se va pastra valoarea maxima)
cu 0;
linia 8 - se cauta cifra maxima pana la limita din dreapta nm+i (daca se merge mai
la dreapta dincolo de aceasta limita nu se mai pot alege restul de mi cifre);
linia 10 - se actualizeaza valoarea maxima;
linia 11 - se actualizeaza pozit ia valorii maxime.
18
Exemplul 1.17 Vom prezenta doi algoritmi ce determina toate tripletele de numere naturale
(a, b, c) ce verica relat ia a
2
+b
2
= c
2
unde c 100.
Prima varianta de lucru prezinta generarea tuturor tripletelor (i, j, k) unde 2 i < j <
k n. Dintre acestea se vor alege doar acelea ce verica relat ia k k = i i +j j. Daca vom
considera doar instruct iunea de ciclare 4, atunci condit ia 5 se va verica de n(j +1) +1 =
n j ori. Deoarece j = i + 1, n 1, vom obt ine faptul ca instruct iunea 5 se va executa de:
(n i 1) + (n i 2) +. . . + (n n + 1) =
ni1
j=1
j =
(n i) (n i 1)
2
ori.
Din i = 2, n 2 rezulta ca
n2
i=2
ni1
j=1
j =
n2
i=2
(n i) (n i 1)
2
=
1
2
n2
i=2
(i
2
+ (1 2n)i +n
2
n)
=
1
2
[
n2
i=2
i
2
+ (1 2n)
n2
i=2
i + (n
2
n) (n 3)]
1: Input {n}
2: for i 2, n 2 do
3: for j i + 1, n 1 do
4: for k j + 1, n do
5: if (k k = i i + j j) then
6: Output {i, j, k}
7: end if
8: end for
9: end for
10: end for
11: return
1: Input {n}
2: for i 2, n 1 do
3: for j i + 1, n do
4: s i i + j j
5: k
s
6: if ((k k = s) (k n)) then
7: Output {i, j, k}
8: end if
9: end for
10: end for
11: return
Pentru cea de-a doua varianta, cea din dreapta, se genereaza perechile de numere (i, j) cu
proprietatea ca 2 i < j n, se calculeaza s i i + j j si se verica daca numarul
obt inut este patrat perfect (linia 6: (
s)
2
= s). Condit ia de la linia 6 se va executa de
n (i + 1) + 1 = n i ori.
Deoarece linia 2 se va executa de n 1 2 + 1 = n 2 ori, valoarea totala reprezentand
numarul de vericari (linia 6) va :
n1
i=2
(n i) =
n1
i=1
(n i) (n 1) =
n1
i=1
i (n 1) =
n(n 1)
2
n + 1
1.3 Exercit ii
1. (a) Sa se determine daca un numar natural este simetric sau nu.
(b) Sa se determine toate cifrele distincte dintr-un numar natural.
(c) Sa se determine reprezentarea n baza 2 a unui numar natural.
19
(d) Sa se determine forma corespunzatoare n baza 10 a unui numar reprezentat n
baza 2.
(e) Sa se elimine dintrun numar natural toate cifrele de forma 3k +1 si sa se aseze
numarul rezultat.
(f) Pentru un numar natural dat sa se construiasca din cifrele acestuia cel mai mare
numar prin amplasarea mai ntai a cifrelor impare si apoi a cifrelor pare.
2. (a) Sa se determine tot i divizorii unui numar natural.
(b) Sa se verice daca doua numere naturale sunt prime ntre ele.
(c) Sa se determine tot i divizorii comuni a doua numere naturale.
(d) Sa se calculeze toate numerele prime mai mici decat o valoare specicata.
(e) Sa se descompuna n factori primi un numar natural.
(f) Sa se determine cel mai mare divizor comun a doua numere naturale folosinduse
descompunerea n factori primi.
3. Pentru un numar natural b (1 < b < 10) sa se determine toate numerele naturale mai
mici decat o valoare specicata n, (1 n 10
5
) care scrise n baza b, cont in numai
cifrele 0 si 1.
Intrare
7 5000
Iesire
0 1 7 8 49 50 56 57 343 344 350 351 392 393 399 400 2401
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 10
6
). Se aplica asupra numarului n una dintre
urmatoarele transformari:
(a) Se calculeaza suma factorialelor cifrelor numarului n si se obt ine un nou numar.
(b) Se calculeaza suma cuburilor cifrelor numarului n.
Dupa aceasta, procedeul se repeta aplicandu-se de ecare data numai una din cele doua
operat ii (a) sau (b) (cea cu care s-a nceput).
Sa se aseze sirul de numere astfel obt inut pana cand elementele lui ncep sa se repete.
Se cere lungimea listei pana la prima repetit ie si pozit ia elementului care se repeta
primul.
6. O secvent a Farey[44] de ordinul n este un sir de numere rat ionale
a
b
unde 0 a b
n, (a, b) = 1. De exemplu, secvent ele Farey de ordinul 1, 2 si 3 sunt:
F
1
= {
0
1
,
1
1
}
F
2
= {
0
1
,
1
2
,
1
1
}
F
3
= {
0
1
,
1
3
,
1
2
,
2
3
,
1
1
}
20
Pentru un numar natural n (n < 2
16
) sa se determine elementele secvent ei Farey de
ordinul n.
Intrare
7
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)
Indicat ie Fie
a
1
b
1
,
a
2
b
2
,
a
3
b
3
trei elemente consecutive ale unei secvent e Farey de ordinul n.
Atunci avem relat iile ([68]):
b
1
a
2
a
1
b
2
= 1 (1.9)
a
2
b
2
=
a
1
+a
3
b
1
+b
3
(1.10)
Daca
a
1
b
1
si
a
3
b
3
sunt doua elemente consecutive ale unei secvent e Farey de ordinul n,
atunci
a
1
+a
3
b
1
+b
3
este un element al secvent ei Farey de ordinul n + 1.
Fie
a
1
b
1
si
a
2
b
2
doua elemente consecutive ale unei secvent e Farey de ordinul n. Elementul
urmator ce apart ine secvent ei,
a
3
b
3
, se poate determina astfel:
a
2
b
2
=
a
1
+a
3
b
1
+b
3
. k N
astfel
ncat ka
2
= a
1
+ a
3
si kb
2
= b
1
+ b
3
a
3
= ka
2
a
1
si b
3
= kb
2
b
1
. Valoarea lui k
trebuie aleasa astfel ncat fract ia
a
3
b
3
sa e cat mai apropiata de
a
2
b
2
. Astfel k trebuie sa
e cat mai mare, nsa respectand restrict ia k
n+b
1
b
2
.
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 (12
10
= 5
10
), nsa ele sunt egale
daca le consideram ca ind reprezentate n bazele 3 si respectiv 6: 12
3
= 5
10
, 5
6
= 5
10
.
Bazele n care se vor considera reprezentate numerele au valoarea maxima 36.
Intrare Iesire
12 5 12
3
= 5
6
10 A 10
10
= A
11
12 34 12
17
= 34
5
123 456 no solution
10 2 10
2
= 2
3
(ACM, 1995)
8. Sa se calculeze 2
n
pentru 30 < n < 100.
9. Fie D o dreapta avand ecuat ia ax + by + c = 0 si un cerc C(O, r) unde O(x
0
, y
0
) este
centrul cercului iar r este raza acestuia.
Sa se realizeze un algoritm care sa stabileasca pozit ia dreptei fat a de cercul C n funct ie
de valorile parametrilor reali a, b, c, x
0
, y
0
, r.
10. Sa se determine toate numerele naturale n (1 n 10
6
) cu proprietatea ca atat n cat
si oglinditul sau sunt numere prime.
21
11. Sa se scrie un algoritm ce calculeaza coecient ii polinomului urmator, ind date nu-
merele a, b si n:
P(X) = (aX +b)
n
, a, b Z, n N. (1.11)
12. Sa se determine toate radacinile rat ionale ale unei ecuat ii cu coecient i ntregi.
Fie ecuat ia:
a
n
x
n
+a
n1
x
n1
+. . . +a
1
x +a
0
= 0. (1.12)
O radacina rat ionala a ecuat iei 1.12 va avea forma
p
q
unde p|a
0
si q|a
n
.
13. Pentru o secvent a de numere naturale de lungime n, sa se realizeze un algoritm care sa
determine cea mai lunga subsecvent a de elemente consecutive avand aceeasi valoare.
14. Sa se realizeze un algoritm care sa determine primele n numere prime, pentru un numar
natural n dat.
15. Sa se realizeze un algoritm pentru calcularea tuturor solut iilor ecuat iei:
3x +y + 4xz = 100, x, y, z N. (1.13)
Indicat ie Pentru o valoare z 25 si x = 0 (x > 0) avem:
3x +y + 4xz 3x + 4xz 3 + 4z > 100.
Astfel pentru z obt inem ca 0 z 24.
Ecuat ia 1.13 se poate scrie astfel:
3x +y + 4xz = 100 x(3 + 4z) = 100 y x =
100 y
3 + 4z
(1.14)
Daca x = 0 si y 0 x =
100y
3+4z
_
100y
3+4z
x [1,
_
100y
3+4z
].
Astfel, pentru ecare pereche de numere (z, x) vom calcula pe y astfel: y = 100 3x
4xz.
De asemenea, n 1.14 daca luam x = 0 obt inem mult imea de solut ii (0, 100, z), z N.
16. Un moment de timp este specicat n formatul ora:min:sec:sutimi.
Sa se elaboreze o subrutina ce calculeaza timpul (n sutimi de secunda) trecut de
la momentul 00 : 00 : 00 : 00 al ecarei zile si pana la momentul curent;
Sa se calculeze timpul scurs ntre doua momente de timp hh
1
: mm
1
: ss
1
: tt
1
si
hh
2
: mm
2
: ss
2
: tt
2
;
Fiind dat un numar natural n, ce reprezinta numarul de secunde trecute de la
momentul 00 : 00 : 00 : 00, sa se realizeze o subrutina ce transforma acest numar
n formatul ora:min:sec:sutimi.
Ora ia valori n intervalul 0 . . . 23, minutul si secundele iau valori n intervalul 0 . . . 59,
iar sutimile de secunda au valori n intervalul 0 . . . 99.
22
Capitolul 2
Elemente de analiza algoritmilor
Gradul de dicultate al unei probleme P poate pus in evident a prin timpul de execut ie al
algoritmului corespunzator si/sau prin spat iul de memorie necesar. Timpul de execut ie n
cazul cel mai defavorabil ne da durata maxima de execut ie a algoritmului. Timpul mediu
de execut ie se obt ine prin nsumarea timpilor de execut ie pentru toate mult imile de date de
intrare urmata de raportarea la numarul acestora.
Denit ia 2.1 Timpul de execut ie n cazul cel mai defavorabil al unui algoritm A este o
funct ie T
A
: N N unde T
A
(n) reprezinta numarul maxim de instruct iuni executate de
catre A n cazul unor date de intrare de dimensiune n.
Denit ia 2.2 Timpul mediu de execut ie al unui algoritm A este o funct ie T
med
A
: N N
unde T
med
A
(n) reprezinta numarul mediu de instruct iuni executate de catre A n cazul unor
date de intrare de dimensiune n.
Fiind data o problema P, o funct ie T(n) se spune ca este o margine superioara daca exista
un algoritm A ce rezolva problema P iar timpul de execut ie n cazul cel mai defavorabil al
algoritmului A este cel mult T(n).
Fiind data o problema P, o funct ie T(n) se spune ca este o margine pentru cazul mediu
daca exista un algoritm A ce rezolva problema P iar timpul mediu de execut ie al algoritmului
A este cel mult T(n).
Fiind data o problema P, o funct ie T(n) se spune ca este o margine inferioara daca orice
algoritm A ce rezolva problema P va avea cel put in un timp T(n) pentru unele date de intrare
de dimensiune n, atunci cand n tinde la innit (n ).
Denit ia 2.3 Fiind data o funct ie g : N R vom nota cu O(g(n)) mult imea: O(g(n)) =
{f(n)| c > 0, n
0
0 a.. 0 f(n) c g(n), n n
0
}.
Vom spune despre f ca nu creste n mod sigur mai repede decat funct ia g. Pentru a indica
faptul ca o funct ie f(n) este un membru al mult imii O(g(n)), vom scrie f(n) = O(g(n)), n
loc de f(n) O(g(n)).
Observat ia 2.1 Vom prezenta cateva proprietat i ale lui O():
O(f(n) +g(n)) = O(max(f(n), g(n))).
De exemplu pentru funct ia f(n) = 7 n
5
n
2
+3 log n aplicand regula valorii maxime
vom avea: O(f(n)) = O(max(7 n
5
, n
2
, 3 log n)) adica O(f(n)) = O(n
5
).
O(log
a
n) = O(log
b
n);
23
f(n) = O(f(n)) (reexivitate);
f(n) = O(g(n)) si g(n) = O(h(n)) atunci f(n) = O(h(n)) (tranzitivitate).
Denit ia 2.4 Fiind data o funct ie g : N R vom nota cu (g(n)) mult imea: (g(n)) =
{f(n)| c
1
, c
2
> 0, n
0
0 a.. 0 c
1
g(n) f(n) c
2
g(n), n n
0
}
Spunem ca g(n) este o margine asimptotic tare pentru f(n).
Denit ia 2.5 Fiind data o funct ie g : N R vom nota cu (g(n)) mult imea: (g(n)) =
{f(n)| c > 0, n
0
0 a.. 0 c g(n) f(n), n n
0
}
La fel cum O furnizeaza o delimitare asimptotica superioara pentru o funct ie, furnizeaza
o delimitare asimptotica inferioara pentru aceasta.
Teorema 2.2 Pentru orice doua funct ii f(n) si g(n), avem f(n) = (g(n)) f(n) =
O(g(n)) si f(n) = (g(n)).
Denit ia 2.6 Fiind data o funct ie g : N R vom nota cu o(g(n)) mult imea: o(g(n)) =
{f(n)| c > 0, n
0
> 0 a.. 0 f(n) < c g(n), n n
0
}
Observat ia 2.3 f(n) = o(g(n)) lim
n
f(n)
g(n)
= 0.
Denit ia 2.7 f(n) (g(n)) g(n) o(f(n))
(g(n)) este o mult ime ce se deneste astfel:
(g(n)) = {f(n)| c > 0, n
0
> 0 a.. 0 c g(n) < f(n), n n
0
}
Observat ia 2.4 1. f(n) = (g(n)), g(n) = (h(n)) = f(n) = (h(n)) (tranzitivitate);
2. f(n) = (f(n)) (reexivitate);
3. f(n) = (g(n)) g(n) = (f(n)) (simetrie).
Denit ia 2.8 O funct ie f are o crestere exponent iala daca c > 1 a.i. f(x) = (c
x
) si
d a.. f(x) = O(d
x
). O funct ie f este polinomiala de gradul d daca f(n) = (n
d
) si
f(n) = O(n
d
), d
d.
Teorema 2.5
lim
n
g(n)
f(n)
=
_
_
c g(n) (f(n)), c > 0
0 g(n) o(f(n))
f(n) o(g(n))
(2.1)
Exemplul 2.6 pentru x R
+
avem x
n
o(n!).
Deoarece
x
n
n!
<
x
n
x
4
. . . x
4
. .
n/2ori
x
n
(x
4
)
n/2
=
x
n
x
2n
= (
1
x
)
n
(2.2)
rezulta ca
lim
n
x
n
n!
= 0 (2.3)
24
log n o(n).
lim
x
log x
x
= lim
x
(log x)
(x)
= lim
x
1
xln 2
1
= 0
pentru a, b > 1, a, b R
avem log
a
n (log
b
n).
Calcularea cu exactitate a timpului de execut ie al unui program oarecare se poate dovedi
o activitate dicila. De aceea, n practica se utilizeaza estimari ncercandu-se eliminarea
constantelor si simplicarea formulelor ce intervin n cadrul evaluarii.
Daca doua part i ale unui program, P
1
si P
2
, au timpii de execut ie corespunzatori T
1
(n) si
T
2
(n), unde T
1
(n) = O(f(n)) si T
2
(n) = O(g(n)), atunci programul P
1
P
2
va avea timpul
de execut ie T
1
(n) +T
2
(n) = O(max(f(n), g(n))) (regula sumei ), unde reprezinta operat ia
de concatenare.
Prin aplicarea acestei reguli rezulta faptul ca timpul necesar execut iei unui numar nit
de operat ii este, neluand n considerare constantele, caracterizat n principal de catre timpul
de execut ie al operat iei cele mai costisitoare.
Cea dea doua regula, regula produsului, spune ca ind date doua funt ii f(n) si g(n) astfel
ncat T
1
(n) = O(f(n)) si T
2
(n) = O(g(n)), atunci avem T
1
(n) T
2
(n) = O(f(n) g(n)).
Pe baza regulilor produsului si sumei putem face urmatoarele reduceri:
O(1) O(log n) O(n) O(nlog n) O(n
2
) O(n
3
) O(2
n
)
Prezentamn continuare cateva reguli generale pentru evaluarea complexitat ii unui algoritm:
timpul de execut ie al unei instruct iuni de atribuire, citire sau asare a unei variabile
este O(1).
timpul de execut ie al unei secvent e de instruct iuni este proport ional cu timpul instruct iunii
care dureaza cel mai mult.
timpul de execut ie al unei instruct iuni de decizie (if ) este timpul executarii instruct iu-
nilor de pe ramura aleasa plus timpul necesar evaluarii condit iei.
timpul de execut ie pentru o instruct iune de ciclare este suma, dupa numarul de pasi
pe care i realizeaza instruct iunea de ciclare, dintre timpul necesar executarii corpului
instruct iunii plus timpul necesar evaluarii condit iei.
1: for i 1, n do
2: A(i)
3: end for
Daca instruct iunii compuse A(i) i corespunde un timp de execut ie constant t, ce nu
depinde de i, atunci timpul corespunzator ntregii instruct iuni de ciclare for anterioare
este:
n
i=1
t = t
n
i=1
1 = t n = O(n) (2.4)
In cazul general, timpul de execut ie al instruct iunii compuse A(i) depinde de pasul i,
notat cu t
i
. Astfel timpul total corespunzator instruct iunii for este
n
i=1
t
i
.
Instruct iunile de ciclare al caror numar de pasi depinde de ndeplinirea unei condit ii
(while) sau de nendeplinirea acesteia (repeat ... until), sunt mult mai dicil
de analizat deoarece nu exista o metoda generala de a aa cu exactitate numarul de
repetari al corpului instruct iunii. De exemplu, pentru fragmentul urmator
25
1: i l
2: while (a
i
< x) do
3: . . . calcule ce pot modica, direct sau indirect, valoarea lui a
i
4: i i + 1
5: end while
nu se poate preciza de cate ori se va ajunge la realizarea instruct iunii de incrementare
din interiorul instruct iunii while.
In marea majoritate a cazurilor se utilizeaza teoria
probabilitat ilor n vederea obt inerii unei estimari a acestui numar.
Exemplul 2.7 De exemplu
1: for i 1, n do
2: instructiune1
3: end for
are timpul de execut ie O(n).
1: for i 1, n do
2: for j 1, m do
3: instructiune2
4: end for
5: end for
Timpul de execut ie pentru secvent a de cod ce cont ine dou a instruct iuni de ciclare imbricate
este O(n m).
Exemplul 2.8 Sa consideram un alt exemplu: se da un sir A de n numere reale, si se doreste
calcularea elementelor unui sir B astfel ncat b
i
=
1
i
i
j=1
a
j
, pentru i = 1, n.
1: for i 1, n do
2: s 0
3: for j 1, i do
4: s s + a
j
5: end for
6: b
i
s
i
7: end for
8: return
Daca notam cu o constanta c
x
timpul necesar pentru efectuarea unei operat ii atomice,
vom obt ine: costul efectuarii liniei 1 este c
1
n, al liniei 2 este c
2
n, al liniei 3 este c
3
i, al
liniei 4 este c
4
i, al liniei 6 este c
5
n iar al liniei 8 este c
6
.
T(n) = c
1
n +c
2
n +
n
i=1
c
3
i +
n
i=1
c
4
i +c
5
n +c
6
= c
1
n +c
2
n +c
3
n
i=1
i +c
4
n
i=1
i +c
5
n +c
6
=
n(n + 1)
2
(c
3
+c
4
) +n(c
1
+c
2
+ c
5
) +c
6
=
1
2
(c
3
+c
4
)n
2
+
1
2
(c
3
+c4)n +n(c
1
+c
2
+c
5
) +c
6
=
1
2
(c
3
+c
4
)n
2
+ [
1
2
(c
3
+c
4
) +c
1
+c
2
+c
5
]n +c
6
= n
2
p +n q +c
6
De obicei nu se efectueaza o analiza asa de detaliata a algoritmului, dar se ncearca o eval-
uare a blocurilor principale cum ar instruct iunile de ciclare, atribuindule direct valori
corespunzatoare complexitat ii timp, atunci cand este posibil:
T(n) = O(n
2
) +O(n) +O(1) = O(n
2
) (2.5)
26
Vom modica algoritmul anterior astfel ncat sa reducem numarul de calcule efectuate:
1: s 0
2: for i 1, n do
3: s s + a
i
4: b
i
s
i
5: end for
6: return
Pentru aceasta varianta de lucru, complexitatea timp este urmatoarea:
T(n) = O(1)(linia 1) +O(n)(liniile 24) +O(1)(linia 6) = O(n) (2.6)
In urma analizei de complexitate, putem concluziona ca cea de-a doua varianta a algoritmului
ruleaza ntrun timp liniar.
2.1 Metoda substitut iei
Metoda substitut iei presupune mai ntai ghicirea (estimarea) formei solut iei pentru relat ia
de recurent a si apoi demonstrarea prin induct ie matematica a corectitudinii solut iei alese.
Metoda poate aplicata n cazul unor ecuat ii pentru care forma solut iei poate estimata.
Sa consideram urmatoarea relat ie de recurent a:
T(n) =
_
1 , daca n = 1
2T(
n
2
) +n , n rest
(2.7)
Vom presupune ca solut ia acestei relat ii de recurent a este T(n) = O(nlog n) (notam
log
2
n = log n). Folosind metoda induct iei matematice, vom ncerca sa demonstram inegali-
tatea:
T(n) cnlog n. (2.8)
Presupunem mai ntai ca inecuat ia (2.8) are loc pentru k < n, inclusiv pentru
n
2
, adica
T(
n
2
) c
n
2
log (
n
2
).
Vom demonstra ca inecuat ia (2.8) este ndeplinita si pentru n: T(n) = 2T(
n
2
) +n.
T(n) 2(c
n
2
log
n
2
) +n = cnlog
n
2
+n
= cnlog n cnlog 2 +n = cnlog n cn +n
cnlog n
(2.9)
Metoda induct iei matematice presupune sa aratam ca solut ia respecta si cazurile particulare
(T(1) = 1). Pentru n = 1 vom avea T(1) = c 1 log 1 = 0 ceea ce contrazice relat ia T(1) = 1.
Aceasta situat ie poate rezolvata daca consideram c a T(n) = cnlog n, n n
0
(n
0
este
o constanta).
Din T(2) = 4 si T(3) = 5 vom alege valoarea parametrului c astfel ncat sa e ndeplinite
inegalitat ile T(2) 2c log 2 si T(3) 3c log 3. Se observa ca orice valoare a lui c 2 satisface
inegalitat ile anterioare.
2.2 Schimbarea de variabila
Aceasta metoda presupune realizarea unei schimbari de variabila n vederea simplicarii
formulei sau pentru regasirea unei formule deja cunoscut a.
27
Sa consideram ecuat ia T(n) = 2T(
Inlocuind succesiv pe n cu
n
2
vom obt ine urmatoarea serie de identitat i:
T(n) = 2T(
n
2
) +n
= 2(2T(
n
2
) +
n
2
) +n = 4T(
n
4
) + 2n
= 4(2T(
n
8
) +
n
4
) + 2n = 8T(
n
8
) + 3n
. . .
= 2
k
T(
n
2
k
) +kn
(2.16)
Deoarece substitut ia se opreste atunci cand
n
2
k
= 1 adica pentru k = log n, vom avea:
T(n) = 2
k
+kn = n +nlog n = O(nlog n) (2.17)
28
2.4 Teorema master
Metoda master pune la dispozit ie o varianta de rezolvare a unor recurent e de forma
T(n) = aT(
n
b
) +f(n) (2.18)
unde a 1, b > 1 sunt constante, iar f(n) este o funct ie asimptotic pozitiva. Formula de
recurent a (2.18) descrie timpul de execut ie al unui algoritm ce presupune descompunerea unei
probleme de dimensiune n n a subprobleme, ecare avand dimensiunea datelor de intrare
n
b
.
f(n) reprezinta costul asociat mpart irii datelor de intrare cat si costul combinarii rezultatelor
celor a subprobleme.
Teorema 2.9 (Teorema master) [30] Fie a 1 si b > 1 doua constante, f(n) o funct ie
asimptotic pozitiva, si T(n) denita de relat ia de recurent a T(n) = aT(
n
b
) +f(n), unde
n
b
va
n
b
sau
n
b
.
Atunci T(n) este marginita asimptotic dupa cum urmeaza:
1. daca f(n) = O(n
log
b
a
) pentru o constanta > 0, atunci T(n) = (n
log
b
a
);
2. daca f(n) = (n
log
b
a
log
k
n), atunci T(n) = (n
log
b
a
log
k+1
n) (de obicei k = 0);
3. daca f(n) = (n
log
b
a+
) pentru o constanta > 0, si daca af(
n
b
) cf(n) pentru o
constanta c < 1 si oricare numar n sucient de mare, atunci T(n) = (f(n)).
Corolarul 2.10 Pentru o funct ie f de tip polinomial unde f(n) = cn
k
avem:
1. daca a > b
k
atunci T(n) = O(n
log
b
a
);
2. daca a = b
k
atunci T(n) = O(n
k
log n);
3. daca a < b
k
atunci T(n) = O(n
k
).
Exemplul 2.11 Fie T(n) = 2T(
n
2
)+n. Avem a = 2, b = 2, k = 1, f(n) = n
k
. Aplicand
corolarul pentru cazul a = b
k
, solut ia este T(n) = O(n
k
log n) = O(nlog n).
Fie T(n) = 9T(
n
3
) + n. Avem a = 9, b = 3, k = 1. Aplicand corolarul pentru cazul
a > b
k
, se obt ine solut ia T(n) = O(n
log
3
9
) = O(n
2
).
Fie T(n) = 4T(
n
2
) + n
3
. Avem a = 4, b = 2, k = 3, f(n) = n
3
. Aplicand corolarul
pentru cazul a < b
k
, se obt ine solut ia T(n) = O(n
3
).
Fie T(n) = 2
n
T(
n
2
) + n
n
. Nu se poate aplica Teorema master deoarece a = 2
n
nu este
constant.
Fie T(n) = 2T(
n
2
) + nlog n. Atunci avem a = 2, b = 2, k = 1, f(n) = (n
log
2
2
log
k
n),
si aplicand Teorema master, cazul al doilea, obt inem: T(n) = (nlog
2
n).
Fie T(n) =
1
2
T(
n
2
) +
1
n
. Nu se poate aplica Teorema master deoarece a =
1
2
< 1.
Fie T(n) =
2T(
n
2
) + log n. Aplicand Teorema master pentru a =
2, b = 2, f(n) =
O(n
1
2
n).
Fie T(n) = 3T(
n
4
) + nlog n. Aplicand Teorema master pentru a = 3, b = 4, f(n) =
(n
log
4
3+
) obt inem: T(n) = (nlog n).
29
Fie T(n) = 16T(
n
4
) nlog n. Nu se poate aplica Teorema master deoarece f(n) =
nlog n nu este o funct ie asimptotic pozitiva.
Exemplul 2.12 Analiza act iunilor
Deschiderea unei act iuni la o anumita data calendaristica se calculeaza drept numarul
maxim de zile consecutive (pana la acea data) n care pret ul act iunii a fost mai mic sau egal
cu pret ul din ziua respectiva. Fie p
k
pret ul unei act iuni n ziua k iar d
k
deschiderea calculata
n aceeasi zi.
In continuare se prezinta un exemplu de calcul al deschiderii unei act iuni pentru un numar
de 7 zile:
p
0
p
1
p
2
p
3
p
4
p
5
p
6
9 6 3 4 2 5 7
d
0
d
1
d
2
d
3
d
4
d
5
d
6
1 1 1 2 1 4 6
Algoritm 15 Algoritm de calcul al deschiderii unei act iuni (prima varianta)
1: procedure ComputeSpan1(p, n; d)
2: for k 0, n 1 do
3: j 1
4: while (j < k) (p
kj
p
k
) do
5: j j + 1
6: end while
7: d
k
j
8: end for
9: return
10: end procedure
Algoritmul 15 calculeaza deschiderea unei act iuni pentru un interval mai lung de timp pe
baza evolut iei pret urilor acesteia. Timpul de execut ie al acestui algoritm este O(n
2
).
Vom prezenta un alt mod de calcul al deschiderii unei act iuni folosind o stiva (vezi algo-
ritmul 16).
Stiva este o structura de date de tip container deoarece ea depoziteaza elemente de un
anumit tip. Pentru a putea sa operam asupra unei colect ii de elemente pastrate ntro stiva
se denesc urmatoarele operat ii, n afara de operat iile 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 situat ie ce genereaza o except ie.
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 cont ine nici un obiect.
isFull(): boolean ntoarce true daca stiva este plina.
init(capacitate:integer) init ializeaza stiva.
Timpul de execut ie al noului algoritm este O(n).
30
Algoritm 16 Algoritm de calcul al deschiderii unei act iuni (a doua varianta)
1: procedure ComputeSpan2(p, n; d)
2: call init(S)
3: for k 0, n 1 do
4: ind 1
5: while (isEmpty(S) = false) (ind = 1) do
6: if (p
k
p
peek(S)
) then
7: call pop(S)
8: else
9: ind 0
10: end if
11: end while
12: if (ind = 1) then
13: h 1
14: else
15: h peek(S)
16: end if
17: d
k
k h
18: call push(S, k)
19: end for
20: return
21: end procedure
2.5 Exercit ii
1. Sa se evalueze complexitatea algoritmului 17, ce realizeaza descompunerea n factori
primi a unui numar natural.
Algoritm 17 Algoritm pentru descompunerea n factori primi a unui num ar natural
1: Input {n}
2: j 2
3: while (j n) do
4: if (n mod j = 0) then
5: k 0
6: while (n mod j = 0) do
7: k k + 1
8: n n div j
9: end while
10: Output {j
k
}
11: end if
12: j j + 1
13: end while
2. Pentru ecare dintre urmatoarele relat ii de recurent a calculat i complexitatea timp:
(a) T(n) = 2T(
n
2
) +
n
log n
;
(b) T(n) = 16T(
n
4
) +n!;
(c) T(n) = 3T(
n
3
) +
n;
31
(d) T(n) = 3T(
n
3
) +
n
2
.
3. Sa se calculeze timpul mediu de lucru T(n) al algoritmului 18.
Algoritm 18 Algoritm Calcul5
1: for i 1, n do
2: j n
3: while (j 1) do
4: . . .
5: j
j
2
6: end while
7: end for
4. Sa se calculeze timpul mediu de lucru T(n) al algoritmului 19.
Algoritm 19 Algoritm Calcul6
1: i n
2: while (i 1) do
3: j i
4: while (j n) do
5: . . .
6: j j 2
7: end while
8: i
i
2
9: end while
5.
Intr-o secvent a de n numere naturale (a
1
, a
2
, . . . , a
n
, a
i
N, 1 a
i
65535, 1 n
10
7
) se spune ca un numar este majoritar daca numarul de aparit ii este cel put in
n
2
+1.
Sa se realizeze un algoritm ce determina daca avem vreun numar majoritar n secvent a
de n elemente, si n caz armativ sa se aseze aceasta valoare.
6. Sa se rezolve urmatoarele ecuat ii recurente:
T(n) = 2 T(
n
2
) + nlg n, n = 2
k
;
T(n) = 3 T(
n
2
) + c n, n = 2
k
> 1.
7. Sa se rezolve ecuat ia recurenta T(n) = n T
2
(
n
2
), n = 2
k
, T(1) = 6.
8. Sa se arate ca e
x
= 1 +x + (x
2
) unde x .
9. Sa se determine primele n elemente ale sirurilor a
k
si b
k
date prin urmatoarele relat ii
de recurent a:
a
k+1
=
5a
k
+ 3
a
k
+ 3
, b
k
=
a
k
+ 3
a
k
+ 1
, k 0, a
0
= 1. (2.19)
10. Fie [P
1
P
2
. . . P
n
] un poligon convex dat prin coordonatele carteziene ale varfurilor sale,
n ordine trigonometrica. Sa se calculeze aria poligonului.
11. Sa se verice daca urmatoarele armat ii sunt adevarate:
(a) n
2
O(n
3
);
32
(b) n
3
O(n
2
);
(c) 2
n+1
O(2
n
);
(d) (n + 1)! O(n!);
(e) f : N R
, f O(n) f
2
O(n
2
);
(f) f : N R
, f O(n) 2
f
O(2
n
).
12. Sa se realizeze un algoritm ce verica daca un text dat T constituie o permutare circu-
lara a unui alt text T
0
.
13. Se da o secvent a de numere ntregi n care pot sa existe si numere duplicate. Sa se
realizeze un algoritm care sa determine o pereche de numere apart inand secvent ei, a
caror suma sa e egala cu o valoare specicata k.
14. Sa se realizeze o subrutina ce verica daca un sir de caractere S reprezinta o anagrama
a altui sir de caractere S
0
.
15. O secvent a S cont ine n 1 numere naturale distincte ale caror valori apart in mult imii
de valori {1, 2, . . . , n}. Sa se realizeze un algoritm ce determina ce numar din intervalul
[1, n] lipseste din secvent a.
16. Sa se elaboreze un algoritm pentru stergerea dintrun vector a unui element aat pe
pozit ia k.
33
Capitolul 3
Grafuri. Grafuri neorientate
3.1 Not iuni de baza
Fie V o mult ime nita si nevida avand n elemente (V = {x
1
, x
2
, . . . , x
n
}). Fie E o mult ime
nita astfel ncat E V V (unde V V este produsul cartezian al mult imii V cu ea nsasi)
si E = {(x, y)|x, y V } (E este mult imea perechilor (x, y) cu proprietatea ca x si y apart in
mult imii V ).
Denit ia 3.1 Se numeste graf o pereche ordonata G = (V, E).
Elementele x
i
V se numesc noduri sau v arfuri. Elementele mult imii E sunt arce
sau muchii. O muchie (x
k
, x
l
) E se mai noteaza si cu [x
k
, x
l
].
|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 [x
k
, x
l
] nu t inem cont de ordine atunci graful este neorientat iar
perechea reprezinta o muchie ([x
k
, x
l
] = [x
l
, x
k
]). Daca se introduce un sens ecarei muchii
atunci aceasta devine arc iar graful se numeste orientat ([x
k
, x
l
] = [x
l
, x
k
]). Muchiile ce au
aceleasi varfuri se spune ca sunt paralele. O muchie de forma [u, u] se numeste bucla.
Un graf G se numeste simplu daca oricare doua varfuri ale sale sunt extremitat i pentru
cel mult o muchie. Un graf este nit daca mult imile V si E sunt nite.
In continuare vom considera un graf neorientat, simplu si nit. 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
Fig. 3.1: a) Un exemplu de graf neorientat cu 6 varfuri b) Graful complet K
5
34
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.
Observat ia 3.1 Un graf neorientat, simplu, nit poate utilizat drept model de reprezentare
al unei relat ii simetrice peste o mult ime.
Denit ia 3.2 Se numeste graf complet de ordinul n, si se noteaza cu K
n
, un graf cu pro-
prietatea ca oricare doua varfuri distincte ale sale sunt adiacente (x V, y V, x = y
[x, y] E).
Exemplul 3.2 Sa consideram grafurile din gura 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
gura 3.1 a));
b) K
5
este graful complet cu 5 varfuri. Varfurile 3 si 5 sunt adiacente (vezi gura 3.1 b)).
Fig. 3.2: a) Un exemplu de graf neorientat cu 5 varfuri. b) Subgraf al grafului din gura 3.1 b). c) Graf
part ial al grafului din gura 3.1 b).
Denit ia 3.3 Un graf part ial al unui graf dat G = (V, E) este un graf G
1
= (V, E
1
) unde
E
1
E.
Denit ia 3.4 Un subgraf al unui graf G = (V, E) este un graf H = (V
1
, E
1
) unde V
1
V
iar muchiile din E
1
sunt toate muchiile din E care au ambele extremitat i n mult imea V
1
(E
1
= E|
V
1
V
1
= {[x, y]|[x, y] E, x, y V
1
}).
Se spune ca graful H este indus sau generat de submult imea de varfuri V
1
si se noteaza
H = G|
V
1
sau H = [V
1
]
G
.
Iata alte cateva notat ii des ntalnite:
- GV
1
= subgraful ce se obt ine din G prin eliminarea submult imii de varfuri V
1
(V
1
V );
- Gx = subgraful G{x};
- < E
1
>
G
= graful part ial al lui G generat de E
1
;
35
- GE
1
=< E \ E
1
>
G
;
- Ge = G{e}, graful part ial obt inut prin eliminarea unei muchii e.
Exemplul 3.3 Graful part ial din gura 3.2 c) se obt ine din graful 3.1 b) prin stergerea
muchiilor [2, 4], [2, 5], [3, 5], [4, 5]. Subgraful din gura 3.2 b) este indus de mult imea V
1
=
{1, 3, 4, 5} din graful complet K
5
(H = K
5
|V
1
).
Denit ia 3.5 Gradul unui varf este egal cu numarul muchiilor incidente cu varful x si se
noteaza 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{d
G
(u)|u G} si (G) = max{d
G
(u)|u G}.
Fig. 3.3: Graful lui Petersen
Exemplul 3.4 Graful lui Petersen din gura 3.3 este un exemplu de graf trivalent sau cubic
(toate varfurile grafului au acelasi grad, 3).
Propozit ia 3.5 Pentru un graf G = (V, E), V = {x
1
, x
2
, . . . , x
n
}, |E| = m, avem urmatoarea
relat ie:
n
k=1
d(x
k
) = 2m. (3.1)
Astfel n orice graf G exista un numar par de varfuri al caror grad este un numar impar.
Denit ia 3.6 Se numeste secvent a grac a un sir de numere naturale d
1
, d
2
, . . . , d
n
cu
proprietatea ca ele reprezinta gradele varfurilor unui graf neorientat.
Corolarul 3.6 Pentru ca o secvent a de numere naturale d
1
, d
2
, . . . , d
n
sa e secvent a graca,
este necesar ca:
1. k = 1, n, d
k
n 1;
2.
n
k=1
d
k
este un numar par.
Denit ia 3.7 Un lant L = [v
0
, v
1
, . . . , v
m
] este o succesiune de varfuri cu proprietatea ca
oricare doua varfuri vecine sunt adiacente ([v
i
, v
i+1
] E, i = 0, m1). Varfurile v
0
si v
m
se numesc extremit at ile lant ului, iar m reprezinta lungimea lant ului.
36
Daca varfurile v
0
, v
1
, . . . , v
m
sunt distincte doua cate doua, lant ul se numeste elementar
(v
i
= v
j
, i, j = 0, m).
Denit ia 3.8 Un lant L pentru care v
0
= v
m
se numeste ciclu.
Denit ia 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.
Denit ia 3.10 Un lant L ce cont ine ecare muchie exact o singura data se numeste lant
eulerian. Daca v
0
= v
m
si lant ul este eulerian atunci ciclul se numeste ciclu eulerian.
Un graf ce cont ine un ciclu eulerian se numeste graf eulerian.
Exemplul 3.7 [1, 2, 3, 1, 4] este un exemplu de lant n graful din gura 3.2 c). Varfurile 1
si 4 sunt extremitat ile lant ului. Lant ul nu este elementar deoarece varful 1 se ntalneste de
doua ori, n schimb [1, 2, 3, 4, 1] este un ciclu elementar.
[3, 4, 5, 1, 2, 3] este un ciclu hamiltonian n graful din gura 3.2 a). [4, 5, 1, 2, 4, 3] este un
lant eulerian, precum si [2, 4, 3, 2, 1, 5, 4].
In acest graf nu exista nici un ciclu eulerian.
Fig. 3.4: Componente conexe
Denit ia 3.11 Un graf se numeste conex daca pentru orice pereche de varfuri x si y exista
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 apart inand grafului init ial.
Denit ia 3.12 O muchie e E se numeste muchie critic a daca prin eliminarea acesteia
o componenta conexa se mparte n doua sau mai multe componente conexe.
Exemplul 3.8 Graful din gura 3.4 are doua componente conexe: {1, 2, 3, 4, 5, 6} si {7, 8, 9}.
Muchia [2, 5] este muchie critica.
Denit ia 3.13 Un graf planar este un graf ce poate reprezentat n plan astfel ncat
muchiile sale sa nu se intersecteze doua cate doua.
Denit ia 3.14 Un graf G = (V, E) se numeste graf bipartit daca exista o partit ie a
mult imii nodurilor {V
1
, V
2
} (V = V
1
V
2
, V
1
V
2
= , V
1
, V
2
= ) astfel ncat orice muchie din
E va avea o extremitate n mult imea V
1
si cealalta extremitate n mult imea V
2
([x, y] E
avem x V
1
si y V
2
).
37
Propozit ia 3.9 Un graf este bipartit daca si numai daca nu cont ine cicluri de lungime
impara.
Denit ia 3.15 Un graf bipartit este complet daca x V
1
, y V
2
, [x, y] E (G =
(V, E), V = V
1
V
2
, V
1
V
2
= ). Daca |V
1
| = p, |V
2
| = q atunci graful bipartit complet se
noteaza K
p,q
.
Fig. 3.5: Un exemplu de graf bipartit si graf bipartit complet.
Exemplul 3.10
In gura 3.5 a) este ilustrat un graf bipartit (V
1
= {1, 2, 3}, V
2
= {4, 5}),
iar n cazul b) avem un graf bipartit complet, K
2,3
.
Denit ia 3.16 Se numeste izomorsm de la graful G la graful G
o funct ie bijectiva :
V (G) V (G
).
Daca exista un izomorsm de la graful G la graful G
.
Denit ia 3.17 Un graf etichetat este un graf n care ecare muchie si varf poate avea
asociata o eticheta.
Vom prezenta n continuare cateva operat ii dintre cele mai ntalnite ce se pot efectua
asupra unui graf oarecare:
ind dat un nod se cere lista vecinilor sai. Aceasta operat ie este cea mai utilizata pentru
o serie ntreaga de algoritmi pe grafuri;
ind 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 situat ie este indicata trans-
formarea primului mod de reprezentare n modul de reprezentare optim;
ind data o muchie sau un nod se cere o informat ie asociata acestui element: de exemplu
lungimea muchiei sau distant a de la sursa la nodul specicat.
38
3.2 Operat ii pe grafuri
1. Complementarul grafului G = (V, E) se deneste astfel: este graful G
c
= (V
c
, E
c
), unde
V
c
= V si E
c
= {[x, y]|x, y V, [x, y] / E}. Altfel spus E
c
= (V V ) \ E.
Fig. 3.6: Un exemplu de graf complementar altui graf.
In gura 3.7 b) este reprezentat graful obt inut prin contract ia muchiei [3, 4] din graful
G = (V, E) (vezi gura 3.6 a)).
4. Se numeste graf reprezentativ al muchiilor unui graf G = (V, E), graful G
R
= (V
R
, E
R
)
n care |V
R
| = |E| si E
R
= {[e, f]|e, f E adiacente} (vezi gura 3.8).
5. Denim graful total al unui graf G = (V, E) ca ind graful G
T
= (V
T
, E
T
) unde V
T
=
V E si E
T
= {[u, v]|u, v V E iar u si v sunt adiacente sau incidente n G}.
39
Fig. 3.8: Figura b) prezinta graful reprezentativ al muchiilor grafului din gura a).
Fig. 3.9: a) Un exemplu de graf neorientat. b) Graful total al grafului din gura a).
Graful total G
T
= (V
T
, E
T
), unde V
T
= {1, 2, 3, 4, v
1
, v
2
, v
3
, v
4
}, si E = {[1, 2], [1, 4],
[2, 3], [3, 4], [v
1
, v
2
], [v
1
, v
4
], [v
2
, v
3
], [v
3
, v
4
], [v
1
, 1], [v
1
, 2], [v
2
, 2], [v
2
, 3], [v
3
, 3], [v
3
, 4], [v
4
, 4], [v
4
, 1]},
corespunde grafului G = (V, E), V = {1, 2, 3, 4}, E = {[1, 2], [1, 4], [2, 3], [3, 4]} (vezi
gura 3.9).
6. Reuniunea si intersect ia a doua grafuri se denesc astfel:
daca V
1
= V
2
atunci: G
1
G
2
= (V
1
, E
1
E
2
), G
1
G
2
= (V
1
, E
1
E
2
);
daca V
1
V
2
= atunci: G
1
G
2
= (V
1
V
2
, E
1
E
2
), G
1
G
2
= (V
1
V
2
, E
1
E
2
);
daca V
1
V
2
= atunci G
1
G
2
se numeste reuniune disjuncta a grafurilor G
1
si
G
2
, iar G
1
G
2
este graful vid.
7. Denim suma grafurilor G
1
si G
2
ca ind graful complementar al reuniunii comple-
mentarelor celor doua grafuri: G
1
G
2
= (G
c
1
G
c
2
)
c
.
Pentru grafurile complete K
p
si K
q
avem: K
p
K
q
= (K
c
p
K
c
q
)
c
= K
p+q
. Un alt
exemplu de suma a doua grafuri poate urmarit n gura 3.10.
8. Se numeste produsul cartezian al grafurilor G
1
= (V
1
, E
1
) si G
2
= (V
2
, E
2
), graful
G
1
G
2
= (V
, E
), unde V
= V
1
V
2
si E
= {[(u
1
, u
2
), (v
1
, v
2
)]|u
1
, v
1
V
1
, u
2
, v
2
V
2
; u
1
= v
1
si [u
2
, v
2
] E
2
sau u
2
= v
2
si [u
1
, v
1
] E
1
}.
40
Fig. 3.10: a) Doua grafuri neorientate, G
1
si G
2
. b) Grafurile complementare G
c
1
si G
c
2
. c) Reuniunea
grafurilor G
c
1
si G
c
2
. d) Graful complementar al grafului reuniune (G
c
1
G
c
2
)
c
.
Fig. 3.11: Graful produs cartezian a doua grafuri
Fie G
1
= (V
1
, E
1
) unde V
1
= {u
1
, v
1
} si E
1
= {[u
1
, v
1
]}, si G
2
= (V
2
, E
2
), unde V
2
=
{u
2
, v
2
, w
2
} si E
2
= {[u
2
, v
2
], [v
2
, w
2
]}, doua grafuri neorientate. Atunci graful produs
cartezian al grafurilor G
1
si G
2
este G
1
G
2
= (V
, E
) unde:
V
= {(u
1
, u
2
), (u
1
, v
2
), (u
1
, w
2
), (v
1
, u
2
), (v
1
, v
2
), (v
1
, w
2
)}, si
E
= {[(u
1
, u
2
), (v
1
, u
2
)], [(u
1
, u
2
), (u
1
, v
2
)], [(u
1
, v
2
), (v
1
, v
2
)], [(u
1
, v
2
), (u
1
, w
2
)],
[(u
1
, w
2
), (v
1
, w
2
)], [(v
1
, u
2
), (v
1
, v
2
)], [(v
1
, v
2
), (v
1
, w
2
)]} (vezi gura 3.11).
9. Se numeste radacina patrata a grafului G, un graf neorientat H cu proprietatea ca
H
2
= G (H
2
= H H).
10. Operat ia de compunere a doua grafuri neorientate G
1
= (V
1
, E
1
) si G
2
= (V
2
, E
2
),
notata cu G
1
[G
2
], se realizeaza astfel: varful (x
1
, y
1
) este adiacent cu varful (x
2
, y
2
) n
graful rezultat daca varful x
1
este adiacent cu varful x
2
n graful G
1
sau (x
1
= x
2
si
varful y
1
este adiacent cu varful y
2
n graful G
2
) (vezi gura 3.12).
41
Fig. 3.12: Graful rezultat n urma compunerii grafurilor G
1
si G
2
din gura 3.11
3.3 Moduri de reprezentare
Fig. 3.13: Un exemplu de graf neorientat
Pentru o mult ime de noduri V vom presupune un mod de ordonare a nodurilor grafului
(primul nod, al doilea nod, etc.), deoarece reprezentarea unui graf depinde de aceasta or-
donare. Astfel, numerotam n mod arbitrar varfurile grafului cu 1, 2, . . . , |V |.
1. Matricea de adiacent a - este o matrice A M
nn
Z unde n = |V |, iar a
i,j
= 1 daca
exista o muchie ntre nodurile numerotate i si j (ntre x
i
si x
j
), sau a
i,j
= 0 daca nu
exista o muchie ntre acestea.
a
i,j
=
_
1 , daca [x
i
, x
j
] E
0 , daca [x
i
, x
j
] / E
Pentru un graf neorientat aceasta matrice este simetrica (a
i,j
= a
j,i
).
Exemplul 3.11 Matricea de adiacent a corespunz atoare grafului din gura 3.13 este:
A =
_
_
_
_
_
_
_
_
_
_
_
_
0 1 1 1 0 0 0 0
1 0 0 0 1 0 0 0
1 0 0 0 1 0 0 0
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 0 1 0 0
_
_
_
_
_
_
_
_
_
_
_
_
42
Fig. 3.14: Un exemplu de graf neorientat ponderat
Matricea de adiacent a are un numar de n
2
elemente, iar elementele neredundante sunt
n numar de
n(n1)
2
. Prin urmare complexitateaspat iu a matricii de adiacent a este
O(n
2
).
2. Matricea costurilor - reprezinta o varianta a matricei de adiacent a ce se obt ine n mod
natural luanduse n considerare graful ponderat: ecarei muchii i se ataseaza un cost
d, iar valorile matricii A se denesc astfel:
a
i,j
=
_
_
0 , daca x
i
= x
j
+ , daca [x
i
, x
j
] / E
d > 0 , daca [x
i
, x
j
] E
(3.2)
sau
a
i,j
=
_
_
0 , daca x
i
= x
j
, daca [x
i
, x
j
] / E
d > 0 , daca [x
i
, x
j
] E.
(3.3)
Exemplul 3.12 Matricea costurilor corespunz atoare grafului din gura 3.14 are urm atoarele
valori:
A =
_
_
_
_
_
_
_
_
_
_
_
_
0 35 7 20
35 0 50
7 0 22
20 0 15
50 22 0 44
15 0 10 27
44 10 0
27 0
_
_
_
_
_
_
_
_
_
_
_
_
Notat ia (3.2) este folosita n aplicat ii n care se cere un drum de lungime minima iar
(3.3) este folosita n aplicat ii n care se cere un drum de lungime maxima ntre doua
noduri.
Matricea costurilor va ocupa un spat iu de memorie mai mare decat cel ocupat de
matricea de adiacent a: pentru reprezentarea unui element al matricii de adiacent a este
sucient un bit, pe cand pentru reprezentarea unui element al matricii costurilor se
foloseste un byte, un ntreg (lung) sau un numar real.
43
Prin urmare pentru reprezentarea unei matrici de adiacent a de dimensiuni n n sunt
necesari n
n
8
octet i, pe cand pentru reprezentarea unei matrici de costuri cu elemente
numere ntregi sunt necesari 2 n
2
octet i (daca presupunem ca un numar ntreg fara
semn este pastrat pe 2 octet i).
3. Liste de adiacent a - pentru ecare nod se ret ine lista nodurilor adiacente. Astfel unui
nod x
k
i se va atasa lista tuturor vecinilor sai.
Aceasta reprezentare se preteaza mai bine pentru grafuri cu un numar mare de noduri
si un numar mic de muchii (graf rar).
In cazul unui graf neorientat numarul elementelor
din listele de adiacent a este 2 |E|, deoarece o muchie [u, v] va prezenta de doua ori,
atat n lista de adiacent a a nodului u cat si n lista de adiacent a a nodului v.
Exemplul 3.13 Listele de adiacent a pentru graful din gura 3.13 sunt:
1: (2, 3, 4) 5: (2, 3, 7)
2: (1, 5) 6: (4, 7, 8)
3: (1, 5) 7: (5, 6)
4: (1, 6) 8: (6)
Aceste liste de adiacent a (sau liste de vecini) pot reprezentate prin intermediul
tablourilor sau prin intermediul listelor simplu sau dublu nlant uite.
Pentru reprezentarea ce utilizeaza tablouri, se vor deni doua tablouri Cap si List
(Cap R
1n
, List R
2m
, m = |E|) unde:
Cap
k
=
_
_
0 , nodul respectiv nu are vecini
j , indica numarul coloanei din matricea List unde se aa primul vecin
al varfului x
k
.
List
1,j
- indicele varfului ce se aa n lista de vecini a varfului x
k
.
List
2,j
=
_
_
0 , daca varful respectiv este ultimul (aceasta valoare are aceeasi
semnicat ie ca si valoarea NIL sau NULL utilizata n lucrul cu pointeri)
i , indica numarul coloanei unde se aa urmatorul vecin din lista
de vecini a nodului x
k
.
Exemplul 3.14 Pentru exemplul din gura 3.13 avem urmatoarea congurat ie pentru
cei doi vectori considerat i:
Nod 1 2 3 4 5 6 7 8
Cap 1 4 6 8 10 13 16 18
List =
_
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
_
In gura 3.16 este reprezentat arborele de acoperire n lat ime rezultat n urma parcurgerii
grafului din exemplu.
46
Algoritmul de parcurgere utilizeaza o structura de date de tip coada n care vor intro-
duse nodurile vizitate, dar care nu au fost nca prelucrate (nu au fost cercetat i vecinii lor).
Reamintim ca numarul de noduri din mult imea V este n. Vectorul vizitat pastreaza situat ia
vizitarii nodurilor grafului G:
vizitat
k
=
_
1 , daca nodul k a fost vizitat
0 , daca nodul k nu a fost vizitat.
Algoritm 20 Algoritm de vizitare n lat ime
1: procedure BFS(k, n, V ecin)
Input:
_
_
k - nodul de la care se porneste vizitarea
n - numarul de noduri din graf
V ecin - matricea de adiacent a a grafului
2: for i 1, n do
3: vizitat
i
0
4: end for
5: vizitat
k
1 marcarea nodului curent ca ind vizitat
6: Q k inserarea nodului curent k n coada
7: call V izitare(k) vizitarea nodului curent
8: while (Q = ) do
9: Q k extragere nod curent din coada
10: for i 1, n do
11: if (vizitat
i
= 0) (vecin
k,i
= 1) then
12: vizitat
i
1 marcarea nodului i ca ind vizitat
13: Q i inserarea nodului i n coada
14: call V izitare(i) vizitarea nodului i
15: end if
16: end for
17: end while
18: end procedure
Implementarea n limbajul C a algoritmului 20 este urmatoarea:
#include <stdio.h>
#include <mem.h>
#define MAX 100
#define TRUE 1
#define FALSE 0
// Numarul de noduri din graf
int n;
// Matricea de adiacenta
char vecin[MAX][MAX];
// coada
int coada[MAX];
// Indicele primului element din coada
int first;
// Indicele ultimului element din coada
int last;
// Pastreaza starea cozii
47
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();
48
for (i = 0; i < n; i++)
if ((vizitat[i] == 0) && (vecin[k][i] == 1)) {
vizitat[i] = 1;
enQueue(i);
printf("%d ", i);
}
}
}
/**
* 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];
}
}
void main(void) {
readInput();
initQueue();
bfs(0);
}
In exemplul anterior funct ia de citire readInput() este foarte simpla: se citeste mai
ntai numarul de noduri al grafului, si apoi se citeste matricea de adiacent a 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 aate
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];
}
}
Funct ia memset() este o funct ie de biblioteca a carei declarat ie poate gasitan header-
ele mem.h si string.h la Borland C sau memory.h si string.h la Visual C++ 2010
1
.
Funct ia are urmatoarea semnatura
2
1
http://msdn.microsoft.com/en-us/library/1fdeehz6.aspx
2
http://en.wikibooks.org/wiki/C_Programming/Strings#The_memset_function
49
void *memset(void *dest, int c, size t count);
si are rolul de a init ializa cu valoarea c primii count octet i din zona de memorie ce
ncepe de la adresa identicata de pointerul dest.
vida este o variabila globala ce ia valoarea true atunci cand coada nu cont ine nici un
element si valoarea false n caz contrar.
Coada este implementata static sub forma unei cozi circulare.
Vizitarea ncepe de la nodul 0 (bfs(0)).
Algoritmul BF este utilizat de catre metoda Branch-and-Bound ca metoda de explorare
a spat iului solut iilor, cat si n cadrul problemelor de determinare a distant ei minime de la
un varf la toate celelalte varfuri n cazul n care lungimea unui drum dintre doua noduri se
considera ca ind numarul de noduri intermediare ale acestuia.
3.4.2 Parcurgerea D (D - Depth)
Metoda de parcurgere D este asemanatoare cu metoda de parcurgere n lat ime (BF):
la nceput se viziteaza varful de pornire k;
urmeaza n ordine tot i vecinii nca nevizitat i ai acestuia;
la pasul urmator se considera ultimul nod vizitat v si se ncearca vizitarea vecinilor nca
nevizitat i ai acestuia. Daca nodul v nu mai are vecini nevizitat i, se continua procesul
de parcurgere cu nodul ce a fost vizitat exact naintea nodului v, etc.
Metoda de parcurgere D este o combinat ie ntre metoda de parcurgere n lat ime (vezi
sect iunea 3.4.1) si metoda de parcurgere n adancime (vezi sect iunea 3.4.3). Pentru un nod
se viziteaza tot i vecinii nca nevizitat i ai acestuia, nsa spre deosebire de metoda de parcurgere
n lat ime unde ordinean care au fost vizitate nodurile se pastreaza si la parcurgere, la metoda
de parcurgere D se prelucreaza mereu ultimul nod vizitat.
Pentru aceasta n algoritmul D se nlocuieste structura de date de tip coada utilizata n
algoritmul BF, cu o structura de date de tip stiva (vezi algoritmul 21).
Fig. 3.17: Arbore de acoperire pentru parcurgerea D
_
k - nodul de la care se porneste vizitarea
n - numarul de noduri din graf
V ecin - matricea de adiacent a a grafului
2: for i 1, n do
3: vizitat
i
0
4: end for
5: vizitat
k
1 marcarea nodului curent ca ind vizitat
6: S k inserarea nodului curent k n stiva
7: call V izitare(k) vizitarea nodului curent
8: while (S = ) do
9: S k extragerea nodului curent din stiva
10: for i 1, n do
11: if (vizitat
i
= 0) (vecin
k,i
= 1) then
12: vizitat
i
1 marcarea nodului i ca ind vizitat
13: S i inserarea nodului i in stiva
14: call V izitare(i) vizitarea nodului i
15: end if
16: end for
17: end while
18: end procedure
3.4.3 Parcurgerea n adancime (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 nevizitat i.
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
tot i vecinii sai au fost vizitat i.
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 gura 3.13), n urma parcurgerii n adancime, vom obt ine
nodurile n ordinea urmatoare: 1, 2, 5, 3, 7, 6, 4, 8 (vezi gura 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 clasicate n urmatoarele cate-
gorii:
1. muchie a arborelui de acoperire - muchia [u, v] este o muchie a arborelui de acoperire
daca dfs(u) apeleaza direct dfs(v) sau invers;
2. muchie de ntoarcere - muchia [u, v] este o muchie de ntoarcere daca dfs(u) apeleaza
indirect dfs(v) (x V a.i. dfs(u) dfs(x) dfs(v)) sau invers.
51
Fig. 3.18: Arbore de acoperire n adancime
Exemplul 3.16 Pentru graful din gura 3.13 avem:
muchiile [1, 2], [2, 5], [3, 5], [4, 6], [5, 7], [6, 7], [6, 8] sunt muchii ale arborelui de acoperire;
muchiile [1, 3], [1, 4] sunt muchii de ntoarcere;
Algoritm 22 Algoritm de vizitare n adancime (varianta recursiva)
1: procedure DFS(k, n, V ecin)
Input:
_
_
k - nodul vizitat
n - numarul de noduri din graf
V ecin - matricea de adiacent a a grafului
2: vizitat
k
1 marcarea nodului curent ca ind vizitat
3: call V izitare(k) vizitarea nodului curent
4: for i 1, n do
5: if (vizitat
i
= 0) (vecin
k,i
= 1) then
6: call DFS(i, n, vecin) apelul recursiv al subrutinei DFS pentru nodul i
7: end if
8: end for
9: end procedure
Implementarea algoritmului se poate realiza e n varianta recursiva (vezi algoritmul 22),
e n varianta nerecursiva (vezi algoritmul 23). Ca si la algoritmul de parcurgere n lat ime,
vectorul vizitat pastreaza situat ia vizitarii nodurilor grafului G:
vizitat
k
=
_
1 , daca nodul k a fost vizitat
0 , n caz contrar.
Subrutina DFS (algoritmul 22)ncepe cu marcarea nodului curent ca ind vizitat (vizitat
k
1) si apelul procedurii V izitare (call V izitare(k)). Apoi se cauta primul vecin nevizitat i
al varfului curent k ((vizitat
i
= 0) (vecin
k,i
= 1)) si se reia secvent a de operat ii pentru
acesta, printrun apel recursiv (call DFS(i)). Enunt ul repetitiv for i 1, n poate opti-
mizat astfel ncat sa nu se mai verice toate varfurile grafului, ci numai nodurile adiacente
cu nodul curent: n aceasta situat ie reprezentarea cu liste de adiacent a este optima din punct
52
de vedere al numarului de operat ii efectuate, ind preferata reprezentarii prin matricea de
adiacent a.
Algoritm 23 Algoritm de vizitare n adancime (varianta nerecursiva)
1: procedure DFS Nerecursiv(k, n, V ecin)
Input:
_
_
k - nodul de la care se porneste vizitarea
n - numarul de noduri din graf
V ecin - matricea de adiacent a a grafului
2: for i 1, n do
3: vizitat
i
0
4: end for
5: vizitat
k
1 marcarea nodului curent ca ind vizitat
6: call V izitare(k) vizitarea nodului curent
7: S k inserarea nodului curent k n stiva
8: found false
9: while (S = ) do cat timp stiva nu este vida
10: if ((found)) then
11: S k extragerea nodului curent din stiva
12: end if
13: i 1
14: found false
15: while (i n) (found = false) do
16: if (vizitat
i
= 0) (vecin
k,i
= 1) then
17: vizitat
i
1 marcarea nodului i ca ind vizitat
18: S k inserarea nodului curent k n stiva
19: call V izitare(i) vizitarea nodului i
20: k i
21: found true
22: else
23: i i + 1
24: end if
25: end while
26: end while
27: end procedure
Algoritmul 23 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). Secvent a
de instruct iuni 23.1323.25 nu este optima deoarece, de ecare data cand se revine la un nod
parinte, se verica ntreaga mult ime V pentru a identica un vecin nevizitat al acestuia.
In
vederea reducerii numarului de vericari, se va utiliza reprezentarea prin liste de adiacent a,
si se salveaza pe stiva, pe langa valoarea nodului curent k, valoarea vecinului nevizitat gasit,
astfel ncat, la revenire, sa se continue cautarea urm atorului 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 aat 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 usurint a de programare si elegant a ei. Prezentam
n continuare implementarea n limbajul C a algoritmului 22:
#include <stdio.h>
53
#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);
}
Complexitatea algoritmilor de parcurgere
Complexitatea algoritmilor prezentat i este O(n
2
) deoarece se utilizeaza matricea de adiacen-
t a drept modalitate de reprezentare a grafului. Daca se utilizeaza reprezentarea prin liste de
54
vecini, atunci complexitatea algoritmilor devine O(n +m), unde m = |E|.
Matricea de adiacent a Liste de vecini Lista de muchii
BFS O(n
2
) O(n + m) O(n + m
2
)
D O(n
2
) O(n + m) O(n + m
2
)
DFS O(n
2
) O(n + m) O(n + m
2
)
3.5 Componente conexe
Se poate deni pe mult imea varfurilor unui graf neorientat G o relat ie astfel: xy daca
x = y sau exista n G un lant de la x la y.
Se demonstreaza foarte usor ca relat ia este o relat ie de echivalent a. Se cunoaste faptul ca
o relat ie de echivalent a determina pe mult imea pe care este denita o partit ie. Componentele
conexe vor elementele acestei partit ii formate din varfurile ce sunt echivalente ntre ele
(conform relat iei de echivalent a).
Cu alte cuvinte, exista o partit ie a mult imii varfurilor V
V =
m
_
i=1
V
i
, V
i
V
j
= , i, j = 1, m, i = j
si o partit ie a mult imii muchiilor E
E =
m
_
i=1
E
i
, E
i
E
j
= , i, j = 1, m, i = j
astfel ncat ecare graf G
i
= (V
i
, E
i
), i = 1, m, este conex.
Algoritm 24 Algoritm pentru determinarea componentelor conexe
Input:
_
n - numarul de noduri din graf
V ecin - matricea de adiacent a a grafului
1: for i 1, n do
2: vizitat
i
0
3: end for
4: cmp conex nr 0
5: for i 1, n do
6: if (vizitat
i
= 0) then
7: cmp conex nr cmp conex nr + 1
8: call DFS(i, n, V ecin) determinarea componentei conexe ce cont ine nodul i
9: end if
10: end for
Pentru determinarea componentelor conexe vom utiliza metoda de vizitare n adancime a
unui graf. Pasii algoritmului descrisi n limbaj natural sunt urmatorii (algoritmul 24 prezinta
descrierea formalizata n pseudocod):
Pas 1. Se cauta un nod nca nevizitat.
Pas 2.
Incepand cu acesta se parcurg toate nodurile accesibile si nevizitate, avand grija sa
marcam vizitarea acestora. Toate aceste noduri formeaza o componenta conexa.
Pas 3. Daca mai exista noduri nevizitate mergi la pasul Pas 1, altfel algoritmul se termina.
55
3.6 Muchie critica
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 prezen-
tate doua variante de rezolvare.
Solut ia I
Pentru simplitate, vom trata situat ia n care graful considerat este conex (daca graful nu
este conex se determina numarul de componente conexe si se aplica algoritmul pentru ecare
componenta conexa n parte). Se elimina, pe rand, ecare muchie a grafului si apoi se verica
daca graful rezultat mai este conex (vezi algoritmul 25).
Algoritm 25 Algoritm de determinare a muchiei critice (prima varianta)
1: procedure MuchieCriticaI(n, m, V ecin)
Input:
_
_
n - numarul de noduri din graf
m - numarul de muchii din graf
V ecin - matricea de adiacent a
2: for k 1, m do
3: elimina muchia k
4: if (conex(n, vecin) = false) then
5: Output {Muchia k este critica}
6: end if
7: adauga muchia k reface graful init ial
8: end for
9: end procedure
10: function Conex(n, V ecin)
11: for i 1, n do
12: vizitat
i
0
13: end for
14: call DFS(1, n, V ecin) vizitarea n adancime a grafului
15: for i 1, n do
16: if (vizitat
i
= 0) then
17: return false
18: end if
19: end for
20: return true
21: end function
Subrutina Conex verica daca graful identicat prin matricea de adiacent a 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 ind 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
56
#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;
57
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;
}
}
Observat ia 3.17 Am presupus ca numerotarea nodurilor se face de la 0 si ca exista un nod
cu eticheta 0.
Complexitateatimp a acestui algoritm (presupunem ca avem o singura componenta
conexa), este O(m(n + m)): se ia ecare muchie (O(m)), se elimina din graf, si se verica
daca graful ramas este conex (O(n +m)).
Solut ia a IIa
Cea dea doua varianta de abordare pentru identicarea unei solut ii foloseste urmatoarea
proprietate:
Observat ia 3.18 O muchie nu este critica daca ea face parte din cel put in un ciclu elemen-
tar al grafului respectiv.
In urma vizitarii DFS, toate muchiile unui graf se mpart n muchii ale arborelui de
acoperire si muchii de ntoarcere.
Vom numerota toate nodurile grafului n preordine - n momentul n care un nod este
marcat ca ind vizitat - toate valorile ind pastrate ntr-un vector prenum. Fie low
u
valoarea
unui nod u, calculata dupa formula:
low
u
= min
_
_
prenum
u
, si
prenum
x
, daca [u, x] este muchie de ntoarcere, si
low
y
, y descendent direct al lui u.
Daca prenum
u
low
v
, v descendent direct al lui u, atunci nseamna ca v sau un
descendent al lui v are o muchie dentoarcere la u sau la un stramos al acestuia. Astfel muchia
[u, v] apart ine unui ciclu elementar, si prin urmare nu este muchie critica (vezi algoritmul
26). Prin negat ie, daca exista cel put in un varf v, descendent direct al lui u, cu proprietatea
ca prenum
u
< low
v
atunci [u, v] este muchie critica. Dupa ecare apel recursiv al subrutinei
DFS (linia 16) se verica gradul de adevar al expresiei (prenum
k
< low
i
) (linia 18).
Variabila counter este globala pentru cele doua subrutine, MuchieCriticaII() si DFS critic().
La numerotarea n preordine se folosesc atribuirile: counter counter + 1, si prenum
k
counter. Cand un nod i este vecin cu nodul curent k, si nu a fost nca vizitat, valoarea lui
low
k
se calculeaza astfel: low
k
min {low
k
, low
i
}. 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 low
k
se calculeaza dupa formula low
k
min {low
k
, prenum
i
}.
Implementarea n limbajul C a algoritmului 26 este:
58
Algoritm 26 Algoritm de determinare a muchiei critice (a doua varianta)
1: procedure MuchieCriticaII(n, V ecin)
Input:
_
n - numarul de noduri din graf
V ecin - matricea de adiacent a
2: for i 1, n do
3: vizitat
i
0
4: end for
5: counter 0
6: call DFS critic(1, n, V ecin) vizitarea n adancime a grafului
7: end procedure
8: procedure DFS critic(k, n, V ecin)
9: vizitat
k
1
10: counter counter + 1
11: prenum
k
counter, low
k
counter
12: for i 1, n do
13: if (vecin
k,i
= 1) then
14: if (vizitat
i
= 0) then
15: tata
i
k
16: call DFS critic(i, n, V ecin)
17: low
k
Min(low
k
, low
i
)
18: if (prenum
k
< low
i
) then
19: Output {Muchia (k,i) este critica}
20: end if
21: else
22: if (tata
k
= i) then
23: low
k
Min(low
k
, prenum
i
)
24: end if
25: end if
26: end if
27: end for
28: end procedure
#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];
/**
59
* Pastreaza tatal fiecarui nod in arborele de acoperire
* in adincime generat de metoda DFS.
*/
int tata[MAX];
/**
* prenum[i] - numerotarea in preordine
*/
int prenum[MAX];
int low[MAX];
/**
* contor global ce numara momentul cand este vizitat un nod.
*/
int counter;
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];
}
}
int min(int x, int y) {
return (x < y) ? x : y;
}
void dfs(int k) {
int i;
vizitat[k] = 1;
counter++;
prenum[k] = counter; low[k] = counter;
for (i = 0; i < n; i++)
if (vecin[k][i] == 1)
if (vizitat[i] == 0) {
tata[i] = k;
dfs(i);
low[k] = min(low[k], low[i]);
if (prenum[k] < low[i])
printf("%d -> %d \n", k, i);
}
else
if (tata[k] != i)
low[k] = min(low[k], prenum[i]);
/*printf("prenum[%d] = %d low[%d] = %d\n",k,prenum[k],k,low[k]);*/
}
void critic(void) {
60
memset(vizitat, 0, sizeof(vizitat));
counter = 0;
dfs(0);
}
void main(void) {
printf("\n");
readInput();
critic();
}
Fig. 3.19: Algoritmul 26 aplicat pentru graful din gura 3.13
Exemplul 3.19 Pentru graful din gura 3.13, valorile prenum si low sunt cele din gura
3.19 (valoarea din partea dreapta a unui varf reprezinta valoarea sa la numerotarea n preor-
dine, prenum, iar numarul din stanga reprezinta valoarea calculata low). Condit ia prenum
u
<
low
v
, unde [u, v] este o muchie a grafului, este ndeplinita doar pentru u = 6 si v = 8. Prin
urmare [6, 8] este muchie critica n graful considerat.
Deoarece acest algoritm este constituit din algoritmul modicat de vizitare n adancime
a unui graf, complexitateatimp este O(n +m) atunci cand graful este reprezentat prin liste
de adiacent a si O(n
2
) n situat ia n care graful este reprezentat prin matricea de adiacent a.
3.7 Exercit ii
1. Un graf neorientat cu n noduri, G = (V, E) se numeste graf scorpion daca poseda trei
noduri speciale:
(a) acul - d
G
(u) = 1 si este legat de coada;
(b) coada - d
G
(u) = 2 si este legata de ac si corp;
(c) corpul - d
G
(u) = n 2 ind legat de toate nodurile din graf cu except ia acului.
61
Fig. 3.20: Un exemplu de graf neorientat cu 8 varfuri
Nu exista alte restrict ii cu privire la nodurile grafului.
Sa se realizeze un algoritm care determina daca un graf este graf scorpion folosind O(n)
ntrebari de forma Exista o muchie ntre nodurile u si v?.
2. Se considera n bazine dispuse circular si lipite unul de altul n ordinea numerelor de
ordine. Se da lista celor m perechi de bazine ce trebuie unite prin canale. Un numar
de bazine poate sa apara n mai multe perechi. Canalele pot realizate n interiorul
sau n exteriorul cercului, cu condit ia ca ele sa nu se intersecteze.
Daca problema are solut ie, vor asate doua liste: cea a perechilor de bazine unite
prin canale interioare si cea a perechilor de bazine unite prin canale exterioare. Daca
problema nu are solut ie, atunci se va raporta acest lucru.
In cazul n care problema are solut ie sa se determine toate posibilitat ile de unire prin
canale exterioare sau interioare.
(ONI, 1993)
3. Sa se parcurga n lat ime si n adancime graful din gura 3.20, sa se construiasca arborii
de parcurgere si sa se puna n evident a muchiile de ntoarcere.
4. Compania TLC (Telephone Line Company) a stabilit o noua ret ea de cablu telefonic.
Ea leaga mai multe locuri numerotate cu numere ntregi de la 1 la N. Nu exista doua
locuri numerotate cu acelasi numar. Liniile sunt bidirect ionale si leaga doua locuri;
ecare loc are un post telefonic. Din orice loc poate apelat oricare alt loc, prin
legatura directa sau conexiuni intermediare. Uneori ret eaua cade n unele locuri si
conexiunea aferenta nu mai este posibila. Tehnicienii de la TLC au realizat ca n acest
caz, nu numai ca locul respectiv nu mai poate apelat, dar el rupe legatura si ntre
alte locuri pentru care asigura conexiunea.
In aceasta situat ie spunem ca locul (unde
a aparut caderea) este critic.
Se cere sa de dezvolte un algoritm care sa calculeze numarul tuturor acestor puncte
critice.
(ACM Europa Centrala, 1996)
5. Fiind dat un graf G = (V, E), sa se verice daca este aciclic.
6. Fiind dat un graf G = (V, E), sa se verice daca este bipartit.
62
7.
Intrun oras intersect iile sunt numerotate de la 1 la n (se considera intersect ie nu
numai locul unde se intersecteaza mai multe strazi ci si punctele terminale capetele
de strazi). Edilii orasului doresc sa numeroteze si strazile orasului, dar ntrun mod
care sa t ina seama de numerotarea intersect iilor, si anume: doua strazi diferite vor avea
numere diferite si n ecare intersect ie trebuie sa soseasca o strada care sa aiba numarul
intersect iei.
Se cere sa se realizeze un algoritm care sa determine o astfel de numerotare, daca exista.
Datele de intrare constau dintrun numar n reprezentand numarul de puncte de inter-
sect ie; ecare punct este identicat prin numarul cu care este numerotat, ntre 1 si n,
urmat de lista punctelor cu care acesta comunica direct printro strada. Prin strada se
nt elege o port iune de drum aata ntre doua puncte de intersect ie, neglijand faptul ca
n practica strazile cuprind mai multe intersect ii.
8. Se considera o tabla de sah de dimensiune n n (n 100) pe care sunt dispuse
obstacole. Se cere sa se determine numarul minim de mutari necesare unui nebun
pentru a se deplasa, respectand regulile jocului de sah si ocolind obstacolele, dintro
pozit ie init iala data ntro pozit ie nala data.
Se considera ca obstacolele nu coincid cu pozit ia init iala si nici cu cea nala a nebunului.
(Concurs student esc, 1993)
9. Pe o tarla agricola sunt mai multe parcele ce trebuie cosite. Parcelele de diferite culturi
(lucerna, trifoi, iarba, furaje) vor cosite de muncitori diferit i 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 core-
spunde unei mult imi de un anumit tip. Doua elemente ale matricii sunt vecine daca
au o latura comuna. O parcela este o mult ime 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.
63
Capitolul 4
Grafuri euleriene si hamiltoniene
4.1 Grafuri Euleriene
In anul 1736, matematicianul Leonhard Euler publica o lucrare asupra problemei podurilor
din Konigsberg
1
(gura 4.1) n care se prezinta un studiu teoretic asupra lant urilor si ciclurilor
Euleriene.
Fig. 4.1: Podurile din Konigsberg
Sa consideram un graf neorientat G = (V, E).
Denit ia 4.1 Un lant L ce cont ine ecare muchie a unui graf G o singura data se numeste
lant Euler sau lant eulerian. Daca extremitat ile lant ului sunt identice si lant ul este
eulerian atunci ciclul se numeste ciclu Euler sau ciclu eulerian. Un graf ce cont ine un
ciclu eulerian se numeste graf eulerian.
O problema mai cunoscuta legata de not iunile de ciclu si lant eulerian este aceea de a
desena cu o singura linie nentrerupta o anumita gura (vezi gura 4.2).
Teorema 4.1 (Euler) Un graf G conex (|V | 3) este eulerian daca si numai daca gradul
ecarui varf este par.
1
http://en.wikipedia.org/wiki/Seven Bridges of Konigsberg
64
Fig. 4.2: Doua guri geometrice ce pot desenate folosind o singura linie
Demonstrat ie: Fie G un graf conex eulerian un ciclu eulerian C. Acesta
trece o singura data prin toate muchiile grafului. Gradul ecarui varf u este par deoarece
pentru ecare muchie incidenta cu u prin intermediul careia se ajunge n varful u, exista o
alta muchie prin intermediul careia se paraseste varful u.
Presupunem ca u V , d
G
(u) este par. Fie L un lant de lungime maxima n G si
e u si v extremitat ile lant ului.
Presupunem ca u = v. Deoarece d
G
(v) este par, [v, w] E astfel ncat [v, w] / L. Lant ul
L {[v, w]} are lungimea mai mare decat L, contradict ie. Prin urmare u = v. Rezulta ca
L = C este un ciclu de lungime maxima.
Presupunem ca ciclul C nu este ciclu eulerian. Atunci x, y V, [x, y] E, [x, y] / C si
x C (exista o muchie a grafului G ce nu apart ine ciclului C cu proprietatea ca cel put in o
extremitate apart ine ciclului).
Fie L
1
= [x, . . . , u, v, . . . , x] un lant n G ce cont ine exact muchiile lui C.
Atunci L
2
= [x, . . . , u, v, . . . , x, y] este un lant n G ce are lungimea mai mare decat L,
contradict ie. Rezulta atunci ca ciclul C este eulerian G este un graf eulerian.
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 e impar.
Demonstrat ie: 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 G
= (V
, E
),
V
= V {w}, E
= E {[u, w], [v, w]}, graful obt inut din G prin adaugarea nodului w si a
muchiilor [u, w], [v, w]. Atunci d
G
(u) este par, u V
.
Daca eliminam muchiile [u, w] si [v, w] obt inem un lant eulerian n G.
Corolarul 4.3 Un graf G conex (|V | 3) este eulerian daca si numai daca mult imea muchi-
ilor sale poate descompusa n cicluri disjuncte.
Teorema 4.4 Pentru un graf conex cu 2k varfuri de grad impar, k 1, exista k lant uri
n G ale caror mult imi de muchii formeaza o partit ie a lui E, si din care cel mult unul are
lungime impara.
4.1.1 Algoritm pentru determinarea unui ciclu eulerian
Pornind de la Teorema 4.1 construim un ciclu eulerian. Se considera drept punct de plecare
un varf oarecare al grafului. La ecare pas se alege pentru varful curent u, un varf adiacent
65
Fig. 4.3: Exemplu de graf eulerian
v, diferit de varful din care sa ajuns n u. Parcurgerea se continua pana n momentul n
care ajungem ntrun varf ce a fost deja vizitat si se nchide ciclul C. Daca ciclul C nu
este eulerian, atunci se elimina din graful G toate muchiile ciclului C, rezultand mai multe
componente conexe G
1
1
, G
1
2
, . . . , G
1
k
.
Pentru ecare astfel de componenta G
1
i
, ce are cel put in doua varfuri, aplicam recursiv
algoritmul. Conform teoremei 4.1, ecare componenta G
1
i
este euleriana. Fie C
1
i
ciclul
eulerian corespunzator componentei G
1
i
.
Ciclul C are cel put in cate un varf comun cu ecare ciclu C
1
i
. Fie acest varf u
i
. Denim
un ciclu C
0
astfel: pentru ecare i, i = 1, k, adaugam la ciclul C, n varful u
i
, ciclul C
1
i
.
Ciclul C
0
cont ine toate muchiile grafului G, prin urmare este eulerian.
Funt ia IsEulerian() verica daca un graf este eulerian conform teoremei 4.1 (vezi algo-
ritmul 27).
Algoritm 27 Algoritm de vericare daca un graf conex este eulerian
1: function IsEulerian(G = (V, E))
2: if (|V | < 3) then
3: return false
4: end if
5: for i 1, n do
6: s 0
7: for j 1, n do
8: s s + vecin
i,j
9: end for
10: if (s mod 2 = 1) then
11: return false
12: end if
13: end for
14: return true
15: end function
Dupa cum se poate vedea din funct ia IsEulerian numai componentele conexe ce au
ordinul mai mare sau egal cu 3 vor luate n considerare.
In algoritmul 28 este prezentata funct ia EulerRec pentru determinarea unui ciclu eulerian.
66
Procedura FindCycle(G) (vezi algoritmul 28) construieste un ciclu: se alege un v arf u
0
si
se cauta prima muchie [u
0
, u
1
].
In varful u
1
se cauta o muchie [u
1
, u
2
] astfel ncat varful u
2
sa
e distinct de varful din care sa ajuns n u
1
(u
2
= u
0
) si sa nu mai fost vizitat. Daca u
2
a
fost vizitat atunci funct ia se termina. Se continua procedeul de cautare pana cand se ajunge
la un varf ce a fost vizitat anterior si se returneaza ciclul cuprins ntre cele doua aparit ii ale
aceluiasi varf.
Procedura FindComponents(G
; k, G
1
1
, G
1
2
, . . . , G
1
k
) determina componentele conexe ale
grafului G
= G E(C), obt inut prin eliminarea din graful G a muchiilor ciclului C, unde
G
1
1
, G
1
2
, . . . , G
1
k
sunt cele k componente conexe returnate de aceasta.
Procedura MergeCycles(C, C
1
1
, . . . , C
1
k
; C
0
) construieste un ciclul C
0
obt inut prin adaugarea
la ciclul C, succesiv, a ciclurilor C
1
1
, . . . , C
1
k
.
Algoritm 28 Algoritm de determinare a unui ciclu eulerian ntrun graf conex
1: function EulerRec(G = (V, E))
2: if (IsEulerian(G) = false) then
3: return
4: end if
5: call FindCycle(G; C)
6: if (E(G) \ E(C) = ) then
7: return C
8: end if
9: G
GE(C)
10: call FindComponents(G
; k, G
1
1
, G
1
2
, . . . , G
1
k
)
11: for i 1, k do
12: C
1
i
EulerRec(G
1
i
)
13: end for
14: call MergeCycles(C, C
1
1
, . . . , C
1
k
; C
0
)
15: return C
0
16: end function
Fig. 4.4: Grafurile part iale obt inute n urma primelor doua etape ale algoritmului 28
Exemplul 4.5 Sa aplicam algoritmul 28 pentru graful din gura 4.3. Presupunem ca primul
element (elementul de start) este varful u
0
= 1. Procedura FindCycle construieste lant ul
67
Fig. 4.5: Grafurile part iale obt inute n urma etapelor 3 si 4 ale algoritmului 28
L = [1, 2, 3, 4, 5, 3]. Se observa prezent a ciclului C
1
= [3, 4, 5, 3].
In urma eliminarii acestuia
ramane graful din gura 4.4 a) care este un graf conex.
Continuam procesul de construire a unui ciclu eulerian, prin apelul procedurii FindCycle
pentru noul graf. Aceasta returneaza ciclul C
2
= [1, 2, 3, 9, 1]. Prin eliminarea muchiilor
acestuia, E(C
2
), se obt ine graful din gura 4.4 b).
In continuare se construieste lant ul
L = [2, 8, 7, 5, 6, 4, 10, 2], ce cont ine ciclul C
3
= [2, 8, 7, 5, 6, 4, 10, 2]. Graful obt inut prin
eliminarea muchiilor ciclului este cel din gura 4.5 a). Apelul procedurii FindCycle conduce
la determinarea lant ului L = [6, 7, 9, 8, 10, 6] si a ciclului C
4
= [6, 7, 9, 8, 10, 6].
Ciclul eulerian se formeaza prin reuniunea ciclurilor intermediare determinate: C =
(((C
4
C
3
) C
2
) C
1
) adica C = [1, 2, 8, 7, 5, 6, 7, 9, 8, 10, 6, 4, 10, 2, 3, 4, 5, 3, 9, 1] (C
4
C
3
=
[2, 8, 7, 5, 6, 7, 9, 8, 10, 6, 4, 10, 2]).
4.1.2 Algoritmul lui Rosenstiehl
Algoritmul lui Rosenstiehl [51] construieste un lant L care la nal 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 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 lant ului L, si se extrage de pe stiva nodul anterior (din care sa ajuns
n u) (vezi algoritmul 29). Vectorul vizit are drept scop sa pastreze situat ia muchiilor:
vizit
e
=
_
1 , daca e E a fost parcursa
0 , daca e E nu a fost nca parcursa.
Exemplul 4.6 Sa aplicam algoritmul 29 pentru graful din gura 4.3. Sa presupunem ca
varful u de la care porneste algoritmul este varful 1. La sfarsitul primului pas al enunt ului
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.
68
Algoritm 29 Algoritm lui Rosenstiehl de determinare a unui ciclu eulerian ntrun graf
conex
1: function Rosenstiehl(u, G)
2: for e E do
3: vizit
e
0
4: end for
5: S u Se insereaza pe stiva nodul u
6: while (S = ) do
7: S u Se extrage din stiva nodul curent
8: while (e = [u, v] E) (vizit
e
= 0)) do
9: vizit
e
1 Se marcheaza muchia e ca ind utilizata
10: S u Se salveaza pe stiva nodul curent u
11: u v Nodul curent devine nodul v
12: end while
13: L u Se adauga la lista L nodul curent
14: end while
15: return L
16: end function
In continuare, deoarece nu mai exista muchii nevizitate, se vor extrage elementele aate pe
stiva S cate unul la ecare pas, formand secvent a (8, 10, 6, 7, 8, 2, 10, 4, 6, 5, 7, 9, 3, 5, 4, 3, 2, 1),
si se vor introduce n lista L:
Pas 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 obt inut se aa n lista L.
_
0 , daca x
i
= x
j
+ , daca [x
i
, x
j
] / E
d > 0 , daca [x
i
, x
j
] E
. (4.1)
Vectorul solut iilor rezultat va X = (x
1
, x
2
, . . . , x
n+1
) V . . . V unde x
1
= x
n+1
= 1:
la pasul i, i = 2, n + 1, se viziteaza orasul x
i
V . Primul oras, orasul din care se pleaca,
este xat la 1 (vezi algoritmul 31).
76
La pasul k (2 k n + 1) se ncearca vizitarea orasului x
k
+ 1 daca nu a fost vizitat
la un pas anterior, si daca costul part ial al lant ului este mai mic decat costul celui mai bun
ciclu hamiltonian obt inut pana n acel moment n graf:
1 x
k
< n si vizitat
x
k
+1
= 0 si cost +a
x
k1
,x
k
+1
cost
optim
(4.2)
(pentru k = n + 1 trebuie vericata si condit ia x
k
= 1).
La nceput costul celui mai scurt ciclu hamiltonian poate determinat cu un algoritm
de tip Greedy sau i se poate atribui valoarea + (o valoare sucient de mare, de exemplu
n max{a
i,j
|i, j = 1, n, i = j, a
i,j
= }).
Daca condit iile anterioare sunt ndeplinite, atunci x
k
x
k
+1. Se marcheaza orasul ales
la pasul k ca ind vizitat (vizitat
k
1), se actualizeaza costul drumului pana n momentul
curent (cost cost +a
x
k1
,x
k
), si se trece la pasul k + 1.
In gura 5.1 este prezentat un arbore binar plin n care varfurile sunt asezate pe 4 niveluri
(k = 4). De regula varfurile unui arbore binar plin se numeroteaza n ordinea nivelurilor, iar
n cadrul unui nivel, de la stanga la dreapta.
Denit ia 5.6 Se numeste arbore binar complet cu n varfuri un arbore binar plin avand
k nivele, unde 2
k1
n < 2
k
, din care se elimina varfurile numerotate n+1, n+2, . . . , 2
k
1.
88
1
2
5
11
10
4
9
3
7
6
12
8
Fig. 5.2: Exemplu de arbore binar complet cu 12 varfuri
i
2
, i 2
nu exista , i = 1
Stang
i
=
_
2 i , 2 i n
nu exista , 2 i > n
Drept
i
=
_
2 i + 1 , 2 i + 1 n
nu exista , 2 i + 1 > n
5.1.2 Metode de parcurgere
Exista mai multe moduri de parcurgere a unui arbore binar. Indiferent de metoda de parcurg-
ere aleasa se parcurge mai ntai subarborele stang si apoi subarborele drept. Dupa momentul
n care un nod k este vizitat fat a de subarborii sai stang si drept, avem:
parcurgere n preordine (radacina, subarbore stang, subarbore drept).
In urma parcurgerii n preordine a arborelui din gura 5.3 rezulta urmatoarea ordine
pentru noduri: 1, 2, 3, 4, 5, 6, 7, 8, 9.
In gura 5.6 este prezentata parcurgerea n preordine a unui arbore binar, descrisa n
mai mult i pasi, conform descrierii recursive a acestei metode.
Vom da exemplu de o procedura nerecursiva de vizitare n preordine (vezi algoritmul
32). Se pleaca de la radacina si se merge spre stanga atata timp cat este posibil (liniile
58). Daca nu se mai poate merge spre stanga se ncearca continuarea algorimului
din descendentul drept, daca este posibil. Daca nu se poate face un pas spre dreapta
(descendentul drept nu exista linia 10), se va urca n arbore cate un nivel (liniile
1222), pana cand se ajunge n situat ia de a se veni din descendentul stang (linia 19)
si se reia algoritmul trecand n descendentul drept, daca exista (linia 25).
In momentul
n care sa ajuns n nodul radacina venind din dreapta, algoritmul se ncheie. Datorita
faptului ca atunci cand se urca n arbore trebuie sa stim din ce descendent venim, vom
utiliza si vectorul tata.
Aceasta procedura poate utilizata si pentru celelalte moduri de parcurgere, modicarile
necesare referinduse la momentul cand trebuie vizitat nodul curent (prin apelul pro-
cedurii V izit - instruct iunea call V izit(k)).
91
Fig. 5.6: Parcurgerea n preordine a unui arbore binar a) arborele init ial; b) arborele vizitat n preordine,
primul nivel; c) arborele vizitat n preordine, al doilea nivel; d) arborele vizitat n preordine, al treilea nivel.
Implementarea n limbajul C a algoritmului de parcurgere n preordine este:
#include <stdio.h>
#define MAX 100
/** stang[k] - descendentul stang al nodului k; 0 daca nu exista */
char stang[MAX];
/** drept[k] - descendentul drept al nodului k */
char drept[MAX];
/** 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]);
}
92
Algoritm 32 Algoritm de parcurgere n preordine
1: procedure Preordine(Rad, Stang, Drept, Tata)
2: k rad
3: q 0
4: while (q = 0) do
5: while (stang
k
= 0) do
6: call V izit(k) prelucrarea informat iei din nodul vizitat
7: k stang
k
8: end while
9: call V izit(k)
10: while (q = 0) (drept
k
= 0) do
11: found 0
12: while (q = 0) (found = 0) do
13: j k
14: k tata
k
15: if (k = 0) then
16: q 1
17: else
18: if (j = stang
k
) then
19: found 1
20: end if
21: end if
22: end while
23: end while
24: if (q = 0) then
25: k drept
k
26: end if
27: end while
28: return
29: end procedure
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];
93
}
vizit(i);
while ((q == 0) && (drept[i] == 0)) {
found = 0;
while ((q == 0) && (found == 0)) {
j = i;
i = tata[i];
if (i == 0)
q = 1;
else
if (j == stang[i])
found = 1;
}
}
if (q == 0)
i = drept[i];
}
}
void main(void){
readInput();
preord(rad);
}
parcurgere n inordine (arbore stang, radacina, arbore drept): 3, 4, 2, 6, 5, 7, 1, 8, 9.
Metoda de parcurgere n inordine este ilustrata de algoritmul 33 [93], [123]. Vom utiliza
o stiva S unde se vor introduce nodurile din care se coboara spre stanga (liniile 69).
Atunci cand nu se mai poate merge spre stanga se ncearca continuarea algoritmului din
descendentul drept al nodului curent. Daca acesta nu exista, se reface drumul napoi
spre radacina, compus numai din descendent ii stangi (liniile 1118). Parcurgerea se
termina n momentul n care stiva este vida (vezi algoritmul 33).
Implementarea n limbajul C a algoritmului 33 este:
#include <stdio.h>
#define MAX 100
/** stang[k] - descendentul stang al nodului k; 0 daca nu exista */
char stang[MAX];
/** drept[k] - descendentul drept al nodului k */
char drept[MAX];
/** Numarul de noduri din arbore */
int n;
/** Nodul radacina */
int rad;
void vizit(int nod) {
printf("%2d ", nod);
}
void readInput(void) {
94
Algoritm 33 Algoritm de parcurgere n inordine
1: procedure Inordine(Rad, Stang, Drept)
2: k rad
3: q 0
4: S init ializare stiva vida
5: while (q = 0) do
6: while (stang
k
= 0) do
7: S k S.push(k)
8: k stang
k
9: end while
10: call V izit(k)
11: while (q = 0) (drept
k
= 0) do
12: if (S = ) then vericare daca stiva este vida
13: q 1
14: else
15: S k S.pop(k)
16: call V izit(k)
17: end if
18: end while
19: if (q = 0) then
20: k drept
k
21: end if
22: end while
23: return
24: end procedure
int k;
printf("n = "); scanf("%d", &n);
printf("rad = "); scanf("%d", &rad);
for (k = 1; k <= n; k++) {
printf("stang[%d] = ", k); scanf("%d", &stang[k]);
}
for (k = 1; k <= n; k++) {
printf("drept[%d] = ", k); scanf("%d", &drept[k]);
}
}
void inord(int rad) {
int i, cap;
int q;
int stack[MAX];
i = rad; q = 0;
cap = -1;
while (q == 0) {
while (stang[i] > 0) {
95
cap++;
stack[cap] = i;
i = stang[i];
}
vizit(i);
while ((q == 0) && (drept[i] == 0)) {
if (cap < 0)
q = 1;
else {
i = stack[cap];
cap--;
vizit(i);
}
}
if (q == 0)
i = drept[i];
}
}
void main(void) {
readInput();
inord(rad);
}
parcurgere n postordine (subarbore stang, subarbore drept, radacina): 4, 3, 6, 7, 5, 2, 9, 8, 1
(vezi algoritmul 34).
Algoritm 34 Algoritm de parcurgere n postordine
1: procedure Postordine(K, Stang, Drept)
2: if (k = 0) then
3: call Postordine(stang
k
, stang, drept)
4: call Postordine(drept
k
, stang, drept)
5: call V izit(k)
6: end if
7: return
8: end procedure
Modalitatea de vizitare recursiva a arborilor binari este exemplicata la arborii binari de
sortare.
Daca presupunem realizarea unei act iuni de vizitare pe exteriorul frontierei arborelui,
deplasarea efectuandu-se n sens trigonometric si avand drept punct de plecare radacina
acestuia, vom ajunge sa vizitam cel put in o data toate nodurile arborelui (vezi gura 5.7).
T inand cont de momentul vizitarii se poate obt ine oricare dintre metodele de parcurgere
(preordine, inordine, postordine): pentru preordine vom marca un nod n momentul n care
l 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.
96
1
2 8
5
7
6
9 3
4
Fig. 5.7: Vizitarea unui arbore pe frontiera
Fig. 5.8: Arbore asociat expresiei aritmetice 2 ((a + b) (c + b))
Arborele asociat unei expresii aritmetice
Oricarei expresii aritmetice i se poate asocia un arbore binar astfel:
ecarui operator i corespunde un nod neterminal avand drept subarbori stang si drept
cei doi operanzi corespunzatori.
ecare nod terminal este etichetat cu o variabila sau o constanta.
Arborele din gura 5.8 corespunde expresiei matematice: 2 ((a +b) (c +b)).
In urma
vizitarii n postordine a acestui arbore se obt ine forma poloneza postxata asociata expresiei
anterioare: 2ab + cb + . Pentru a obt ine valoarea expresiei respective, arborele poate
parcurs n postordine.
5.2 Arbori binari de cautare
Denit ia 5.7 [93] Se numeste arbore binar de c autare un arbore binar ce verica urmatoarea
proprietate:
97
pentru orice varf i apart inand arborelui, avem
_
Inf
i
Inf
j
, pentru toate varfurile j ce apart in descendentului stang al varfului i
Inf
i
Inf
j
, pentru toate varfurile j ce apart in descendentului drept al varfului i
unde Inf
i
este informat ia asociata varfului i. Aceasta informat ie este de un tip de date peste
care avem o relat ie de ordine (de obicei un tip numeric sau tipul sir de caractere).
Un arbore binar de cautare mai este cunoscut si sub numele de arbore binar de sortare
deoarece parcurgerea n inordine a unui arbore binar de cautare ne conduce la obt inerea
elementelor vectorului Inf n ordine crescatoare.
In principal aceasta structura de date este
utilizata pentru regasirea ecienta a unor informat ii.
Operat iile de creare a arborelui, stergere a unui nod, modicare a informat iei asociate
unui nod sau inserare a unui nod se pot realiza ntr-un mod optim astfel ncat s a nu se
distruga proprietatea de arbore de cautare.
Daca dorim ca arborele sa nu cont ina chei duplicate, vom modica denit ia de mai sus
astfel:
pentru orice varf i apart inand arborelui, avem
_
Inf
i
< Inf
j
, pentru toate varfurile j ce apart in descendentului stang al varfului i
Inf
i
> Inf
j
, pentru toate varfurile j ce apart in descendentului drept al varfului i.
In gura 5.9 este ilustrata inserarea unui nod ntrun arbore binar.
98
Algoritm 35 Algoritm de cautare a unei valori ntr-un arbore binar de cautare
1: function SearchNode(p, Key)
2: if (p = NULL) then
3: if (p.info > key) then
4: return SearchNode(p.left, key)
5: else
6: if (p.info < key) then
7: return SearchNode(p.right, key)
8: else
9: return p
10: end if
11: end if
12: else
13: return NULL
14: end if
15: end function
Algoritm 36 Algoritm de inserare ntr-un arbore binar de cautare
1: function InsertNode(p, Key)
2: if (p = NULL) then
3: call CreateNode(p, key)
4: else
5: if (p.info > key) then
6: p.left InsertNode(p.left, key)
7: else
8: if (p.info < key) then
9: p.right InsertNode(p.right, key)
10: end if
11: end if
12: end if
13: return p
14: end function
Stergerea unui nod dintr-un arbore de cautare
Mai ntai se va cauta nodul ce se doreste sa e eliminat.
Sa presupunem ca am gasit nodul ce urmeaza a sters. Avem urmatoarele situat ii (vezi
algoritmul 38):
1. nodul ce urmeaza sa e sters este un nod terminal - se face stergerea normal avand
grija sa se nlocuiasca legatura din nodul parinte c atre el cu null;
2. nodul ce urmeaza sa e sters are un singur descendent - nodul respectiv se sterge iar
parintele va cont ine acum noua legatura catre descendentul fostului u;
3. nodul ce urmeaza a sters (notat A) are doi descendent i:
se determina cel mai din stanga nod (notat B) din subarborele drept al nodului
ce trebuie sters (vezi algoritmul 37); acesta va sters n mod zic, dar la un pas
ulterior;
99
Fig. 5.9: Arbore binar de cautare. Inserarea unui nod.
toate informat iile legate de datele cont inute n nodul B vor copiate n nodul A;
subarborele drept al nodului B se va lega e ca descendent drept al nodului A
(daca nodul B este descendent direct al nodului A), e ca descendent stang al
tatalui nodului B;
se va sterge zic nodul B.
Exemplul 5.3 Fie un arbore binar, eticheta ecarui nod ind un sir de caractere (un cuvant).
Se cere sa se realizeze un program care sa creeze un arbore binar de sortare, sa-l parcurga n
inordine si sa permita inserarea si stergerea unui nod specicat.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <conio.h>
typedef struct nod {
100
Fig. 5.10: Arbore binar de cautare. Stergerea unui nod. a) Nod fara descendent i b) Nod cu un singur
descendent c) Nod cu doi descendent i.
101
Algoritm 37 Algoritm pentru determinarea celui mai din stanga nod
1: function LeftmostNode(Parent, Curent)
2: while (curent.left = NULL) do
3: parent curent
4: curent curent.left
5: end while
6: if (parent.right = curent) then
7: parent.right curent.right
8: else
9: parent.left curent.right
10: end if
11: return curent
12: end function
char *cuvint;
struct nod *left,*right;
} NOD;
NOD *rad;
NOD* Search(NOD *p, char *sir) {
int ind;
if (p != NULL) {
ind = strcmp(p->cuvint, sir);
if (ind < 0)
return Search(p->right, sir);
else
if (ind > 0)
return Search(p->left, sir);
else
return p;
}
else
return NULL;
}
NOD* CreareNod(char *sir) {
NOD *p;
p = (NOD*)malloc(sizeof(NOD));
p->left = p->right = NULL;
p->cuvint = (char*)malloc(sizeof(char) * (strlen(sir) + 1));
strcpy(p->cuvint, sir);
return p;
}
NOD* Insert(NOD *p, char *sir) {
int ind;
102
Algoritm 38 Algoritm de stergere a unui nod ntr-un arbore binar de cautare
1: function DeleteNode(p, Key)
2: if (p = NULL) then
3: if (p.info > key) then
4: p.left DeleteNode(p.left, key)
5: else
6: if (p.info < key) then
7: p.right DeleteNode(p.right, key)
8: else
9: if (p.left = NULL) (p.right = NULL) then
10: if (p.left = NULL) then
11: tmp p.left
12: else
13: tmp p.right
14: end if
15: call DisposeNode(p)
16: p tmp
17: else
18: tmp p.right
19: tmp LeftmostNode(p, tmp)
20: tmp.left p.left
21: tmp.right p.right
22: call DisposeNode(p)
23: p tmp
24: end if
25: end if
26: end if
27: end if
28: return p
29: end function
if (p == NULL)
p = CreareNod(sir);
else {
ind = strcmp(p->cuvint, sir);
if (ind < 0)
p->right = Insert(p->right, sir);
else
if (ind > 0)
p->left = Insert(p->left, sir);
}
return p;
}
void VisitInord(NOD *p) {
if (p != NULL) {
VisitInord(p->left);
printf("[%s] ", p->cuvint);
103
VisitInord(p->right);
}
}
NOD* LeftmostNod(NOD* parent, NOD* curent) {
while (curent->left != NULL) {
parent = curent;
curent = curent->left;
}
if (parent->right == curent)
parent->right = curent->right;
else
parent ->left = curent->right;
return curent;
}
NOD* ElibNod(NOD *p) {
free(p->cuvint);
free(p);
return NULL;
}
NOD* DeleteNod(NOD* p, char *sir) {
NOD *tmp;
int ind;
if (p != NULL){
ind = strcmp(p->cuvint, sir);
if (ind < 0)
p->right = DeleteNod(p->right, sir);
else
if (ind > 0)
p->left = DeleteNod(p->left, sir);
else
if (p->left == NULL || p->right == NULL) {
if (p->left != NULL)
tmp = p->left;
else
tmp = p->right;
ElibNod(p);
p = tmp;
}
else{
tmp = p->right;
tmp = LeftmostNod(p, tmp);
tmp->left = p->left;
tmp->right = p->right;
ElibNod(p);
p = tmp;
}
104
}
return p;
}
void main(void) {
char cuvint[100];
char ch;
NOD *p;
rad = NULL;
while (1) {
printf("***********************************\n");
printf("1. Inserare \n");
printf("2. Cautare\n");
printf("3. Stergere\n");
printf("4. Afisare arbore\n");
printf("0. Exit \n");
ch = getch();
if (ch != 0 && ch != 4) {
printf("Cuvint: "); scanf("%s", cuvint);
}
switch (ch) {
case 1: rad = Insert(rad, cuvint); break;
case 2: p = Search(rad, cuvint);
if (!p)
printf("Cuvintul %s nu a fost gasit! \n", cuvint);
else
printf("Cuvintul %s exista in arbore! \n", cuvint);
break;
case 3: rad = DeleteNod(rad, cuvint); break;
case 4: VisitInord(rad); break;
case 0: exit(1);
}
}
}
5.3 Exercit ii
1. (a) Sa se realizeze o subrutina ce determina cea mai mare valoare pastrata ntrun
arbore binar de cautare;
(b) Aceeasi cerint a pentru cea mai mica valoare.
2. Sa se realizeze o subrutina ce determina toate nodurile unui arbore binar de cautare cu
proprietatea ca informat ia k asociata unui nod verica relat ia a k b, unde a si b
sunt date.
3. (a) Sa se realizeze un algoritm care determina numarul arborilor binari distinct i cu
n noduri, unde n este un numar natural dat.
105
Fig. 5.11: Exemplu de arbore binar complet cu 7 varfuri
(b) Aceeasi problema pentru arbori binari de cautare.
4. Sa consideram o secvent a de intrare compusa din termeni denit i 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 cont ine nici un subtermen care sa poata redus. Un termen poate considerat
ca subtermen pentru el nsusi.
Sa se realizeze un algoritm care determina pentru un termen de intrare, termenul redus
corespunzator. Un termen cont ine doar simbolurile X, ( si ), fara spat ii ntre ele.
Lungimea unui termen de intrare este 100.
Observat ia 5.4 O expresie va avea maxim 9 nivele de parantezare. Numarul de redu-
ceri ce pot efectuate este nit. Liniile de iesire cont in maxim 80 caractere. Caracterele
posibile din termenii de iesire sunt tot X, ( si ).
Intrare Iesire
(((XX)X)X) ((XX)(XX))
(((XX)(XX)X) ((XX)((XX)X))
(XX) (XX)
(ACM, Bucuresti, 1996)
5. Prin arbore binar completnt elegem un arbore binarn care un nod are e doi descendent i,
e nu are nici unul.
Un arbore binar complet poate reprezentat prin codicarea drumurilor de la radacina
la ecare 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.
106
Pentru arborele din gura 5.11 drumurile de la radacina la ecare frunza se codica
astfel:
ABC : 00
ABDE : 010
ABDF : 011
AG : 1
Concatenand toate drumurile de la radacina catre frunze, arborele anterior poate
reprezentat prin secvent a ce poate interpretata ca reprezentarea n baza 2 a unui
numar (000100111).
Fiind date m si n doua numere naturale, se cere sa se determine daca exist a un arbore
binar complet a carui reprezentare va cont ine exact m cifre 0 si n cifre 1 (0 < m, n
100).
(Timisoara-pregatire, 1996)
6. Scriet i o subrutina nerecursiva care numara nodurile frunza ale unui arbore binar.
7. Realizat i o subrutina care sa determine nivelul cu cele mai multe noduri frunze dintrun
arbore binar.
8. Determinat i nat imea unui arbore binar printro funct ie nerecursiva.
9. Se considera un dict ionar ce este implementat sub forma unei structuri de arbore.
Pentru ecare cuvant se cunoaste traducerea acestuia precum si probabilitatea lui de
aparit ie n texte.
Se cere sa se realizeze un algoritm care sa conduca la o cautare optima a cuvintelor
n dict ionar. Se ment ioneaza faptul ca probabilitatea de a cauta un cuvant care nu se
gaseste n dict ionar este zero.
Datele de iesire constau din asarea arborelui optimal de cautare compus din cuvintele
introduse.
Spre exemplu, un set de date de intrare poate :
5
1 abroad in_strainatate
4 baker brutar
2 calf gamba
1 dice zaruri
2 ear ureche
10. O casa de comenzi este interesata de crearea unui program pe calculator care sa t ina
evident a comenzilor n ordinea datelor la care au fost onorate, iar n cadrul ecarei date
de livrare, n ordine lexicograca dupa numele produsului. Sa se scrie un program care
foloseste structuri de date de tip arbore.
Intrarea este formata din mai multe linii. Pe ecare linie avem cate o comanda data
sub forma: [nume
p
rodus] [zi] [luna] [an]. Intrarea se termina cu o linie pe care avem
doar &.
Iesirea este formata prin asarea comenzilor n ordinea datei la care au fost onorate,
iar n cadrul unei date, n ordine lexicograca n funct ie de numele comenzii.
Indicat ie: Se va crea un arbore de cautare dupa data de livrare si n ecare nod va
un arbore de cautare dupa numele comenzii.
107
11. Se considera o expresie logica formata din n variabile logice reprezentate printro sin-
gura litera si operatorii & (AND), | (OR), ! (NOT) (nu avem paranteze). Expresia este
reprezentata sub forma unui arbore binar.
Se cere sa se realizeze un algoritm care pentru o expresie logica data cerceteaza existent a
unei combinat ii de valori logice (true/false), pentru care expresia data ia valoarea logica
true.
De exemplu pentru expresia a|!b|c&d solut ia este a = false, b = false, c = false,
d = false, iar pentru expresia !a&a nu avem solut ie.
12. Se considera o expresie matematica reprezentata printr-un arbore binar. Operatorii
corespund operat iilor matematice uzuale +, , , /, si ? (pentru ridicare la putere).
Nu avem paranteze si tot i operatorii sunt operatori binari. Operanzii sunt identicat i
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 mult ime 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 obt inem valoarea 1.
108
Capitolul 6
Arbori oarecare
Reamintim denit ia unui arbore oarecare:
Denit ia 6.1 Fiind data o mult ime M de elemente denumite noduri, vom numi arbore o
submult ime nita de noduri astfel ncat:
1. exista un nod cu destinat ie speciala, numit radacina arborelui;
2. celelalte noduri sunt repartizate n n mult imi disjuncte doua cate doua, A
1
, A
2
, , A
n
, ecare
mult ime A
i
constituind la randul ei un arbore.
6.1 Moduri de reprezentare
Pentru reprezentarea arborilor oarecare pot utilizate urmatoarele metode:
legaturi u-frate: fiu
k
- este primul descendent al varfului k, frate
k
- urmatorul descen-
dent al tatalui nodului k, ce urmeaza dupa k.
Intre descendent ii unui varf se presupune
ca denim o relat ie de ordine.
Radacina arborelui din gura 6.1 este Rad = 1.
1
2 4
5 9 6 7 8
3
10
Fig. 6.1: Exemplu de arbore oarecare
Table 6.1: Reprezentarea cu legaturi ufrate a arborelui din gura 6.1.
Nod 1 2 3 4 5 6 7 8 9 10
Fiu 2 0 5 7 0 0 0 0 0 0
Frate 0 3 4 0 6 0 8 9 10 0
109
Daca identicam Fiu cu Stang si Frate cu Drept, unui arbore oarecare i se poate
asocia un arbore binar.
Pentru reprezentarea unui arbore oarecare, modelul de reprezentare u-frate n varianta
de alocare statica poate usor extins la o varianta de alocare dinamica, folosind pointeri
pentru legaturile catre primul descendent respectiv pentru urmatorul frate. Astfel un
nod al arborelui poate avea urmatoarea congurat ie:
typedef struct nod {
TipOarecare data;
struct nod* fiu;
struct nod* frate;
}Nod;
Asemanator cu modelul construit la arbori binari, rad (Nod rad;) este o variabila de
tip pointer la Nod (variabila pastreaza adresa unei zone de memorie). rad desemneaza
adresa radacinii arborelui.
Fig. 6.2: Exemplu de arbore oarecare cu 10 noduri reprezentat prin legaturi u-frate
In gura 6.2 este reprezentat arborele din gura 6.1 prin legaturi u-frate.
lista descendent ilor.
In cadrul acestui mod de reprezentare ecare varf este descris prin
lista descendent ilor sai. Pentru memorare se va utiliza un vector cu n componente:
C
k
=
_
0 , varful respectiv nu are descendent i
j , j indica adresa (coloana) unde ncepe lista descendent ilor varfului k.
Listele de descendent i se pastreaza prin intermediul unei matrice L cu 2 linii si N 1
coloane:
L
1,k
- un descendent al varfului a carui lista cont ine coloana k a matricei date.
L
2,k
=
_
0 , daca descendentul respectiv este ultimul
j , j indica coloana unde se aa urmatorul descendent
110
Table 6.2: Reprezentarea arborelui din gura 6.1 folosind liste cu descendent i.
Nod 1 2 3 4 5 6 7 8 9 10
C 1 0 4 6 0 0 0 0 0 0
L =
_
2 3 4 5 6 7 8 9 10
2 3 0 5 0 7 8 9 0
_
Exploatand mai departe aceasta idee, ntr-un nod al arborelui oarecare se poate pastra o
lista cu adresele descendent ilor sai, lista ind reprezentata sub forma unui tablou alocat
static sau dinamic. Oricum modelul se recomanda atunci cand numarul descendent ilor
unui nod este limitat superior, sau cand acesta este cunoscut/stabilit n momentul n
care se construieste arborele. Modicarile efectuate asupra arborelui, cum ar inserari
de descendent i noi, atunci cand intrarile alocate pentru acesti descendent i sunt deja
alocate nu poate conduce decat la realocarea spat iului de memorie cu un cost reectat
n complexitatea algoritmului. Astfel daca numarul de descendent i este limitat superior
putem deni
#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;
atunci cand numarul maxim de descendent i nu poate cunoscut decat n momentul
construirii arborelui. Astfel, n acel moment, se aloca spat iu pentru un tablou de
pointeri catre structura de tip Nod denita (children = malloc(sizeof(Nod*) *
no of children)).
In gura 6.3 este reprezentat arborele din gura 6.1 folosind liste cu descendent i.
Tata - ecarui nod k se indica nodul parinte.
Table 6.3: Reprezentarea arborelui din gura 6.1 folosind vectorul tata.
Nod 1 2 3 4 5 6 7 8 9 10
Tata 0 1 1 1 3 3 4 4 4 4
Acest mod de reprezentare are dezavantajul ca nu pastreaza n mod explicit ordinea
descendent ilor unui nod. Aceasta ordine poate dedusa printro numerotare adecvata a
nodurilor. De obicei reprezentarea cu vectorul tata nu este folosita singura ci mpreuna
cu alte moduri de reprezentare, ca o completare.
111
3 1
0 2 2 3 4 4
0 5 0 6
rad
0 7 0 8 0 9 0 10
Fig. 6.3: Exemplu de arbore oarecare cu 10 noduri reprezentat prin liste cu descendent i
De exemplu, reprezentarea unui nod propusa la primul punct, poate modifcata astfel
ncat sa incorporeze si informat ii despre nodul parinte:
typedef struct nod {
TipOarecare data;
struct nod* fiu;
struct nod* tata;
struct nod* frate;
}Nod;
1
2 3 4
5
rad
NULL
NULL
6
NULL
NULL 7
10
NULL
NULL
8 9
NULL NULL NULL
NULL
NULL
NULL
NULL
Fig. 6.4: Exemplu de arbore oarecare cu 10 noduri reprezentat prin legaturi u-frate-tata
In gura 6.4, reprezentarea din gura 6.2 este mbunatat ita prin legatura tata.
6.2 Metode de parcurgere
Pentru parcurgerea unui arbore oarecare se pot folosi metodele generale pentru parcurg-
erea arborelui binar asociat. Arborele binar asociat unui arbore oarecare se obt ine n urma
realizarii corespondent ei Fiu Stang si Frate Drept.
112
In plus fat a de acestea, avem si doua metode de parcurgere pe care putem sa le numim
specice unui arbore oarecare. Acestea se pot clasica dup a momentul n care se realizeaza
vizitarea nodului parinte astfel:
Apreordine: se viziteaza mai ntai radacina, si apoi, n ordine, subarborii sai [93],
[123]. Metoda este identica cu metoda de parcurgere n preordine a arborelui binar
atasat: 1, 2, 3, 5, 6, 4, 7, 8, 9, 10 (vezi algoritmul 39). Parcurgerea n Apreordine este
exemplicata pe arborele oarecare din gura 6.1.
Algoritm 39 Algoritm de parcurgere n A-preordine
1: procedure APreordine(Rad, Fiu, Frate)
2: k rad
3: q 0
4: S init ializare stiva vida
5: while (q = 0) do
6: if (k = 0) then
7: call V izit(k)
8: S k S.push(k)
9: k fiu
k
10: else
11: if (S = ) then vericare daca stiva este vida
12: q 1
13: else
14: S k S.pop(k)
15: k frate
k
16: end if
17: end if
18: end while
19: return
20: end procedure
Apostordine: se viziteaza mai ntai tot i subarborii ce au drept radacini, descendent ii
radacinii arborelui si apoi radacina arborelui [93], [123].
In urma parcurgerii n A
postordine a arborelui din gura 6.1 se obt ine 2, 5, 6, 3, 7, 8, 9, 10, 4, 1.
Exemplul 6.1 [31]
In continuare se prezinta implementarea n limbajul de programare C
a variantelor recursive pentru ecare dintre cele doua metode de vizitare ale unui arbore
oarecare.
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
/**
* Definitii de tipuri necesare cozii
*/
typedef struct nod {
int id;
float info;
struct nod *down, *next;
113
}NOD;
typedef NOD* TINFO;
/**
* 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;
114
if (prim == ultim) {
n = prim->info;
free(ultim);
prim = ultim = NULL;
} else {
curent = prim;
n = prim->info;
prim = prim->next;
free(curent);
}
return n;
}
/**
* Functia realizeaza crearea unui arbore oarecare. Se introduce initial radacina,
* apoi descendentii ei, dupa care se introduc descendentii nodurilor de pe
* nivelul 1, dupa care se introduc descendentii nodurilor de pe nivelul 2 s.a.m.d.
*/
NOD* creare(void) {
NOD *p, *c;
p = (NOD *)malloc(sizeof(NOD));
p->next = NULL;
printf("Dati id:"); scanf("%d", &p->id);
add(p);
while (!isEmpty()) {
c = get();
c->down = citListaDescendenti(c);
}
return p;
}
/**
* Functia citeste informatia asociata unui nod.
* @return 1 daca citirea s-a facut corect
* 0 altfel
*/
int citInfo(NOD *pn) {
printf("Id:");
pn->next = NULL;
pn->down = NULL;
return scanf("%d",&pn->id) == 1;
}
/**
* Functia realizeaza citirea listei de descendenti ai nodului up si
* returneaza adresa primului descendent din lista.
*/
NOD* citListaDescendenti(NOD *up) {
NOD *prim, *ultim, *p;
NOD n;
115
printf("\nLista de descendenti pt %d (CTRL+Z) daca nu are\n", up->id);
prim = ultim = NULL;
while (citInfo(&n)) {
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");
116
aPostordine(rad);
}
6.3 Arbori de acoperire de cost minim
Fie G = (V, E) un graf neorientat si e c : E R o funct ie de cost ce asocieaza o valoare
reala ecarei muchii. Notam cu T
G
mult imea arborilor part iali ai grafului G (un arbore
part ial este un graf part ial conex si fara cicluri al grafului init ial).
Cerint a problemei este aceea de a determina un arbore T
T
G
avand costul cel mai mic
dintre tot i arborii ce apart in mult imii T
G
:
c(T
) = min{c(T)|T T
G
}
unde costul unui arbore T se denet te ca c(T) =
eE
T
c(e). Arborele T
ce poseda aceasta
proprietate se numeste arbore de acoperire de cost minim (eng. minimum spanning tree -
MST). Se mai ntalneste si sub denumirea de arbore part ial de cost minim sau arbore part ial
minim.
Altfel spus, se cere sa se determine un graf part ial conex G
1
= (V, E
1
) (E
1
E) cu
proprietatea ca suma costurilor tuturor muchiilor este minima, graful part ial de cost minim
ind chiar un arbore.
Determinarea arborelui de acoperire de cost minim are multe aplicat ii practice: de exem-
plu, daca se dau n orase precum si costul legaturilor ntre acestea, se cere sa se determine o
conectare a tuturor oraselor astfel ncat oricare doua orase sa e conectate direct sau indirect
iar costul conectarii sa e minim.
Lema 6.2 (Proprietatea taieturii) Fie S o submult ime de noduri a lui V si e muchia ce are
costul minim dintre toate muchiile ce au o singura extremitate n S. Atunci arborele part ial
de cost minim va cont ine muchia e.
Demonstrat ie: Fie T
=
T
{e} \ {f}.
Deoarece c(e) < c(f) vom avea ca c(T
) = c(T
) +c(e) c(f)
. .
<0
c(T
) < c(T
) adica am
obt inut un arbore de acoperire T
, contradict ie cu faptul ca
T
=
T
{e} \ {f}.
Deoarece c(e) < c(f) c(T
) < c(T
ce are
costul mai mic decat T
, contradict ie.
Majoritatea algoritmilor ce calculeaza arborele de acoperire de cost minim prezinta aceeasi
tehnica generala de calcul. Lanceput se porneste cu o padure de arbori, T
0
= {T
0
1
, T
0
2
, . . . , T
0
n
}
unde T
0
i
= {x
i
}, i = 1, n este un arbore format dintrun singur nod. La pasul k vom avea
mult imea T
k
compusa din n k arbori: T
k
= {T
k
1
, T
k
2
, . . . , T
k
nk
}.
La ecare pas, se alege un arbore T
k
i
si o muchie (u
, v
T
k
i
) si cealalta extremitate apart ine mult imii V \ V
T
k
i
(v
V \ V
T
k
i
).
Prin urmare, mult imea T
k+1
se obt ine din mult imea T
k
prin reuniunea arborilor T
k
i
si
T
k
j
(i = j), unde u
T
k
i
si v
T
k
j
(T
k+1
= T
k
\ {T
k
i
, T
k
j
} {T
k
i
T
k
j
}).
In tabelul 6.4 sunt prezentat i mai mult i algoritmi dezvoltat i dea lungul timpului pentru
determinarea arborelui de acoperire minimal si complexitat ile lor. Karger, Klein si Tarjan [79]
pornind de la algoritmul lui Boruvka au realizat un algoritm randomizat pentru determinarea
arborelui de acoperire minimal, avand o complexitate liniara, iar Chazelle [26] a dezvoltat
un algoritm avand complexitatea O(n(m, n)) ((m, n) este inversa funct iei lui Ackerman).
Pe de alta parte, Pettie si Ramachandran [105] au propus un algoritm demonstrat ca ind
optimal, avand complexitatea cuprinsa ntre O(n +m) si O(n(m, n)).
118
Algoritm 41 Algoritmul lui Prim (varianta schematica)
1: procedure Prim1(n, C, u; L)
2: S {u}, L
3: for i 1, n do
4: d
i
c
u,i
5: end for
6: for i 1, n 1 do
7: k min{d
k
|k V \ S}
8: L (tata
k
, k)
9: S S {k}
10: for each j V \ S do
11: d
j
min{d
j
, c
k,j
}
12: end for
13: end for
14: end procedure
Algoritm 42 Algoritmul lui Kruskal (varianta schematica)
1: procedure Kruskal1(G, C, n; L)
2: ordoneaza muchiile n ordine crescatoare dupa cost
3: L
4: for each u V do
5: creaza o mult ime compusa din {u}
6: end for
7: count 0
8: while count < n 1 do
9: alege muchia (u, v)
10: if (u si v sunt n mult imi diferite) then
11: L (u, v)
12: reuneste mult imile ce cont in pe u si v
13: count count + 1
14: end if
15: end while
16: end procedure
Fie G(V, E) un graf neorientat unde V = {1, 2, ..., n} este mult imea nodurilor si E este
mult imea muchiilor (E V V ). Pentru reprezentarea grafului se utilizeaza matricea
costurilor C:
c
i,j
=
_
_
0 , daca i = j
, daca (i, j) / E
d > 0 , daca (i, j) E
6.3.1 Algoritmul lui Boruvka
Algoritmul lui Boruvka [77] a fost descoperit de catre matematicianul ceh Otakar Boruvka
n 1926 [21], si redescoperit apoi de catre alt i cercet atori. Dintre acestia, Sollin (1961) este
cel care a mai dat numele algoritmului, acesta ind cunoscut n literatura de specialitate si
sub numele de algoritmul lui Sollin. Pentru ca acest algoritm sa poata aplicat, trebuie ca
muchiile grafului sa aiba costuri distincte (vezi algoritmul 43).
119
Table 6.4: Algoritmi pentru determinarea arborelui de acoperire minim
Anul Complexitate Autori
1975 E log log V Yao
1976 E log log V Cheriton-Tarjan
1984 E log
V, E +V log V Friedman-Tarjan
1986 E log log
V Gabow-Galil-Spencer-Tarjan
1997 E(V ) log (V ) Chazelle
2000 E(V ) Chazelle [26]
2002 optimal Pettie-Ramachandran [105]
Algoritm 43 Algoritmul lui Boruvka
1: procedure Boruvka2(G, C, n; L)
2: for i 1, n do
3: V
i
{i}
4: end for
5: L , M {V
1
, . . . , V
n
}
6: while (|T| < n 1) do
7: for U M do
8: e (u, v) muchia pentru care se obt ine valoarea minima min{c(u
, v
)|(u
, v
) E, u
U, v
/ V \ U}
9: determina componenta U
ce cont ine pe v
10: L (u, v)
11: end for
12: for U M do
13: reuneste mult imile ce cont in pe u si v, U si U
m
i=1
A
i
si A
i
A
j
= , i, j = 1, m, i = j).
123
Algoritm 45 Algoritmul lui Prim folosind structuri de date avansate
1: procedure Prim3(n, C, u)
2: for ecare v V do
3: d
v
4: end for
5: Q init ializeaza coada cu prioritate Q cu mult imea vida
6: for ecare v V do
7: Q v
8: end for
9: S init ializeaza mult imea S cu mult imea vida
10: while Q = do
11: u deleteMin(Q) extrage nodul de prioritate minima din Q
12: S S {u}
13: for ecare muchie e = (u, v) E do
14: if (v / S) (c(e) < d
v
) then
15: actualizeaza prioritatea lui v: d
v
c(e)
16: end if
17: end for
18: end while
19: end procedure
Exista mai multe probleme ai caror algoritmi de rezolvare depind de urmatoarele operat ii
efectuate asupra elementelor partit iei: vericarea dac a doua elemente fac parte din aceeasi
submult ime precum si operat ia de reuniune a doua submult imi.
O structura de date pentru mult imi disjuncte memoreaza o colect ie de mult imi disjuncte
dinamice. Fiecare mult ime este identicata printrun reprezentant [30]. Operat iile de baza
ale acestei structuri de date sunt [2]:
init(x, B) - procedura creaza o mult ime B formata dintrun singur element x;
find(x) - ntoarce reprezentantul mult imii careia i apart ine x;
merge(A, B) - reuneste mult imile distincte A si B (x A, y B) ntro noua mult ime
ce cont ine elementele celor doua mult imi.
O astfel de structura de date pentru mult imi disjuncte se mai numeste si structura de date
unionnd (eng. unionnd data structure).
Reprezentarea folosind vectori
Vom folosi un vector C de dimensiune n, unde n reprezinta numarul de elemente, iar c
k
= u
indica faptul ca elementul k apart ine mult imii u.
Init consta din init ializarea lui c
x
cu identicatorul mult imii, u. Find ntoarce valoarea
lui c
x
(valoarea identicatorului mult imii).
In funct ia Merge se cauta toate elementele ce
fac parte din mult imea de identicator c
y
si se trec n mult imea al carui identicator este c
x
(vezi algoritmul 46).
Exemplul 6.6 Pentru o putea opera cu modul de reprezentare ales, cel cu vectori, trebuie
ca elementele mult imii sa ia valori naturale n intervalul [1,. . . ,n]. Daca acest lucru nu
este posibil atunci folosim un vector auxiliar A ce pastreaza valorile elementelor, n cadrul
reprezentarii utilizanduse indicele acestora. Sa presupunem ca avem mult imea de elemente
124
Algoritm 46 Algoritmi pentru operat iile init, nd, merge (varianta ntai)
1: procedure Init(x, u)
2: c
x
u
3: end procedure
4: function Find(x)
5: return c
x
6: end function
7: function Merge(x, y)
8: setx c
x
9: sety c
y
10: for k 1, n do
11: if (c
k
= sety) then
12: c
k
setx
13: end if
14: end for
15: return setx
16: end function
A = {a, b, e, x, y, z, u, v} si partit ia {a, x, y}, {b, z, v}, {e, u}. Atunci conform modului de
reprezentare descris avem:
1 2 3 4 5 6 7 8
A a b e x y z u v
C 1 2 3 1 1 2 2 3
a
2
=
. c
2
= 2 -
elementul de pe pozit ia 2 face parte din mult imea de identicator 2. Sau a
4
=
- elementul
de pe pozit ia 4 are valoarea
x
, si c
4
= 1 - elementul de pe pozit ia 4 face parte din mult imea
de identicator 1.
Reprezentarea folosind liste nlant uite
In cadrul acestei metode ecare mult ime este reprezentat a printro lista simplu nlant uita,
elementul reprezentant ind elementul aat n capul listei. Un nod al listei va avea un camp
ce pastreaza informat ia cu privire la un element al unei mult imi, si un camp ce cont ine
legatura catre urmatorul nod al listei.
Vom folosi un vector de adrese (List), ce cont ine adresa primului element din ecare lista
(List
i
).
Procedura Init aloca un nod si init ializeaza campurile acestuia. Adresa nodului alocat
este pastrata n List
k
(vezi algoritmul 47).
Funct ia Find cauta un element printre elementele pastrate de ecare lista n parte. Se
parcurge vectorul List si se obt ine reprezentantul ecarei submult imi 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 pan a la sfarsit nu a fost identicat elementul
x, atunci funct ia returneaza valoarea NULL.
Funct ia Merge reuneste doua liste: practic se adauga o lista la sfarsitul celeilalte. Pentru
a realiza aceasta operat ie pe langa adresa primului element avem nevoie de adresa ultimului
element. Acest lucru se poate obt ine indirect prin parcurgerea listei de la primul la ultimul
element, e direct daca n momentul crearii pastram pentru ecare lista nca o variabila ce
cont ine adresa ultimului element (last).
125
Algoritm 47 Algoritmi pentru operat iile init, nd, merge (varianta a II-a)
1: procedure Init(x, k)
2: List
k
new node aloca spat iu pentru un nod al listei
3: List
k
.data x
4: List
k
.next NULL
5: end procedure
6: function Find(x)
7: for i 1, m do
8: p List
i
, reprezentant p
9: while (p = NULL) (p.data = x) do
10: p p.next
11: end while
12: if (p = NULL) then
13: return reprezentant
14: end if
15: end for
16: return NULL
17: end function
18: function Merge(x, y)
19: cap
x
Find(x)
20: cap
y
Find(y)
21: curent cap
x
22: while (curent.next = NULL) do
23: curent curent.next
24: end while
25: curent.next cap
y
26: return cap
x
27: end function
Reprezentarea folosind o padure de arbori
In cadrul acestei modalitat i de reprezentare ecare mult ime este pastrata sub forma unui
arbore. Pentru ecare nod pastram informat ia despre parintele sau: tata
k
reprezinta indicele
nodului parinte al nodului k.
Se poate observa foarte usor (vezi algoritmul 48) faptul c a arborele obt inut n urma unor
operat ii repetate Merge poate deveni degenerat (o lista liniara). Pentru a putea sa evitam o
astfel de situat ie, reuniunea se poate realiza t inand cont de una din urmatoarele caracteristici:
1. numarul de elemente din ecare arbore - radacina arborelui ce are mai put ine elemente
(notam arborele cu T
B
) va deveni descendentul direct al radacinii arborelui ce poseda
mai multe elemente (notat cu T
A
). Astfel toate nodurile din arborele T
A
vor ramane cu
aceeasi adancime, pe cand nodurile din arborele T
B
vor avea adancimea incrementata
cu 1. De asemenea, arborele rezultat n urma reuniunii va avea cel put in de doua ori
mai multe noduri decat arborele T
B
.
126
Algoritm 48 Algoritmi pentru operat iile init, nd, merge (varianta a III-a)
1: procedure Init(x)
2: tata
x
x
3: end procedure
4: function Find(x)
5: while (x = tata
x
) do
6: x tata
x
7: end while
8: return x
9: end function
10: function Merge(x, y)
11: radx Find(x)
12: rady Find(y)
13: tata
rady
radx
14: return radx
15: end function
1: procedure Init(x)
2: tata
x
1
3: end procedure
4: function Find(x)
5: while (tata
x
> 0) do
6: x tata
x
7: end while
8: return x
9: end function
1: function Merge(x, y)
2: radx Find(x)
3: rady Find(y)
4: size |tata
radx
| +|tata
rady
|
5: if (|tata
radx
| > |tata
rady
|) then
6: tata
rady
radx
7: tata
radx
size
8: return radx
9: else
10: tata
radx
rady
11: tata
rady
size
12: return rady
13: end if
14: end function
Prin aceasta euristica simpla se urmareste o echilibrare a arborelui rezultat pentru a se
evita cazurile degenerate. Complexitatea timp a procedurii Find este O(log n).
2. nalt imea ecarui arbore - radacina arborelui ce are nalt imea mai mica (notam arborele
cu T
B
) va deveni descendentul direct al radacinii arborelui cunalt imea mai mare (notat
cu T
A
). Daca nalt imile lui T
A
si T
B
sunt egale si T
A
devine subarbore al lui T
B
atunci
nalt imea lui T
B
creste cu o unitate.
In gura 6.7, sunt ilustrat i pasii algoritmului lui Kruskal aplicat pe graful considerat.
La nceput se init ializeaza padurea de arbori, ecare arbore ind alcatuit dintrun singur
nod, acesta ind si radacina arborelui. Apoi la ecare pas se alege o muchie si se verica
daca extremitat ile acesteia fac parte din arbori diferit i. Daca raspunsul este armativ atunci
muchia respectiva este selectata, altfel se trece la muchia urmatoare.
129
Fig. 6.7: Algoritmului lui Kruskal exemplicat pe graful din gura 6.6
La pasul ntai evaluam muchia (1, 5), si deoarece cele doua extremitat i fac parte din arbori
distinct i, vom selecta aceasta muchie (L = {(1, 5)}). Reunim arborii din care fac parte cele
doua extremitat i, (vezi gura 6.7 (1)).
La pasul al doilea, se ia n considerare muchia (1, 3) si mult imea muchilor selectate devine
L = {(1, 5), (1, 3)} (vezi gura 6.7 (2)).
La pasul al patrulea, ajungem la muchia (4, 8) ce are costul 10. Nodul 4 face parte din
arborele de radacina 4 iar nodul 8 face parte din arborele de radacina 6. Deoarece extremitat ile
muchiei sunt amplasate n arbori distinct i selectam muchia curenta pentru arborele part ial
de cost minim, L = {(1, 5), (1, 3), (6, 8), (4, 8)}.
In urma reuniunii arborilor corespunzatori
celor doua noduri se obt ine congurat ia din gura 6.7 (4).
130
Subliniem faptul ca arborii reprezentat i n gura 6.7 sunt diferit i de arborii ce con-
duc la obt inerea solut iei problemei: arborii din gura constituie suportul necesar pentru
reprezentarea structurii de date pentru mult imi disjuncte, ce este utilizata pentru efectuarea
ecienta a operat iilor Find si Merge. Acest lucru justica faptul ca desi la pasul al patrulea
selectam muchia (4, 8) pentru arborele part ial de cost minim, ea nu se regaseste n congurat ia
(4) (nodul 4 este unit direct cu nodul 6, vezi gura 6.7). Singura legatura dintre un arbore
suport pentru reprezentarea unei mult imi si un arbore folosit pentru obt inerea arborelui part ial
de cost minim, este mult imea de noduri ce este comuna.
La pasii urmatori, cinci si sase, sunt alese muchiile (7, 8) si respectiv (2, 4): L = {(1, 5), (1, 3),
(6, 8), (4, 8), (7, 8), (2, 4)}.
La pasul al saptelea se evalueaza muchia (3, 6). Nodurile 3 si 6 fac parte din arbori diferit i,
astfel ncat muchia curenta poate selectata pentru solut ia problemei, L = {(1, 5), (1, 3), (6, 8),
(4, 8), (7, 8), (2, 4), (3, 6)}.
6.4 Exercit ii
1. Sa se realizeze o subrutina ce ntoarce numarul de noduri dintrun arbore oarecare.
2. Se da o secvent a formata din n numere naturale d
1
, d
2
, . . . , d
n
. Sa se realizeze un
algoritm prin care sa se verice daca exista un arbore cu n noduri ale caror grade sunt
d
1
, d
2
, . . . , d
n
.
Daca exista un astfel de arbore, acesta va reprezentat prin liste ale nodurilor. O lista
a unui nod cont ine numarul nodului urmat de i sai.
Intrare Iesire
1 1 2 3 1 1 3 DA
1 2 3 4
2 5 6
3 7
4
5
6
7
(Timisoara-pregatire, 1996)
3. Fie un graf G cu n varfuri si m muchii de cost pozitiv. Alegand un nod, numit nod
central, sa se determine un subarbore al lui G astfel ncat drumurile de la nodul central
la toate celelalte noduri sa aiba lungimea minima.
4. Fiind date n puncten spat iul R
3
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 cont ine n interiorul ei toate cele n puncte.
5. Se considera procesul de proiectare a unei placi electronice cu N componente (1 N
100). Pentru ecare componenta electronica C se cunoaste numarul de interconexiuni.
Se considera graful determinat de mult imea pinilor si mult imea interconectarilor posi-
bile ale tuturor componentelor, precum si lungimile lor. Dintre toate modalitat ile de
interconectare posibile se cere cea corespunzatoare arborelui de acoperire minim (inter-
conectarea pentru care suma tuturor circuitelor imprimate are lungimea minima).
131
Datele de intrare sunt alcatuite din descrierile mai multor placi electronice. Descrierea
ecarei placi cont ine numarul N de componente si numarul M de interconexiuni. O
conexiune se caracterizeaza prin trei valori: doua varfuri, u si v, precum si lungimea
acesteia l.
Datele de iesire constau dintr-un raspuns pentru ecare mult ime 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 2 3 35 1 5 27 1 4 37 4 5 30 2 4 58 3 4 60
0 0
vom obt ine rezultatul
Cazul 1: [1 5] [4 5] [2 3] [1 2]
Interconectarea de cost minim are valoarea 132.
In cazul unui graf orientat, not iunea de muchie este nlocuita cu not iunea de arc (o
pereche de noduri (x, y) devine ordonata, adica (x, y) = (y, x)). Pentru un arc (x, y) E,
varful x reprezinta extremitatea init iala a arcului, iar varful y reprezinta extremitatea nala.
Vom spune ca varfurile x si y sunt adiacente.
Denit ia 7.1 Un graf orientat este o pereche ordonata G = (V, E), unde V este o
mult ime de varfuri sau noduri, iar E este o mult ime de arce.
Exemplul 7.1 Fie graful orientat G = (V, E), V = {1, 2, 3, 4, 5, 6, 7, 8},
E = {(1, 2), (1, 3), (1, 4), (2, 1), (3, 5), (4, 6), (5, 2), (5, 7), (6, 4), (6, 8), (7, 5), (7, 6)}. Acest graf
se poate reprezenta ca n gura 7.1.
Denit ia 7.2 Un graf part ial al unui graf orientat G = (V, E) este un graf orientat
G
1
= (V, E
1
) unde E
1
E.
Exemplul 7.2 Pentru graful G din exemplul anterior (G = (V, E)), consideram graful
part ial G
1
= (V, E
1
), cu E
1
= {(1, 3), (2, 1), (3, 5), (4, 6), (5, 2), (5, 7), (6, 4), (6, 8)}, si V =
{1, 2, 3, 4, 5, 6, 7, 8}.
Denit ia 7.3 Un subgraf al unui graf orientat G = (V, E) este un graf orientat H =
(V
1
, E
1
) unde V
1
V iar arcele din E
1
sunt toate arcele din E ce au ambele extremitat i n
mult imea V
1
(E
1
= E|
V
1
V
1
).
Exemplul 7.3 Fie subgrafurile H
1
si H
2
ale grafului G din exemplul anterior: H
1
= (V
1
, E
1
),
unde V
1
= {1, 2, 3, 5}, iar E
1
= {(1, 2), (1, 3), (2, 1), (3, 5), (5, 2)}, si H
2
= (V
2
, E
2
), unde
V = {4, 6, 7, 8} si E
2
= {(4, 6), (6, 4), (6, 8), (7, 6)}.
Denit ia 7.4 Gradul exterior al unui varf d
+
(x) este egal cu numarul arcelor ce au ca
extremitate init iala pe x. Gradul interior al unui varf d
In urma vizitarii unui graf neorientat de obicei nu rezult a numai un arbore de acoperire ci o
padure de arbori de acoperire.
In urma unei parcurgeri n lat ime 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 lat ime.
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 gura 7.2 este prezentat arborele de acoperire n lat ime corespunzator grafului din
gura 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 schit a unui algoritm mai general pentru parcurgerea unui graf (vezi al-
goritmul 50). Pentru aceasta se utilizeaza doua mult imi de noduri, V izitat si Neexplorat,
unde V izitat reprezinta mult imea nodurilor vizitate iar Neexplorat (Neexplorat V izitat)
reprezinta mult imea nodurilor vizitate dar neexplorate (noduri ce prezinta vecini nca nevizitat i).
In urma unei parcurgeri n adancime a unui graf orientat, ecare arc din mult imea arcelor
poate ncadrat ntrunul dintre tipurile urmatoare:
arc al arborelui de acoperire - arcul (u, v) este un arc al arborelui de acoperire daca
dfs(u) apeleaza dfs(v) (dfs(u)
call
dfs(v)).
arc de naintare - arcul (u, v) este un arc de naintare daca este paralel cu un drum de
la u la v din arborele de acoperire (u v) (nu face parte din arborele de acoperire).
136
Algoritm 50 Algoritm de vizitare a unui graf (model general)
1: procedure ParcurgereGraf(u, G)
Input:
_
u - varful de unde se porneste vizitarea
G - graful
2: V izitat {u}
3: Neexplorat V izitat
4: while (Neexplorat = ) do
5: extrage un nod x din Neexplorat
6: determina y, urmatorul vecin al lui x ce nu a fost vizitat
7: if (y = NULL) then
8: elimina x din Neexplorat
9: else
10: if (y / V izitat) then
11: V izitat V izitat {y}
12: Neexplorat Neexplorat {y}
13: end if
14: end if
15: end while
16: end procedure
arc de ntoarcere - arcul (u, v) se numeste arc de ntoarcere daca are sensul contrar unui
drum de la v la u din arborele de acoperire (v u).
arc de traversare - arcul (u, v) este un arc de traversare daca dfs(v) a fost apelat si sa
terminat nainte de apelul lui dfs(u).
Fig. 7.3: Arbore de acoperire n adancime pentru graful orientat din gura 7.1
Pentru ecare nod v al unui graf vom introduce doua numere, prenum
v
si postnum
v
,
numere ce depind de ordinea n care sunt ntalnite nodurile acestuia n timpul vizitarii n
adancime: prenum
v
marcheaza momentul cand este ntalnit nodul v pentru prima oara
137
iar postnum
v
momentul n care prelucrarea nodului v sa ncheiat. Variabila counter este
init ializata cu valoarea 1:
1: procedure Prenum(v)
2: prenum
v
counter
3: counter counter + 1
4: end procedure
si
1: procedure Postnum(v)
2: postnum
v
counter
3: counter counter + 1
4: end procedure
Un arc (u, v) va avea urmatoarele proprietat i, n funct ie de una din cele patru categorii
de arce introduse anterior n care se ncadreaza:
1. arc al arborelui de acoperire: prenum
u
< prenum
v
si postnum
u
> postnum
v
;
2. arc de naintare: prenum
u
< prenum
v
si postnum
u
> postnum
v
;
3. arc de ntoarcere: prenum
u
> prenum
v
si postnum
u
< postnum
v
;
4. arc de traversare: prenum
u
> prenum
v
si postnum
u
> postnum
v
.
Arborele de acoperire n adancime corespunzator grafului din gura 7.1 ilustreaza aceste
tipuri de arce: arc de naintare (1, 4), arce de ntoarcere (2, 1), (7, 5), (4, 6) arc de
traversare (5, 2), arce ale arborelui de acoperire (1, 2), (1, 3), (3, 5), (5, 7), (7, 6), (6, 8),
(6, 4). (vezi gura 7.3).
Algoritm 51 Algoritm de vizitare n adancime pentru un graf orientat
1: procedure DFSNum(k, n, V ecin)
Input:
_
_
k - nodul curent vizitat
n - numarul de noduri din graf
V ecin - matricea de adiacent a a grafului
2: vizitat
k
1 marcarea nodului curent ca ind vizitat
3: call Prenum(k)
4: call V izitare(k) vizitarea nodului curent
5: for i 1, n do
6: if (vizitat
i
= 0) (vecin
k,i
= 1) then
7: call DFSNum(i, n, V ecin) apelul recursiv al subrutinei DFSNum pentru nodul
i
8: end if
9: end for
10: call Postnum(k)
11: end procedure
Lema 7.5 Fiind dat un graf orientat G si doua noduri oarecare u, v G ce apart in aceluiasi
arbore de acoperire n adancime rezultat n urma parcurgerii cu metoda DFS a grafului G
avem:
1. daca (u, v) este un arc al arborelui de acoperire sau un arc de naintare sau un arc de traver-
sare postnum
u
> postnum
v
;
138
2. daca (u, v) este un arc de ntoarcere postnum
u
< postnum
v
.
Lema 7.6 Fiind dat un graf orientat G pentru oricare doua noduri u, v G avem:
1. v este un descendent al lui u n padurea de arbori de acoperire rezultat i n urma vizitarii n
adancime a grafului G intervalul [prenum
v
, postnum
v
] este inclus n intervalul [prenum
u
,
postnum
u
];
2. nu exista nici o legatura ntre u si v n padurea de arbori de acoperire rezultat i n urma
vizitarii n adancime a grafului G intervalele [prenum
u
, postnum
u
] si [prenum
v
, postnum
v
]
sunt disjuncte;
3. situat ii prenum
u
< prenum
v
< postnum
u
< postnum
v
sau prenum
v
< prenum
u
< postnum
v
< postnum
u
nu sunt posibile.
Lema 7.7 Fiind dat un graf neorientat G, daca pentru doua noduri oarecare u, v G avem
relat ia prenum
u
< prenum
v
< postnum
u
atunci:
prenum
u
< prenum
v
< postnum
v
< postnum
u
.
exista un drum de la u la v n G.
Exemplul 7.8
In urma apelarii procedurii DFSNum(1, 8, V ecin) (vezi algoritmul 51), sec-
vent a de apeluri recursive ale procedurii DFSNum este ilustrata prin arborele de acoperire
n adancime din gura 7.3.
Numerotarea nodurilor n preordine si postordine rezultata n urma vizitarii este urmatoarea:
1 2 3 4 5 6 7 8
prenum 1 2 4 8 5 6 7 10
postnum 16 3 15 9 14 13 12 11
Pentru arcul (1, 4) avem prenum
1
= 1, postnum
1
= 16, prenum
4
= 8 si postnum
4
= 9.
Deoarece relat ia prenum
u
< prenum
v
< postnum
u
este adevarata conform lemei 7.7 ar
trebui sa avem prenum
u
< prenum
v
< postnum
v
< postnum
u
si sa existe un drum de la u
la v, lucruri care sunt adevarate (prenum
1
< prenum
4
< postnum
4
< postnum
1
si exista un
drum de la 1 la 4 n arborele de acoperire n adancime).
7.3 Sortarea topologica
Fig. 7.4: Un graf orientat aciclic
139
Denit ia 7.12 Un graf orientat si care nu poseda circuite se numeste graf orientat aciclic
(directed acyclic graph - DAG).
Lema 7.9
Intrun graf orientat aciclic, daca prenum
u
< prenum
v
si exista un drum de la
u la v n G, atunci prenum
u
< prenum
v
< postnum
v
< postnum
u
.
In practica, exista mai multe situat ii n care o mult ime de activitat i sau sarcini, trebuie
organizate ntro anumita ordine, ntre acestea exist and, de obicei, o mult ime de restrict ii
sau dependent e.
In activitatea de construire a unei cladiri, anumite activitat i nu pot
ncepute decat dupa nalizarea altor activitat i: spre exemplu, nu se poate ncepe ridicarea
peret ilor decat dupa turnarea fundat iei si nalizarea structurii de rezistent a, nu se poate rea-
liza instalat ia 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 cont ine pe langa cursurile obligatorii, si
o serie de cursuri opt ionale, va trebui sa t ina cont de anul de studiu n care se preda ecare
materie, precum si de cerint ele obligatorii ale acestora: un curs nu poate inclus n planul de
studiu individual al unui student decat daca acesta a parcurs si a obt inut creditele la toate
materiile anterioare cerute explicit n programa cursului.
Algoritm 52 Algoritm de sortare topologica a unui graf orientat (prima varianta)
1: procedure SortTop1(n, V ecin)
Input:
_
n - numarul de noduri din graf
V ecin - vector ce cont ine listele cu vecini ai ecarui nod
2: for k 1, n do
3: dminus
k
0
4: end for
5: for (u, v) E do
6: dminus
v
= dminus
v
+ 1
7: end for
8: Q se init ializeaza coada
9: for k 1, n do
10: if (dminus
k
= 0) then
11: Q k se insereaza ntro coada nodurile cu gradul interior 0
12: end if
13: end for
14: while (Q = ) do
15: Q k se extrage din coada un nod
16: L k se insereaza nodul ntr-o lista
17: w V ecin
k
v ia valoarea capului listei de vecini a nodului k
18: while (w = NULL) do
19: dminus
w.nodeIndex
dminus
w.nodeIndex
1
20: if (dminus
w.nodeIndex
= 0) then
21: Q w.nodeIndex se insereaza n coada nodul w.nodeIndex
22: end if
23: w w.next se trece la urmatorul vecin
24: end while
25: end while
26: end procedure
Dependent a dintre doua activitat i Asi B o putem modela prin introducerea a doua noduri
n graf x
i
si x
j
, asociate celor doua activitat i. Daca activitatea A trebuie realizata naintea
activitat ii B, atunci se adauga arcul (x
i
, x
j
).
140
Denit ia 7.13 Se numeste sortare topologic a pentru un graf orientat G = (V, E) o or-
donare {x
1
, x
2
, . . . , x
n
} a nodurilor grafului astfel ncat pentru orice arc (x
i
, x
j
) sa avem
i < j.
Prin urmare o sortare topologica presupune aranjarea liniara a varfurilor unui graf astfel
ncat toate arcele sale sa e orientate de la stanga la dreapta.
Lema 7.10 Daca un graf orientat G admite o sortare topologica atunci G este aciclic.
Lema 7.11 Daca un graf orientat G este aciclic atunci el admite o sortare topologica.
Observat ia 7.12
Intrun graf orientat aciclic exista cel put in un nod al c arui grad interior
este 0 (graful nu poseda arce care sa aiba nodul v drept extremitate nala).
Pornind de la aceasta observat ie se schit eaza urmatorul algoritm [78]: ntrun graf G se
determina un nod v astfel ncat gradul sau interior sa e zero (d
= (V
, E
), unde
V
= V \ {v}, E
= E|
V
V
.
Algoritmul 52 se termina n cel mult n pasi (|V | = n). Daca se termina mai devreme,
atunci graful G nu este aciclic (la un moment dat nu mai exista nici un nod v astfel ncat
d
(v) = 0).
Fig. 7.5: Sortare topologica cu algoritmul 52 pentru graful orientat din gura 7.4
Exemplul 7.13 Fie graful orientat din gura 7.5. Varful 3 are d
(v) = 0. Se
adauga la lista de rezultate, si se elimina din graf mpreuna cu arcele ce pleaca din el. Se
continua procedeul pana cand graful devine vid (ntregul proces poate urmarit n gura 7.5).
141
Algoritm 53 Algoritm de sortare topologica a unui graf orientat (a doua varianta)
1: procedure SortTop2(n, V ecin)
Input:
_
n - numarul de noduri din graf
V ecin - matricea de adiacent a a grafului
2: for k 1, n do
3: prenum
k
0, postnum
k
0
4: vizitat
k
0
5: end for
6: for k 1, n do
7: if (vizitat
k
= 0) then
8: call DFSNum(k, n, V ecin)
9: end if
10: end for
11: se ordoneaza descrescator nodurile dupa postnum
k
12: end procedure
Lema 7.14 Un graf orientat G este aciclic daca si numai daca n urma unei vizitari n
adancime a acestuia nu este ntalnit nici un arc de ntoarcere.
Ideea algoritmului 53 o reprezinta lema 7.14[118].
Exemplul 7.15 Sa consideram ca date de intrare pentru algoritmul 53 graful orientat din
gura 7.4. Dupa etapa de init ializare (liniile 2 - 5) avem:
1 2 3 4 5 6 7
prenum 0 0 0 0 0 0 0
postnum 0 0 0 0 0 0 0
vizitat 0 0 0 0 0 0 0
Se apeleaza mai ntai DFSNum(1, 7, V ecin). Secvent a rezultata de apeluri recursive este
urmatoarea: DFSNum(1, 7, V ecin) DFSNum(2, 7, V ecin) DFSNum(4, 7, V ecin)
DFSNum(5, 7, V ecin) DFSNum(7, 7, V ecin) DFSNum(6, 7, V ecin).
=
{y|x y}). Relat ia de echivalent a determina o partit ie n clase de echivalent a a mult imii
peste care a fost denita. Prin urmare relat ia de tare conexitate determina o partit ie a
mult imii V . Clasele de echivalent a determinate de relat ia de tare conexitate sunt componen-
tele tare conexe.
Pentru determinarea componentelor tare conexe exista mai mult i algoritmi, dintre care
amintim algoritmul lui Tarjan, algoritmul lui Kosaraju si algoritmul lui Gabow.
7.4.1 Algoritmul lui Kosaraju
Algoritmul lui KosarajuSharir a fost prezentat de Aho, Hopcroft si Ullmann lucrarea lor [2],
ind preluat dintrun manuscris al lui S. Rao Kosaraju (M. Sharir la prezentat n lucrarea
[110]).
Algoritmul foloseste graful transpus G
T
asociat grafului init ial G ([11], [23], [30]), ind
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 obt ine un nou graf G
T
= (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 G
T
ce are cel mai mare numar atribuit n urma
parcurgerii de la Pasul 1. Din acest nod se init iaza 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 componenta tare conexa
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.
*/
144
char vizitat[MAX];
/**
* Numarul de varfuri din graf
*/
int n;
int nump;
/**
* Numarul asociat fiecarui varf la vizitarea in postordine.
*/
int postnum[MAX];
void dfs(int k) {
int i;
vizitat[k] = TRUE;
for (i = 0; i < n; i++)
if ((vizitat[i] == FALSE) && (vecin[k][i] > 0))
dfs(i);
nump++;
postnum[k] = nump;
}
void dfs1(int k) {
int i;
vizitat[k] = TRUE;
for (i = 0; i < n; i++)
if ((vizitat[i] == FALSE) && (vecin[k][i] > 0))
dfs1(i);
printf("%d ", k);
}
void readInput(void) {
int i, j;
printf("n = "); scanf("%d", &n);
do{
printf("nod1 nod2 : "); scanf("%d %d", &i, &j);
if (i >= 0)
vecin[i][j] = 1;
} while (i >= 0);
}
void main(void) {
int i, j, k, tmp;
int maxim;
int nod;
readInput();
// prima etapa
memset(vizitat, 0, sizeof(vizitat));
145
nump = 0;
for (i = 0; i < n; i++)
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 2 3 4 5 6 7 8
postnum 8 7 5 6 1 2 3 4
vizitat 1 1 1 1 1 1 1 1
Fig. 7.7: Graful transpus G
T
corespunzator grafului din gura 7.6
Se construieste graful transpus, G
T
(vezi gura 7.7). Se cauta primul nod u nca neviz-
itat (vizitat
u
= 0), caruia i corespunde cea mai mare valoare postnum
u
.
In acest mod se
146
identica componenta tare conexa compusa numai din nodul 1: {1}.
Urmatorul nod, ca valoarea a vectorului postnum ordonat descrescator, este 2. Din nodul
2 se parcurg nodurile 3 si 4, rezultand o alta componenta tare conexa: {2, 3, 4}.
Urmatorul nod nevizitat u ce are valoarea prenum
u
maxima este nodul 8. Se identica
mai ntai componenta tare conexa {7, 8} si apoi {5, 6}.
Analiza algoritmului
Algoritmul lui Kosaraju realizeaza doua parcurgeri ale tuturor elementelor grafului G: prima
data parcurge graful G si a doua oara parcurge graful G
T
. Prin urmare complexitatea timp a
algoritmului este (|V | +|E|) n cazul n care graful este reprezentat prin liste de adiacent a si
este patratican |V |
2
(O(|V |
2
)) atunci cand graful este reprezentat prin matricea de adiacent a.
7.4.2 Algoritmul lui Tarjan
Algoritmul lui Tarjan [117] este considerat drept o mbun atat ire a algoritmului lui Kosaraju
prin aceea ca nu mai este nevoie de parcurgerea grafului G de doua ori.
Algoritmul init iaza 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 echivalent a.
Prin urmare componentele tare conexe se obt in prin descompunerea arborelui/arborilor
de acoperire n adancime daca eliminam anumite arce ale acestora. Un nod este capul unei
componente tare conexe (sau radacina), daca acesta constituie radacina subarborelui core-
spunzator componentei. Arcul ce are nodulcap drept extremitate nala 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. Pentru a pastra o ordine
a vizitarii acestora pe parcursul algoritmului, nodurile vor adaugate pe o stiva. Ele vor
extrase din stivan momentul n care procedura de vizitare DFS a unui nod estencheiata: se
determina daca nodul curent este radacina unei componente tare conexe, si, n caz armativ,
toate nodurile care au fost vizitate din nodul curent n cadrul parcurgerii n adancime sunt
marcate ca ind elemente ale componentei tare conexe.
Vom numerota toate nodurile grafului n preordine (altfel spus acest numar indica pen-
tru nodul curent numarul de noduri vizitate naintea sa), valorile ind pastrate n vectorul
prenum (cititorul este invitat sa revada si sect iunea despre Muchie critica, cea de-a doua
solut ie). Pentru un nod u G denim low
u
astfel:
low
u
= min
_
_
prenum
u
prenum
x
, daca [u, x] este arc de ntoarcere sau de traversare si x S
low
y
, 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
fost vizitat naintea lui u (prenum
u
> prenum
v
). Daca un astfel de nod nu exista atunci
low
u
= prenum
u
. Stiva S pastreaza nodurile grafului, pe masura ce sunt vizitate ele ind
adaugate la S. Daca u este un nodcap (low
u
= prenum
u
) 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 54) seam ana foarte mult cu algoritmul 26
de determinare a muchiei critice (varianta a II-a).
147
Algoritm 54 Algoritmul lui Tarjan pentru determinarea componentelor tare conexe
1: procedure Tarjan(n, V ecin)
Input:
_
n - numarul de noduri din graf
V ecin - matricea de adiacent a
2: for i 1, n do
3: vizitat
i
0
4: end for
5: counter 0
6: S
7: for i 1, n do
8: if (vizitat
i
= 0) then
9: call DFSTarjan(i, n, V ecin) vizitarea n adancime a grafului
10: end if
11: end for
12: end procedure
13: procedure DFSTarjan(k, n, V ecin)
14: S k
15: vizitat
k
1
16: counter counter + 1
17: prenum
k
counter, low
k
counter
18: for i 1, n do
19: if (vecin
k,i
= 1) then
20: if (vizitat
i
= 0) then
21: call DFSTarjan(i, n, V ecin)
22: low
k
Min(low
k
, low
i
)
23: else
24: if (i S) then
25: low
k
Min(low
k
, prenum
i
)
26: end if
27: end if
28: end if
29: end for
30: if (low
k
= prenum
k
) then
31: repeat
32: S u
33: Output u
34: until (u = k)
35: end if
36: end procedure
Exemplul 7.18 La nceput valorile vectorilor vizitat si prenum sunt urmatoarele:
1 2 3 4 5 6 7 8
prenum 0 0 0 0 0 0 0 0
vizitat 0 0 0 0 0 0 0 0
Primul element nevizitat este nodul 1 (linia 9), prin urmare se apeleaza DFSTarjan(1, 8, V ecin).
Rezulta o secvent a de apeluri recursive:
DFSNum(1, 8, V ecin) DFSNum(2, 8, V ecin) DFSNum(3, 8, V ecin)
DFSNum(6, 8, V ecin) DFSNum(5, 8, V ecin).
148
1 2 3 4 5 6 7 8
prenum 1 2 3 0 5 4 0 0
vizitat 1 1 1 0 1 1 0 0
low 1 2 0 0 4 4 0 0
Stiva cont ine urmatoarele elemente (varful stivei ind n dreapta): {1, 2, 3, 6, 5}.
low
5
= min{prenum
6
, low
5
} = min{4, 5} = 4 (deoarece desi vizitat
6
= 1, avem 6 S,
adica nodul 6 se aa pe stiva). Astfel n momentul terminarii apelului DFSTarjan(5, 8, V ecin),
low
5
= 4.
Acum la nivelul lui DFSTarjan(6, 8, V ecin), n urma revenirii din DFSTarjan(5, 8, V ecin),
se calculeaza low
6
= min{low
6
, low
5
} = min{4, 4} = 4 (linia 22).
Nodul 6 este un nodcap (low
6
= prenum
6
) (vezi linia 30), si prin urmare se extrag de pe
stiva toate elementele dintre varful stivei si elementul 6 inclusiv, rezultand prima componenta
tare conexa: {5, 6}. Stiva ramane cu elementele (varful stivei ind n dreapta) {1, 2, 3}.
Din nodul 3 se continua cu vizitarea nodului 8, nca nevizitat:
DFSNum(3, 8, V ecin) DFSNum(8, 8, V ecin) DFSNum(7, 8, V ecin).
Cont inutul stivei este {1, 2, 3, 8, 7}.
La nivelul apelului DFSNum(7, 8, V ecin) nu se ia n considerare la calculului valorii lui
low
7
nodul 6: desi exista arcul (7, 6), nodul 6 a fost vizitat (vizitat
i
= 0) si nu se mai aa
pe stiva (i / S). Astfel low
7
= min{low
7
, prenum
8
} = min{7, 6} = 6.
La nivelul lui DFSNum(8, 8, V ecin), low
8
= min{low
8
, low
7
} = min{6, 6} = 6 (linia 22).
Avem ca (low
8
= prenum
8
) (linia 30) si prin urmare 8 este un nodcap. Se extrag de pe stiva
toate elementele dintre varful stivei si elementul 8 inclusiv, rezultand a doua componenta tare
conexa: {7, 8}.
Stiva ramane cu elementele (varful stivei ind n dreapta) {1, 2, 3}.
1 2 3 4 5 6 7 8
prenum 1 2 3 0 5 4 7 6
vizitat 1 1 1 0 1 1 1 1
low 1 2 0 0 4 4 6 6
In afara unui abonat, cercul sau de apel consta din toate persoanele pe care le suna si
care l suna, direct sau indirect.
De exemplu, daca Ben l suna pe Alexander, Alexander o suna pe Dolly si Dolly l
suna pe Ben, atunci ei tot i fac parte din acelasi cerc. Dac a Dolly l mai suna si pe
Benedict iar Benedict o suna pe Dolly, atunci Benedict este n acelasi cerc cu Dolly,
Ben si Alexander.
In ne, daca Alexander l suna pe Aron dar Aaron nu l sun a pe
Alexander, Ben, Dolly sau Benedict, atunci Aaron nu este n cerc.
Sa se realizeze un algoritm ce determina cercurile de apel, cunoscanduse lista apelurilor
telefonice dintre abonat i.
(Finala ACM 1995, Calling Circles)
4. La facultatea X exista doua alternative pentru ca student ii sa aiba timp sa asimileze
cunostint ele:
(a) Marirea zilei la 30 de ore, si
(b) Reducerea programei scolare.
Optand pentru a doua varianta, Liga Student ilor introduce o platforma program care:
(a) Stabileste care sunt materiile necesare pentru a putea studia o noua materie.
De exemplu, pentru a studia cursul Management este nevoie de cursul Teorie
Economica si de cursul Marketing.
(b) Stabileste care sunt materiile cu adevarat utile dintre toate cele studiate n fac-
ultate. De exemplu, cursul de Masurari electrice nu este util la absolut nimic.
(c) Cere sa se elimine din programa materiile care nu sunt nici folositoare, nici nu
servesc (direct sau indirect) la nvat area unor materii folositoare.
(d) Cere sa se indice care dintre materii nu pot predate n nici o ordine. De ex-
emplu, cursul Mecanica se bazeaza pe cursul Ecuat ii Diferent iale, dar cursul
de Ecuat ii Diferent iale si preia exemplele din Mecanica. Prin urmare nu ex-
ista nici o ordine n care aceste materii sa e predate fara a introduce cunostint e
nedemonstrate.
5. Distribuirea cart ilor cerute de catre cititorii de la sala de lectura a unei biblioteci este
facuta de catre un robot ce are posibilitatea sa ajunga la orice carte ce poate solicitata.
Din pacate, rafturile unde sunt depozitate cart ile sunt dispuse astfel ncat robotul nu
poate lua cart ile ntrun singur drum. Dupa recept ionarea comenzilor de la mai mult i
cititori, robotul cunoaste pozit ia cart ilor n rafturi si drumurile catre acestea. Din
pacate, de la pozit ia unei cart i, robotul nu se poate deplasa decat spre anumite cart i.
Acesta porneste si culege cart ile ce sunt accesibile ntrun drum, apoi porneste de la
o alta pozitie de carte si culege acele cart i ce sunt accesibile din acel punct si asa mai
departe.
154
Datele de intrare constau din numarul de cart i n precum si numarul m de legaturi
dintre pozit iile acestora (1 n 100, 11 m 10000), urmate de m perechi de
numere naturale, ce semnica legaturile directe ntre pozit iile cart ilor.
Datele de iesire constau din drumurile pe care le face robotul pentru a culege cart ile
cerute.
Fig. 7.9: Pozit iile cart ilor ntro biblioteca precum si posibilitat ile de deplasare ale robotului
6. Se considera o mult ime de n elevi dintro clasa. Fiecare elev are cunostint e mai avansate
ntrun anumit domeniu. Pentru ridicarea nivelului clasei, dirigintele vrea sai aranjeze
n grupuri astfel ncat tot i elevii dintrun grup sa ajunga sa cunoasca, ntro anumita
perioada de timp, toate cunostint ele tuturor celorlalt i colegi din acelasi grup.
Grupurile de elevi nu si vor schimba cunostint ele ntre ele. Se stie ca un elev nu se
poate face nt eles de catre oricine. El are o lista de preferint e fat a de care el le va
mpartasi cunostintele sale. Relat ia de preferint a nu este simetrica.
Sa se determine numarul minim de grupuri n care va mp art ita clasa precum si
component a acestora.
155
Capitolul 8
Distant e n grafuri
Ret eaua de drumuri europene, nat ionale si judet ene dintro t ara, ret eaua feroviara reprezentand
infrastructura cailor ferate absolut necesara pentru realizarea transportului de marfuri si
calatori cu trenul, ret eaua de bra optica folosita pentru tracul de date n Internet, ret eaua
de transport a energiei electrice folosita pentru alimentarea cu energie electrica a consumato-
rilor casnici si a celor industriali, ret eaua de canalizare dintrun oras folosita pentru evacuarea
deseurilor, ret eaua CATV a unui operator de televiziune prin cablu, ret eaua de linii de auto-
buz ce face parte din sistemul public de transport al unui oras sunt exemple tipice de grafuri
cu care interact ionam n viat a de zi cu zi.
Putem spune ca ret elele de transport si cele de comunicat ii de date ne inuent eaza n
mod direct modul de viat a, devenind elemente indispensabile ale omului modern, si astfel,
problemele referitoare la studiul cailor de comunicat ie, drumurilor, conexiunilor, capata un
interes special.
De obicei suntem interesat i 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 dispozit ie mijloacele pentru aarea raspunsului la multe
astfel de ntrebari.
Denit iile 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 funct ie de cost ce asocieaza o valoare real a ecarei muchii
sau arc, dupa caz:
c : E R sau c : V V R
Notam costul unui drum ca ind suma costurilor arcelor componente. Astfel pentru un
drum D = [v
0
, v
1
, . . . , v
m
] de lungime m, costul acestuia va dat de urmatoarea formula:
c(D) =
m1
i=0
c((v
i
, v
i+1
))
Notam cu M
s,t
mult imea drumurilor dintre nodul x
s
si nodul x
t
, x
s
, x
t
V . Pentru doua
noduri oarecare x
s
si x
t
, se doreste sa se determine un drum
s,t
de la x
s
la x
t
a carui valoare
sa e optima (minima sau maxima):
c(
opt
s,t
) = min
s,tMs,t
c(
s,t
)
156
8.1 Drumul minim de la un varf la celelalte varfuri
8.1.1 Algoritmul lui Moore
Fie G = (V, E) un graf orientat unde V = {x
1
, x
2
, . . . , x
n
} este mult imea nodurilor si E este
mult imea arcelor (E V V ). Pentru reprezentarea grafului vom utiliza listele de adiacent a
(listele cu vecini).
Fie u un varf al grafului numit sursa. Dorim sa determinam pentru ecare varf w
V, w = u, daca exista, un drum de lungime minima de la u la w. Lungimea unui drum se
deneste ca ind numarul arcelor ce l compun.
Algoritmul lui Moore (algoritmul 56) se bazeaza pe structura algoritmului de parcurgere
n lat ime al unui graf (breadth rst search vezi algoritmul 20).
In lucrarea [98], Moore si
prezinta algoritmul astfel:
Write 0 on the source. Then look at all the neighbors of the source and write
1 on them. Then look at all the neighbors of nodes with 1 on them and write 2
on them. And so on.
Vom nota cu d
k
costul drumului minim de la varful u la varful x
k
, iar tata
k
varful anterior
lui x
k
, pe drumul de cost minim de la u la x
k
.
Algoritm 56 Algoritmul lui Moore
1: procedure Moore1(k, n, V ecin; d, tata)
Input:
_
_
k - nodul sursa
n - numarul de noduri din graf
V ecin - vector ce cont ine capetele listelor de vecini
Output:
_
_
d - vectorul distant elor de la nodul sursa la celelalte noduri
tata - vector ce cont ine pentru ecare nod k, predecesorul acestuia pe drumul
de cost minim de la nodul sursa la nodul k
2: for i 1, n do
3: d
i
+
4: end for
5: d
k
0 distant a de la un nod la el nsusi este 0
6: Q k inserarea nodului curent k n coada
7: while (Q = ) do cat timp coada nu este vida
8: Q k extrage nodul curent din coada
9: v vecin
k
se pleaca cu primul vecin din lista de vecini
10: while (v = NULL) do
11: if (d
v.nodeIndex
= +) then v.nodeIndex este indicele nodului vecin
12: d
v.nodeIndex
d
k
+ 1, tata
v.nodeIndex
k
13: Q v.nodeIndex inserarea nodului v.nodeIndex n coada
14: end if
15: v v.next se trece la urmatorul vecin
16: end while
17: end while
18: end procedure
Exemplul 8.1 Fie un graf orientat denit de catre urmatoarele liste de vecini:
157
1: (2, 4) 7: (8)
2: (3, 6) 8: (6, 7)
3: (2) 9: ()
4: (1, 5) 10: (11)
5: (4, 6, 9) 11: (10)
6: (7, 8)
In urma aplicarii algoritmului lui Moore pentru acest graf si avand nodul 1 drept sursa,
se obt in urmatoarele valori pentru vectorii d si tata:
1 2 3 4 5 6 7 8 9 10 11
d 0 1 2 1 2 2 3 3 3
tata 1 2 1 4 2 6 6 5
Urmarind valorile asate, 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 57 calculeaza lungimea drumurilor minime de la un nod sursa la toate nodurile
accesibile din acesta, n cazul n care lungimea unui drum se deneste ca ind suma costurilor
asociate arcelor ce-l compun.
Algoritm 57 Algoritm lui Moore (a doua varianta)
1: procedure Moore2(k, n, C; d, tata)
Input:
_
_
k - nodul sursa
n - numarul de noduri din graf
C - matricea costurilor
2: for i 1, n do
3: d
i
+
4: end for
5: d
k
0 distant a de la un nod la el nsusi este 0
6: Q k inserarea nodului curent k n coada
7: while (Q = ) do cat timp coada nu este vida
8: Q k extrage nodul curent din coada
9: for ecare vecin (v = x
i
) al lui x
k
do
10: if (d
k
+ c
k,i
< d
i
) then
11: d
i
d
k
+ c
k,i
, tata
i
k
12: Q i inserarea nodului i n coada
13: end if
14: end for
15: end while
16: end procedure
8.1.2 Algoritmul lui Dijkstra
Fie G = (V, E) un graf orientat unde V = {1, 2, . . . , n} este mult imea nodurilor si E este
mult imea arcelor (E V V ). Pentru reprezentarea grafului se utilizeaza matricea costurilor
C:
c
i,j
=
_
_
0 , daca i = j
, daca (i, j) / E, i = j
d > 0 , daca (i, j) E
158
Fie u un varf al grafului numit sursa. Dorim sa determinam pentru ecare varf j V, j =
u, daca exista, un drum de lungime minima de la u la j. Lungimea unui drum se deneste
ca ind suma costurilor asociate arcelor ce-l compun.
Algoritmul lui Dijkstra [38] utilizeaza metoda generala de elaborare a algoritmilor Greedy,
drumurile de lungime minima ind generate n ordinea crescatoare a lungimii lor. Vom nota
cu S mult imea nodurilor grafului G pentru care se cunoaste (s-a calculat) drumul de lungime
minima de la sursa. La nceput mult imea S este formata doar din nodul sursa. La ecare
pas, se adauga la mult imea S un nod k V \ S cu proprietatea ca drumul de la sursa la
acel nod este cel mai mic dintre toate drumurile posibile de la sursa la noduri din mult imea
V \ S, cu proprietatea ca un astfel de drum are drept noduri intermediare numai elemente
din mult imea S, cu except ia extremitat ii nale. Vom utiliza un tablou D ce va pastra pentru
ecare nod k, lungimea drumului cel mai scurt de la sursa ce trece numai prin noduri din
mult imea S (vezi algoritmul 58).
Dupa ce am identicat un astfel de nod k, mult imea S se modica astfel: S = S {k}.
Este posibil ca lungimea unui drum de la sursa la un nod j V \ S, ce are drept noduri
intermediare noduri din mult imea S, sa se modice datorita faptului ca, mai nainte, nodul k
nu fusese luat n considerare, deoarece nu apart inea mult imii S. Astfel, se poate ca un drum
de la sursa la nodul j, ce trece prin nodul k, sa e mai mic decat drumul anterior de la sursa
la nodul j: d
k
+c
k,j
< d
j
.
Algoritm 58 Algoritmul lui Dijkstra (schema generala)
1: procedure Dijkstra1(n, C, u)
2: S {u}
3: for i 1, n do
4: d
i
c
u,i
5: end for
6: for i 1, n 1 do
7: k min{d
k
|k V \ S}
8: S S {k}
9: for each j V \ S do
10: d
j
min(d
j
, d
k
+ c
k,j
)
11: end for
12: end for
13: end procedure
Vom utiliza trei vectori (vezi algoritmul 59):
vizitat - vector caracteristic
vizitat
k
=
_
1 , daca nodul k S
0 , daca nodul k V \ S
d - vectorul distant elor de la nodul u la celelalte noduri ale grafului.
In momentul
init ial d
j
= c
u,j
. Fie k nodul ales la un moment dat. Atunci d
j
se modica numai daca
d
k
+c
k,j
< d
j
, ind actualizat astfel: d
j
= d
k
+c
k,j
.
tata
k
- cont ine pentru ecare nod k nodul anterior j (j S) pe drumul de cost minim
de la u la k. La nceput,
tata
k
=
_
0 , daca nodul k = u sau c
u,k
=
u , n rest (k = u si c
u,k
= )
159
Un element al acestui vector se poate modica atunci cand se modica d
j
, caz n care
tata
j
= k.
Algoritm 59 Algoritmul lui Dijkstra
1: function DistantaMinima(n, vizitat, d)
2: min
3: for j 1, n do
4: if (vizitat
j
= 1) (d
j
< min) then
5: min d
j
6: j
0
j
7: end if
8: end for
9: if (min = ) then
10: return 1
11: else
12: return j
0
13: end if
14: end function
15: procedure Dijkstra2(n, C, u)
16: vizitat
u
1
17: d
u
0, tata
u
0
18: for i 1, n do
19: if (i = u) then
20: vizitat
i
0
21: d
i
c
u,i
, tata
i
u
22: end if
23: end for
24: while (true) do
25: k DistantaMinima(n, vizitat, d)
26: if (k < 0) then
27: break forteaza iesirea dintr-o instructiune de ciclare
28: else
29: vizitat
k
1
30: for i 1, n do
31: if (vizitat
i
= 1) (d
k
+ c
k,i
< d
i
) then
32: tata
i
k
33: d
i
d
k
+ c
k,i
34: end if
35: end for
36: end if
37: end while
38: end procedure
Observat ia 8.2 Algoritmul lui Dijkstra prezinta asemanari cu algoritmul de cautare n
lat ime, la nal, vectorul tata pastrand un arbore al drumurilor minime ce are drept rad acina
nodul sursa.
160
u
k
x
j
Fig. 8.1: O cale speciala mai scurta
Demonstrarea corectitudinii algoritmului
_
0 , daca i = j
+ , daca (x
i
, x
j
) / E, i = j
d > 0 , daca (x
i
, x
j
) E
(8.2)
(un drum ce nu are nici un varf intermediar este determinat de costul legaturii directe dintre
x
i
si x
j
).
Drumul de lungime minima de la x
i
la x
j
ce trece numai prin nodurile {x
1
, x
2
, . . . , x
k
} e
nu cont ine nodul x
k
caz n care d
k
i,j
= d
k1
i,j
, e l cont ine, si atunci d
k
i,j
= d
k1
i,k
+ d
k1
k,j
. Prin
urmare d
k
i,j
= min{d
k1
i,j
, d
k1
i,k
+d
k1
k,j
}.
Desi aceasta ultima relat ie sugereaza un algoritm recursiv, o abordare iterativa este mai
ecienta atat n ceea ce priveste complexitatea timp c at si din punctul de vedere al necesarului
167
de memorie. Se observa ca matricea D
k
nu mai este necesara de ndata ce matricea D
k+1
a
fost calculata.
D
n
= (d
n
i,j
), d
n
i,j
=
i,j
, x
i
, x
j
V.
Algoritm 62 Algoritmul FloydWarshall
1: procedure FloydWarshall1(n, C; D
n
)
2: for i 1, n do
3: for j 1, n do
4: d
0
i,j
c
i,j
5: end for
6: end for
7: for k 1, n do
8: for i 1, n do
9: for j 1, n do
10: d
k
i,j
min {d
k1
i,j
, d
k1
i,k
+ d
k1
k,j
}
11: end for
12: end for
13: end for
14: end procedure
Inchiderea tranzitiva
Denit ia 8.1 Fie G = (V, E) un graf orientat, simplu si nit, V = {x
1
, x
2
, . . . , x
n
}.
Inchiderea tranzitiva a unui graf orientat prezinta multiple aplicat ii drept subproblema n
cazul unor probleme computat ionale cum ar : construirea unui automat de parsare utilizat
la realizarea unui compilator, evaluarea interogarilor recursive realizate asupra unei baze de
date sau analiza elementelor accesibile ntro ret ea de tranzit ie 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 obt ine o
matrice n care, d
i,j
= + daca nu exista un drum de la x
i
la x
j
sau d
i,j
= c < n daca exista.
In cadrul celei de-a doua variante de calcul (vezi algoritmul 64), se nlocuiesc operat iile
min cu (sau logic) si respectiv + cu (si logic).
d
k
i,j
= 1 daca exista un drum de la x
i
la x
j
n graful G avand varfurile intermediare numai
din mult imea {x
1
, . . . , x
k
}.
d
k
i,j
=
_
1 , daca i = j sau (x
i
, x
j
) E
0 , daca i = j si (x
i
, x
j
) / E
(8.3)
d
k
i,j
= d
k1
i,j
(d
k1
i,k
d
k1
k,j
)
Exemplul 8.5 Fie graful din gura 8.2, din care am eliminat arcul (1, 2). Matricea cos-
turilor asociata acestui graf modicat este:
C =
_
_
_
_
_
_
0 21
0 8 4
3 0 12
9 0
3 0
_
_
_
_
_
_
169
Algoritm 64 Algoritm de calcul a nchiderii tranzitive
1: procedure InchidereTranzitiva(n, C; D)
2: for i 1, n do
3: for j 1, n do
4: if (x
i
= x
j
) (c
i,j
= +) then
5: d
0
i,j
1
6: else
7: d
0
i,j
0
8: end if
9: end for
10: end for
11: for k 1, n do
12: for i 1, n do
13: for j 1, n do
14: d
k
i,j
d
k1
i,j
(d
k1
i,k
d
k1
k,j
)
15: end for
16: end for
17: end for
18: end procedure
D
0
=
_
_
_
_
_
_
1 0 0 1 0
0 1 1 0 1
1 0 1 1 0
0 0 1 1 0
0 0 1 0 1
_
_
_
_
_
_
D
1
=
_
_
_
_
_
_
1 0 0 1 0
0 1 1 0 1
1 0 1 1 0
0 0 1 1 0
0 0 1 0 1
_
_
_
_
_
_
D
2
=
_
_
_
_
_
_
1 0 0 1 0
0 1 1 0 1
1 0 1 1 0
0 0 1 1 0
0 0 1 0 1
_
_
_
_
_
_
D
3
=
_
_
_
_
_
_
1 0 0 1 0
1 1 1 1 1
1 0 1 1 0
1 0 1 1 0
1 0 1 1 1
_
_
_
_
_
_
D
4
=
_
_
_
_
_
_
1 0 1 1 0
1 1 1 1 1
1 0 1 1 0
1 0 1 1 0
1 0 1 1 1
_
_
_
_
_
_
D
5
=
_
_
_
_
_
_
1 0 1 1 0
1 1 1 1 1
1 0 1 1 0
1 0 1 1 0
1 0 1 1 1
_
_
_
_
_
_
8.3 Exercit ii
1. Cutii ndimensionale Sa consideram o cutie ndimensionala, data prin lungimea ecarei
laturi (o cutie bidimensionala este un dreptunghi, o cutie tridimensionala este un par-
alelipiped, etc.).
Problema cere analizarea unui grup de k cutii ndimensionale: trebuie gasita o secvent a
de lungime maxima (b
1
, b
2
, . . .) din grupul de k cutii astfel ncat ecare cutie b
i
sa poata
introdusa n cutia b
i+1
.
Cutia D = (d
1
, d
2
, . . . , d
n
) poate introdusa n cutia E = (e
1
, e
2
, . . . , e
n
) numai daca
laturile cutiei D pot combinate n asa fel cu laturile cutiei E, astfel ncat lungimea
170
ecarei laturi a cutiei D sa nu depaseasca lungimea laturii corespunzatoare din cutia
E. De exemplu cutia (2, 6) poate introdusa n cutia (7, 3).
Cutiile egale pot 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)
2. Suntet i angajat la o companie ce asigura servicii de transport marfuri en-gross. Client ii
au magazine n orase din toata t ara si ei sunt aprovizionat i din depozitele locale ale
companiei.
Pentru o aprovizionare optima, compania vrea sa investeascan construirea unui depozit
central din care sa se aprovizioneze toate zonele; va deci necesar ca acest depozit sa
e plasat ntr-unul din orase n asa fel ncat timpul total de livrare din acest depozit
n toate orasele sa e 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 ecare data calea cea mai rapida, iar n ecare oras depozitul local se aa
amplasat la intrare) si timpul de descarcare al camionului la destinat ie, 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 , de asemenea, drumuri
cu sens unic.
Pentru a simplica modelul, s-a stabilit pentru ecare oras o lista cu toate drumurile
ce pleaca din oras spre celelalte orase si cat timp ia parcurgerea ecarui drum.
(Concurs ACM Zona Pacicul de Sud)
3. Se da ret eaua hidrograca a unei t ari constituita din mult imea raurilor si auent ii lor.
Cele N rauri (2 n 1000) sunt numerotate de la 1 la N. Legatura dintre un rau v
si un auent al sau u este specicata prin perechea (u, v).
Pentru a se putea face estimari cu privire la potent ialul risc de inundai e pe cursul unui
rau trebuie sa se calculeze debitul ecarui rau n parte.
Debitul unui izvor se deneste ca ind cantitatea de apa ce trece prin sect iunea izvorului
n unitatea de timp. Debitul raului u la varsare va egal cu debitul izvorului raului u
plus suma debitelor auent ilor la varsare n raul u.
Se cere sa se realizeze un algoritm care sa calculeze debitul la varsare al ecarui rau.
4. 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 mutat numai n casut ele fara gauri.
(ACM, Eastern Europe, 1994)
5. Un traducator gaseste o carte scrisa cu litere latine, n care nsa ordinea literelor din
alfabet este schimbata. La sfarsitul cart ii se aa un index de cuvinte complet si necon-
tradictoriu. Se cere sa se determine ordinea literelor din noul alfabet.
(ONI, 1991)
6. Profesorul Heif realizeaza niste experimente cu o specie de albine din America de Sud
pe care lea descoperit n timpul unei expedit ii n jungla Amazonului. Mierea produsa
171
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 nmult esc n
captivitate. Profesorul Hei crede ca pozit iile larvelor (albine lucratoare, regina) din
fagure depind de condit iile de mediu, care sunt diferite n laborator fat a de padurea
tropicala.
Ca un prim pas pentru a-si verica teoria, profesorul Hei doreste sa calculeze diferent a
dintre pozit iile larvelor. Pentru aceasta el masoara distant a dintre celulele fagurelui
n care sunt plasate larvele. El a numerotat celulele aleg and n mod arbitrat una si
numerotando cu 1, apoi celelalte celule ramase sunt numerotate circular, n sensul
acelor de ceas, ca n gura.
__ __ __ __
__/ \__/ \__/ \__/ \__
__/ \__/ \__/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\__/ \__/ \__/
\__/ \__/ \__/ \__/ \__/
\__/ \__/ \__/ \__/
De exemplu, doua larve pozit ionate n celulele 19 si 30 se aa la distant a de 5 celule.
Unul din drumurile minime ce unesc cele doua celule trece prin celulele 19-7-6-5-15-30.
Sa se scrie un algoritm ce calculeaza distant a minima dintre oricare doua celule.
Datele de intrare constau din doua numere naturale u si v (u, v 10.000) reprezentand
numarul celulelor ntre care se doreste sa se determine distant a minima.
(ACM Final, 1999)
7.
In ecare noapte un print intra n subsolurile unui castel unde locuieste o print esa.
Drumul catre print esa este ca un labirint. Sarcina print ului este sa se grabeasca si sa
gaseasca drumul prin labirint catre print esa, 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 destinat ie.
(a) gasit i lungimea celui mai scurt drum din labirint din locul n care print ul intra n
labirint pana n locul n care traieste print esa;
172
(b) gasit i 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 peret i spre E, S, V si N codicat i 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 peret i n toate direct iile (codul < 15).
Lungimea celui mai scurt drum se deneste ca ind numarul de camere dintre sursa si
destinat ie inclusiv.
Datele de intrare constau din M linii ce cont in ecare N numere ntregi reprezentand
codicarea peret ilor din ecare camera, urmate se coordonatele print ului si ale print esei.
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
avem rezultatul 26 si 12.
(ACM Bucuresti, 1999)
173
Capitolul 9
Fluxuri n ret ele de transport
Problema studierii uxului n ret ele de transport si are originea n analiza unor probleme de
transport [108]. Un graf orientat poate modela procesul de transport dintre un producator si
un consumator prin intermediul unei ret ele de transport. Ceea ce se trimite pe un drum nu
poate depasi capacitatea sa de transport. La destinat ie nu poate sa ajunga o cantitate mai
mare decat cea care a fost realizata de catre producator.
In jurul nostru exista foarte multe ret ele cum ar ret eaua electrica, ret eaua de apa,
ret eaua de drumuri, ret eaua de legaturi telefonice, ce au devenit elemente indispensabile ale
modului de viat a actual. O ret ea de transport poate modela curgerea unui lichid ntr-o ret ea
de conducte, deplasarea curentului prin ret ele electrice, transportul de marfuri de-a lungul
unei ret ele de drumuri, etc.
Doua dintre lucrarile considerate a avea un caracter de pionierat n acest domeniu si
bazeaza studiul de caz pe ret eaua feroviara existenta n fosta U.R.S.S. [108]. Asa cum oberva
si autorii lui [63] o abordare a acestei probleme folosea algoritmi avand la baza programarea
liniara, mai exact metoda simplex [33], nsa algoritmii dezvoltat i independent de aceasta
metoda au condus la rezultate mai bune n ceea ce priveste complexitatea timp.
Prima formulare a problemei determinarii uxului maximntr-o ret ea de transport a fost
facuta de T. E. Harris n [70]:
Consider a rail network connecting two cities by way of a number of intermediate
cities, where each link of the network has a number assigned to it representing its
capacity. Assuming a steady state condition, nd a maximal ow from one given
city to the other.
1
Exista doua probleme celebre care s-a demonstrat matematic a duale una alteia: prob-
lema uxului maxim ntr-o ret ea de transport si problema determinarii taieturii de capacitate
minima.
9.1 Ret ea de transport. Flux. Taietura
Denit ia 9.1 Se numeste ret ea de transport o secvent a < G, c, s, t > ce prezinta urmatoarele
proprietat i:
1
Luat i n considerare o ret ea de cale ferata ce leaga doua orase prin intermediul unui num ar de orase
intermediare, unde ecare legatura are atribuit un numar ce reprezinta capacitatea acesteia. Presupun and
ca sistemul se aa ntr-o stare de echilibru, se cere sa se determine un ux maxim de la un oras specicat la
altul.
174
1. G = (V, E) este un graf orientat, unde ecarui arc (u, v) E i este asociata o valoare
pozitiva denumita capacitate, c(u, v) 0;
2. exista doua noduri speciale, s, t V (s - sursa, t - destinat ie);
3. u V \ {s, t}, nodul u se gaseste pe cel put in un drum de la sursa la destinat ie.
Observat ia 9.1 1. Daca (u, v) / E vom considera ca valoarea capacitat ii arcului este 0 (c(u, v) =
0).
2. Graful este conex si |E| |V | 1.
Denit ia 9.2 Pentru o ret ea de transport < G, c, s, t > denim uxul asociat ret elei de
transport drept o funct ie f : V V R ce satisface urmatoarele condit ii:
1. u, v V , avem 0 f(u, v) c(u, v) (restrict ie de capacitate);
2. u, v V , avem f(u, v) = f(v, u) (antisimetrie);
3. u V \ {s, t} avem
vV
f(u, v) = 0 (conservarea uxului).
Denit ia 9.3 Se spune ca arcul (u, v) este saturat daca f(u, v) = c(u, v).
Daca f(u, v) > 0 se spune ca uxul paraseste nodul u, iar daca pentru arcul (u, v) avem
f(u, v) < 0 (echivalent cu f(v, u) > 0) spunem ca uxul intra n nodul u.
Observat ia 9.2 f(u, u) = 0, u V .
Observat ia 9.3 Pentru doua submult imi oarecare A, B (A, B V ), se deneste f(A, B)
astfel:
f(A, B) =
uA
vB
f(u, v).
Condit ia de conservare a uxului ntr-un nod poate descrisa astfel: f(v, V ) = 0.
Propozit ia 9.4 1. A V , f(A, A) = 0;
2. A, B, C V avem:
f(A B, C) = f(A, C) +f(B, C) f(A B, C)
f(A, B C) = f(A, B) +f(A, C) f(A, B C); (9.1)
3. A, B, C V , B A
f(A\ B, C) = f(A, C) f(B, C)
f(C, A\ B) = f(C, A) f(C, B);
(9.2)
4. A, B V , avem f(A, B) = f(B, A);
5. A V , f(A, ) = f(, A) = 0.
175
Denim valoarea uxului f astfel: |f| =
vV
f(s, v) . Drept urmare, valoarea uxului
n exces ce paraseste nodul sursa s este:
|f| =
vV,f(s,v)>0
f(s, v)
uV,f(u,s)>0
f(u, s) (9.3)
Se poate demonstra ca |f| =
uV,f(u,t)>0
f(u, t)
vV,f(t,v)>0
f(t, v) (valoarea uxului
f este diferent a dintre suma uxurilor ce intra n destinat ie minus suma uxurilor ce parasesc
destinat ia).
Din regula de conservare a uxului rezulta ca:
vV,f(u,v)>0
f(u, v) =
vV,f(v,u)>0
f(v, u), u V \ {s, t} (9.4)
Cu alte cuvinte, suma uxurilor ce intra ntr-un nod u este egala cu suma uxurilor ce
parasesc acelasi nod u.
uV
(
vV,f(u,v)>0
f(u, v)
wV,f(w,u)>0
f(w, u)) = 0. (9.5)
Pe de alta parte
uV
(
vV,f(u,v)>0
f(u, v)
wV,f(w,u)>0
f(w, u)) = (
vV,f(s,v)>0
f(s, v)
uV,f(u,s)>0
f(u, s)) +
(
vV,f(t,v)>0
f(t, v)
uV,f(u,t)>0
f(u, t))
= |f| + (
vV,f(t,v)>0
f(t, v)
uV,f(u,t)>0
f(u, t))
Astfel rezulta ca
|f| =
uV,f(u,t)>0
f(u, t)
vV,f(t,v)>0
f(t, v). (9.6)
Denit ia 9.4 O < s, t > - taietura este o pereche de mult imi disjuncte (A, B) din V (AB =
, A B = V , A, B V ) cu proprietatea ca s A si t B.
Capacitatea unei < s, t > - taieturi este suma capacitat ilor arcelor ce au o extremitate n
mult imea A si cealalta extremitate n mult imea B:
c(A, B) =
uA,vB
c(u, v) (9.7)
Se deneste uxul de-a lungul taieturii (A, B) ca ind:
f(A, B) =
uA
vV
f(u, v) (9.8)
|f|
not
= f({s}, V \ {s}) =
vV
f(s, v) (uxul net ce paraseste nodul sursa) (9.9)
Problema uxului maxim presupune determinarea unui ux de valoare maxima de la s la
t pentru ret eaua de transport < G, c, s, t > [47], [48].
176
Lema 9.5 Pentru < G, c, s, t > ret ea de transport, (A, B) o < s, t >-taietura si f un ux
n ret eaua de transport, avem |f| = f(A, B) (valoarea uxului n ret eaua de transport este
egala cu valoarea uxului de-a lungul taieturii).
Demonstrat ie: Sa ne reamintim ca daca (A, B) este o < s, t >-taietura avem A B =
, A B = V . Atunci din propozit ia 9.4 obt inem
f(A, V ) = f(A, A B) = f(A, A) +f(A, B) f(A, A B) = f(A, B) (9.10)
deoarece f(A, A) = 0 si f(A, A B) = f(A, ) = 0. Astfel pentru o < s, t >-taietura avem
f(A, B) = f(A, V ). Consideram identitatea A = (A\ {s}) {s}.
f(A, V ) = f(A\ {s}, V ) +f({s}, V ) f(, V ) = f(A\ {s}, V ) +f({s}, V ) (9.11)
Pe de alta parte avem identitatea:
f(A\ {s}, V ) = 0. (9.12)
Din ultimele trei relat ii concluzionam faptul ca:
f(A, B)
9.10
= f(A, V )
9.11
= f({s}, V ) +f(A\ {s}, V )
9.12
= f({s}, V ) = |f|. (9.13)
Corolarul 9.6 Pentru un ux f oarecare n ret eaua de transport < G, c, s, t > avem inegal-
itatea: |f| c(A, B)
Demonstrat ie: Din Lema 9.5 avem:
|f|
L9.5
= f(A, B)
def
=
uA
vB
f(u, v)
def
uA
vB
c(u, v)
def
= c(A, B). (9.14)
Taietura minima reprezinta modalitatea cea mai ecienta / simpla de a ntrerupe uxul
(curgerea) de la s la t.
Problema taieturii minime se refera la a determina o < s, t > - taietura a carei capacitate
sa e maxima.
Exemplul 9.7
In gura 9.1 este prezentat un exemplu de ret ea de transport si un ux f.
Ca o ilustrare a celor discutate vom calcula suma uxurilor pentru nodul 1:
vV
f(1, v) = f(1, s) +f(1, 2) +f(1, 3) +f(1, 4) +f(1, t)
= f(s, 1) + (1) + 12 + 0 + 0
= 11 1 + 12 = 0
(9.15)
f(1, 2) = 0 1 = 1
Sau
vV
f(4, v) = f(4, s) +f(4, 1) +f(4, 2) +f(4, 3) +f(4, t)
= 0 + 0 + (f(2, 4)) + 7 + 4
= 11 + 7 + 4 = 0
(9.16)
177
Fig. 9.1: Un exemplu de ret ea de transport
9.2 Graf rezidual. Drum de ameliorare. Flux maxim
taietura minima
Denit ia 9.5 Denim capacitatea rezidual a a unui arc (u, v) apart inand grafului G ast-
fel:
c
R
(u, v) = c(u, v) f(u, v) . (9.17)
Denit ia 9.6 Graful rezidual[47] asociat cu graful G = (V, E) si capacitatea c, este graful
G
R
= (V, E
R
) si capacitate c
R
, unde E
R
se deneste astfel: E
R
= {(u, v) V V |c
R
(u, v) >
0}.
Observat ia 9.8
Intre doua noduri u si v din graful G vom avea cel mult doua arce n graful
rezidual G
R
:
1. daca (u, v) E si f(u, v) < c(u, v) atunci exista arcul (u, v) n graful rezidual ((u, v) E
R
)
si c
R
(u, v) = c(u, v) f(u, v) (c
R
(u, v) > 0);
2. daca (u, v) E si f(u, v) > 0 atunci exista arcul (v, u) n graful rezidual ((v, u) E
R
) si
c
R
(v, u) = f(u, v).
Altfel spus, n graful rezidual G
R
pot sa apara arce noi, care nu existau n graful init ial
G. Un alt element ce merita subliniat se refera la faptul ca daca ntre doua noduri nu exista
nici un arc n graful init ial G, atunci nu va exista nici un arc ntre cele doua noduri nici n
graful rezidual G
R
. Prin urmare, numarul total de arce din graful rezidual G
R
este cel mult
de doua ori mai mare decat numarul arcelor din graful G.
Lema 9.9 Fie < G, c, s, t > o ret ea de transport si f un ux n aceasta ret ea de transport.
Daca f
R
este un ux n ret eaua de transport < G
R
, c
R
, s, t > (G
R
este graful rezidual asociat
cu G), atunci f +f
R
este un ux n < G, c, s, t >, iar |f +f
R
| = |f| +|f
R
|.
Demonstrat ie: Pentru a arata ca f +f
R
este un ux n ret eaua de transport < G, c, s, t >
trebuie sa vericam daca condit iile din denit ia 9.2 sunt ndeplinite:
1. restrict ie de capacitate
Avem f
R
(u, v) c
R
(u, v), u, v V .
(f +f
R
)(u, v) = f(u, v) +f
R
(u, v) f(u, v) +c
R
(u, v)
= f(u, v) + (c(u, v) f(u, v)) = c(u, v)(9.18)
178
2. antisimetrie
(f +f
R
)(u, v) = f(u, v) +f
R
(u, v) = f(v, u) f
R
(v, u)
= (f(v, u) +f
R
(v, u)) = (f +f
R
)(v, u) (9.19)
3. conservarea uxului
vV
(f +f
R
)(u, v) =
vV
(f(u, v) +f
R
(u, v))
=
vV
f(u, v) +
vV
f
R
(u, v) = 0 + 0 = 0 (9.20)
Sa demonstram ca |f +f
R
| = |f| +|f
R
|:
|f +f
R
| =
vV
(f +f
R
)(s, v) =
vV
(f(s, v) +f
R
(s, v))
=
vV
f(s, v) +
vV
f
R
(s, v) = |f| +|f
R
|. (9.21)
not
= f+f
R
, unde f
R
este un ux n ret eaua de transport < G
R
, c
R
, s, t >
a grafului rezidual, atunci f
| = |f| +|f
R
| > |f|.
Demonstrat ia acestui corolar rezulta imediat din lema 9.9.
Lema 9.11 Fie f un ux n < G, c, s, t > si G
R
graful rezidual asociat cu G. Atunci avem:
1. funct ia f
R
este un ux maxim n < G
R
, c
R
, s, t > daca f + f
R
este un ux maxim n <
G, c, s, t >;
2. valoarea funct iei este aditiva: |f +f
R
| = |f| +|f
R
|, |f f
R
| = |f| |f
R
|;
3. daca f este un ux oarecare si f
| |f|.
Denit ia 9.7 Fiind data o ret ea de transport < G, c, s, t > si un ux f n aceasta ret ea,
atunci o cale de crestere d (drum de ameliorare, drum de augmentare) este un drum de la
s la t n graful rezidual G
R
.
Numim capacitate rezidual a a lui d, cantitatea maxima a uxului ce poate transportat
de-a lungul drumului de ameliorare d:
c
R
(d) = min{c
R
(u, v)|(u, v) arc pe drumul d} (9.22)
Observat ia 9.12 Unui drum de ameliorare n graful rezidual G
R
i corespunde un lant n
graful G.
Denit ia 9.8 Un arc (u, v) se numeste arc critic daca face parte dintr-un drum de ame-
liorare d si daca capacitatea sa reziduala este egala cu capacitatea reziduala a drumului de
ameliorare (c(u, v) = c
R
(d)).
Teorema 9.13 (ux maxim - taietura minima) [46], [48], [49] Fie o ret ea de transport
< G, c, s, t > si f un ux n aceasta ret ea. Urmatoarele armat ii sunt echivalente:
179
1. f este un ux maxim n ret eaua de transport;
2. nu exista nici o cale de crestere (drum de ameliorare) n ret eaua de transport < G, c, s, t >;
3. exista o < s, t >-taietura a lui G pentru care |f| = c(A, B) (exista o < s, t >-taietura pentru
care valoarea uxului maxim este egala cu capacitatea taieturii).
Demonstrat ie: Vom demonstra aceasta teorema abordand urmatoarea serie de implicat ii:
1) 2) 3) 1).
1) 2)
Sa presupunem prin reducere la absurd ca exista un drum de ameliorare n ret eaua de trans-
port < G, c, s, t > pentru un ux maxim |f|.
Daca exista un drum de ameliorare atunci exista un ux nenul f
p
n ret eaua < G
R
, c
R
, s, t >.
Fie f
= f + f
p
. Din corolarul 9.10 obt inem ca f
este un ux
n ret eaua de transport < G, c, s, t > si |f
| > |f|.
Am determinat astfel un ux f
In tabelul 9.1 [127] sunt prezentat i principalii algoritmi pentru determinarea uxului maxim
ntr-o ret ea de transport, anul n care au aparut precum si complexitatea lor. Astfel acestia se
pot compara din punct de vedere al complexitat ii teoretice si se poate alege varianta potrivita
rezolvarii unor probleme concrete.
9.3.1 Algoritmul Ford-Fulkerson (varianta)
Prezentam n continuare o varianta a algoritmului Ford-Fulkerson:
Pas 1. Se considera un ux init ial f
0
. De obicei se alege drept ux init ial uxul nul (f
0
(u, v) =
0, u, v V, (u, v) E).
Pas 2. Se construieste un ux maxim:
Pas 2.1. Se marcheaza nodul s cu semnul +.
182
Table 9.1: Principalii algoritmi pentru determinarea uxului maxim ntr-o ret ea de transport
Autor(i) Anul aparit iei Complexitate
Ford & Fulkerson 1956 [48] O(V EU)
Dinic 1970 [39] O(V
2
E)
Edmonds & Karp 1972 [42] O(V E
2
)
Karzanov 1974 [80] O(V
3
)
Cherkassky 1976 [29] O(V
2
E)
Malhotra, Kumar si Maheswari 1978 [94] O(V
3
)
Galil 1980 [58] O(V
5
3
E
2
3
)
Galil & Naamad 1980 [59] O(EV (log V )
2
)
Sleator 1980 [113] O(EV log V )
Shiloach & Vishkin 1982 [111] O(V
2
log V )
Sleator & Tarjan 1983 [114] O(EV log V )
Tarjan 1984 [120] O(V
3
)
Goldberg 1985 [61] O(V
3
)
Goldberg & Tarjan 1986 [62] O(EV log (
V
2
E
)
)
Ahuja & Orlin 1989 [4] O(V E +V
2
log U)
Pas 2.2. Pentru un varf u etichetat avem cazurile:
i. daca (u, v) E, v este neetichetat iar arcul (u, v) este nesaturat atunci se
eticheteaza v cu [+u];
ii. daca (v, u) E, v este neetichetat iar uxul corespunzator arcului (v, u)
este nenul atunci se eticheteaza v cu [u];
Se repeta pasul 2.2 atata timp cat este posibil (cat timp nu a fost etichetat nodul
t sau se mai pot eticheta noduri noi).
Pas 2.3. Daca t nu a fost etichetat, atunci uxul construit pana la momentul actual este
maxim si procesul se opreste.
Pas 2.4. Daca t a fost etichetat, atunci se identica un drum d de la s la t. Notam cu U
+
mult imea arcelor (u, v) d unde v a fost etichetat cu + si U
0
= min
(u,v)U
+
{c(u, v) f(u, v)}
1
= min
(u,v)U
{f(u, v)}
= min{
0
,
1
} (9.23)
Valoarea uxului de-a lungul ecarui arc din d, devine f +
.
Exemplul 9.17 Fie ret eaua de transport din gura 9.4 n care a fost considerat la nceput
uxul nul.
183
Fig. 9.4: Flux ntr-o ret ea de transport
Se eticheteaza varful s cu [+]. Deoarece arcele (s, 1) si (s, 2) sunt nesaturate, varfurile 1
si 2 pot etichetate cu [+s].
In continuare, deoarece arcele (1, 3) si (2, 4) sunt nesaturate vor
etichetate nodurile 3 si 4 cu [+1] respectiv [+2]. Arcul (3, 2) are uxul nul si astfel nodul
3 nu poate etichetat cu [2]. Analog, se eticheteaza nodul t cu [+3] (vezi gura 9.4).
Deoarece nodul destinat ie t a fost etichetat, se poate trage concluzia ca uxul curent nu
este maxim.
Trebuie subliniat faptul ca etichetarea nodurilor nu este unica, aceasta depinzand de modul
de parcurgerea al nodurilor grafului. Spre exemplu, nodul 3 poate etichetat si cu [+4]
ajungandu-se la el prin intermediul nodului 4 si al arcului (4, 3).
Pornind de la nodul t si mergand napoi pe secvent a de etichetare, se identica un drum
de la s la t, d
1
= (s, 1, 3, t).
= min
(u,v)U
+
{c(u, v) f(u, v)}
= min{c(s, 1) f(s, 1), c(1, 3) f(1, 3), c(3, t) f(3, t)} = min{16, 12, 20} = 12
Pe acest drum avem arcele (s, 1), (1, 3), (3, t) U
+
. Prin urmare valoarea uxului de-a
lungul acestor arce devine (vezi gura 9.5):
f(s, 1) = f(s, 1) +
In gura 9.5 poate urmarita o noua etichetare a nodurilor grafului. Se marcheaza nodul
s cu [+]. Din nodul s prin intermediul arcelor nesaturate (s, 1) si (s, 2) pot etichetate
nodurile 1 si 2 cu [+s]. Arcul (1, 3) nu mai poate utilizat n procesul de etichetare deoarece
este saturat (f(1, 3) = c(1, 3) = 12).
Din nodul 1 mai avem arcul (1, 2) nsa nodul 2 a fost etichetat cu [+s]. Prin intermediul
arcului (2, 4) vom eticheta nodul 4 cu [+2]. Arcul (3, 2) nu poate utilizat pentru etichetarea
nodului 3 cu [2] deoarece uxul corespunzator are n continuare valoarea 0.
Din nodul 4 se eticheteaza nodurile 3 si t cu [+4] prin intermediul arcelor (4, 3) si (4, t).
Pornind de la nodul t si mergand napoi pe secvent a de etichetare, se identica un drum
de la s la t, d
2
= (s, 2, 4, t).
= min
(u,v)U
+
{c(u, v) f(u, v)}
= min{c(s, 2) f(s, 2), c(2, 4) f(2, 4), c(4, t) f(4, t)} = min{13, 14, 4} = 4
184
Fig. 9.5: Flux ntr-o ret ea de transport
Arcele ntalnite de-a lungul drumului d
2
, (s, 1), (1, 3), (3, t), apart in mult imii U
+
. Prin
urmare valoarea uxului de-a lungul acestor arce devine (vezi gura 9.6):
f(s, 2) = f(s, 2) +
= 0 + 4 = 4, f(2, 4) = 0 + 4 = 4, f(4, t) = 0 + 4 = 4
Fig. 9.6: Flux ntr-o ret ea de transport
Cea de-a treia etapa de etichetare este ilustrata n gura 9.6: nodul s se eticheteaza cu
[+]. Urmeaza apoi nodurile 1 si 2 cu [+s] ecare, si nodul 4 cu [+2]. De aici mai departe
nodul 3 se eticheteaza cu [+4] iar t cu [+3].
Pentru aceasta etichetare se identica drumul d
3
de la s la t: d
3
= (s, 2, 4, 3, t).
= min
(u,v)U
+
{c(u, v) f(u, v)}
= min{c(s, 2) f(s, 2), c(2, 4) f(2, 4), c(4, 3) f(4, 3), c(3, t) f(3, t)}
= min{13 4, 14 4, 7 0, 20 12} = 7
Toate arcele din care este alcatuit drumul d
3
apart in mult mii U
+
. Valoarea uxul calculat
pentru ecare arc n parte este (vezi gura 9.7):
f(s, 2) = f(s, 2)+
In aceasta situat ie procesul iterativ de etichetare se opreste conform Pasului 2.3 din algo-
ritmul anterior.
Fluxul maxim determinat n ret eaua de transport este:
|f| = f(s, 1) +f(s, 2) = 11 + 12 = 23 sau |f| = f(3, t) +f(4, t) = 19 + 4 = 23.
9.4 Exercit ii
1. Aplicadu-se algoritmul Ford-Fulkerson sa se determine uxul maximn ret elele de trans-
port din gura 9.1. Nodul 0 este nodul sursa iar nodul 5 respectiv nodul 6 este nodul
destinat ie.
186
0
1
2
3
4
5
10
4
10
9
6
10
10
8
2
0
1
2
3
4
5
5
4
3
3
1
9
9
8
2
0
1
2
4
5 5
10
9
10
8
15
5 10
4
15
3
15
4
4
6
30
10
15
a) b)
c) d)
0
1
6
4
3
5
3
1
1
3
1
3
6
2
4
2
2
9
Fig. 9.8: Alte exemple de ret ele de transport
187
Appendix A
Probleme. Algoritmi. Complexitate
Vom considera o problema computat ionala ca ind o aplicat ie
P : I O (A.1)
unde I reprezinta mult imea intrarilor problemei, mult imea instant elor problemei, iar O reprezinta
mult imea iesirilor, raspunsurilor, solut iilor. Aceasta problema P pentru ecare intrare i I
ofera o iesire P(i) O.
Denit ia A.1 Daca O = {da, nu} atunci P se va numi problem a de decizie, P(i)
{da, nu} va raspunsul la ntrebarea pusa de P, iar forma uzuala de prezentare a pro-ble-
mei va :
P Intrare: i I.
Intrebare: . . ..
Exemplul A.1 Sa consideram urmatoarea problema:
NrCompus Intrare: n N, n 2.
. Lungimea
cuvantului w se va nota cu |w|. Orice mult ime de cuvinte peste , adica orice submult ime a
lui
si A(w) = da}.
Denit ia A.7 Limbajul L
.
2. Se verica usor ca daca P P atunci si
\ P P.
A.0.1 Vericare n timp polinomial
Un algoritm de vericare este o funct ie
A :
si y
|y
n
timp polinomial.
Din denit iile de mai sus ar fost mai normal sa folosim notat ia VP (vericabil poli-
nomial ). Sintagma Nedeterminist Polinomial se justica daca am considera algoritmi nede-
terministi n care, dupa ecare pas, este posibil sa se execute unul dintre pasii specicat i
dintro mult ime nita de pasi succesori. Un astfel de algoritm accepta un cuvant de intrare
daca este posibila o execut ie care sa conduca la rezultatul da. Se poate arata ca aceasta
denit ie a acceptarii nedeterministe este echivalenta cu cea de vericare data mai sus, n
care certicatul este utilizat pentru efectuarea alegerilor corecte ale passilor urmatori ai unei
execut ii a algoritmului nedeterminist.
NP noteaza clasa problemelor de decizie pentru care raspunsurile armative au certicate
care pot folosite pentru a demonstra succint (n timp polinomial) corectitudinea lor.
190
Intuitiv, NP este clasa tuturor problemelor de decizie pentru care se poate verica un
raspuns pozitiv (da) rapid daca ni se da o solut ie.
De exemplu, pentru problema MaxCut D un raspuns armativ are drept certicat o
partit ie (S
, T
si cealalta n T
\ 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.
. Spunem ca L
1
se reduce polinomial la L
2
, si notam
aceasta prin L
1
L
2
, daca exista f :
: w L
1
daca si numai daca f(w) L
2
.
f se numeste funct ie de reducere si algoritmul polinomial F ce calculeaza f se numeste
algoritm de reducere polinomiala.
Observat ia A.5 1. Daca L P si L
L, atunci L
P.
Fie A un algoritm polinomial care decide L si F un algoritm de reducere polinomiala a lui L
la L. Atunci, A
. (x
, A
(x) = da
A(F(x)) = da F(x) L x L
; A
NP are loc
L
L.
Denit ia A.15 Limbajul L
NP are loc P
P.
Denit ia A.17 Problema de decizie P este NPcompleta daca
P NP si P este NPdicila.
Folosind observat ia anterioara si faptul ca P NP rezulta urmatoarea teorema.
Teorema A.6 Daca P o problema oarecare NPcompleta satisface P P atunci P = NP.
Rezulta ca problemele NPcomplete formeaza o submult ime a lui NP ce cont ine cele mai
dicile probleme. Daca gasim un algoritm polinomial pentru una dintre ele, putem construi
un algoritm polinomial pentru oricare alta problema din NP. Din pacate, desi nu exista o
demonstrat ie, oamenii cred ca, de fapt, aceste probleme nu admit algoritmi polinomiali de
rezolvare.
Se obisnuieste sa se spuna ca o problema de optimizare este NPdicila, daca problema
de decizie asociata este asa. Pentru a consistent i fat a de denit iile precedente, vom spune
ca o problema oarecare P (nu neaparat de decizie) este NPdicila daca existent a unui
algoritm polinomial pentru P implica P = NP.
Sa prezentam un exemplu pentru clasa de complexitate a problemelor NPcomplete.
Primul om care a demonstrat existent a unei astfel de probleme este Cook, care n 1971 a
aratat ca SAT NP, unde
SAT Intrare: U = {u
1
, . . . , u
n
} o mult ime nita de variabile booleene.
C = C
1
C
2
. . . C
m
o formula n forma conjunctiva peste U:
C
i
= v
i
1
v
i
2
. . . v
i
k
i
, i = 1, m unde
i
j
{1, . . . , n} astfel ncat v
i
j
= u
sau v
i
j
= u
la L.
4. Se demonstreaza ca F este algoritm de reducere.
5. Se arata ca F este algoritm polinomial.
192
Appendix B
Metoda Backtracking
Metoda Backtracking este una dintre cele mai cunoscute metode de elaborare a algoritmilor.
Metoda se aplica numai n situat ia n care nu exista nici o alta modalitate de rezolvare a
problemei propuse deoarece timpul de execut ie depinde exponent ial de dimensiunea datelor
de intrare. Se utilizeaza o structura de tip stiva iar metoda poate implementata atat
iterativ cat si recursiv. Metoda se aplica acelor probleme n care solut ia se poate reprezenta
sub forma unui vector x = (x
1
, x
2
, . . . , x
n
) A
1
A
2
. . . A
n
, unde mult imile A
i
, (i = 1, n)
sunt nite si nevide (|A
i
| = n
i
> 0).
In plus, pentru ecare problema n parte este necesar ca
solut ia x
1
, x
2
, . . . , x
n
sa satisfaca anumite condit ii interne (x
1
, x
2
, . . . , x
n
) (vezi algoritmul
67).
Algoritm 67 Algoritm Backtracking (varianta generala)
1: procedure Backtracking(n, A)
2: k 1
3: x
k
prima valoare din afara domeniului de valori
4: while (k > 0) do
5: gasit false
6: while ( valori neverificate pentru x
k
) (gasit = true) do
7: x
k
urmatoarea valoare neverificata
8: if (
k
(x
1
, x
2
, . . . , x
k
) = true) then
9: gasit true
10: end if
11: end while
12: if (gasit = true) then
13: if (k = n) then
14: call Afis Solutie(x
1
, . . . , x
n
)
15: else
16: k k + 1
17: x
k
prima valoare din afara domeniului de valori
18: end if
19: else
20: k k 1
21: end if
22: end while
23: end procedure
De exemplu, sa consideram doua mult imi, S
1
= {a, b, c} si S
2
= {m, n}. Se doreste sa se
determine perechile (x
1
, x
2
), cu x
1
A
1
si x
2
A
2
, cu proprietatea ca daca x
1
este a sau b
193
atunci x
2
nu poate n. Rezolvarea conduce la urmatoarele variantele: (a, m), (b, m), (c, m), (c, n).
Din cele sase solut ii posibile (a, m), (a, n), (b, m), (b, n), (c, m), (c, n), numai acestea patru
ndeplinesc condit iile interne.
Mult imea A = A
1
A
2
... A
n
se numeste spat iul solut iilor posibile, iar elementele
x A ce satisfac condit iile interne se numesc solut ii rezultat. Ne propunem determinarea
tuturor solut iilor rezultat, eventual pentru a alege dintre ele pe aceea ce minimizeaza sau
maximizeaza o funct ie obiectiv.
Metoda Backtracking evita generarea tuturor solut iilor posibile (toate elementele pro-
dusului cartezian A
1
A
2
... A
n
). Construirea unei solut ii se face n mai mult i pasi,
elementele vectorului X primind valori pe rand: elementului x
k
A
k
, k = 1, n, i se atribuie
o valoare numai dupa ce au fost atribuite valori pentru componentele x
1
A
1
, x
2
A
2
,
. . ., x
k1
A
k1
. Metoda trece la atribuirea unei valori pentru x
k+1
A
k+1
doar daca
x
k
mpreuna cu x
1
, x
2
, . . . , x
k1
verica condit iile de continuare, notate cu
k
(x
1
, x
2
, . . . , x
k
).
Daca aceste condit ii
k
(x
1
, x
2
, . . . , x
k
) sunt ndeplinite se trece la cautarea unei valori pentru
elementul x
k+1
A
k+1
al solut iei.
Nendeplinirea condit iilor are urmatoarea semnicat ie: pentru orice alegere x
k+1
A
k+1
,
. . . , x
n
A
n
, nu vom putea ajunge la o solut ie rezultat n care condit iile interne sa e
ndeplinite.
In aceasta situat ie va trebui sancercam o alta alegere pentru x
k
, acest lucru ind
posibil doar daca nu am epuizat toate valorile disponibile din mult imea A
k
. Daca mult imea
A
k
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 x
k1
A
k1
. Micsorarea valorii curente
a variabilei k cu o unitate da numele metodei si semnica faptul ca atunci cand nu putem
avansa, vom urmari napoi secvent a curenta din solut ie. Faptul ca valorile v
1
, v
2
, . . . , v
k1
,
ale componentelor x
1
, x
2
, . . . , x
k1
, satisfac condit iile de continuare, nu este sucient pentru
a garanta obt inerea unei solut ii ale carei prime k 1 componente coincid cu aceste valori.
O alegere buna a condit iilor de continuare conduce la reducerea numarului de calcule,
ind de dorit ca aceste condit ii de continuare sa e nu numai necesare, dar si suciente
pentru obt inerea unei solut ii. Condit iile interne devin chiar condit ii de continuare pentru
k = n. Orice vector solut ie se obt ine progresiv ncepand cu prima componenta si deplasandu-
ne catre ultima, cu eventuale reveniri asupra valorilor atribuite anterior. Anumite valori sunt
consumate n urma unor atribuiri sau ncercari de atribuire esuate din cauza nendeplinirii
condit iilor de continuare.
Exista doua variante fat a de metoda standard:
solut iile pot avea numar variabil de componente;
dintre solut ii se alege cea care minimizeaza sau maximizeaza o funct ie cost sau o funct ie
obiectiv data.
Exemplul B.1 Enunt ul problemei: Se cere sa se genereze permutari de n elemente.
Rezolvare: Spat iul solut iilor este A =
n
i=1
A
i
, A
i
= {1, 2, . . . , n}, |A
i
| = n. Solut ia va
obt inuta n vectorul x = (x
1
, x
2
, . . . , x
n
) A
1
A
2
. . . A
n
.
La pasul k se ncearca atribuirea unui alt element din mult imea A
k
lui x
k
. Vom trece la
pasul k + 1 doar daca sunt ndeplinite condit iile de continuare: (x
1
, x
2
, . . . , x
k
) nu cont ine
elemente care se repeta. Deoarece aceasta vericare se face la ecare pas, este sucient sa
vericam daca valoarea elementului x
k
nu se regaseste printre valorile x
1
, x
2
, . . . , x
k1
(vezi
funct ia CanContinue() din algoritmul 68).
Exemplul B.2 Fie n = 4. Atunci A
i
va compus din elementele A
i
= {1, 2, 3, 4}.
Pentru k = 1, x
1
va primi valuarea 1 (vezi linia 15): 1
194
Pentru k = 2, se verica x
2
= 1, nsa nu se respecta condit iile de continuare (linia 16).
Astfel se trece la urmatoarea valoare, x
2
= x
1
+1 = 2 (linia 15). Pentru aceasta congurat ie
condit iile de continuare sunt ndeplinite, si se trece la pasul urmator, k = k + 1 = 3 (linia
24): 1 2
Se verica valorile 1, 2 si 3 pentru x
3
(linia 16), dintre care, numai ultima satisface
condit iile de continuare: 1 2 3
Ajunsi la ultimul pas, k = 4, se verica valorile 1, 2, 3 si 4 pentru x
4
.
In urma vericarii
condit iilor de continuare pentru x
4
= 4, se urmareste trecerea la pasul k = k+1 = 5. Deoarece
suntem la ultimul element al vectorului x (linia 21), se obt ine prima solut ie, ce se si aseaza:
1 2 3 4
Mai departe, nu mai exista valori netestate pentru x
4
(linia 14), si revenim la nivelul
anterior k = k1 = 3 (linia 28). Aici urmatoarea valoare din domeniul de valori nevericata
este x
3
= 4 (linia 15). Solut ia part iala respecta condit iile de continuare (linia 16), astfel ncat
se trece la nivelul urmator (linia 24), k = k + 1 = 4: 1 2 4
Pentru x
4
sunt vericate valorile, 1, 2, 3, 4 (liniile 1516), dintre acestea numai valoarea
3, conducand la o solut ie nala (linia 21): 1 2 4 3 Algoritmul se continua n acelasi
mod pana se obt in toate solut iile:
_
_
(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 68 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);
}
195
Algoritm 68 Algoritm pentru generare permutari (varianta backtracking)
Input: n - numarul de elemente din mult imea init iala
1: function CanContinue(A, k)
2: for i 1, k 1 do
3: if (a
i
= a
k
) then
4: return false
5: end if
6: end for
7: return true
8: end function
9: procedure Permutari(n)
10: k 1
11: x
k
0
12: while (k > 0) do
13: gasit false
14: while (x
k
+ 1 n) (gasit = true) do
15: x
k
x
k
+ 1
16: if ((CanContinue(X, k)) = true) then
17: gasit true
18: end if
19: end while
20: if (gasit = true) then
21: if (k = n) then
22: Output {x
1
, . . . , x
n
}
23: else
24: k k + 1
25: x
k
0
26: end if
27: else
28: k k 1
29: end if
30: end while
31: end procedure
/**
* 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.
*/
196
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();
}
Exemplul B.3 Enunt ul problemei: Sa se genereze combinari de n luate cate m (0
197
m n).
Rezolvare: Spat iul solut iilor este A =
m
i=1
A
i
, A
i
= {1, 2, . . . , n}, |A
i
| = n. Solut ia va
obt inuta n vectorul x = (x
1
, x
2
, . . . , x
m
) A
1
A
2
. . . A
m
(vezi algoritmul 69).
La pasul k se ncearca atribuirea unui alt element din mult imea A
k
lui x
k
. Condit ia de
continuare se refera la proprietatea ca elementele vectorului solut ie trebuie sa respecte relat ia
x
1
< x
2
< . . . < x
k
. Acest lucru se obt ine foarte usor prin urmatorul procedeu: la trecerea la
pasul k +1, variabila x
k+1
va primi, ca valoare init iala, valoarea curenta a variabilei x
k
(vezi
linia 15 din algorimul 69).
Pentru n = 5 si m = 3 avem: A =
3
i=1
A
i
= A
1
A
2
A
3
, unde A
1
= A
2
= A
3
=
{1, 2, 3, 4, 5}. Solut iile obt inute sunt:
_
_
(1, 2, 3)
(1, 2, 4)
(1, 2, 5)
(1, 3, 4)
(1, 3, 5)
(1, 4, 5)
(2, 3, 4)
(2, 3, 5)
(2, 4, 5)
(3, 4, 5)
Algoritm 69 Algoritm pentru generare combinari (varianta backtracking)
1: procedure Combinari(n, m)
2: k 1
3: x
k
0
4: while (k > 0) do
5: gasit false
6: while (x
k
+ 1 n) (gasit = true) do
7: x
k
x
k
+ 1
8: gasit true
9: end while
10: if (gasit = true) then
11: if (k = m) then
12: Output {x
1
, . . . , x
m
}
13: else
14: k k + 1
15: x
k
x
k1
16: end if
17: else
18: k k 1
19: end if
20: end while
21: end procedure
_
A - matricea de adiacent a/de vecinatate a t arilor
n - numarul de t ari
m - numarul de culori disponibile
1: function CanContinue(A, X, k)
2: for j 1, k 1 do
3: if (x
j
= x
k
) (a
j,k
= 1) then
4: return false
5: end if
6: end for
7: return true
8: end function
9: procedure ColorBacktracking(A, n, m)
10: k 1
11: x
k
0
12: while (k > 0) do
13: gasit false
14: while (x
k
+ 1 m) (gasit = true) do
15: x
k
x
k
+ 1
16: gasit CanContinue(A, X, k)
17: end while
18: if (gasit = true) then
19: if (k = n) then
20: call Afis Solutie(x
1
, . . . , x
n
)
21: else
22: k k + 1
23: x
k
0
24: end if
25: else
26: k k 1
27: end if
28: end while
29: end procedure
Exemplul B.6 Enunt ul problemei: Se da o suma s si n tipuri de monede avand valorile
a
1
, a
2
, . . . , a
n
lei. Realizat i un algoritm care sa determine o modalitate de plata a sumei s
utilizand un numar minim de monede.
Rezolvare:
#include <stdio.h>
#define FALSE 0
#define TRUE 1
#define NN 100
#define MAX 10000
int suma_plata; //suma ce trebuie platita
int n; //numarul de monezi
202
int limit[NN]; //limit[i] numarul maxim de monede de tipul val[i]
int val[NN]; //val[i] valoarea unei monede
int taken[NN]; //cate monede de valoare val[i] au fost luate
int minim; //numarul minim de monede cu care se poate face plata
int keep[NN]; //solutia optima pina la momentul curent
/**
* Se citesc datele de intrare: suma de plata, numarul de
* tipuri de monezi si valoarea fiecarui tip.
*/
void readInput(void) {
int i;
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 sunt satisfacute conditiile de continuare:
* suma partiala sa nu depaseasca valoarea totala de platit.
* @param k - pasul curent
*/
int canContinue(int k) {
int i, suma_tmp;
suma_tmp = 0;
for (i = 0; i <= k; i++)
suma_tmp += val[i] * taken[i];
if ((k == n - 1 && suma_tmp == suma_plata)
|| (k != n - 1 && suma_tmp <= suma_plata))
return TRUE;
else
return FALSE;
}
/**
* Se pastreaza solutia actuala (taken) numai daca este
* mai buna decat cea anterioara (keep).
*/
void final(void) {
int i;
int monezi = 0;
//se numara cate monede sunt in solutie
for (i = 0; i < n; monezi += taken[i], i++);
if (monezi < minim) {
minim = monezi;
203
for (i = 0; i < n; i++)
keep[i] = taken[i];
}
}
/**
* Se afiseaza solutia optima sau mesajul Nu avem solutie.
*/
void print(void) {
int i, first = 0;
if (minim != MAX) { //verificam daca am obtinut o solutie
printf("%d =", suma_plata);
for (i = 0; i < n; i++)
if (keep[i] != 0) {
printf((first == 1) ? " + " : " ");
printf("%d X %d", keep[i], val[i]);
first = 1;
}
printf("\n");
}
else
printf("Nu avem solutie! \n");
}
/**
* Implementarea metodei backtracking.
*/
void run(void) {
int i;
int gata; /* are valoarea TRUE cand sunt verificate conditiile
de continuare */
minim = MAX;
i = 0; taken[i] = -1;
while (i >= 0) {
gata = FALSE;
while ((taken[i] + 1 <= limit[i]) && !gata) {
/* cat timp exista elementul urmator si nu sunt verificate conditiile
de continuare */
taken[i]++; //treci la elementul urmator
//verifica conditiile de continuare
if (canContinue(i))
gata = TRUE;
//sau: gata = canContinue(i);
}
if (gata) //daca s-au verificat conditiile de continuare
if (i == n-1)
//daca suntem la ultimul element pastram solutia
final();
else { //altfel trecem la elementul urmator
i++; taken[i] = -1;
204
}
else
//altfel revenim la un element anterior si alegem o alta valoare
i--;
}
}
void main(void) {
readInput();
run();
print();
}
205
Bibliograe
[1] A. Aho, J. Hopcroft, J. Ullman, On nding lowest common ancestors in trees, Proc. 5th
ACM Symp. Theory of Computing (STOC), pp. 253-265, 1973.
[2] A. V. Aho, J. E. Hopcroft, J. D. Ulmann, Data Structures and algorithms, Addison-
Wesley, 1983.
[3] A. V. Aho, J. D. Ulmann, Foundation of Computer Science, Computer Science Press,
1992.
[4] R. K. Ahuja, J. B. Orlin, A Fast and Simple Algorithm for the Maximum Flow Problem,
Operations Research, vol. 37(5), pp. 748759, 1989.
[5] R. Ahuja, T. Magnanti, J. Orlin, Network Flows, Prentice Hall, 1993.
[6] R. Andonie, I. Garbacea, Algoritmi fundamentali, o perspectiva C++, Editura Libris,
1995.
[7] C. Aragon, R. Seidel, Randomized Search Trees, in Proc. of 30th IEEE Symposium on
Foundations of Computer Science, pp. 540546, 1989.
[8] A. Atanasiu, Concursuri de 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 eciency and data structures, Proceedings of
the eighth SIGCSE technical symposium on Computer science education, pp. 2127,
1977.
[11] S. Baase, Computer algorithms. Introduction to Design and Analysis, Addison-Wesley,
1992.
[12] P. Bazavan, Elemente de Teoria Algoritmilor, Editura Sitech, Craiova, 2007.
[13] R. Bellman, On a routing problem, Quarterly Applied Mathematics, XVI(1), pp. 87-90,
1958.
[14] M. A. Bender, M. Farach-Colton, The LCA problem revisited, Proceedings of the 4th
Latin American Symposium on Theoretical Informatics, LNCS, vol. 1776, Springer-
Verlag, pp. 88-94, 2000.
[15] J. Bentley, R. Sedgewick, Fast Algorithms for Sorting and Searching Strings, Proceedings
of the 8th Annual ACM-SIAM Symposium on Discrete Algorithms, 1997.
206
[16] J. Bentley, R. Sedgewick, Ternary Search Trees, Dr. Dobbs Journal, 1998.
[17] C. Bereanu, Algoritmica Grafurilor, Editura Sitech, Craiova, 2006.
[18] C. Berge, Graphes et hypergraphes, Dunod, Paris 1970.
[19] O. Berkman, D. Breslauer, Z. Galil, B. Schieber, si U. Vishkin, Highly parallelizable
problems, in Proceedings of the 21st Annual ACM Symposium on Theory of Computing,
pp. 309-319, 1989.
[20] O. Berkman, U. Vishkin, Recursive Star-Tree Parallel Data Structure, SIAM Journal on
Computing, vol.22(2), pp.221-242, 1993.
[21] O. Boruvka, O jistem problemu minimalnm (About a certain minimal problem), Acta
Societ. Scient. Natur. Moravicae, 3, pp. 37-58, 1926.
[22] R. P. Brent, An improved Monte Carlo factorization algorithm, BIT Numerical Mathe-
matics, Springer, vol. 20(2), pp. 176184, 1980.
[23] D. D. Burdescu, Analiza complexitat ii algoritmilor, Editura Albastra, ClujNapoca,
1998.
[24] D. D. Burdescu, M. Brezovan, M. Cosulschi, Structuri de date arborescente cu aplicat ii
n Pascal si C, Reprograa Universitat ii din Craiova, 2000.
[25] D. D. Burdescu, Liste, arbori, grafuri, Editura Sitech, Craiova, 2005.
[26] B. Chazelle, A minimum spanning tree algorithm with inverse-Ackerman type complexity,
J. ACM, 47, pp. 1028-1047, 2000.
[27] D. Cherition, R. E. Tarjan, Finding minimum spanning trees, SIAM Journal on Com-
puting, vol. 5, pp. 724-741, 1976.
[28] J. Cheriyan, K. Mehlhorn, Algorithms for dense graphs and networks on the random
access computer, Algorithmica, vol. 15, pp. 521-549, 1996.
[29] B. V. Cherkassky, Algorithm for construction of maximal ows in networks with com-
plexity of O(V
2