Cuprins 1 Intro ducere ˆın algoritmi 1.1 Limba jul pseudo co d . . . . . . . 1.2 Exemple . . . . . . . . . . . . . . 1.3 Elemente de analiza algoritmilor . 1.3. 1. 3.11 Me Metoda toda su subs bsti titut tut ¸iei . . . . 1.3.22 Sc 1.3. Schim himbare bareaa de de varia ariabil bil˘ a˘ . 1.3. 1. 3.33 Me Metoda toda it iter erati ativ˘ v˘ a . . . . . 1.3.4 Teorema master . . . . . . 1.44 Ex 1. Exer erci cit¸tii . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
2 Grafuri. Grafuri neorientate 2.1 No¸iuni ¸tiuni de baz˘a . . . . . . . . . . . . . . . . . . . . . . . . 2.22 Op 2. Oper erat at¸ii p e grafuri . . . . . . . . . . . . . . . . . . . . . . 2.3 Mo duri de reprezentare . . . . . . . . . . . . . . . . . . . . 2.4 Parcurgerea grafurilor . . . . . . . . . . . . . . . . . . . . 2.4.1 Parc Parcurgerea urgerea ˆın l˘ lat a˘¸time (B (BFS FS-B -Bre read adth th Fi Firs rstt Sea Searc rch) h) . 2.4. 2. 4.22 Parc arcurg urger erea ea D (D - Depth) . . . . . . . . . . . . . 2.4.3 Parc Parcurgerea urgerea ˆın adˆ adˆ a ncime anci me (D (DFS FS-D -Dep epth th Fi Firs rstt Sea Searc rch) h) 2.5 Comp onente conexe . . . . . . . . . . . . . . . . . . . . . . 2.66 Mu 2. Much chie ie cri critic tic˘ a˘ . . . . . . . . . . . . . . . . . . . . . . . . . 2.77 Ex 2. Exer erci cit¸tii . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Grafuri euleriene ¸si hamiltoniene 3.1 Grafuri Euleriene . . . . . . . . . . . . . . . . . . . . . . 3.1. 3. 1.11 Alg lgor oriitm pe pen ntr tru u de dete term rmin inar area ea un unui ui ci cicl clu u eu euller eriian 3.1.2 Algoritmul lui Rosenstiehl . . . . . . . . . . . . . 3.1.3 Algoritmul lui Fleury . . . . . . . . . . . . . . . . 3.2 Grafuri Hamiltoniene . . . . . . . . . . . . . . . . . . . . 3.2.1 Problema comis–voiaj ajoorului . . . . . . . . . . . . 4 Arb ori. Arbori binari 4.1 Arb ori binari . . . . . . . . . . 4.1.1 Mod odu uri de reprezentare . 4.1.2 Metod odee de parcurgere . . 4.22 Ar 4. Arbori bori bi binar narii de c˘ autare . . . . 4.33 Ex 4. Exer erci cit¸tii . . . . . . . . . . . . .
. . . . .
. . . . .
. . . . .
1
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . . . . . . .
. . . . .
. . . . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . .
3 4 12 16 20 21 21 22 25
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
28 28 33 36 40 40 44 45 49 50 55
. . . . . .
. . . . . .
58 58 59 62 66 68 70
. . . . . . . . . .
80 81 82 84 90 98
. . . . . . . .
. . . . .
. . . . . . . .
. . . . . . . . . . .
. . . . . . . .
. . . . . .
5 Arb ori oarecare 5.1 Mod odu uri de reprezentare . . . . . . . . . . . . . . . . . . 5.2 Meto de de parcurgere . . . . . . . . . . . . . . . . . . . 5.3 Arbo borri de acope perrire de cost minim . . . . . . . . . . . . 5.3.1 Algoritmul lui Boruvka . . . . . . . . . . . . . . 5.3.2 Algoritmul lui Prim . . . . . . . . . . . . . . . . 5.3.33 Stru 5.3. Structur cturii de date pen pentru tru mult ¸imi disjuncte . . 5.3.4 Algoritmul lui Kruskal . . . . . . . . . . . . . . 5.44 Ex 5. Exer erci cit¸tii . . . . . . . . . . . . . . . . . . . . . . . . . . 6 Grafuri orientate 6.1 No¸iuni ¸tiuni de baz˘a . . . . . . . . . 6.2 Parcurgerea grafurilor . . . . . 6.33 Sor 6. Sortar tarea ea topo topolo logi gic˘ c˘ a . . . . . . . 6.4 Compo pon nente tare conexe . . . . 6.4.1 Algoritmul lui Kosaraj aju u 6.4.2 Algoritmul lui Tarjan . . 6.4.3 Algoritmul lui Gabo bow w . 6.55 Ex 6. Exer erci cit¸tii . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
7 Heap-uri 7.11 He 7. Heap ap-u -uri ri bi binar naree (Min -heapuri -heapuri sau Max -heapuri) . . . . . . . . . . 7.1.1 Inserarea unui element . . . . . . . . . . . . . . . . . . . . 7.1. 7. 1.22 S¸ter ¸ terge gere reaa elementu tullui minim . . . . . . . . . . . . . . . . . 7.1.3 Crearea unui heap . . . . . . . . . . . . . . . . . . . . . . 7.2 Ordonare prin metod odaa HeapSort . . . . . . . . . . . . . . . . . . . 7.33 Ap 7. Apli lica catie tie - Co Coad˘ ad˘ a cu prioritate . . . . . . . . . . . . . . . . . . . . 7.44 Ex 7. Exer erci cit¸tii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Dist Distan ant¸te ˆın grafuri 8.11 Dru 8. Drum mul min minim im de de la un vˆ varf aˆrf la celelalte vˆarfuri . 8.1.1 Algoritmul lui Moo oorre . . . . . . . . . . . 8.1.2 Algoritmul lui Dijkstra . . . . . . . . . . 8.2 Drum Drumuri uri minime ˆınt ıntre re toate perec p erechile hile de vˆ arfuri 8.2. 8. 2.11 Algor oriitm tmu ul lui Roy-Fl Flooyd-War arsshal alll . . . 8.33 Ex 8. Exer erci cit¸tii . . . . . . . . . . . . . . . . . . . . . . A Meto da Backtracking
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
102 . . . . 1 02 . . . . 1 05 . . . . 1 10 . . . . 1 13 . . . . 1 13 . . . . 116 . . . . 1 21 . . . . 1 24
. . . . . . . .
127 . 1 27 . 1 29 . 1 32 . 1 36 . 1 37 . 1 39 . 1 42 . 1 45
. . . . . . . .
. . . . . . . .
. . . . . . . .
149 . . . . . . . 149 . . . . . . . 1 51 . . . . . . . 15 1522 . . . . . . . 1 53 . . . . . . . 1 55 . . . . . . . 158 . . . . . . . 1 61 . . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
165 . 1 66 . 1 66 . 1 67 . 1 75 . 17 1755 . 1 82 186
2
Capitolul 1 Intro Intr oduc ducer ere e ˆın al algor gorit itmi mi Definit¸ia ¸ia 1.1 Algoritmul Algoritmul constit onstituie uie o repr eprezent ezentar aree finit˘ a a unei metode de calcul ce permite rezolvarea unei anumite probleme. Se poate spune c˘ a un algoritm reprezint˘ a o secvent ¸˘ a finit˘ a de operat ¸ii, ordonat˘ a ¸si si complet defini definit˘ t˘ a, care, pornind de la datele de intrare, produce rezultate. Termenul de algoritm ˆ ˆıi este atribui atribuitt matemati matematician cianului ului persan Abu Ja‘far Mohamm Mohammed ed ibn Musa al-Khowarizmi (sec. VIII-IX), care a scris o carte ca rte de matematic˘a cunoscut˘ a ˆın tr traaducere latin˘ a sub titlul de ”Algorithmi de numero indorum ”, ”, iar apoi ca ”Liber algorithmi ”, unde termenul de ”algorithm ” provine de la ”al-Khowarizmi ”, ”, ceea c eea ce litera literall ˆınseamn˘ ınseam n˘ a ”din orasul Khowarizm ”. ”. Matematicienii din Evul Mediu ˆınt ınt¸elegeau ¸elegeau prin algoritm o regul˘ a (sau o mult¸ime ¸ime de reguli) pe baza c˘ areia se efectuau calcule aritmet areia aritmetice: ice: de exempl exemplu u ˆın secol secolul ul al XVI-lea, algoritmii se foloseau la ˆınmult¸iri ¸iri sau ˆınj ınjum˘ um˘at˘ at˘at a¸iri ¸t iri de numere. Fiecare propozit¸ie ¸ie ce face parte din descrierea unui algoritm este, de fapt, o comand˘ a ce trebuie executat˘ a de cinev cineva, a, acesta putˆ and fi o persoan˘ and a sau o ma¸sin˘ sin˘a de calcul. calcul. De altfel, altfel, un algoritm poate fi descris cu ajutorul oric˘ arui lim arui limbaj, baj, de la lim limbajul bajul natural ¸si si pˆ an˘a la an˘ limbajul de asamblare al unui calculator. Denumim limbaj algoritmic un un limbaj al c˘arui arui scop este acela de a descrie algoritmi. Algoritmul specific˘ a succesiuni posibile de transform˘ ari ale datelor. Un tip de date poate ari poate fi caracterizat printr-o mult¸ime ¸ime de valori ce reprezint˘ a domeniul tipului de date ¸si si o mult ¸ime de operat¸ii ¸ii definite peste acest domeniu. Tipuri Tipurile le de date pot fi organizate ˆın ın urm˘ atoarele categorii:
reg g , tipul real ) - valorile sunt unit˘at 1. tipuri de date elementare (de exemplu tipul ˆıntre a¸i ¸ti atomice de informat¸ie; ¸ie; 2. tipuri de date structurate (de exemplu tipul tablou , tipul ˆınregi ın regist strare rare ) - valorile sunt structuri relativ simple rezultate ˆın urma combinat ¸iei unor valori elementare; 3. t tipuri ipuri de date d ate structurate s tructurate de nivel ˆınalt ınalt (de exemplu stiva ) - se pot descrie independent de limbaj iar valorile au o structur˘ a mai complex˘a. a. Un algoritm trebuie s˘a posede urm˘ atoarele tr˘ atoarele as˘ aturi car caracteristice acteristice : 1. claritate - la fiecare pas trebuie s˘ a specifice operat¸ia ¸ia pe care urmeaz˘ a s˘a o efectueze algoritmul asupra datelor de intrare; 2. corectitudine - rezultatele trebuie s˘ a fie corecte;
3
3. generalitate - algoritmul trebuie s˘ a ofere solut¸ia ¸ia nu numai pentru o singur˘ a problem˘ a ci pentr pentru u o ˆıntreag ıntreag˘ a˘ clas˘a de probleme; 4. finitudine - algoritmul trebuie s˘ a se termine ˆıntr-un timp finit; 5. eficient utilizat at num numai ai ˆın situat ¸ia ˆın care resurs resursele ele de calcul ¸˘ a - un algoritm poate fi utiliz necesare necesa re acest acestuia uia sunt ˆın ın cantit˘ c antit˘ a¸i at ¸ti rezon rezonabile, abile, ¸si si nu dep˘ d ep˘ a¸sesc a¸ sesc cu mult po posibilit˘ sibilit˘at a¸ile ¸t ile calculatoarelor la un moment dat. Un program repr reprezint˘ ezint˘a impleme implementarea ntarea unui algor a lgoritm itm ˆıntr-un ıntr-un limba limbajj de progr p rogramare amare.. Studiul algoritmilor cuprinde mai multe aspecte: activit vitatea atea de conc concepere epere a un unui ui algo algorit ritm m are ¸si si un cara caracte cterr crea creativ tiv,, din • elaborare - acti aceast˘a cauz˘ a nefiind posibil˘a deprinderea numai pe cale mecanic˘ a. Pe a. Pent ntru ru a facilita facilita obt¸inerea ¸inerea unei solut¸ii ¸ii la o problem˘a concret˘ a se recomand˘ a folosi folosirea rea tehnici tehnicilor lor general generalee de elaborare a algoritmilor la care se adaug˘ a ˆın mod m od hot˘arˆ arˆator ator intuit¸ia ¸ia programatorului;
• exprimare - implementarea unui algoritm intr-un limbaj de programare se poate face
utilizˆand and mai multe stiluri: programare programare structurat˘ s tructurat˘ a , progr programare amare orientat˘ a pe obiecte etc.;
• validare - - verificarea corectitudinii algoritmului prin metode formale; ¸ei unui algoritm pentru a-i • analiz˘ a - stabilirea unor criterii pentru evaluarea eficient¸ei putea compa compara ra ¸si si clasifi clasifica. ca.
Un model mo del de reprezen reprezentare tare al memoriei unei ma¸sini sini de calcul este acela al unei structuri liniaree compus liniar compus˘ a˘ din celule, fiecare celul˘ a fiind identificat˘ a printr-o adres˘ a ¸si si pu putˆ tˆand and p˘ astra o astra valoare corespunz˘ atoare unui anumit tip de dat˘ atoare a. Accesul la celule este facilitat de variabile. a. a se O variabil˘ se cara caracteri cterizeaz˘ zeaz˘a prin:
• un identificator - un nume ce refer˘a variabila; ¸ie de memorie; • o adres˘ a - - desemneaz˘a o locat¸ie a sociat˘ a. a. • un tip de date - - descrie tipul valorilor memorate ˆının celula de memorie asociat˘ 1.1 1. 1
Lim Li mbaju bajull ps pseu eudoc docod od
Limbajul natural nu permite o descriere suficient Limbajul suficient de riguroas˘ a a algoritm algoritmilor, ilor, de aceea, pentru reprezentarea acestora se folosesc alte modalit˘ a¸i at ¸t i de descriere precum:
• scheme logice ; • limbajul pseudocod .
ˆIn continuare vom prezenta pricipalele construct ¸ii din cadrul limbajului pseudocod.
Int ntrr˘ ari/ie¸siri Citirea datelor de intrare se poate realiza prin intermediul enunt ¸ului Input: 1: Input lista variabile Afi¸sarea sarea rezultatelor este reprezentat˘ a cu ajutorul instruct¸iunii ¸iunii Output: 1: Output lista de valori
{
}
{
}
4
Instruct¸iunea de atribuire Este instruct¸iunea cel mai des utilizat˘ a ˆıntr-un algoritm ¸si realizeaz˘ a ˆınc˘arcarea unei variabile (locat¸ii de memorie) cu o anumit˘a valoare. Are urm˘ atoarea sintax˘ a: 1: < variabila >
←< expresie >
unde < expresie > este o expresie aritmetic˘ a sau logic˘ a. Se evalueaz˘ a expresia < expresie > iar rezultatul se atribuie variabilei < variabila >, memorˆandu-se ˆın locat¸ia de memorie asociat˘ a. Aceast˘ a variabil˘a trebuie s˘ a fie de acela¸si tip de dat˘ a cu expresia sau un tip de dat˘ a care s˘ a includ˘a ¸si tipul expresiei. O expresie este constituit˘ a din operanzi ¸si operatori . Operanzii pot fi variabile ¸si valori constante, iar operatorii pot fi:
• operatori aritmetici - + (adunare), − (sc˘adere), ∗ (ˆınmult¸ire), / (ˆımp˘art¸ire), ˆ (ridicare la putere), div (cˆatul ˆımp˘art¸irii ˆıntregi), mod (restul ˆımp˘ art¸irii ˆıntregi);
• operatori relat ¸ionali - = (egal), = (diferit), < (strict mai mic), ≤ (mai mic sau egal), > (strict mai mare), ≥ (mai mare sau egal); • operatori logici - OR sau ∨ (disjunct¸ie), AN D sau ∧ (conjunct¸ie), N OT sau ¬ (negat¸ie). Cea mai simpl˘a expresie este format˘ a dintr-o variabil˘ a sau o constant˘ a (operand). Expresiile mai complicate se obt¸in din operat¸ii efectuate ˆıntre variabile ¸si constante. La scrierea expresiilor trebuie s˘ a se ¸tin˘a cont de faptul c˘ a, ˆın momentul evalu˘ arii lor, ˆın primul rˆand se vor evalua expresiile din paranteze, iar operat¸iile se execut˘a ˆın ordinea determinat˘ a de priorit˘a¸tile lor.
Enunt¸uri de ramificare 1: if
then 2:
1: if (a mod 2 = 0) then 2: Output ’Numarul este par’ 3: else 4: Output ’Numarul este impar’ 5: end if
[
{ {
3: else 4:
]
}
}
5: end if
Enunt¸ul if . . . then . . . else evalueaz˘ a mai ˆıntˆai expresia boolean˘ a pentru a determina unul din cele dou˘a drumuri pe care le poate lua execut¸ia algoritmului. Ea poate include opt¸ional o clauz˘ a else. Dac˘a se evalueaz˘ a la valoarea de adev˘ar true, atunci se execut˘a atoarea instruct¸iune dup˘ a if. Dac˘a ¸si se continu˘a cu urm˘ are valoarea de adev˘ ar false, atunci se execut˘ a . ¸si sunt instruct¸iuni compuse ce pot s˘ a cont¸in˘a, la rˆandul lor, o alt˘a instruct¸iune if.
Exemplul 1.1 Un algoritm simplu este cel ce rezolv˘ a ecuat ¸ia de gradul I , ax+b = 0, a, b (algoritmul 1). Acesta se bazeaz˘ a pe rezolvarea matematic˘ a a ecuat ¸iei de gradul I :
∈R
1. dac˘ a a = 0, atunci ecuat ¸ia devine 0 x + b = 0. Pentru cazul ˆın care b = 0 avem acut˘ a pentru nicio valoare a lui x R sau 0 x + b = 0, egalitate ce nu poate fi satisf˘ C. Spunem, ˆ ın acest caz, c˘ a ecuat ¸ia este incompatibil˘ a. Dac˘ a b = 0, avem 0 x + 0 = 0, relat R. Spunem c˘ ¸ia fiind adev˘ arat˘ a pentru orice valoare a lui x a ecuat ¸ia este compatibil nedeterminat˘ a.
·
·
∈
5
∈ ·
2. dac˘ a a = 0, ecuat ¸ia are o singur˘ a solut ¸ie, x1 =
b a
− ∈ R.
Algoritm 1 Algoritm pentru rezolvarea ecuat¸iei de gradul I 1: Input a, b 2: if (a = 0) then 3: if (b = 0) then 4: Output ’Ecuatie compatibil nedeterminata’ 5: else 6: Output ’Ecuatie incompatibila’ end if 7: 8: else b 9: x a 10: Output ’Solutia este:’, x 11: end if
{ }
{ {
←−
{
}
}
}
Avˆ and acest algoritm drept model s˘ a se realizeze un algoritm pentru rezolvarea ecuat ¸iei 2 generale de gradul al II-lea, ax + bx + c = 0, unde a,b,c R.
∈
Enunt¸uri repetitive Enunt¸urile repetitive permit descrierea unor prelucr˘ ari ce trebuie efectuate ˆın mod repetat, ˆın funct¸ie de pozit¸ia condit¸iei de continuare existˆ and dou˘ a variante de structuri repetitive: structura repetitiv˘ a condit ¸ionat˘ a anterior ¸si structura repetitiv˘ a condit ¸ionat˘ a posterior . Structura repetitiv˘ a condit¸ionat˘a anterior while (sau instruct¸iune de ciclare cu test init¸ial) are urm˘ atoarea sintax˘ a: 1: sum 0 2: i 1 3: while (i n) do 4: sum sum + i 5: i i + 1 6: end while
←
1: while do 2: 3: end while
←
←
≤ ←
a , se execut˘a ; dac˘a este adevarat˘ booleana > este fals˘ a chiar la prima evaluare, atunci nu ajunge s˘a fie realizat˘a niciodat˘ a. Acest comportament este opus celui corespunz˘ ator structurii repetitive condit¸ionat˘ a posterior repeat, unde este executat˘ a cel put¸in o dat˘a. (Dac˘ a o expresie are valoarea de adev˘ ar true spunem atunci c˘ a expresia este adev˘ arat˘ a ; dac˘a o expresie are valoarea de adev˘ ar false spunem atunci c˘ a expresia nu este adev˘ arat˘ a - este fals˘a). Exemplul 1.2 Vom realiza un algoritm pentru calculul cˆ atului ¸si restului ˆımp˘ art ¸irii a dou˘ a numere ˆıntregi, prin sc˘ aderi succesive (a se vedea algoritmul 2). Mai ˆıntˆ ai, se init ¸ializeaz˘ a cˆ atul cu valoarea zero (linia 3) ¸si restul cu valoarea deˆımp˘ art ¸itului (linia 4). Apoi, atˆ ata timp cˆ at restul este mai mare decˆ at valoarea ˆımp˘ art ¸itorului (liniile 5 8), vom incrementa cˆ atul cu o unitate, ¸si decrementa restul cu valoarea ˆımp˘ art ¸itorului. La final, sunt afi¸sate valorile cˆ atului ¸si restului. Un caz particular de structur˘ a repetitiv˘ a condit¸ionat˘ a anterior este for , utilizat˘a ˆın cazul ˆın care o instruct¸iune sau un grup de instruct¸iuni trebuie s˘ a se repete de 0 sau mai multe ¸urile repetitive bazate ori - num˘arul de repetit¸ii fiind cunoscut ˆınainte de ˆınceperea sa. Enunt 6
Algoritm 2 Algoritm pentru calculul ˆımp˘ art¸irii ˆıntregi
{ } ∧ ← ← ≥ ← − ← { } {
1: Input a, b 2: if ((a > 0) (b > 0)) then 3: cat 0 4: rest a 5: while (rest b) do 6: rest rest b 7: cat cat + 1 8: end while 9: Output cat, rest 10: else 11: Output ’Numerele trebuie sa fie strict pozitive!’ 12: end if
}
pe instruct ¸iunile while ¸si repeat sunt mult mai potrivite ˆın cazul ˆın care condit ¸ia de terminare trebuie reevaluat˘ a ˆın timpul cicl˘ arii (atunci cˆ and num˘ arul de repetit ¸ii nu este cunoscut apriori). Instruct¸iunea for are urm˘atoarea sintax˘ a:
← ←
, , 1: sum 0 2: for i 1, n do Step < p > do 3: sum sum + i
1: for
←
2: 3: end for
4: end for
←
Comportamentul enunt¸ului repetitiv for se descrie cel mai bine prin comparat ¸ie cu enunt¸ul repetitiv while. Astfel secvent¸a urm˘ atoare de limbaj pseudocod
←
1: for v e 1 , e2 STEP p do 2: < instructiune > 3: end for
este echivalent˘ a cu secvent¸a ce cont¸ine enunt¸ul repetitiv while: 1: v e 1 2: while (v e 2 ) do 3: 4: v v + p 5: end while
←
≤
←
e1 reprezint˘a valoarea init¸ial˘ a, e2 limita final˘a, iar p pasul de lucru: la fiecare pas, valoarea variabilei v este incrementat˘ a cu valoarea variabilei p, valoarea ce poate fi atˆ at pozitiv˘ a cˆat ¸si negativ˘a . Dac˘a p 0 atunci v va lua valori ˆın ordine cresc˘atoare, iar dac˘ a p < 0 atunci v va primi valori ˆın ordine descresc˘atoare. ˆIn cazul ˆın care pasul de incrementare este 1, atunci el se poate omite din enunt ¸ul repetitiv a automat la fiecare repetit¸ie cu valoarea 1. Dac˘ a pasul for, variabila contor fiind incrementat˘ de incrementare p are valoarea 1 atunci avem urm˘ atoarele situat¸ii posibile pentru construct¸ia for:
≥
• Expresie1 > Expresie2: nu se execut˘a niciodat˘a; • Expresie1 = Expresie2: se execut˘a exact o dat˘a; • Expresie1 < Expresie2: se execut˘a de Expresie2 − Expresie1 +1 ori. Exemplul 1.3 Un caz destul de frecvent ˆıntˆ alnit ˆın practic˘ a este cel ˆın care se cere determinarea elementului de valoare maxim˘ a, respectiv minim˘ a dintr-un ¸sir de elemente. Acest 7
¸sir de elemente poate fi p˘ astrat ˆıntr-o structur˘ a de date elementar˘ a, cunoscut˘ a sub numele de vector. Un vector este un caz particular de matrice avˆ and o singur˘ a linie ¸si n coloane. Prezent˘ am ˆın continuare algoritmul pentru determinarea elementului de valoare minim˘ a ce apart ¸ine unui ¸sir de numere naturale (a se vedea algoritmul 3). Algoritm 3 Algoritm pentru calculul elementului de valoare minim˘a dintr-un ¸sir
{ ← ←
}
1: Input N, x1 , x2 , . . . , xN 2: min x 1 3: for i 2, N do 4: if (min > xi ) then min x i 5: 6: end if 7: end for 8: Output min
←
{
}
ˆ In acest algoritm se observ˘ a utilizarea enunt ¸ului repetitiv for , ˆıntˆ alnit, ˆın general, ˆın situat ¸iile ˆın care se cunoa¸ste apriori num˘ arul de pa¸si pe care trebuie s˘ a-l realizeze instruct ¸iunea repetitiv˘ a. Mai ˆıntˆ ai se init ¸ializeaz˘ a variabila min cu valoarea elementului x1 , presupunˆ and c˘ a eleˆ continuare mentul de valoare minim˘ a este primul element din cadrul vectorului X (linia 2). In vom compara valoarea variabilei min cu valoarea fiec˘ arui element din ¸sir (liniile 3–7): dac˘ a vom g˘ asi un element a c˘ arui valoare este mai mic˘ a decˆ at cea a minimului calculat pˆ an˘ a la momentul curent (linia 4), vom ret ¸ine noua valoare ˆın variabila min (linia 5). Structura repetitiv˘ a condit¸ionat˘a posterior repeat . . . until prezint˘ a urm˘atoarea sintax˘a:
←
1: sum 0 2: i 1 3: repeat sum sum + i 4: 5: i i + 1 6: until (i > n)
←
1: repeat 2: 3: until
←
←
Atˆ ata timp cˆat este fals˘a, se execut˘ a . Se observ˘ a faptul c˘ a instruct¸iunile dintre repeat ¸si until se vor executa cel put¸in o dat˘a.
Exemplul 1.4 Algoritmul 4, prezentat ˆın continuare, calculeaz˘ a suma primelor n numere naturale, S = 1 + 2 + 3 + . . . + n. Not˘ am cu S n suma primelor n numere naturale ( S n = 1 + 2 + 3 + . . . + n). Aceasta se poate calcula ˆıntr-o manier˘ a incremental˘ a astfel: S 1 S 2 S 3 S 4 S n
= = = = ... =
1 1 + 2 = S 1 + 2 (1 + 2) + 3 = S 2 + 3 (1 + 2 + 3) + 4 = S 3 + 4 (1 + 2 + . . . + n
8
− 1) + n = S
n−1 +
n
a) Algoritm 4 Algoritm pentru calculul sumei primelor n numere naturale (prima variant˘
{ }
1: Input N 2: S 0 3: i 1 4: repeat S S + i 5: 6: i i + 1 7: until (i > N ) 8: Output S
← ←
← ←
{ }
Observat ¸ia 1.5 until.
1. Algoritmul 4 utilizeaz˘ a enunt ¸ul repetitiv cu test final, repeat . . .
0 ¸si i 1 au rolul de a atribui valori init 2. Enunt ¸urile S ¸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 ˆınsum˘ ari succesive se va init ¸ializa ˆıntotdeauna cu valoarea 0, valoare ce nu va influent ¸a rezultatul final. O variabil˘ a ce p˘ astreaz˘ a rezultatul unei operat ¸ii (sum˘ a sau produs) se va init ¸ializa cu elementul neutru fat ¸˘ a de operat ¸ia respectiv˘ a.
←
←
3. Atribuirile i = i + 1 ¸si S = S + i (liniile 5 ¸si 6) sunt lipsite de sens din punct de ˆ a, atunci cˆ vedere algebric. Ins˘ and ne referim la un sistem de calcul electronic, aceste operat ¸ii se refer˘ a la valoarea curent˘ a ¸si la cea anterioar˘ a a unei variabile. De asemenea, trebuie s˘ a se fac˘ a o distinct ¸ie clar˘ a ˆıntre operat ¸ia de atribuire ¸si cea de egalitate: operat ¸ia de atribuire (’ ’) face ca valoarea curent˘ a a variabilei din stˆ anga operatorului de atribuire s˘ a fie init ¸ializat˘ a cu valoarea expresiei din dreapta acestuia (ˆın expresia ’ k i + 2 ’ variabila k prime¸ste valoarea rezultat˘ a ˆın urma evalu˘ arii expresiei ’ i + 2 ’), pe cˆ and operat ¸ia de egalitate verific˘ a dac˘ a valoarea elementului din stˆ anga operatorului ’ =’ (sau a expresiei aflate ˆın partea stˆ ang˘ a a operatorului) este egal˘ a cu valoarea expresiei din dreapta acestuia (de exemplu rezultatul evalu˘ arii expresiei k = i + 2 ) are valoarea de adev˘ ar true dac˘ a valoarea variabilei ’ k’ este egal˘ a cu valoarea rezultat˘ a ˆın urma evalu˘ arii expresiei ’ i + 2 ’ ¸si are valoarea de adev˘ ar false ˆın caz contrar).
←
←
Prin urmare, ˆın cazul expresiei i i + 1, valoarea curent˘ a a variabilei i devine valoarea anterioar˘ a a aceleia¸si variabile incrementat˘ a cu o unitate.
←
Suma primelor n numere naturale se constituie ˆın suma termenilor unei progresii aritmetice ce se poate calcula direct cu formula sumei, astfel 1 + 2 + . . . + n = n(n+1) . Avˆ and 2 ˆın vedere aceast˘ a identitate, algoritmul de calcul a sumei primelor n numere naturale se simplific˘ a foarte mult (a se vedea algoritmul 5). Algoritm 5 Algoritm pentru calculul sumei primelor n numere naturale (a doua variant˘ a)
{ } ← · { }
1: Input N 2: S n (n + 1)/2 3: Output S
9
Proceduri ¸si funct¸ii Procedurile sunt subrutine ale c˘ aror instruct¸iuni se execut˘ a ori de cˆate ori acestea sunt apelate prin numele lor. Apelarea procedurilor se face ˆın unitatea de program apelant˘ a prin numele procedurii, primit ˆın momentul definirii, urmat de lista parametrilor actuali. Aceast˘ a list˘a trebuie s˘ a corespund˘a ca num˘ar ¸si tip cu lista parametrilor formali, ˆın situat¸ia ˆın care exist˘a o list˘a de parametri formali ˆın antetul subrutinei. Definit ¸ia unei proceduri are urm˘ atoarea sintax˘ a: 1: procedure < nume>() 2: 3: end procedure
a) simbolizeaz˘ a o list˘a de identificatori (parametri) lista parametri formali (opt¸ional˘ ce permite transferul datelor ˆıntre subrutina apelant˘ a ¸si subrutina apelat˘ a . Ace¸sti parametri se specific˘ a prin nume (identificator) urmat, eventual, de tipul de dat˘ a al parametrului. Mai mult¸i parametri de acela¸si tip pot fi grupat¸i, folosindu-se drept separator virgula. De fapt, lista parametrilor formali poate fi descris˘a mai detaliat astfel: 1: procedure < nume>() 2: end procedure
unde PFI = lista parametrilor formali de intrare. PFO = lista parametrilor formali de ie¸sire. Enunt¸ul de apel al unei proceduri are urm˘ atoarea sintax˘ a: 1: CALL (PAI; PAO)
PAI - lista parametrilor actuali de intrare, ce corespund parametrilor formali de intrare.
Corespondent¸ a se face de la stˆ anga la dreapta ¸si trebuie s˘ a fie de acela¸si tip cu parametrii formali. PAO - lista parametrilor actuali de ie¸sire.
Exemplul 1.6 S˘ a se realizeze un algoritm care determin˘ a cel mai mare divizor comun a dou˘ a numere naturale. Reamintim cˆ ateva rezultate teoretice ce vor fi folositoare pentru ˆınt ¸elegerea procesului de calcul al algoritmului ce va fi prezentat. a numere ˆıntregi a ¸si b, cu b = Teorema 1.7 (Teorema ˆımp˘ art ¸irii cu rest) Pentru dou˘ a numere ˆıntregi q ¸si r , unice, astfel ˆıncˆ at: 0, dou˘
∃
a = b q + r, 0
·
≤ r < |b|
unde a se nume¸ste deˆımp˘ art ¸itul, b ˆımp˘ art ¸itorul, q cˆ atul iar r restul. Definit ¸ia 1.2 Cel mai mare divizor comun (notat cmmdc) a dou˘ a numere naturale a ¸si b este un num˘ ar natural d cu propriet˘ at ¸ile: 1. d a, d b ( d este un divizor comun al lui a ¸si b);
| | 2. ∀d ∈ N astfel ˆıncˆ at d |a, d |b avem d |d (oricare alt divizor comun al lui a ¸si b , ˆıl divide ′
′
′
′
¸si pe d).
Observat ¸ia 1.8
1. cmmdc(a, 0) = a.
2. Dac˘ a cmmdc(a, b) = 1 se spune c˘ a numerele a ¸si b sunt prime ˆıntre ele.
10
ˆ 3. Intre cel mai mic multiplu comun ¸si cel mai mare divizor comun exist˘ a urm˘ atoarea relat ¸ie: a b cmmmc(a, b) = . (1.1) cmmdc(a, b)
·
Metoda de calcul pentru a obt ¸ine cel mai mare divizor comun a dou˘ a numere a ¸si b prin ˆımp˘ art ¸iri succesive, cunoscut˘ a sub numele de algoritmul lui Euclid, se poate descrie astfel: Se ˆımparte a la b ¸si se ret ¸ine restul r . Dac˘ a r este nul atunci cel mai mare divizor comun ˆ este b. In caz contrar, se ˆımparte b la r ¸si se p˘ astreaz˘ a noul rest. Calculele se continu˘ a, folosindu-se ca deˆımp˘ art ¸it vechiul ˆımp˘ art ¸itor, iar, ca ˆımp˘ art ¸itor, ultimul rest obt ¸inut, pˆ an˘ a cˆ and restul obt ¸inut devine nul. Operat ¸iile anterioare pot fi transpuse prin intermediul urm˘ atoarelor formule matematice: a = bq 1 + r1 b = r 1 q 2 + r2 r1 = r 2 q 3 + r3 ... rk = r k+1 q k+2 + rk+2 ... rn−2 = r n−1q n + rn rn−1 = r nq n+1 + rn+1
0 0 0
≤ r < b ≤ r < r ≤ r < r
0
≤ r
0 0
≤ r ≤ r
1 2
1
3
2
k+2
< rk+1
< rn−1 n+1 < rn n
Aceste ˆımp˘ art ¸iri nu se constituie ˆıntr-un proces infinit, deoarece secvent ¸a de numere naturale r1 , r2, . . . , rn+1, . . . este strict descresc˘ atoare ( r1 > r2 > . . . > rn > . . .) ¸si m˘ arginit˘ a inferior de 0. Rezult˘ a c˘ a p N astfel ˆıncˆ at r p = 0, ¸si r p+1 = 0.
∃ ∈
Algoritm 6 Algoritmul lui Euclid pentru calculul cmmdc a dou˘ a numere 1: procedure cmmdc(x, y; b) 2: a x 3: b y 4: if (b = 0) then b a 5: 6: else 7: r a mod b 8: while (r = 0) do 9: a b b r 10: 11: r a mod b 12: end while 13: end if 14: end procedure
← ←
← ←
← ← ←
⊲ Procesul se ˆıncheie atunci cˆand r ia valoarea 0
Dac˘a analiz˘ am ˆıntregul proces de calcul, se observ˘ a faptul c˘a deˆımp˘ art¸itul este ˆımp˘ art¸itorul de la etapa anterioar˘ a, iar ˆımp˘ art¸itorul este restul de la etapa anterioar˘ a. De asemenea, trebuie ment¸ionat c˘a ˆın acest proces de calcul, cˆ atul nu particip˘ a ˆın mod activ. Algoritmul 6 este un foarte bun exemplu de utilizare a instruct¸iunii de ciclare cu test init¸ial, while. Din analiza algoritmului reiese faptul c˘ a, mai ˆıntˆai, se efectueaz˘ a o evaluare a restului r (calculul s˘ au, liniile 7 ¸si 11), dup˘ a care se testeaz˘ a condit¸ia egalit˘ a¸t ii acestuia cu valoarea 0 (linia 8). 11
Funct¸iile au aceea¸si sintax˘a ca ¸si procedurile: 1: function < nume>() 2: 3: end function
Funct¸iile pot fi apelate prin numele lor, ca termen al unei expresii.
1.2
Exemple
Exemplul 1.9 S˘ a se verifice printr-un algoritm dac˘ a un num˘ ar natural este prim sau nu. Definit ¸ia 1.3 Un num˘ ar natural k sunt 1 ¸si k.
≥ 2 se nume¸ste prim dac˘ a singurii s˘ ai divizori naturali
Pe baza acestei definit ¸ii se poate concluziona c˘ a un num˘ ar natural k este prim dac˘ a nu are niciun divizor propriu ˆın intervalul [2, k 1].
−
a un num˘ ar este prim Algoritm 7 Algoritm pentru verificarea dac˘
{ } { ← ← ≤ − ∧ ← ←
1: Input N 2: if (n < 2) then 3: Output ’Numarul nu este prim.’ 4: else 5: i 2 6: prim true 7: while ((i n 1) ( prim = true)) do if (n mod i = 0) then 8: 9: prim f alse 10: else 11: i i + 1 12: end if 13: end while 14: if ( prim = true) then 15: Output ’Numarul este prim.’ 16: else 17: Output ’Numarul nu este prim.’ 18: end if 19: end if
{ {
}
}
}
Dac˘ a n este num˘ ar prim, corpul enunt ¸ului de ciclare while se va executa de n 2 ori (a se vedea algoritmul 7). Condit ¸ia i n 1 poate fi ˆımbun˘ at˘ at ¸it˘ a cu i n/2, deoarece ˆıntre jum˘ atatea num˘ arului ¸si n nu mai exist˘ a niciun alt divizor, pentru orice valoare a lui n. Dac˘ a 2 este un divizor al num˘ arului n atunci ¸si n/2 este un divizor al lui n. Dac˘ a 2 nu este divizor al lui n atunci nici n/2 nu este divizor al lui n . Un enunt ¸ asem˘ an˘ ator este valabil
−
≤ −
≤
12
¸si pentru numerele 3, 4, 5, . . .. Astfel se formeaz˘ a dou˘ a ¸siruri: 2 3 4
corespunde
→ → →
corespunde corespunde
.. .
corespunde
k
→
Sir 1
n 2 n 3 n 4 .. . n k
Sir 2
Se observ˘ a faptul c˘ a primul ¸sir ( Sir 1) este compus din elemente cu valori consecutive, iar al doilea ¸sir ( Sir 2 ) nu respect˘ a aceast˘ a proprietate. Numerele dintr-o pereche ( p, pn ) sunt legate ˆıntre ele astfel: ˆın momentul ˆın care am verificat faptul c˘ a num˘ arul p nu este divizor al lui n, implicit am dedus c˘ a nici n/p nu este un divizor al lui n. Prin urmare nu mai este nevoie s˘ a verific˘ am ¸si valoarea n/p. Primul ¸sir este strict cresc˘ ator iar cel de-al doilea este strict descresc˘ ator. Elementele din primul ¸sir devin mai mari decˆ at cele din al doilea atunci 2 cˆ and k ≅ n/k a condit ¸ia pentru limita superioar˘ a a k ≅ n k ≅ n. Astfel rezult˘ cicl˘ arii: i n (a se vedea algoritmul 8).
⇒ √ ≤ ⌊ ⌋
⇒
√
Algoritm 8 Algoritm pentru verificarea dac˘ a un num˘ ar este prim (variant˘ a optimizat˘ a) 1: Input N 2: if (n < 2) then 3: Output ’Numarul nu este prim.’ 4: else 5: i 2 6: prim true n ) ( prim = true)) do 7: while ((i 8: if (n mod i = 0) then 9: prim f alse 10: else i i + 1 11: end if 12: 13: end while 14: if ( prim = true) then 15: Output ’Numarul este prim.’ 16: else Output ’Numarul nu este prim.’ 17: 18: end if 19: end if
{ } { ← ← √ ≤⌊ ⌋ ∧ ← ← { {
}
}
}
Exemplul 1.10 Se spune c˘ a un vector este simetric dac˘ a primul element este egal cu ultimul, al doilea cu penultimul etc. Algoritmul urm˘ ator verific˘ a dac˘ a un vector de numere ˆıntregi este simetric. Vom utiliza dou˘ a variabile de indici, ce pornesc, una din stˆ anga, ¸si cealalt˘ a din dreapta. Atˆ ata timp cˆ at variabila din stˆ anga este mai mic˘ a decˆ at variabila din dreapta iar elementele din vector corespunz˘ atoare variabilelor de indiciere sunt egale, se incrementeaz˘ a variabila din stˆ anga ¸si se decrementeaz˘ a variabila din dreapta. Dup˘ a p˘ ar˘ asirea instruct ¸iunii de ciclare, se 13
face o verificare pentru determinarea condit ¸iei care nu a mai fost ˆındeplinit˘ a ¸si a condus la ie¸sirea din bucl˘ a. Dac˘ a i j atunci condit ¸ia (i < j) nu a mai fost ˆındeplinit˘ a. Acest lucru conduce la concluzia c˘ a cealalt˘ a condit ¸ie, (xi = x j ) a fost ˆındeplinit˘ a tot timpul ( i < j, i, j = 1, n). Deci ¸sirul este simetric ˆın aceast˘ a situat ¸ie.
≥
∀
Algoritm 9 Algoritm pentru verificarea simetriei unui vector 1: Input N, x1 , x2 ,...,xN N 2: i 1, j 3: while ((i < j ) (xi = 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.11 Se d˘ a un num˘ ar format din n cifre. Vom prezenta un algoritm ˆın care se determin˘ a cel mai mare num˘ ar obt ¸inut prin eliminarea a k cifre ( k < n) dintre cele n (vezi algoritmul 10). Compararea a dou˘ a numere naturale ce prezint˘ a acela¸si num˘ ar de cifre se face ˆıncepˆ and de la stˆ anga spre dreapta (de la cifrele mai semnificative c˘ atre cele mai put ¸in semnificative). Fie A = a 1 a2 . . . an num˘ arul nostru.Vom nota prin m num˘ arul de cifre ce vor r˘ amˆ ane din num˘ arul init ¸ial dup˘ a eliminarea celor k cifre ( m = n k). Problema se reduce la determinarea celui mai mare num˘ ar format cu m cifre dintre cele n cifre init ¸iale. La ˆınceput num˘ arul cont ¸ine n cifre, p˘ astrate ˆın ordine, ˆın vectorul A: a1 , a2, . . . , an . Solut ¸ia se construie¸ste ˆın mod iterativ, la pasul i alegˆ andu–se o valoare corespunz˘ atoare: se determin˘ a prima cifr˘ a de valoare maxim˘ a ¸si pozit ¸ia acesteia ˆın cadrul subsecvent ¸ei de limite l ¸si n m + i, al , . . . , an−m+i .
−
−
• ˆIn instruct¸iunea for (linia 4) pentru fiecare pozit¸ie liber˘a din num˘arul rezultat, se va alege cifra maxim˘ a dintre cele disponibile;
• linia 5 - se trece la pozit¸ia urm˘atoare: l reprezint˘a indexul din ¸sirul A al cifrei aleas˘a la pasul anterior (i − 1) ¸si cont¸ine pozit¸ ia pe care a fost g˘asit˘a cifra maxim˘ a . Cifra maxim˘a pentru pasul curent va fi c˘ autat˘ a ˆın intervalul l . . . n − m + i, unde n − m + i
reprezint˘ a limita superioar˘ a a indexului pˆan˘a la care se poate alege un element la pasul curent; a1 . . . al . . . an−m+i . . . an
subsecvent¸a pentru care se determin˘a cifra maxim˘ a
• liniile 6, 7 - se init¸ializeaz˘a variabila de ciclare j cu l ¸si valoarea maxim˘a cu 0; Deoarece a ≥ 0, ∀i = 1, n, se poate init¸ializa variabila max (unde se va p˘ astra valoarea maxim˘ a) i
cu 0;
• linia 8 - se caut˘a cifra maxim˘a pˆan˘a la limita din dreapta n − m + i (dac˘a se merge mai la dreapta dincolo de aceast˘ a limit˘a nu se mai pot alege restul de m − i cifre); • linia 10 - se actualizeaz˘a valoarea maxim˘a; 14
Algoritm 10 Algoritm pentru determinarea num˘ arului maxim obt¸inut dup˘ a eliminarea a k cifre
{ ← − ← ← ← ← ←
}
1: Input n, a1 , . . . , an , k 2: m n k 3: l 0 1, m do 4: for i 5: l l + 1 6: j l 7: max 0 8: while ( j n m + i) do 9: if (max < a j ) then 10: max a j 11: l j 12: end if j j + 1 13: end while 14: 15: Output max 16: end for
≤ − ← ←
←
{
}
• linia 11 - se actualizeaz˘a pozit¸ia valorii maxime. Exemplul 1.12 Vom prezenta doi algoritmi ce determin˘ a toate tripletele de numere naturale 2 2 2 (a,b,c) ce verific˘ a relat ¸ia a + b = c unde c 100 . Prima variant˘ a de lucru prezint˘ a generarea tuturor tripletelor (i,j,k) unde 2 i < j < k n . Dintre acestea se vor alege doar acelea ce verific˘ a relat ¸ia k k = i i + j j . Dac˘ a vom considera doar instruct ¸iunea de ciclare 4, atunci condit ¸ia 5 se va verifica de n ( j +1)+ 1 = ¸ine faptul c˘ a instruct ¸iunea 5 se va executa de: n j ori. Deoarece j = i + 1, n 1, vom obt
≤
≤ −
·
·
· −
−
n−i−1
(n
− i − 1) + (n − i − 2) + . . . + (n − n + 1) =
ori. Din i = 2, n
j =
(n
≤
− i) · (n − i − 1) 2
j=1
− 2 rezult˘ a c˘ a
n−2 n−i−1
i=2 j=1
n−2
j =
− (n
i=2
i) (n 2
· − i − 1) = 1
n−2
1 = [ i2 + (1 2 i=2
2
n−2
− 2n)
i=2
15
n−2
(i2 + (1
i=2
i + (n2
2
− 2n)i + n − n)
− n) · (n − 3)]
{ } ← − ← ←
{ } ← − ← ← √ · · ← ⌊ ⌋ · ∧ ≤ n)) then { }
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 7: Output i,j,k 8: end if 9: end for 10: end for 11: return
−
·
· {
· }
Pentru cea de-a doua variant˘ a, cea din dreapta, se genereaz˘ a perechile de numere (i, j) cu i < j n, se calculeaz˘ i i + j j ¸si se verific˘ proprietatea c˘ a 2 a s a dac˘ a num˘ arul 2 obt ¸inut este p˘ atrat perfect (linia 6: ( s ) = 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 total˘ a reprezentˆ and num˘ arul de verific˘ ari (linia 6) va fi:
≤ −
−
n−1
1.3
← · · √ ⌊ ⌋ − − −
n−1
(n
i=2
≤
− i) =
n−1
(n
i=1
− i) − (n − 1) =
− i
(n
i=1
− 1) = n(n2− 1) − n + 1
Elemente de analiza algoritmilor
Gradul de dificultate al unei probleme P poate fi pus in evident¸a˘ prin timpul de execut¸ie al algoritmului corespunz˘ ator ¸si/sau prin spat¸iul de memorie necesar. Timpul de execut ¸ie ˆın cazul cel mai defavorabil ne d˘ a durata maxim˘ a de execut¸ie a unui algoritm. Timpul mediu de execut¸ie se obt¸ine prin ˆınsumarea timpilor de execut¸ie pentru toate mult¸imile de date de intrare urmat˘a de raportarea la num˘ arul acestora.
Definit¸ia 1.4 Timpul de execut ¸ie ˆın cazul cel mai defavorabil al unui algoritm A este o funct ¸ie T A : N a num˘ arul maxim de instruct ¸iuni executate de N unde T A (n) reprezint˘ c˘ atre A ˆın cazul unor date de intrare de dimensiune n.
−→
Definit¸ia 1.5 Timpul mediu de execut ¸ie al unui algoritm A este o funct ¸ie T Amed : N N med unde T A (n) reprezint˘ a num˘ arul mediu de instruct ¸iuni executate de c˘ atre A ˆın cazul unor date de intrare de dimensiune n.
−→
Fiind dat˘a o problem˘ a P , o funct¸ie T (n) se spune c˘ a este o margine superioar˘ a dac˘a exist˘a un algoritm A ce rezolv˘ a problema P iar timpul de execut¸ie ˆın cazul cel mai defavorabil al algoritmului A este cel mult T (n). Fiind dat˘a o problem˘ a P , o funct¸ie T (n) se spune c˘a este o margine pentru cazul mediu dac˘a exist˘a un algoritm A ce rezolv˘ a problema P iar timpul mediu de execut¸ie al algoritmului A este cel mult T (n). a dac˘a orice Fiind dat˘a o problem˘ a P , o funct¸ie T (n) se spune c˘a este o margine inferioar˘ algoritm A ce rezolv˘ a problema P va avea cel put¸in un timp T (n) pentru unele date de intrare de dimensiune n, atunci cˆand n tinde la infinit (n ).
→∞
R vom nota cu O(g(n)) mult Definit¸ia 1.6 Fiind dat˘ a o funct ¸ie g : N ¸imea: O(g(n)) = f (n) c > 0, n0 0 a.ˆı. 0 f (n) c g(n), n n 0 .
{
|∃
≥
≤
≤ ·
−→
∀ ≥ }
16
Vom spune despre f c˘a nu cre¸ste ˆın mod sigur mai repede decˆ at funct¸ia g. Pentru a indica faptul c˘ a 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 1.13 Vom prezenta cˆ ateva propriet˘ at ¸i ale lui O( ):
·
• O(f (n) + g(n)) = O(max(f (n), g(n))). De exemplu pentru funct ¸ia f (n) = 7 · n − n + 3 · log n aplicˆ and regula valorii maxime, vom avea: O(f (n)) = O(max(7 · n , n , 3 · log n)) adic˘ a O(f (n)) = O(n ). • O(log n) = O(log n); • f (n) = O(f (n)) (reflexivitate); • f (n) = O(g(n)) ¸si g(n) = O(h(n)) atunci f (n) = O(h(n)) (tranzitivitate). Definit¸ia 1.7 Fiind dat˘ a o funct ¸ie g : N −→ R vom nota cu Θ(g(n)) mult ¸imea: Θ(g(n)) = {f (n)| ∃c , c > 0, ∃n ≥ 0 a.ˆı. 0 ≤ c · g(n) ≤ f (n) ≤ c · g(n), ∀n ≥ n } 5
5
a
1
2
2
5
b
2
0
1
2
0
Spunem c˘ a g(n) este o margine asimptotic tare pentru f (n).
Definit¸ia 1.8 Fiind dat˘ a o funct ¸ie g : N ¸imea: Ω(g(n)) = R vom nota cu Ω(g(n)) mult f (n) c > 0, n0 0 a.ˆı. 0 c g(n) f (n), n n 0
{
|∃
≥
≤ ·
≤
−→
∀ ≥ }
La fel cum O furnizeaz˘ a o delimitare asimptotic˘ a superioar˘ a pentru o funct¸ie, Ω furnizeaz˘a o delimitare asimptotic˘ a inferioar˘ a pentru aceasta.
Teorema 1.14 Pentru orice dou˘ a funct ¸ii f (n) ¸si g(n), avem f (n) = Θ(g(n)) O(g(n)) ¸si f (n) = Ω(g(n)).
⇐⇒
f (n) =
R vom nota cu o(g(n)) mult Definit¸ia 1.9 Fiind dat˘ a o funct ¸ie g : N ¸imea: o(g(n)) = f (n) c > 0, n0 > 0 a.ˆı. 0 f (n) < c g(n), n n 0
{
|∀
∃
−→ · ∀ ≥ }
≤
f (n) n→∞ g(n)
= 0. ⇐⇒ lim Definit¸ia 1.10 f (n) ∈ ω(g(n)) ⇐⇒ g(n) ∈ o(f (n)) ω(g(n)) este o mult ¸ime ce se define¸ste astfel: ω(g(n)) = {f (n)| ∀c > 0, ∃n > 0 a.ˆı. 0 ≤ c · g(n) < f (n), ∀n ≥ n } Observat¸ia 1.16 1. f (n) = Θ(g(n)), g(n) = Θ(h(n)) =⇒ f (n) = Θ(h(n)) (tranzitivi-
Observat¸ia 1.15 f (n) = o(g(n))
0
0
tate);
2. f (n) = Θ(f (n)) (reflexivitate); 3. f (n) = Θ(g(n))
⇐⇒
g(n) = Θ(f (n)) (simetrie).
Definit¸ia 1.11 O funct ¸ie f are o cre¸stere exponent ¸ial˘ a dac˘ a c > 1 a.i. f (x) = Ω(cx ) ¸si d a.ˆı. f (x) = O(dx). O funct ¸ie f este polinomial˘ a de gradul d dac˘ a f (n) = Θ (nd) ¸si f (n) = O(nd ), d′ d .
∃
∃
′
∀ ≥
17
Teorema 1.17
⇒ g(n) ∈ Θ(f (n)), c > 0 ⇒ g(n) ∈ o(f (n)) ⇒ f (n) ∈ o(g(n)) • pentru x ∈ R avem x ∈ o(n!). g(n) lim = n→∞ f (n)
Exemplul 1.18 Deoarece
∞ c 0
(1.2)
n
∗ +
xn xn < 4 n! x . . . x4
≤
xn xn 1 n = = ( ) (x4 )n/2 x2n x
(1.3)
⌈n/2⌉ori
rezult˘ a c˘ a
xn lim =0 n→∞ n!
• log n ∈ o(n).
(1.4)
1 log x (log x)′ x ln 2 lim = lim = lim =0 x→∞ x x→∞ (x)′ x→∞ 1
∗
• pentru a,b > 1, a , b ∈ R
avem loga n Θ(logb n).
∈
Calcularea cu exactitate a timpului de execut¸ie al unui program oarecare se poate dovedi o activitate dificil˘ a. De aceea, ˆın practic˘ a se utilizeaz˘ a estim˘ari ˆıncercˆ andu-se eliminarea constantelor ¸si simplificarea formulelor ce intervin ˆın cadrul evalu˘ arii. Dac˘a dou˘ a p˘art¸i ale unui program, P 1 ¸si P 2, au timpii de execut¸ie corespunz˘ atori 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 reprezint˘ a operat¸ia de concatenare. Prin aplicarea acestei reguli, rezult˘ a faptul c˘a, timpul necesar execut¸iei unui num˘ar finit de operat¸ii este, neluˆ and ˆın considerare constantele, caracterizat ˆın principal de c˘ atre timpul de execut¸ie al operat¸iei cele mai costisitoare. Cea de-a doua regul˘ a, regula produsului , spune c˘ a, fiind date dou˘ a funt¸ii f (n) ¸si g(n) astfel ˆıncˆat 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 urm˘ atoarele reduceri:
⊕ ⊕
·
O(1)
2
·
3
n
⊆ O(log n) ⊆ O(n) ⊆ O(n log n) ⊆ O(n ) ⊆ O(n ) ⊆ O(2 )
Prezent˘ am ˆın continuare cˆ ateva reguli generale pentru evaluarea complexit˘ a¸tii unui algoritm:
• timpul de execut¸ie al unei instruct¸iuni de atribuire, citire sau afi¸sare 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 dureaz˘ a cel mai mult.
• timpul de execut¸ie al unei instruct¸iuni de decizie (if) este timpul execut˘arii instruct¸iunilor de pe ramura aleas˘ a plus timpul necesar evalu˘ arii condit¸iei.
• timpul de execut¸ie pentru o instruct¸iune de ciclare este suma, dup˘a num˘arul de pa¸si
pe care ˆıi realizeaz˘a instruct¸iunea de ciclare, dintre timpul necesar execut˘ arii corpului instruct¸iunii plus timpul necesar evalu˘ arii condit¸iei. 1: for i
← 1, n do 18
A(i) 2: 3: end for
Dac˘a instruct¸iunii compuse A(i) ˆıi corespunde un timp de execut ¸ie constant t, ce nu depinde de i, atunci timpul corespunz˘ ator ˆıntregii instruct¸iuni de ciclare for anterioare este: n n
t = t
i=1
1 = t n = O(n)
i=1
·
(1.5)
ˆIn cazul general, timpul de execut¸ie al instruct¸iunii compuse A(i) depinde de pasul i, notat cu ti . Astfel timpul total corespunz˘ ator instruct¸iunii for este ni=1 ti .
• Instruct¸iunile de ciclare al c˘aror num˘ar de pa¸si depinde de ˆındeplinirea unei condit¸ii (while) sau de neˆındeplinirea acesteia (repeat . . . until), sunt mult mai dificil de analizat deoarece nu exist˘ a o metod˘ a general˘ a de a afla cu exactitate num˘ arul de repet˘ ari al corpului instruct¸iunii. De exemplu, pentru fragmentul urm˘ator
←
1: i l 2: while (ai < x) do 3: ... i i + 1 4: 5: end while
⊲ calcule ce pot modifica, direct sau indirect, valoarea lui a i
←
nu se poate preciza de cˆ ate ori se va ajunge la realizarea instruct¸iunii de incrementare din interiorul instruct¸iunii while. ˆIn marea majoritate a cazurilor se utilizeaz˘ a elemente de teoria probabilit˘ a¸tilor ˆın vederea obt¸inerii unei estim˘ari a num˘arului de repet˘ ari al corpului instruct¸iunii.
Exemplul 1.19 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 instructiune2 3: 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 1.20 S˘ a consider˘ am un alt exemplu: se d˘ a un ¸sir A de n numere reale, ¸si se i dore¸ste calcularea elementelor unui ¸sir B astfel ˆıncˆ at bi = 1i j=1 a j , pentru i = 1, n.
← ←
·
1: for i 1, n do s 0 2: 3: for j 1, i do 4: s s + a j 5: end for s 6: bi i 7: end for 8: return
← ←
←
19
Dac˘ a not˘ am cu o constant˘ a cx timpul necesar pentru efectuarea unei operat ¸ii atomice, vom obt ¸ine: costul efectu˘ arii liniei 1 este c1 n, al liniei 2 este c2 n, al liniei 3 este c3 i, al liniei 4 este c4 i, al liniei 6 este c5 n iar al liniei 8 este c6.
·
·
n
T (n) = c1n + c2n +
·
n
c3 i +
i=1
·
·
n
c4 i + c5 n + c6 = c 1n + c2 n + c3
i=1
n
i + c4
i=1
i + c5 n + c6
i=1
n(n + 1) (c3 + c4) + n(c1 + c2 + c5 ) + c6 2 1 1 = (c3 + c4 )n2 + (c3 + c4)n + n(c1 + c2 + c5) + c6 2 2 1 1 = (c3 + c4 )n2 + [ (c3 + c4 ) + c1 + c2 + c5 ]n + c6 = n 2 p + n q + c6 2 2 =
·
·
De obicei nu se efectueaz˘ a o analiz˘ a atˆ at de detaliat˘ a a algoritmului, dar se ˆıncearc˘ a o evaluare a blocurilor principale, cum ar fi instruct ¸iunile de ciclare, atribuindu-le direct valori corespunz˘ atoare complexit˘ at ¸ii timp, atunci cˆ and este posibil: T (n) = O(n2 ) + O(n) + O(1) = O(n2)
(1.6)
Vom modifica algoritmul anterior astfel ˆıncˆ at s˘ a reducem num˘ arul de calcule efectuate:
←
1: s 0 2: for i 1, n do 3: s s + ai s bi 4: i 5: end for 6: return
← ← ←
Pentru aceast˘ a variant˘ a de lucru, complexitatea timp este urm˘ atoarea: T (n) = O(1)(linia 1) + O(n)(liniile 2–4) + O(1)(linia 6 ) = O(n)
(1.7)
ˆ In urma analizei de complexitate, putem concluziona faptul c˘ a cea de-a doua variant˘ a a algoritmului ruleaz˘ a ˆıntr-un timp liniar.
1.3.1
Metoda substitut¸iei
Metoda substitut¸iei presupune mai ˆıntˆ ai ghicirea (estimarea) formei solut¸iei pentru relat¸ia de recurent¸a˘ ¸si apoi demonstrarea prin induct¸ie matematic˘ a a corectitudinii solut¸iei alese. Metoda poate fi aplicat˘ a ˆın cazul unor ecuat¸ii pentru care forma solut ¸iei poate fi estimat˘ a. S˘a consider˘ am urm˘atoarea relat¸ie de recurent¸a˘: T (n) =
1 , dac˘ a n = 1 2T ( n2 ) + n , ˆın rest
(1.8)
Vom presupune c˘ a solut¸ia acestei relat¸ii de recurent¸a˘ este T (n) = O(n log n) (not˘ am ¸iei matematice , vom ˆıncerca s˘ log2 n = log n). Folosind metoda induct a demonstr˘ am inegalitatea: T (n) cn log n. (1.9)
≤
Presupunem, mai ˆıntˆ ai, c˘a inecuat¸ia (1.9) are loc pentru k < n, inclusiv pentru n2 , adic˘a T ( n2 ) c n2 log( n2 ).
∀
≤ · ·
20
Vom demonstra c˘ a inecuat¸ia (1.9) este ˆındeplinit˘a ¸si pentru n: T (n) = 2T ( n2 ) + n. T (n)
≤ 2(c n2 log n2 ) + n = cn log n2 + n = cn log n − cn log 2 + n = cn log n − cn + n ≤ cn log n
(1.10)
Metoda induct¸iei matematice presupune s˘ a ar˘ at˘am c˘a solut¸ia respect˘ a ¸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. Aceast˘a situat¸ie poate fi rezolvat˘ a dac˘ a consider˘ am c˘a T (n) = cn log n, n n 0 (n0 este o constant˘ a). Din T (2) = 4 ¸si T (3) = 5 vom alege valoarea parametrului c astfel ˆıncˆ at s˘a fie ˆındeplinite inegalit˘a¸tile T (2) 2c log 2 ¸si T (3) 3c log 3. Se observ˘ a c˘a orice valoare a lui c 2 satisface inegalit˘a¸tile anterioare.
· ·
≤
1.3.2
∀ ≥
≤
≥
Schimbarea de variabil˘ a
Aceast˘a metod˘ a presupune realizarea unei schimb˘ari de variabil˘ a ˆın vederea simplific˘ arii formulei sau pentru reg˘asirea unei formule deja cunoscut˘ a. S˘a consider˘ am ecuat¸ia T (n) = 2T ( n) + log n. Dac˘a ˆınlo cuim pe n cu 2m obt¸inem:
√
m
m
2
2
T (2m) = 2T (2 ) + log 2m = 2T (2 ) + m
(1.11)
Not˘am cu S (m) = T (2m) ¸si ˆınlocuind ˆın (1.11), obt¸inem o nou˘ a relat¸ie de recurent¸a˘: m S (m) = 2S ( ) + m 2
(1.12)
Se observ˘ a c˘a aceast˘ a relat¸ie are o form˘ a similar˘a cu cea din formula (1.8). Solut¸ia relat¸iei (1.12) este: S (m) = O(m log m)
⇒ T (n) = T (2
m
) = S (m) = O(m log m) = O(log n log log n)
(1.13)
ˆIn concluzie avem T (n) = O(log n log log n).
1.3.3
Metoda iterativ˘ a
Metoda iterativ˘ a este mult mai eficient˘ a deoarece nu presupune ’ghicirea ’ solut¸iei, variant˘ a ce conduce deseori la rezultate gre¸site ¸si timp pierdut. De exemplu, s˘ a consider˘ am urm˘atoarea formul˘ a de recurent¸a˘: 1 , dac˘ a n = 1 T (n) = (1.14) T ( n2 ) + 1 , ˆın rest
Pentru rezolvarea acestei recurent ¸e vom substitui succesiv pe n cu n2 : n T (n) = T ( ) + 1 2 n = T ( ) + 2 4 n = T ( ) + 3 8 ... n = T ( k ) + k 2 21
(1.15)
Prin urmare atunci cˆ a nd 1 = 2nk k = log n, vom avea T (n) = 1 + k = 1 + log n T (n) = O(log n). S˘a consider˘ am o alt˘ a formul˘ a de recurent¸a˘:
⇒
⇒
T (n) =
1 , dac˘ a n = 1 2T ( n2 ) + n , ˆın rest
(1.16)
ˆInlocuind succesiv pe n cu n vom obt¸ine urm˘atoarea serie de identit˘ a¸ti: 2 n T (n) = 2T ( ) + n 2 n n n = 2(2T ( ) + ) + n = 4T ( ) + 2n 2 2 4 n n n = 4(2T ( ) + ) + 2n = 8T ( ) + 3n 8 4 8 ... n = 2k T ( k ) + kn 2 Deoarece substitut¸ia se opre¸ste atunci cˆ and
n = 2k
(1.17)
1, adic˘a pentru k = log n, vom avea:
T (n) = 2k + kn = n + n log n = O(n log n)
1.3.4
(1.18)
Teorema master
Metoda master pune la dispozit¸ie o variant˘ a de rezolvare a unor recurent¸e avˆ and forma urm˘atoare n T (n) = aT ( ) + f (n) (1.19) b unde a 1, b > 1 sunt constante, iar f (n) este o funct¸ie asimptotic pozitiv˘ a. Formula de recurent¸a˘ (1.19) descrie timpul de execut¸ie al unui algoritm ce presupune descompunerea unei probleme de dimensiune n ˆın a subprobleme, fiecare avˆ and dimensiunea datelor de intrare nb . f (n) reprezint˘ a costul asociat ˆımp˘ art¸irii datelor de intrare cˆ at ¸si costul combin˘arii rezultatelor celor a subprobleme.
≥
Teorema 1.21 (Teorema master) [30] Fie a 1 ¸si b > 1 dou˘ a constante, f (n) o funct ¸ie asimptotic pozitiv˘ a, ¸si T (n) definit˘ a de relat ¸ia de recurent ¸a T (n) = aT ( nb ) + f (n), unde nb va fi nb sau nb . Atunci T (n) este m˘ arginit˘ a asimptotic dup˘ a cum urmeaz˘ a:
≥
⌊ ⌋
⌈ ⌉
1. dac˘ a f (n) = O(nlogb a−ǫ ) pentru o constant˘ a ǫ > 0 , atunci T (n) = Θ(nlogb a ); 2. dac˘ a f (n) = Θ(nlogb a logk n), atunci T (n) = Θ(nlogb a logk+1 n) (de obicei k = 0); 3. dac˘ a f (n) = Ω(nlogb a+ǫ ) pentru o constant˘ a ǫ > 0, ¸si dac˘ a af ( nb ) cf (n) pentru o constant˘ a c < 1 ¸si oricare num˘ ar n suficient de mare, atunci T (n) = Θ(f (n)).
≤
Corolarul 1.22 Pentru o funct ¸ie f de tip polinomial unde f (n) = cnk avem: 1. dac˘ a a > bk atunci T (n) = O(nlogb a ); 2. dac˘ a a = b k atunci T (n) = O(nk log n); 22
T ((n) = O O((nk ). 3. dac˘ a a < bk atunci T T (n) = 2T T (( n2 )+ )+n n. Avem a = a = 2, b = 2, k = 1, f f ((n) = n k . Aplicˆ Exemplul 1.23 Fie T ( and k k = b b , solut T ((n) = O O((n log n) = O O((n log n). corolarul pentru cazul a = ¸ia este T
•
• Fie T T ((n) = 9T T ((
n ) 3
+ n . Avem a = 9, b = 3, k = 1. Ap Apli licˆ cˆ and corolarul pentru cazul log 9 2 ¸ine solut ¸ia T a > b , se obt T ((n) = O O((n ) = O O((n ). k
3
n ) 2 k
vem m a = 4, b = 2, k = 3, f Apli licˆ cˆ and corolarul + n 3 . Ave f ((n) = n3 . Ap 3 pentru cazul a < b , se obt ¸ine solut ¸ia T T ((n) = O O((n ).
• Fie T T ((n) = 4T T (( n
• Fie T T ((n) = 2 T T (( constant.
n ) + n + nn. 2
Nu se po poate ate aplica ap lica Teorema Teorema master deo d eoare arece ce a = 2n nu este
n ) + n + n log n. 2
Θ(nlog Atuncii avem a = 2, b = 2, k = 1, f Atunc f ((n) = Θ(n ¸si si ap apli licˆ cˆ and Teorema master, cazul al doilea, obt ¸inem: T T ((n) = Θ(n Θ(n log2 n).
• Fie T T ((n) = 2T T (( 1 2
n 2
2
1 n
2
logk n),
1 2
• Fie T T ((n) = T T (( ) + . Nu se po poate ate aplica Teorema Teorema master deo d eoare arece ce a = < 1 . √ f ((n) = • Fie T T ((n) = √ 2T T (( ) + log n. √ Ap Apli licˆ cˆ and Teorema master pentru a = 2, b = 2, f O (n
1
2
−ǫ
n 2
) obt ¸inem: T T ((n) = Θ( n).
• Fie T T ((n) = 3T T (( log4 3+ 3+ǫǫ
Ω(n Ω( n
n ) 4
Apli licˆ cˆ and Teorema master pentru a = 3, b = 4, f + n log n. Ap f ((n) = ¸inem: T ) obt T ((n) = Θ(n Θ(n log n). n 4
16T (( ) − n log n. Nu se po f ((n) = • Fie T T ((n) = 16T poate ate aplic aplicaa Teor orema ema master de deoa oarrece f −n log n nu este o funct ¸ie asimptotic pozitiv˘ a.
Exemplul 1.24 Analiza act ¸iunilor Deschiderea unei act ¸iuni la o anumit˘ a dat˘ a calendaristic˘ a se calculeaz˘ a drept num˘ arul maxim de zile consecutive (pˆ an˘ a la acea dat˘ a) ˆın care pret ¸ul act ¸iunii a fost mai mic sau egal p k pret k iar dk deschiderea calculat˘ cu pret ¸ul din ziua respectiv˘ a. Fie p ¸ul unei act ¸iuni ˆın zi ziua ua k a ˆın aceea ceea¸¸si zi. ˆ In continuare se prezint˘ a un exemplu de calcul al deschiderii unei act ¸iuni pentru un num˘ ar de 7 zile: p0 9 d0 1
p1 6 d1 1
p2 3 d2 1
p3 4 d3 2
p4 2 d4 1
p5 5 d5 4
p6 7 d6 6
¸iuni (prima variant˘ a) a) Algoritm 11 Algoritm de calcul al deschiderii unei act¸iuni 1: procedure ComputeSpan1( p, n; d) 2: for k 0,, n 1 do 0 3: j 1 ( j j < k ) ( pk− j p k ) do 4: while ( j j j + + 1 5: 6: end while 7: dk j 8: end for 9: return 10: end procedure
← ←
← ←
−
∧
≤
23
Algoritmul 11 calculeaz˘ a 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 (n2 ). Vom prezenta un alt mod de calcul al deschiderii unei act ¸iuni folosind o stiv˘ a (a se vedea algoritmul 12). Stiva este o structur˘ a de date de tip container deoarece ea depoziteaz˘ a elemente de un anumit tip. Pentru a putea s˘ a oper˘ am asupra unei colect ¸ii de elemente p˘ astrate ast rate ˆıntr ıntr-o -o stiv stiv˘ a, ˘ se definesc urm˘ atoarele operat ¸ii, ˆın af afar˘ ar˘ a de operat ¸iile fundamentale push si pop: push ¸si
• pus push(x h(x: : • po pop( p(): ):
a obiectul x ˆın varful ˆ stivei. objec obj ect) t) - adaug˘ obje ob ject ct - elimin˘ a ¸si si ˆıntoa ıntoarc rcee obi obiectul ectul din vˆ arful stivei; dac˘ a stiva este goal˘ a
avem o situat ¸ie ce genereaz˘ a o except ¸ie.
ıntoarcee valoare valoareaa obiectului din vˆ arful stivei f˘ ar˘ a a-l extrage. • pe peek ek() (): : ob obje ject ct - ˆıntoarc • siz - ˆın ıntoarce toarce nu num˘ m˘ arul de obiecte din stiv˘ a. size() e(): : int integ eger er • isE isEmpt mpty() y(): : bo boole olean an - ˆın ınto toarce arce true dac˘ a stiva nu cont ¸ine nici un obiect. ıntoa toarce rce true dac˘ a stiva este plin˘ a. • isF isFull ull(): (): boo boolea lean n - ˆın • init(capacitate:integer) - init ¸ializeaz˘ a stiva. ¸iuni (a doua variant˘ a) a) Algoritm 12 Algoritm de calcul al deschiderii unei act¸iuni 1: procedure ComputeSpan2( p, n; d) 2: call init init((S ) 3: for k 0,, n 1 do 0 4: ind 1 (isEmpty isEmpty((S ) = f als alsee) 5: while ( 6: if ( pk p peek peek((S ) ) then 7: call pop pop((S ) 8: else ind 0 9: end if 10: 11: end while 12: if (ind ind = = 1) then 13: h 1 14: else h peek peek((S ) 15: 16: end if 17: dk k h 18: call push push((S, k) 19: end for 20: return 21: end procedure
← − ← ≥
ind = = 1) do ∧ (ind
←
←− ← ← −
Timpul de execut ¸ie al noului algoritm este O (n).
24
1.4
Exerci¸tii
1. (a) S˘ Sa˘ se determine dac˘ a un num˘ ar natural este simetric sau nu. ar (b) S˘ a se determine toate cifrele distincte dintr-un num˘ ar natural. ar (c) S˘ a se determine deter mine reprezentarea r eprezentarea ˆın ın baza 2 a unui num˘ ar natural. (d) S˘ a se determine forma corespunz˘ atoare ˆın baza 10 a unu atoare unuii num num˘ a˘r repr ar reprezentat ezentat ˆın baza 2. (e) S˘ a se elimine dintr–un num˘ ar natural toate cifrele de forma 3k ar 3k + 1 ¸si si s˘a se afi¸seze se ze num˘arul arul rezultat. (f) Pentru un num˘ ar natural dat s˘ ar a se construiasc˘ a din cifrele acestuia cel mai mare num˘ar ar prin amplas amplasarea area mai ˆıntˆ ıntˆ ai a cifrelor impare ¸si ai si apoi ap oi a cifrelor pare. 2. (a) S˘ Sa˘ se determine tot¸i ¸i divizorii unui num˘ar ar natural. (b) S˘ a se verifice dac˘ a dou˘ a numere naturale sunt prime ˆıntre ele. (c) S˘ a se determine tot¸i ¸i divizorii comuni a dou˘a numere naturale. (d) S˘ a se calculeze toate numerele prime mai mici decˆ at o valoare specificat˘ at a. a. (e) S˘ a se descompun˘ a ˆın factori primi un num˘ ar natural. ar (f) S˘ a se determine cel mai mare divizor comun a dou˘ a numere naturale folosindu–se descompuner desco mpunerea ea ˆın facto factori ri primi. p rimi. 3. S˘a se determine toate numerele naturale n naturale n (1 ¸si si ogl oglind inditu itull s˘au au sunt numere prime.
6
at n cˆat at ≤ n ≤ 10 ) cu proprietatea c˘a atˆat n
4. Pentru Pentru o secvent secvent¸˘ ¸a˘ de numere naturale de lungime n lungime n,, s˘a se realizeze un algoritm care s˘ a determine cea mai lung˘ a subsecvent¸˘ ¸˘a de elemente consecutive avˆ and ace and aceea¸ ea¸si si valoa valoare. re. 5. S˘a se realizeze un algoritm care s˘ a determi determine ne primel primelee n n numere numere prime, pentru un num˘ ar ar natural n dat. 6. S˘a se realizeze un algoritm pentru calcularea tuturor solut ¸iilor ¸iil or ecuat¸iei: ¸iei: 3x + y + 4xz 4 xz = = 100, 100, x,y,z
∈ N. ∈
(1.20)
Indicat¸ie ¸ie Pentru o valoare z valoare z 2 255 ¸si x = 0 (x > 0) avem:
≥ ≥
3x + y + 4xz 4 xz 3 3x x + 4xz 4xz 3 + 4z 4z > 100 100..
≥≥ Astfel pentru z obt¸inem ¸inem c˘ a 0 ≤ z ≤ ≤ 24.
≥ ≥
Ecuat¸ia ¸ia 1.20 se poate scrie astfel: 3x + y + 4xz 4 xz = = 100 Dac˘a x = 0 ¸si y
≥ 0 ⇒ x =
100−y 3+4zz 3+4
100 − y (3 + 4z 4z ) = 100 − y ⇔ x x = = ⇔ x x(3 3 + 4z 4 z
≥
⇒ ∈ 100−y 3+4zz 3+4
x
[1,, [1
100−y 3+4zz 3+4
(1.21)
].
Astfel, pentru fiecare pereche de numere (z, (z, x) vom calcula pe y pe y astfel: y = 100 4xz .
− 3x −
De asemen asemenea, ea, ˆın 1.21 dac˘ a lu˘am x am x = = 0 obt¸inem ¸inem mult¸imea ¸imea de solut¸ii ¸ii (0, (0, 100 100,, z ), ), z
∀ ∈ ∈ N.
25
Algoritm 13 Algoritm pentru descompunerea ˆın factori primi a unui num˘ ar natural
{ } ← ≤ ←
1: Input n 2: j 2 3: while ( ( j j n n)) do mod j j = 0) then 4: if (n mod k 0 5: 6: while ( (n n mod j = 0) do 7: k k k + + 1 8: n n div j 9: end while Output j k 10: 11: end if 12: j j j + + 1 13: end while
← ←
{ }
←
7. S˘a se evalueze complexitatea algoritmului 13, ce realizeaz˘ a descompunerea ˆın factori primi a unui num˘ ar natural. ar 8. Pen Pentru tru fiecare dintre dintre urm˘ atoarele relat¸ii atoarele ¸ii de recurent¸˘ ¸a˘ calculat¸i ¸i complexitatea timp: (a) T T ((n) = 2T T (( n2 ) +
n ; log n
(b) T T ((n) = 16T 16T (( n4 ) + n!; (c) T T ((n) = 3T T (( n3 ) +
√ n;
(d) T T ((n) = 3T T (( n3 ) + n2 . 9. S˘a se calculeze timpul mediu de lucru T T ((n) al algoritmului 14.
Algoritm 14 Algoritm Calcul5
← ←
1: for i 1 1,, n do 2: j n 3: while ( ( j j 1) do 4: ... j 5: j 2 6: end while 7: end for
≥ ← ⌊ ⌋
10. S˘a se calculeze timpul mediu de lucru T T ((n) al algoritmului 15.
Algoritm 15 Algoritm Calcul6 1: i n 2: while ( (ii 1) do 3: j i 4: while ( ( j j n n)) do 5: ... 6: j j 2 7: end while i 8: i 2 9: end while
←
←
≥
≤ ← · ← ⌊ ⌋
26
11. S˘a se rezolve urm˘ atoarele ecuat¸ii atoarele ¸ii recurente:
• T T ((n) = 2 · T T (( • T T ((n) = 3 · T T ((
n ) + n + n lg n, n = 2k ; 2 n ) + c + c n, n = 2k > 1. 2
·
12. S˘a se rezolve ecuat¸ia ¸ia recurent˘ a T T ((n) = n T 2( n2 ), ), n n = = 2k , T T (1) (1) = 6. 13. S˘a se arate c˘ a e x
· = 1 + x + Ω(x Ω(x ) unde x → ∞. 2
14. S˘a se determine primele n eleme elemente nte ale ¸sirurilor sirur ilor ak ¸si si bk date prin urm˘ atoarele relat¸ii atoarele ¸ii de recurent¸˘ ¸a: a˘: 5ak + 3 ak + 3 ak+1 = , bk = , k 0 0,, a0 = 1. (1.22) ak + 3 ak + 1
≥
15. S˘a se verifice dac˘ a urm˘atoarele atoarele afirmat¸ii ¸ii sunt adev˘ arate: arate: (a) n2
3
3
2
O((n ); ∈ O (b) n ∈ O O((n ); (c) 2 O(2 (2 ); ∈ O (d) (n (n + 1)! ∈ O O((n!); (e) ∀f : N −→ R , f ∈ O((n) ⇒ f ∈ O O((n ); ∈ O (f)) ∀f : N −→ R , f ∈ (f O((n) ⇒ 2 ∈ O O(2 (2 ). ∈ O n+1
n
∗
2
2
∗
f
n
27
Capitolul 2 Grafuri. Grafu ri. Grafu Grafuri ri neorientate neorientate 2.1
No¸iuni ¸tiu ni de ba baz˘ z˘ a
Fie V V o mult¸ime ¸ime finit˘a ¸si si ne nevid vid˘ a˘ avˆand and n elemente (V (V = x1, x2 , . . . , xn ). Fie E o o mult¸ime ¸ime finit˘a astfe astfell ˆıncˆat E at E V V V (unde V (unde V V V este este produsul cartezian al mult¸imii V ¸imii V cu c u ea ˆıns˘a¸si) ¸si si E = (x, y ) x, y V (E este este mult¸imea ¸imea perechilor (x, (x, y ) cu proprietatea c˘ a x ¸si si y apart¸in ¸in mult¸imii ¸imii V V ). ).
{
⊆⊆ × × | ∈ }
{
× ×
}
Definit¸ia ¸ia 2.1 Se S e nu nume me¸¸ste st e graf graf o o pereche ordonat˘ a G = (V, E ). Elementele xi V se numesc noduri noduri sau vˆ arfuri . Ele arfuri Elemen mentel telee mult ¸imii E sunt arce sau muchii muchii . O muchie (xk , xl ) E se mai noteaz˘ a ¸si cu [xk , xl ].
∈
∈
nume¸ e¸ste st e ordinul grafului G ¸si si rep reprez rezint˘ int˘a num˘arul arul vˆarfurilor arfurilor acestuia, iar |E | se |V | se num num nu me¸ste st e dimensiunea grafului G ¸si si re repr prez ezint int˘ a˘ num˘arul arul muchiilor/ arcelor grafului G. Graful Φ = (∅, ∅) se num nume¸ e¸ste st e graful vid.
Dac˘a ˆıntr-o per pereche eche [xk , xl ] nu ¸inem ¸t inem cont de ordine atunci graful este neorientat iar perechea reprezint˘ a o muchie ([ ([x xk , xl ] = [xl , xk ]). Dac˘a se introduce un sens fiec˘ arei muchii arei atunci aceasta devine arc iar graf graful ul se nume¸ste ste orientat ([ ([x xk , xl ] = [xl , xk ]). Muchiile ce au acel ac elea¸ ea¸si si vˆarfuri arfuri se spune c˘ a sunt paralele. O muchie de forma [u, [u, u] se num nume¸ e¸ste st e b bu ucl˘ a. Un graf G graf G se num nume¸ e¸ste st e simplu dac˘ a oricare dou˘ a vˆ arfuri ale sale sunt extremit˘ arfuri a¸i at ¸t i pentru cel mult o muchie. Un graf este finit dac˘ a mult¸imile ¸imile V ¸si si E sunt sunt finite. ˆIn conti continuare nuare vom considera un graf neorientat, neorientat, simpl simplu u ¸si si finit. Dac˘ a [x, y ] E vom spune c˘ a x ¸si si y sunt adiacente, iar muchia [x, [x, y ] este incident˘ a cu vˆarfurile arfurile x ¸si si y . x ¸si si y se
∈
Fig. 2.1: a) Un exemplu de graf neorientat cu 6 vˆarfuri arfuri b) Graful complet K 5
28
mai numesc capetele muchiei. Dou˘ a muchii sunt adiacente dac˘ a au un vˆ arf comun. Un graf arf este trivial dac˘ a are un singur vˆarf. arf. Dac˘ a E = atunci graful G = (V, E ) se num nume¸ e¸ste st e graf nul.
∅
Observat¸ia ¸ia 2.1 Un graf neorientat, simplu, finit poate fi utilizat drept model de reprezentare al unei relat ¸ii simetrice peste o mult ¸ime. Definit¸ia ¸ia 2.2 Se nume¸ste ste graf complet de d e ordinul n, ¸si si se noteaz˘ a cu K n , un graf cu proprietatea c˘ a oricare dou˘ a vˆ arfuri distincte ale sale sunt adiacente ( x V, y V, x = y [x, y ] E ). ).
∀ ∈
∈
∈
⇒
Exemplul 2.2 S˘ a consider˘ am grafurile din figura 2.1. [1,, 2] 2],, [1 [1,, 5] 5],, [2 [2,, 3] 3],, [2 [2,, 6] 6],, [3 [3,, 4] 4],, [4 [4,, 5] 5],, [5 [5,, 6] (vezi a) G = (V, E ), V = 1, 2, 3, 4, 5, 6 , E = [1 figuraa 2.1 a)); figur
{
}
{
}
5 vˆ 3 ¸si 5 sunt adiacente (vezi figura 2.1 b)). b) K 5 este graful complet cu 5 arfuri. Vˆ arfurile 3 si 5
Fig. 2.2 Fig. 2.2:: a) Un exe exempl mplu u de graf neorien neorientat tat cu 5 vˆ arfuri.. b) Subgraf arfuri Subgraf al grafului grafului din figura figura 2.1 b). c) Graf part¸ial ¸ial al grafului din figura 2.1 b).
Definit¸ia ¸ia 2.3 Un graf graf part ¸ial al al unui graf dat G = (V, E ) este un graf G1 = (V, E 1 ) unde E 1 E .
⊆
Definit¸ia ¸ia 2.4 Un subgraf al unui graf G = (V, E ) este un graf H = (V 1 , E 1 ) unde V V 1 V subgraf al V 1 iar muchiile din E 1 sunt toate muchiile din E care au ambele extremit˘ at ¸i ˆın mul mult ¸imea t E1 = = E E V ×V = [x, y ] [x, y ] E,x E,x,, y V 1 ). ( E
⊂
|
1
1
{
|
∈
∈ }
Se spune c˘ a graful H ¸imea de vˆarfuri arfuri V 1 ¸si si se not noteaz eaz˘ a˘ H este indus sau generat de submult¸imea H = G V sau H = [V 1 ]G. Iat˘a alte cˆateva ateva notat¸ii ¸ii de dess ˆınt ıntˆ aˆlnite: alnite:
|
1
- G V 1 = subgraful ce se obt¸ine ¸ine din G din G prin eliminarea submult¸imii ¸imii de vˆarfuri V arfuri V 1 (V 1 ); V ); V
−
- G
⊆
− x = subgraful G − {x};
- < E 1 >G = graful part¸ial ¸ial al lui G generat de E 1; 29
- G
− E =< E \ E > ; - G − e = G − {e}, graful part¸ial obt¸inut prin eliminarea unei muchii e. 1
1
G
Exemplul 2.3 Graful part ¸ial din figura 2.2 c) se obt ¸ine din graful 2.1 b) prin ¸stergerea muchiilor [2, 4], [2, 5], [3, 5], [4, 5]. Subgraful din figura 2.2 b) este indus de mult ¸imea V 1 = 1, 3, 4, 5 din graful complet K 5 ( H = K 5 V 1 ).
{
}
|
Definit¸ia 2.5 Gradul unui vˆ arf este egal cu num˘ arul muchiilor incidente cu vˆ arful x ¸si se noteaz˘ a cu d(x) ( d(x) = [x, u] [x, u] arf cu gradul 0 ( d(x) = 0) se E, u V ). Un vˆ nume¸ste vˆ arf izolat .
|{
|
∈
∈ }|
Not˘am δ (G) = min dG (u) u G ¸si ∆(G) = max dG(u) u G .
{
| ∈ }
{
| ∈ }
Fig. 2.3: Graful lui Petersen
Exemplul 2.4 Graful lui Petersen din figura 2.3 este un exemplu de graf trivalent sau cubic (toate vˆ arfurile grafului au acela¸si grad, 3). Propozit¸ia 2.5 Pentru un graf G = (V, E ), V = x1 , x2 , . . . , xn , E = m , avem urm˘ atoarea relat ¸ie:
{
}| |
n
d(xk ) = 2m.
(2.1)
k=1
Astfel ˆın orice graf G exist˘a un num˘ ar par de vˆ arfuri al c˘ aror grad este un num˘ ar impar.
Definit¸ia 2.6 Se nume¸ste secvent ¸˘ a grafic˘ a un ¸sir de numere naturale d1 , d2, . . . , dn cu proprietatea c˘ a ele reprezint˘ a gradele vˆ arfurilor unui graf neorientat. Corolarul 2.6 Pentru ca o secvent ¸˘ a de numere naturale d 1 , d2, . . . , dn s˘ a fie secvent ¸˘ a grafic˘ a, este necesar ca: 1. 2.
∀k = 1, n , d ≤ n − 1; k
n k=1 dk este
un num˘ ar par.
Definit¸ia 2.7 Un lant ¸ L = [v0 , v1 , . . . , vm ] este o succesiune de vˆ arfuri cu proprietatea c˘ a oricare dou˘ a vˆ arfuri vecine sunt adiacente ( [vi , vi+1 ] E, i = 0, m 1). Vˆ arfurile v0 ¸si vm se numesc extremit˘ at ¸ile lant ¸ului , iar m reprezint˘ a lungimea lant ¸ului .
∈
30
∀
−
Dac˘a vˆarfurile v0 , v1, . . . , vm sunt distincte dou˘ a cˆate dou˘ a, lant¸ul se nume¸ste elementar (vi = v j , i, j = 0, m).
∀
Definit¸ia 2.8 Un lant ¸ L pentru care v0 = v m se nume¸ste ciclu . Definit¸ia 2.9 Se nume¸ste ciclu hamiltonian un ciclu elementar ce trece prin toate vˆ arfurile grafului. Un graf ce admite un ciclu hamiltonian se nume¸ste graf Hamilton sau graf hamiltonian . Definit¸ia 2.10 Un lant ¸ L ce cont ¸ine fiecare muchie exact o singur˘ a dat˘ a se nume¸ste lant ¸ eulerian . Dac˘ a v0 = vm ¸si lant ¸ul este eulerian atunci ciclul se nume¸ste ciclu eulerian . Un graf ce cont ¸ine un ciclu eulerian se nume¸ste graf eulerian . Exemplul 2.7 [1, 2, 3, 1, 4] este un exemplu de lant ¸ ˆın graful din figura 2.2 c). Vˆ arfurile 1 ¸si 4 sunt extremit˘ at ¸ile lant ¸ului. Lant ¸ul nu este elementar deoarece vˆ arful 1 se ˆıntˆ alne¸ste de dou˘ a ori, ˆın schimb [1, 2, 3, 4, 1] este un ciclu elementar. [3, 4, 5, 1, 2, 3] este un ciclu hamiltonian ˆın graful din figura 2.2 a). [4, 5, 1, 2, 4, 3] este un ˆ acest graf nu exist˘ lant ¸ eulerian, precum ¸si [2, 4, 3, 2, 1, 5, 4]. In a nici un ciclu eulerian.
Fig. 2.4: Componente conexe
Definit¸ia 2.11 Un graf se nume¸ste conex dac˘ a pentru orice pereche de vˆ arfuri x ¸si y exist˘ a un lant ¸ de la x la y ( x, y V, x y ). Se nume¸ste component˘ a un subgraf conex ˆın a conex˘ a un subgraf conex maximal, adic˘ care nici un vˆ arf din subgraf nu este adiacent cu unul din afara lui prin intermediul unei muchii apart ¸inˆ and grafului init ¸ial.
∀ ∈ ∃
Definit¸ia 2.12 O muchie e E se nume¸ste muchie critic˘ a dac˘ a prin eliminarea acesteia o component˘ a conex˘ a se ˆımparte ˆın dou˘ a sau mai multe componente conexe.
∈
Exemplul 2.8 Graful din figura 2.4 are dou˘ a componente conexe: 1, 2, 3, 4, 5, 6 ¸si 7, 8, 9 . Muchia [2, 5] este muchie critic˘ a.
{
} {
}
Definit¸ia 2.13 Un graf planar este un graf ce poate fi reprezentat ˆın plan astfel ˆıncˆ at muchiile sale s˘ a nu se intersecteze dou˘ a cˆ ate dou˘ a. Definit¸ia 2.14 Un graf G = (V, E ) se nume¸ste graf bipartit dac˘ a exist˘ a 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 ˆıncˆ at orice muchie din E va avea o extremitate ˆın mult ¸imea V 1 ¸si cealalt˘ a extremitate ˆın mult ¸imea V 2 ( [x, y] E avem x V 1 ¸si y V 2 ).
∈
{ ∈
}
∪
∩
31
∅
∅
∀
∈
Propozit¸ia 2.9 Un graf este bipartit dac˘ a ¸si numai dac˘ a nu cont ¸ine cicluri de lungime impar˘ a. Definit¸ia 2.15 Un graf bipartit este complet dac˘ a x V 1 , y V 2 , [x, y] E ( G = a V 1 = p, V 2 = q atunci graful bipartit complet se (V, E ), V = V 1 V 2 , V 1 V 2 = ). Dac˘ noteaz˘ a K p,q .
∪
∩
∅
| |
∀ ∈ ∀ ∈ ∃ | |
∈
Fig. 2.5: Un exemplu de graf bipartit ¸si graf bipartit complet.
ˆ figura 2.5 a) este ilustrat un graf bipartit ( V 1 = 1, 2, 3 , V 2 = 4, 5 ), Exemplul 2.10 In iar ˆın cazul b) avem un graf bipartit complet, K 2,3 .
{
}
{ }
Definit¸ia 2.16 Se nume¸ste izomorfism de la graful G la graful G′ o funct ¸ie bijectiv˘ a ϕ : ′ ′ at [u, v] E (G) V (G) V (G ) astfel ˆıncˆ ϕ([u, v]) E (G ). Dac˘ a exist˘ a un izomorfism de la graful G la graful G′ atunci se spune c˘ a G este izomorf ′ ′ cu G ¸si not˘ am acest lucru astfel: G ≅ G .
→
∈
⇔
∈
Definit¸ia 2.17 Un graf etichetat este un graf ˆın care fiecare muchie ¸si vˆ arf poate avea asociat˘ a o etichet˘ a. Vom prezenta ˆın continuare cˆ ateva operat¸ii dintre cele mai ˆıntˆ a lnite ce se pot efectua asupra unui graf oarecare:
• fiind dat un nod se cere lista vecinilor s˘ai. Aceast˘a operat¸ie este cea mai utilizat˘a pentru o serie ˆıntreag˘ a de algoritmi pe grafuri;
• fiind dat˘a o pereche de noduri {u, v} se cere s˘a se determine dac˘a aceasta constituie o muchie sau un arc al grafului;
• ad˘augarea sau ¸stergerea unui nod sau a unei muchii/arc; • translatarea dintr–un mod de reprezentare ˆın altul. De foarte multe ori modul de reprezentare sub forma c˘ aruia au fost introduse datele, difer˘ a de modul de reprezentare optim recomandat pentru un anumit algoritm. ˆIn aceast˘ a situat¸ie este indicat˘ a transformarea primului mod de reprezentare ˆın modul de reprezentare optim;
• fiind dat˘a o muchie sau un nod se cere o informat¸ie asociat˘a acestui element: de exemplu lungimea muchiei sau distant¸a de la surs˘a la nodul specificat. 32
2.2
Operat¸ii pe grafuri
1. Complementarul grafului G = (V, E ) se define¸ste 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. 2.6: Un exemplu de graf complementar altui graf.
ˆIn figura 2.6 b) este reprezentat graful complementar Gc al grafului G = (V, E ), din figura 2.6 a) (V = 1, 2, 3, 4, 5 , E = [1, 2], [1, 5], [2, 3], [3, 4], [4, 5] ). Conform definit¸iei Gc = (V c , E c ) unde V c = V ¸si E c = [1, 3], [1, 4], [2, 4], [2, 5], [3, 5] .
{
}
{ {
} }
¸ia unui vˆarf v / V pe o muchie [x, y] 2. Graful obt¸inut din G = (V, E ) prin insert i i i i este graful G = (V , E ) unde V = V v , E i = (E [x, y] ) [x, v], [v, y] .
∪ { }
\{
∈
} ∪{
}
∈ E
Considerˆand graful din figura 2.6 a), prin insert¸ia vˆarfului 6 pe muchia [1, 5] se obt¸ine graful din figura 2.7 a). 3. Graful obt¸inut din G = (V, E ) prin contract ¸ia unei muchii u = [x, y] la un vˆarf t este ct ct ct ct graful G = (V , E ), unde V = (V x, y ) t .
\ { } ∪ { }
Fig. 2.7: a) Graful obt¸inut prin inserarea vˆarfului 6 ˆın graful din figura 2.6 a). b) Graful obt¸inut prin contract¸ia muchiei [3, 4] ˆın graful din figura 2.6 a).
ˆIn figura 2.7 b) este reprezentat graful obt ¸inut prin contract¸ia muchiei [3, 4] din graful G = (V, E ) (vezi figura 2.6 a)). 4. Se nume¸ste 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 figura 2.8).
| | | |
{
| ∈
}
5. Definim graful total al unui graf G = (V, E ) ca fiind graful GT = (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 .
∪
{
| ∈ ∪
}
33
Fig. 2.8: Figura b) prezint˘a graful reprezentativ al muchiilor grafului din figura a).
Fig. 2.9: a) Un exemplu de graf neorientat. b) Graful total al grafului din figura a).
Graful total GT = (V T , E T ), unde V T = 1, 2, 3, 4, v1, v2 , v3, v4 , ¸si E = [1, 2], [1, 4], [2, 3], [3, 4], [v1, v2], [v1 , v4 ], [v2, v3], [v3 , v4 ], [v1 , 1], [v1, 2], [v2 , 2], [v2 , 3], [v3 , 3], [v3 , 4], [v4 , 4], [v4, 1] , corespunde grafului G = (V, E ), V = 1, 2, 3, 4 , E = [1, 2], [1, 4], [2, 3], [3, 4] (vezi figura 2.9).
{
}
{
}
{
{
}
}
6. Reuniunea ¸si intersect ¸ia a dou˘a grafuri se definesc astfel:
• dac˘a V = V atunci: G ∪ G = (V , E ∪ E ), G ∩ G = (V , E ∩ E ); • dac˘a V ∩ V = ∅ atunci: G ∪ G = (V ∪ V , E ∪ E ), G ∩ G = (V ∩ V , E ∩ E ); • dac˘a V ∩ V = ∅ atunci G ∪ G se nume¸ste reuniune disjunct˘ a a grafurilor G ¸si G , iar G ∩ G este graful vid. 1
2
1
2
1
2
1
2
1
2
1
1
2
1
2
1
1
2
2
1
1
2
2
1
1
1
2
2
1
2
1
2
1
2
7. Definim suma grafurilor G1 ¸si G2 ca fiind graful complementar al reuniunii complementarelor celor dou˘ a grafuri: G1 G2 = (Gc1 Gc2 )c.
⊕
∪ K ⊕ K
c Pentru grafurile complete K p ¸si K q avem: p K qc )c = K p+q . Un alt q = (K p exemplu de sum˘ a a dou˘a grafuri poate fi urm˘ arit ˆın figura 2.10.
∪
8. Se nume¸ste produsul cartezian al grafurilor G1 = (V 1 , E 1 ) ¸si G2 = (V 2 , E 2 ), graful G1 G2 = (V × , E × ), unde V × = V 1 V 2 ¸si E × = [(u1, u2), (v1 , v2)] u1, v1 V 1 , u2, v2 V 2 ; u1 = v 1 ¸si [u2 , v2 ] E 2 sau u2 = v 2 ¸si [u1 , v1 ] E 1 .
×
∈
×
{ ∈ }
34
|
∈
∈
Fig. 2.10: a) Dou˘a grafuri neorientate, G1 ¸si G2 . b) Grafurile complementare Gc1 ¸si Gc2 . c) Reuniunea grafurilor G c1 ¸si G c2 . d) Graful complementar al grafului reuniune (Gc1 Gc2 )c .
∪
Fig. 2.11: Graful produs cartezian a dou˘a grafuri
Fie G1 = (V 1 , E 1 ) unde V 1 = u1 , v1 ¸si E 1 = [u1 , v1 ] , ¸si G2 = (V 2, E 2 ), unde V 2 = u2, v2 , w2 ¸si E 2 = [u2 , v2], [v2 , w2] , dou˘a grafuri neorientate. Atunci graful produs cartezian al grafurilor G1 ¸si G2 este G1 G2 = (V × , E × ) unde:
{
}
{
{
} }
{
}
×
V × = (u1 , u2), (u1, v2 ), (u1, w2 ), (v1, u2 ), (v1 , v2), (v1 , w2 ) , ¸si
{ } E = {[(u , u ), (v , u )], [(u , u ), (u , v )], [(u , v ), (v , v )], [(u , v ), (u , w )], [(u , w ), (v , w )], [(v , u ), (v , v )], [(v , v ), (v , w )]} (vezi figura 2.11). ×
1
1
2
2
1
1
2
2
1
1
2
2
1
1
2
2
1
1
2
1
2
1
2
1
2
1
2
2
ad˘ acina p˘ atrat˘ a a grafului G, un graf neorientat H cu proprietatea c˘ 9. Se nume¸ste r˘ a 2 2 H = G (H = H H ).
×
10. Operat¸ia de compunere a dou˘ a grafuri neorientate G1 = (V 1 , E 1 ) ¸si G2 = (V 2 , E 2 ), notat˘ a cu G1 [G2], se realizeaz˘a astfel: vˆarful (x1 , y1) este adiacent cu vˆ arful (x2 , y2) ˆın graful rezultat dac˘ a vˆ arful x1 este adiacent cu vˆ arful x2 ˆın graful G1 sau (x1 = x2 ¸si vˆarful y 1 este adiacent cu vˆ arful y2 ˆın graful G2 ) (vezi figura 2.12).
35
Fig. 2.12: Graful rezultat ˆın urma compunerii grafurilor G 1 ¸si G 2 din figura 2.11
2.3
Moduri de reprezentare
Fig. 2.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 aceast˘ a ordonare. Astfel, numerot˘ am ˆın mod arbitrar vˆ arfurile grafului cu 1, 2, . . . , V .
| | 1. Matricea de adiacent atratic˘a A ∈ M (N), n = |V |, unde a ¸˘ a - este o matrice p˘
=1 dac˘a exist˘a o muchie ˆıntre nodurile numerotate i ¸si j (ˆıntre xi ¸si x j ), sau ai,j = 0 dac˘a nu exist˘a o muchie ˆıntre aceste noduri. 1 , dac˘ a [xi , x j ] E ai,j = 0 , dac˘ a [xi , x j ] / E. n×n
i,j
∈ ∈
Observat¸ia 2.11 Pentru un graf neorientat aceast˘ a matrice este simetric˘ a ( ai,j = a j,i). Exemplul 2.12 Matricea de adiacent ¸˘ a A corespunz˘ atoare grafului din figura 2.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
36
1 0 0 0 0 1 0 0
0 1 1 0 0 0 1 0
0 0 0 1 0 0 1 1
0 0 0 0 1 1 0 0
0 0 0 0 0 1 0 0
Fig. 2.14: Un exemplu de graf neorientat ponderat
Matricea de adiacent¸a˘ are un num˘ ar de n 2 elemente, iar elementele neredundante sunt ˆın num˘a r de n(n2−1) . Prin urmare complexitatea-spat¸iu a matricii de adiacent¸a˘ este O(n2).
¸˘ a ce se obt¸ine ˆın mod 2. Matricea costurilor - reprezint˘ a o variant˘ a a matricei de adiacent natural luˆ andu-se ˆın considerare graful ponderat: fiec˘ arei muchii i se ata¸seaz˘a un cost d (d R), iar valorile matricii C (C n×n (R)) se definesc astfel:
∈
∈ M
ci,j = sau
∞ −∞
0 , dac˘ a xi = x j + , dac˘ a [xi , x j ] / E d > 0 , dac˘ a [xi , x j ] E
(2.2)
, dac˘a xi = x j , dac˘ a [xi , x j ] / E d > 0 , dac˘ a [xi , x j ] E.
(2.3)
∈ ∈
0
ci,j =
∈ ∈
Exemplul 2.13 Matricea costurilor corespunz˘ atoare grafului din figura 2.14 are urm˘ atoarele
valori:
C =
∞ ∞ ∞ ∞ ∞ ∞ ∞∞ ∞∞ ∞∞ ∞∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞∞ ∞∞ ∞∞ ∞∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ 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
Observat¸ia 2.14 Notat ¸ia (2.2) este folosit˘ a ˆın aplicat ¸ii ˆın care se opereaz˘ a cu drumuri de lungime minim˘ a iar notat ¸ia (2.3) este folosit˘ a ˆın aplicat ¸ii ˆın care se opereaz˘ a cu drumuri de lungime maxim˘ a ˆıntre dou˘ a noduri. Matricea costurilor va ocupa un spat¸iu de memorie mai mare decˆa t cel ocupat de matricea de adiacent¸a˘: pentru reprezentarea unui element al matricii de adiacent¸˘ a este suficient un bit , pe cˆand pentru reprezentarea unui element al matricii costurilor se folose¸ste un byte , un ˆıntreg (lung) sau un num˘ a pe ar real , ˆın funct¸ie de valoarea maxim˘ care o poate lua d. 37
Observat¸ia 2.15 Matricea costurilor pentru un graf neorientat este simetric˘ a. Prin urmare pentru reprezentarea unei matrici de adiacent ¸˘ a de dimensiuni n n sunt n necesari n 8 octet¸i, pe cˆand pentru reprezentarea unei matrici de costuri cu elemente numere ˆıntregi sunt necesari 2 n 2 octet¸i, dac˘a presupunem c˘ a un num˘ ar ˆıntreg f˘ar˘a semn este p˘ astrat pe 2 octet¸i.
×
·⌈ ⌉
·
3. Liste de adiacent ¸˘ a - pentru fiecare nod se ret¸ine lista tuturor nodurilor sale adiacente. Mai exact, unui nod xk se ata¸seaz˘ a lista tuturor vecinilor s˘ ai. Aceast˘a reprezentare se preteaz˘ a mai bine pentru grafuri cu un num˘ ar mare de noduri ¸si ˆ un num˘ar mic de muchii (graf rar ). In cazul unui graf neorientat, num˘ arul elementelor din listele de adiacent a de dou˘ a ori, ¸˘ a este 2 E , deoarece o muchie [u, v] va fi prezent˘ atˆat ˆın lista de adiacent¸a˘ a nodului u cˆat ¸si ˆın lista de adiacent¸a˘ a nodului v.
·| |
Exemplul 2.16 Listele de adiacent ¸˘ a pentru graful din figura 2.13 sunt: 1: 2: 3: 4:
(2, (1, (1, (1,
3, 4) 5) 5) 6)
5: 6: 7: 8:
(2, 3, 7) (4, 7, 8) (5, 6) (6)
Aceste liste de adiacent ¸˘ a (sau liste de vecini) pot fi reprezentate, ca structuri de date, prin intermediul tablourilor sau prin intermediul listelor simplu sau dublu ˆınl˘ ant ¸uite . Pentru reprezentarea ce utilizeaz˘ a tablouri, se vor defini dou˘ a tablouri notate Cap ¸si List (Cap 1×n (N), List 2×2m (N), n = V , m = E ) unde:
∈M
∈M
| |
| |
0 , dac˘ a nodul respectiv nu are vecini Capk = j , unde j indic˘a num˘arul coloanei din matricea List unde se afl˘a memorat primul vecin al vˆarfului xk . List1,j = indicele unui vˆarf ce se afl˘a ˆın lista de vecini a vˆ arfului xk .
List2,j =
0 , dac˘ a vˆ arful respectiv este ultimul (aceast˘ a valoare are aceea¸si semnificat¸ie ca ¸si valoarea NIL sau NULL utilizat˘a ˆın lucrul cu pointeri) i , unde i reprezint˘ a num˘arul coloanei unde se afl˘ a memorat urm˘ atorul nod (vecin) din lista de vecini a nodului xk .
Exemplul 2.17 Pentru exemplul din figura 2.13 avem urm˘ atoarea configurat ¸ie pentru cei doi vectori considerat ¸i, Cap ¸si List: Nod 1 Cap 1
List =
2 4
3 6
4 8
5 10
6 13
7 16
8 18
2 3 4 1 5 1 5 1 6 2 3 7 4 7 8 5 6 6 2 3 0 5 0 7 0 9 0 11 12 0 14 15 0 17 0 0
De exemplu, ˆın limbajul C putem defini urm˘ atoarele structuri de date, avˆ and rolul de a ne ajuta la ment¸inerea ˆın memorie a listelor de adiacent¸a˘ ale unui graf oarecare folosind liste liniare simplu ˆınl˘ant¸uite (a se vedea figura 2.15): 38
1
2
3
4
2
1
5
NULL
3
1
5
NULL
4
1
6
NULL
5
2
3
7
NULL
6
4
7
8
NULL
7
5
6
8
6
NULL
NULL
NULL
Fig. 2.15: Liste de adiacent¸a˘
t y pe de f s t r u c t nod in t nodeIndex ; struct nod next ; NOD;
{
}
∗
#define MAXN 100 NOD v e c i n i [MAXN] ;
∗
NOD* vecini[MAXN] este un array (tablou) de pointeri; un element al tabloului vecini[k]
p˘astreaz˘a capul listei de vecini ai nodului de indice k. struct nod *next reprezint˘ a un pointer c˘ atre urm˘ atoarea ˆınregistrare de tip NOD, ˆınregistrare ce cont¸ine informat¸ii referitoare la urm˘ atorul vecin. nodeIndex p˘ astreaz˘a indicele (eticheta) unui nod. Cantitatea de memorie necesar˘ a pentru p˘ astrarea listelor de adiacent¸a˘ se poate aproxima astfel:
• reprezentarea folosind matricile Cap ¸si List - putem presupune c˘a n, m ≤ 65535 adic˘a sunt suficient¸i 2 octet¸i pentru reprezentarea acestor numere ˆın memorie (2 − 1 = 65535). ˆIn aceast˘ a situat¸ie, cantitatea de memorie necesar˘ a pentru 16
matricea Cap este 2n octet¸i, iar pentru matricea List este 8m octet¸ i. Astfel complexitatea-spat¸iu este O(n + m).
• reprezentarea folosind pointeri - presupunem c˘a folosim 2 octet¸i pentru informat¸ia
util˘a ¸si 2 octet¸i pentru informat¸ia de leg˘atur˘a. Num˘ arul de octet¸i folosit¸i pentru reprezentarea unui pointer ˆın limbajul C poate depinde de modelul de memorie ales (de exemplu small , huge etc.) sau de dimensiunea instruct¸iunilor (pe 16, 32 sau 64 de bit¸i). S˘a presupunem c˘ a se utilizeaz˘ a 2n octet¸i pentru vectorul ce cont¸ine adresele primului element (capul) al fiec˘ arei liste de vecini, ¸si 8m octet¸i pentru ˆınregistr˘arile listelor de vecini. Din aceast˘ a cantitate de memorie, 4m reprezint˘ a informat¸ie util˘a (indicii vecinilor), iar 2n + 4m informat¸ie auxiliar˘a (pointeri).
4. Lista de muchii - ˆıntr-o structur˘a de date se p˘ astreaz˘a lista tuturor muchiilor grafului, practic, pentru fiecare muchie fiind memorate valorile indicilor nodurilor-extremit˘ a¸ti ale acesteia. Aceasta constituie cea mai simpl˘ a modalitate de reprezentare a unui graf. 39
Operat¸ia de ad˘augare a unui nod sau a unei muchii se realizeaz˘ a ˆıntr-un timp constant, alte operat¸ii fiind mai costisitoare: de exemplu, determinarea listei de vecini a unui nod necesit˘a un timp Ω(m).
Exemplul 2.18 Pentru p˘ astrarea ˆın memorie se poate folosi o matrice M cu dou˘ a linii ¸si E coloane ( M 2×|E | (N)), unde:
| |
∈ M
arfului ce reprezint˘ a prima extremitate a muchiei k; M 1,k - indicele vˆ arfului ce reprezint˘ a cea de-a doua extremitate a muchiei k. M 2,k - indicele vˆ M =
1 1 1 2 3 4 5 6 6 2 3 4 5 5 6 7 7 8
Complexitatea timp pentru diverse operat¸ii efectuate asupra fiec˘ aruia dintre structurile de date utilizate de modurile de reprezentare amintite se poate sintetiza astfel: Gradul unui nod x i (xi , x j ) E ? Urm˘atorul vecin al lui x i
∈
2.4
Matricea de adiacent¸a˘ O(n) O(1) O(n)
Liste de vecini O(d(xi )) O(d(xi )) O(d(xi ))
Lista de muchii O(m) O(m) O(m)
Parcurgerea grafurilor
Pentru un graf dat este important s˘ a se stabileasc˘ a o modalitate sistematic˘ a de vizitare a tuturor vˆ arfurilor grafului, parcurgere ce se realizeaz˘ a ˆın scopul prelucr˘ arii informat¸iei cont¸inute de c˘ atre acestea.
2.4.1
Parcurgerea ˆın l˘ a¸t ime (BFS-Breadth First Search)
at ¸ime viziteaz˘a nodurile grafului ˆın felul urm˘ Metoda de parcurgere ˆın l˘ ator (a se vedea algoritmul 16):
• se viziteaz˘a mai ˆıntˆai vˆarful de pornire (s˘a ˆıl not˘am k); • urmeaz˘a, ˆın ordine, tot¸i vecinii ˆınc˘a nevizitat¸i ai nodului k; • se continu˘a cu vecinii ˆınc˘a nevizitat¸i ai acestora, ¸s.a.m.d. Pentru graful considerat ˆın figura 2.13 ordinea de parcurgere este urm˘ atoarea: 1, 2, 3, 4, 5, 6, 7, 8. Dac˘a consider˘ am muchiile folosite ˆın timpul parcurgerilor (muchiile prin intermediul adure de parcurgere/vizitare/acoperire . ˆIn c˘arora s-a ˆınaintat ˆın graf ) se obt¸ine un arbore/p˘ at ¸ime rezultat ˆın urma parcurgerii grafigura 2.16 este reprezentat arborele de acoperire ˆın l˘ fului din exemplu. Algoritmul de parcurgere utilizeaz˘ a o structur˘ a de date de tip coad˘ a ˆın care vor fi memorate nodurile vizitate, dar care nu au fost ˆınc˘ a prelucrate (nu au fost cercetat¸i vecinii lor). Reamintim c˘ a num˘arul de noduri din mult¸imea V este n. Vectorul vizitat p˘astreaz˘a situat¸ia vizit˘arii nodurilor grafului G, astfel: 1 , dac˘ a nodul k a fost vizitat vizitatk = 0 , dac˘ a nodul k nu a fost vizitat. Implementarea ˆın limbajul C a algoritmului 16 este urm˘ atoarea:
40
Fig. 2.16: Arbore de acoperire ˆın l˘a¸time
Algoritm 16 Algoritm de vizitare ˆın l˘ a¸time 1: procedure BFS(k, n,V ecin)
k - nodul de la care ˆıncepe vizitarea Input: n - num˘ arul de noduri din graf V ecin - matricea de adiacent¸˘a a grafului 2: for i 1, n do 3: vizitati 0
←
←
4: end for 1 5: vizitatk Q k 6: 7: call V izitare(k) 8: while (Q = ) do 9: Q k 10: for i 1, n do 11: if ((vizitati = 0) (vecink,i = 1)) then 12: vizitati 1 13: Q i 14: call V izitare(i) 15: end if 16: end for 17: end while 18: end procedure
⇐
←
∅ ⇒ ←
⇐
←
∧
⊲ marcarea nodului curent ca fiind vizitat ⊲ inserarea nodului curent k ˆın coad˘ a ⊲ vizitarea nodului curent ⊲ extragere nod curent din coad˘a
⊲ marcarea nodului i ca fiind vizitat ⊲ inserarea nodului i ˆın coad˘ a ⊲ vizitarea nodului i
Listing 2.1: parcurgerebf.c #include #include #define MAXN 100 #define TRUE 1 #define FALSE 0
in t n ; char v ec in [MAXN] [MAXN] ; in t coa da [MAXN] ; in t f i r s t ; in t l a s t ;
// // // // //
Numarul de n o d u r i d i n g r a f Ma tr ice a de ad ia ce nt a St ru ct ur a de da te c e pa s tr e az a el e me n te l e c o z i i I n d i c e l e pr i m u l u i element d i n coada I n d i c e l e u l t i m u l u i element d i n coada
41
} /
∗∗ ∗ Se ∗/
c i t e s c n um ar ul d e n o du r i precum s i m a tr i ce a d e a d i a c en t a .
void readI nput ( void ) in t i , j ;
{
p r i n t f ( ” n = ” ) ; s c a n f ( ”%d ” ,& n ) ; fo r ( i = 0 ; i < n 1 ; i ++) fo r ( j = i + 1 ; j < n ; j ++) p r i n t f ( ” a[%d,%d ] = ” , i , j ) ; sc an f ( ”%d” , &ve ci n [ i ] [ j ] ) ; veci n [ j ] [ i ] = veci n [ i ] [ j ] ;
−
{
}
}
void main( void ) r e a d I n pu t ( ) ; i n i t Q ue u e ( ) ; bfs (0) ;
{
}
• ˆIn exemplul anterior, funct¸ia de citire readInput() este una foarte simpl˘a:
se cite¸ste mai ˆıntˆai num˘arul de noduri al grafului, iar apoi, se citesc elementele matricei de adiacent¸a˘ a grafului. Variabilele n ¸si vecin sunt declarate drept variabile globale. Deoarece matricea vecin este simetric˘ a , pentru a reduce num˘ a rul de citiri, se vor solicita numai valorile aflate deasupra diagonalei principale: void readI nput ( void ) in t i , j ;
{
p r i n t f ( ” n = ” ) ; s c a n f ( ”%d ” ,& n ) ; fo r ( i = 0 ; i < n 1 ; i ++) fo r ( j = i + 1 ; j < n ; j ++) p r i n t f ( ” a[%d,%d ] = ” , i , j ) ; sc an f ( ”%d” , &vec in [ i ] [ j ] ) ; vec in [ j ] [ i ] = veci n [ i ] [ j ] ;
−
}
{
}
• Funct¸ia memset() este o funct¸ie de bibliotec˘a a c˘arei declarat¸ie poate fi g˘asit˘a ˆın header-
ele mem.h ¸si string.h la Borland C sau memory.h ¸si string.h la Visual C++ 2010 1. Funct¸ia respectiv˘ a prezint˘ a urm˘atoarea semn˘ atur˘ a2 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 identificat˘ a de pointerul dest.
• vida este o variabil˘a global˘a ce ia valoarea true atunci cˆand coada nu cont¸ine nici un element, sau valoarea false ˆın caz contrar.
1 2
http://msdn.microsoft.com/en-us/library/1fdeehz6.aspx http://en.wikibooks.org/wiki/C_Programming/Strings#The_memset_function
43
• Structura de date abstract˘a de tip coad˘a este implementat˘a static sub forma unei cozi circulare.
• Vizitarea ˆıncepe de la nodul 0 (bfs(0)). • Subrutina vizitare(), ˆın principiu, afi¸seaz˘a valoarea etichetei nodului trimis ca argument, ˆıns˘a poate efectua ¸si alte prelucr˘ ari necesare asupra nodului curent, ˆın funct¸ie de cerint¸ele specifice ale algoritmului.
a de explorare Algoritmul BFS este utilizat de c˘atre metoda Branch-and-Bound ca metod˘ a spat¸iului solut¸iilor, cˆat ¸si ˆın cadrul problemelor de determinare a distant ¸ei minime de la un vˆarf la toate celelalte vˆ arfuri, ˆın cazul ˆın care lungimea unui drum dintre dou˘ a noduri se consider˘a ca fiind egal˘ a cu num˘arul de noduri intermediare ale acestuia.
2.4.2
Parcurgerea D (D - Depth)
at ¸ime (BFS): Metoda de parcurgere D este asem˘ an˘atoare cu metoda de parcurgere ˆın l˘
• la ˆınceput se viziteaz˘a vˆarful de pornire (notat cu k); • urmeaz˘a, ˆın ordine, tot¸i vecinii ˆınc˘a nevizitat¸i ai nodului de pornire; • la pasul urm˘ator se consider˘a ultimul nod vizitat v ¸si se ˆıncearc˘a vizitarea vecinilor ˆınc˘a
nevizitat¸i ai acestuia. Dac˘ a nodul v nu mai are vecini nevizitat¸i, se continu˘a 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 l˘ at¸ ime (a se vedea sect¸iunea 2.4.1) ¸si metoda de parcurgere ˆın adˆ ancime (a se vedea sect¸iunea 2.4.3). Pentru un nod, se viziteaz˘ a tot¸i vecinii ˆınc˘a nevizitat¸i ai acestuia, ˆıns˘ a spre deosebire de metoda de parcurgere ˆın l˘ a¸time unde ordinea ˆın care au fost vizitate nodurile se p˘ astreaz˘a ¸si la parcurgere, la metoda de parcurgere D se prelucreaz˘ a mereu ultimul nod vizitat. Pentru aceasta, ˆın algoritmul BFS se ˆınlocuie¸ste structura de date de tip coad˘ a, cu o structur˘a de date de tip stiv˘ a ¸si se obt¸ine algoritmul D (a se vedea algoritmul 17).
Fig. 2.17: Arbore de acoperire pentru parcurgerea D
ˆIn figura 2.17 poate fi urm˘ arit arborele de acoperire pentru parcurgerea D a grafului G prezentat ˆın figura 2.13. 44
Algoritm 17 Algoritm de vizitare D 1: procedure D(k, n, V ecin) 2: for i 1, n do 3: vizitati 0 4: end for 1 ⊲ marcarea nodului curent ca fiind vizitat 5: vizitatk 6: S k ⊲ inserarea nodului curent k ˆın stiv˘a 7: call V izitare(k) ⊲ vizitarea nodului curent 8: while (S = ) do S k ⊲ extragerea nodului curent din stiv˘a 9: for j 1, n do 10: 11: if ((vizitat j = 0) (vecink,j = 1)) then ⊲ se cauta un vecin nevizitat 12: vizitat j 1 ⊲ marcarea nodului j ca fiind vizitat 13: S j ⊲ inserarea nodului j in stiv˘a ⊲ vizitarea nodului j 14: call V izitare( j) end if 15: 16: end for 17: end while 18: end procedure
←
←
←
⇐
∅ ⇒ ←
⇐
2.4.3
←
∧
Parcurgerea ˆın adˆ ancime (DFS-Depth First Search)
ˆIn cadrul acestei metode se va merge ˆın adˆ ancime ori de cˆate ori este posibil: prelucrarea unui vˆarf const˘ a ˆın prelucrarea primului dintre vecinii s˘ ai ˆınc˘a nevizitat¸i.
• se viziteaz˘a vˆarful de pornire (notat cu k); • urmeaz˘a, primul vecin ˆınc˘a nevizitat al acestuia; • se caut˘a primul vecin, ˆınc˘a nevizitat, al primului vecin nevizitat al nodului de start, ¸s.a.m.d.;
• se merge ˆın adˆancime pˆan˘a cˆand se ajunge la un vˆarf ce nu are vecini, sau pentru care tot¸i vecinii s˘ai au fost vizitat¸i. ˆIn acest caz, se revine ˆın nodul s˘au p˘ arinte (nodul din care a fost vizitat nodul curent), ¸si se continu˘ a algoritmul, cu urm˘atorul vecin ˆınc˘a nevizitat al nodului curent.
Pentru graful considerat (vezi figura 2.13), ˆın urma parcurgerii ˆın adˆ ancime, vom obt¸ine nodurile ˆın ordinea urm˘atoare: 1, 2, 5, 3, 7, 6, 4, 8 (a se vedea figura 2.18). Dac˘ a ne propunem s˘a vizit˘am exact o singur˘ a dat˘ a toate nodurile unui graf, aplicˆ and algoritmul DFS de cˆate ori adure de este nevoie, ¸si select˘am numai muchiile utilizate ˆın timpul explor˘ arii, rezult˘ a o p˘ arbori . Fiecare dintre ace¸sti arbori constituie un arbore de acoperire ˆın adˆ ancime . ˆIn urma parcurgerii ˆın adˆ atoarele caancime , muchiile unui graf pot fi clasificate ˆın urm˘ tegorii: 1. muchie a arborelui de acoperire - muchia [u, v] este o muchie a arborelui de acoperire dac˘a dfs(u) apeleaz˘ a direct dfs(v) sau invers; 2. muchie de ˆıntoarcere - muchia [u, v] este o muchie de ˆıntoarcere dac˘ a dfs(u) apeleaz˘ a indirect dfs(v) ( x V a.ˆı. df s(u) df s(x) df s(v)) sau invers, dfs(v) apeleaz˘ a indirect dfs(u).
∃ ∈
45
Fig. 2.18: Arbore de acoperire ˆın adˆancime
Exemplul 2.19 Pentru graful din figura 2.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; a) Algoritm 18 Algoritm de vizitare ˆın adˆancime (varianta recursiv˘ 1: procedure DFS(k, n, V ecin)
k - nodul curent ce se viziteaz˘ a Input: n - num˘ arul de noduri din graf V ecin - matricea de adiacent¸˘a a grafului 2: vizitatk 1 ⊲ marcarea nodului curent ca fiind vizitat 3: call V izitare(k) ⊲ vizitarea nodului curent 4: for i 1, n do 5: if ((vizitati = 0) (vecink,i = 1)) then 6: call DF S (i, n, vecin) ⊲ apelul recursiv al subrutinei DF S pentru nodul i
←
←
∧
7: end if 8: end for 9: end procedure
Implementarea algoritmului se poate realiza fie ˆın variant˘ a recursiv˘ a (a se vedea algoritmul ˆ 18), fie ˆın variant˘ a nerecursiv˘ a (a se vedea algoritmul 19). In mod asem˘ an˘ator ca ¸si la algoritmul de parcurgere ˆın l˘ a¸time, vectorul vizitat gestioneaz˘ a situat¸ia vizit˘arii nodurilor grafului G: 1 , dac˘ a nodul k a fost vizitat vizitatk = 0 , ˆın caz contrar. Subrutina DFS (algoritmul 18) ˆıncepe cu marcarea no dului curent ca fiind vizitat (vizitatk 1) (linia 2) ¸si apelul procedurii Vizitare() (call Vizitare(k)) (linia 3). Se caut˘ a apoi primul vecin nevizitat i al vˆarfului curent k (nod ce verific˘ a condit¸ia (vizitati = 0) (vecink,i = 1)) ¸si se reia secvent¸a de operat¸ii pentru nodul i cu aceast˘ a proprietate, printr-un apel recursiv (call DFS(i)) (linia 6).
←
∧
46
Enunt¸ul repetitiv for i 1, n (linia 4) poate fi optimizat astfel ˆıncˆ at s˘a nu se mai verifice toate vˆ arfurile grafului, ci numai nodurile adiacente cu nodul curent: ˆın aceast˘ a situat¸ie reprezentarea cu liste de adiacent ¸˘ a este optim˘ a din punct de vedere al num˘ arului de operat¸ii efectuate, fiind preferat˘ a reprezent˘ arii prin matricea de adiacent¸a˘.
←
Algoritm 19 Algoritm de vizitare ˆın adˆancime (varianta nerecursiv˘ a) 1: procedure DFS Nerecursiv(k,n,V ecin)
k - nodul de la care se porne¸ste vizitarea Input: n - num˘ arul de noduri din graf V ecin - matricea de adiacent¸˘a a grafului 1, n do 2: for i vizitati 0 3:
←
←
4: end for 5: vizitatk 1 6: call V izitare(k) S k 7: 8: found f alse 9: while (S = ) do 10: if ( found) then 11: S k 12: end if 13: i 1 14: found f alse 15: while ((i n) (found = f alse)) do 16: if ((vizitati = 0) (vecink,i = 1)) then vizitati 1 17: 18: S k 19: call V izitare(i) 20: k i found true 21: else 22: 23: i i + 1 24: end if 25: end while 26: end while 27: end procedure
←
⇐
← ∅ ¬ ⇒ ← ←
≤ ∧ ∧ ← ⇐ ← ← ←
⊲ marcarea nodului curent ca fiind vizitat ⊲ vizitarea nodului curent ⊲ inserarea nodului curent k ˆın stiv˘a ⊲ cˆat timp stiva nu este vid˘a ⊲ extragerea nodului curent din stiv˘a
⊲ marcarea nodului i ca fiind vizitat ⊲ inserarea nodului curent k ˆın stiv˘a ⊲ vizitarea nodului i
Algoritmul 19 utilizeaz˘ a o structur˘ a de date de tip stiv˘ a S pentru a p˘ astra tot timpul nodul grafului din care s-a ajuns la nodul curent (tat˘ al nodului curent din arborele de acoperire ˆın adˆancime). Secvent¸a de instruct¸iuni 19.13-19.25 nu este optim˘ a deoarece, de fiecare dat˘ a cˆand se revine la un nod p˘ arinte, se verific˘ a ˆıntreaga mult¸ime de noduri V pentru a identifica un vecin nevizitat al nodului curent. ˆIn vederea reducerii num˘ arului de verific˘ ari, se poate utiliza reprezentarea prin liste de adiacent ¸˘ a, ¸si se salveaz˘a pe stiv˘a, pe lˆang˘ a valoarea nodului curent a ˆıl not˘a m cu u), astfel ˆıncˆ at, la revenire, k, valoarea vecinului nevizitat g˘asit al acestuia (s˘ s˘a se continue c˘ autarea urm˘ atorului vecin nevizitat al nodului curent k, pornind de la acest nod u. Dac˘a nu mai exist˘ a nici un vecin nevizitat al vˆ arfului k (linia 10), atunci se revine la p˘ arintele nodului curent, p˘ astrat pe stiv˘ a. Algoritmul se opre¸ste ˆın cazul ˆın care stiva este vid˘a (atunci cˆ and nodul curent este r˘ ad˘acina arborelui de vizitare ˆın adˆ ancime). Am ales ˆın vederea implement˘arii, varianta recursiv˘ a pentru u¸surint¸a de programare ¸si elegant¸a ei. Prezent˘ am ˆın continuare aceast˘ a implementare a algoritmului 18 ˆın limbajul C : 47
2.5
Componente conexe
Definim pe mult¸imea vˆarfurilor V a unui graf neorientat G o relat¸ie ρ ˆın felul urm˘ ator: xρy dac˘a x = y sau exist˘a ˆın G un lant¸ de la x la y. Se demonstreaz˘ a foarte u¸sor c˘ a relat¸ia ρ este o relat¸ie de echivalent¸˘a (exercit¸ iu). Se cunoa¸ste faptul c˘ a o relat¸ie de echivalent¸a˘ determin˘ a pe mult¸imea pe care este definit˘ a o partit¸ie. Componentele conexe vor fi elementele acestei partit¸ii, formate din vˆ arfurile ce sunt echivalente ˆıntre ele (conform relat¸iei de echivalent¸a˘). Cu alte cuvinte, exist˘ a o partit¸ie a mult¸imii vˆarfurilor V m
V =
V i , V i
i=1
∩ V = ∅, ∀i, j = 1, m , i = j
(2.4)
∩ E = ∅, ∀i, j = 1, m , i = j
(2.5)
j
¸si o partit¸ie a mult¸imii muchiilor E m
E =
E i , E i
i=1
j
astfel ˆıncˆat fiecare graf Gi = (V i , E i ), i = 1, m, este conex (E i = E V i ×V i ).
∀
|
Algoritm 20 Algoritm pentru determinarea componentelor conexe 1: procedure ComponenteConexe(n,V ecin)
n - num˘ arul de noduri din graf V ecin - matricea de adiacent¸a˘ a grafului for i 1, n do vizitati 0
Input:
←
2: 3: 4: end for 5: cmp conex nr 0 6: for i 1, n do 7: if (vizitati = 0) then cmp conex nr cmp conex nr + 1 8: ⊲ determinarea componentei conexe ce cont¸ine nodul i 9: call DF S (i, n, V ecin) 10: end if 11: end for 12: end procedure
←
← ←
←
Pentru determinarea componentelor conexe vom utiliza meto da de vizitare ˆın adˆ ancime a unui graf. Pa¸sii algoritmului descri¸si ˆın limbaj natural sunt urm˘ atorii (algoritmul 20 prezint˘ a descrierea formalizat˘ a ˆın pseudo-cod): Pas 1. La ˆınceput, toate nodurile sunt marcate ca nevizitate. Pas 2. Se caut˘ a un nod ˆınc˘ a nevizitat. Pas 3. ˆIncepˆand cu acesta se parcurg toate nodurile accesibile ¸si nevizitate, avˆ and grij˘ a s˘a marc˘am vizitarea acestora. Toate aceste noduri formeaz˘ a o component˘ a conex˘ a. Pas 4. Dac˘a mai exist˘a noduri nevizitate, se reia procesul de calcul de la pasul 2 , altfel procesul de calcul se ˆıncheie.
49
2.6
Muchie critic˘ a
Reamintim faptul c˘ a ˆıntr-un graf neorientat G = (V, E ), o muchie critic˘ a este acea muchie care prin eliminarea ei conduce la cre¸sterea num˘ arului de componente conexe ale grafului. Se cere s˘ a se determine toate muchiile critice ale unui graf dat. ˆIn continuare sunt prezentate dou˘ a variante de rezolvare.
Solut¸ia I Pentru simplitate, vom trata situat¸ia ˆın care graful considerat este conex (dac˘a graful nu este conex se determin˘ a componentele conexe ¸si num˘ arul acestora ¸si se aplic˘ a algoritmul pentru fiecare component˘ a conex˘ a ˆın parte). Se elimin˘ a , pe rˆand, fiecare muchie a grafului ¸si apoi se verific˘ a dac˘ a graful rezultat mai este conex (a se vedea algoritmul 21). a) Algoritm 21 Algoritm de determinare a muchiei critice (prima variant˘ 1: procedure MuchieCriticaI(n,m,V ecin)
n - num˘ arul de noduri din graf Input: m - num˘ arul de muchii din graf V ecin - matricea de adiacent¸˘a 2: for k 1, m do 3: elimin˘a muchia k 4: if (Conex(n,V ecin) = f alse) then 5: Output ”Muchia k este critica”
←
{
end if 6: 7: adaug˘ a muchia k 8: end for 9: end procedure 10: function Conex(n,V ecin) 11: for i 1, n do 12: vizitati 0 13: end for 14: call DF S (1, n,V ecin) for i 1, n do 15: 16: if (vizitati = 0) then 17: return f alse 18: end if 19: end for 20: return true 21: end function
←
}
⊲ reface graful init¸ial
←
⊲ vizitarea ˆın adˆancime a grafului
←
Subrutina Conex() verific˘a dac˘ a graful identificat prin matricea de adiacent¸˘ a Vecin este conex (este compus dintr-o singur˘ a component˘a conex˘ a). Subrutina ˆıntoarce valoarea true ˆın cazul ˆın care graful considerat este conex ¸si false, ˆın caz contrar. Pentru aceasta, la ˆınceput, se marcheaz˘ a toate nodurile ca fiind nevizitate, ¸si se ˆıncearc˘ a parcurgerea nodurilor grafului, prin intermediul unui apel al subrutinei de vizitare, DFS(1, n, Vecin). Implementarea algoritmului 21 ˆın limbajul C este urm˘atoarea: Listing 2.3: muchiecriticav1.c #include #include
50
vecin [ i ] [ j ] = 0; vecin [ j ] [ i ] = 0; i f ( co ne x ( ) == FALSE) p r i n t f ( ” Muc hi a (%d ,% d ) e s t e c r i t i c a !
{
}
\ n” ,
i , j );
vecin [ i ] [ j ] = 1; vecin [ j ] [ i ] = 1;
}
}
Observat¸ia 2.20 Am presupus c˘ a numerotarea nodurilor se face de la 0 ¸si c˘ a exist˘ a un nod cu eticheta 0. Complexitatea-timp a acestui algoritm, presupunˆ and c˘ a avem o singur˘ a component˘ a conex˘a, este O(m(n + m)): se consider˘ a fiecare muchie (O(m)), se elimin˘a din graf, ¸si se verific˘ a dac˘ a graful r˘amas este conex (O(n + m)).
Solut¸ia a II-a Cea de-a doua variant˘ a de abordare pentru identificarea unei solut¸ii folose¸ste urm˘atoarea proprietate:
Observat¸ia 2.21 O muchie nu este critic˘ a dac˘ a ea face parte din cel put ¸in un ciclu elementar al grafului respectiv. ˆIn urma vizit˘ arii DFS, toate muchiile unui graf se ˆımpart ˆın muchii ale arborelui de acoperire ¸si muchii de ˆıntoarcere . Vom numerota toate nodurile grafului ˆın preordine (numerotarea se realizeaz˘ a efectiv ˆın momentul ˆın care un nod este marcat ca fiind vizitat ), toate valorile fiind p˘ astrate ˆıntr-un vector prenum. Fie lowu valoarea unui nod u, calculat˘ a dup˘ a formula urm˘ atoare:
prenumu lowu = min prenumx lowy
, dac˘a [u, x] este muchie de ˆıntoarcere , y descendent direct al lui u.
(2.6)
∀
Dac˘a prenumu low v , v descendent direct al lui u, atunci ˆınseamn˘ a c˘a nodul v sau un descendent al lui v prezint˘ a o muchie de ˆıntoarcere la u sau la un str˘ amo¸s al acestuia. Astfel muchia [u, v] apart¸ine unui ciclu elementar, ¸si, prin urmare, nu este muchie critic˘ a (a se vedea algoritmul 22). Prin negat¸ie, dac˘ a exist˘a cel put¸in un vˆarf v, descendent direct al lui u, cu proprietatea c˘ a prenumu < lowv atunci [u, v] este muchie critic˘a. Dup˘a fiecare apel recursiv al subrutinei DFS (linia 16) se verific˘ a gradul de adev˘ ar al expresiei ( prenumk < lowi ) (linia 18). Variabila counter este global˘a pentru cele dou˘ a subrutine, MuchieCriticaII() ¸si DFS critic(). La numerotarea ˆın preordine se folosesc atribuirile: counter counter + 1, ¸si prenumk counter. Cˆand un nod i este vecin cu nodul curent k, ¸si nu a fost ˆınc˘ a vizitat, ˆ valoarea lui lowk se calculeaz˘ a astfel: lowk min lowk ,lowi . In situat¸ia ˆın care pentru un nod i, vecin cu nodul curent k, deja vizitat, avem o muchie de ˆıntoarcere (nodului k i s-a atribuit deja un num˘ ar ˆın preordine), atunci valoarea lowk se calculeaz˘ a dup˘ a formula
≥
∀
←
←
←
lowk
{
}
← min {low , prenum }. k
i
Implementarea ˆın limbajul C a algoritmului 22 este urm˘ atoarea: 52
a) Algoritm 22 Algoritm de determinare a muchiei critice (a doua variant˘ 1: procedure MuchieCriticaII(n,V ecin)
n - num˘ arul de noduri din graf V ecin - matricea de adiacent¸a˘ for i 1, n do vizitati 0
Input:
←
2: 3: 4: end for 5: counter 0 6: call DFS critic(1, n, V ecin) 7: end procedure 8: procedure DFS critic(k, n,V ecin) 9: vizitatk 1 10: counter counter + 1 11: prenumk counter, lowk counter 12: for i 1, n do 13: if (vecink,i = 1) then 14: if (vizitati = 0) then tatai k 15: call DFS critic(i, n, V ecin) 16: 17: lowk M in(lowk ,lowi ) 18: if ( prenum k < low i ) then 19: Output ’Muchia (k,i) este critica’ 20: end if else 21: 22: if (tatak = i) then 23: lowk M in(lowk , prenumi ) 24: end if 25: end if end if 26: 27: end for 28: end procedure
←
←
←
← ← ←
⊲ vizitarea ˆın adˆancime a grafului
←
← ←
{
}
←
Listing 2.4: muchiecriticav2.c #include #include #define MAXN 100 char v ec in [MAXN] [MAXN] ; char v i z i t a t [MAXN] ; in t n ; in t t a ta [MAXN] ; in t prenum [MAXN ] ; in t low [MAXN] ; in t c o un te r ;
// // // // // //
Ma tr ice a de ad ia ce nt a V ec to r c e p a st r ea z a s t a r ea u nu i nod : v i z i t a t sau nu Numarul de n o d u r i d i n g r a f P as tr ea za ta t al f i e c a r u i nod i n ar bo r e l e de a c o p e ri r e i n a d in c i me g e n e r a t d e m eto da DFS . prenum [ k ] n um er o ta re a i n p r e o r d i n e
−
// c o nt o r g l o b a l c e numara momentul cand e s t e v i z i t a t un nod
void readI nput ( void ) in t i , j ;
{
p r i n t f ( ” n = ” ) ; s c a n f ( ”%d ” , &n ) ; fo r ( i = 0 ; i < n 1 ; i ++)
−
53
fo r ( j = i + 1 ; j < n ; j ++) p r i n t f ( ” a[%d,%d ] = ” , i , j ) ; sc an f ( ”%d” , &ve ci n [ i ] [ j ] ) ; veci n [ j ] [ i ] = veci n [ i ] [ j ] ;
{
}
}
in t min( in t x , i nt y ) return ( x < y ) ? x : y ;
{
} void df s ( i nt k ) in t i ;
{
vizitat [k] = 1; counter++; p ren um [ k ] = c o u n t e r ; l o w [ k ] = c o u n t e r ; fo r ( i = 0 ; i < n ; i ++) i f ( veci n [ k ] [ i ] == 1) i f ( v i z i t a t [ i ] == 0 ) tata [ i ] = k ; df s ( i ); low [ k ] = min( low [ k ] , low [ i ] ) ; i f (prenum [ k ] < low [ i ]) pr in tf ( ”%d > %d n ” , k , i ) ;
{
−
}
\
else i f ( t a t a [ k ] ! = i ) low [ k ] = min( low [ k ] , prenum [ i ] ) ; / pr in tf (”prenum[%d] = %d low[%d] = %d n” , k , prenum [ k ] , k , low [ k ] ) ; /
}
∗
\
∗
void c r i t i c ( void ) m em se t ( v i z i t a t , 0 , s i z e o f ( v i z i t a t ) ) ; counter = 0; dfs (0) ;
{
} void main( void ) p r i n t f ( ” n” ) ; r e a d I n pu t ( ) ; cr it ic () ;
\
{
} Exemplul 2.22 Pentru graful din figura 2.13, valorile prenum ¸si low sunt cele din figura 2.19 (valoarea din partea dreapt˘ a a unui vˆ arf reprezint˘ a valoarea sa la numerotarea ˆın preordine, prenum , iar num˘ arul din stˆ anga reprezint˘ a valoarea calculat˘ a low ). Condit ¸ia prenumu < lowv , unde [u, v] este o muchie a grafului, este ˆındeplinit˘ a doar pentru u = 6 ¸si v = 8. Prin urmare [6, 8] este muchie critic˘ a ˆın graful considerat. Deoarece acest algoritm este constituit din algoritmul modificat de vizitare ˆın adˆ ancime a unui graf, complexitatea-timp este O(n + m) atunci cˆ and graful este reprezentat prin liste 2 de adiacent¸a˘ ¸si O(n ) ˆın situat¸ia ˆın care graful este reprezentat prin matricea de adiacent ¸˘ a.
54
Fig. 2.19: Algoritmul 22 aplicat pentru graful din figura 2.13
2.7
Exercit¸ii
1. Un graf neorientat cu n noduri, G = (V, E ) se nume¸ste graf scorpion dac˘ a posed˘ a trei noduri speciale: (a) acul - dG(u) = 1 ¸si este legat de coad˘ a; (b) coada - dG (u) = 2 ¸si este legat˘ a de ac ¸si corp; (c) corpul - dG (u) = n
− 2 fiind legat de toate nodurile din graf cu except¸ia acului.
Nu exist˘a alte restrict¸ii cu privire la nodurile grafului. S˘a se realizeze un algoritm care determin˘ a dac˘ a un graf este graf scorpion folosind O(n) ˆıntreb˘ ari de forma ”Exist˘ a o muchie ˆıntre nodurile u ¸si v? ”. 2. Se consider˘ a n bazine dispuse circular ¸si lipite unul de altul ˆın ordinea numerelor de ordine. Se d˘ a lista celor m perechi de bazine ce trebuie unite prin canale. Un num˘ ar de bazine poate s˘ a apar˘ a ˆın mai multe perechi. Canalele pot fi realizate ˆın interiorul sau ˆın exteriorul cercului, cu condit¸ia ca ele s˘a nu se intersecteze. Dac˘a problema are solut¸ie, vor fi afi¸sate dou˘ a liste: cea a perechilor de bazine unite prin canale interioare ¸si cea a perechilor de bazine unite prin canale exterioare. Dac˘ a problema nu are solut¸ie, atunci se va raporta acest lucru. ˆIn cazul ˆın care problema are solut¸ie s˘a se determine toate posibilit˘ a¸t ile de unire prin canale exterioare sau interioare. (ONI, 1993)
3. S˘a se parcurg˘ a ˆın l˘a¸time ¸si ˆın adˆ ancime graful din figura 2.20, s˘a se construiasc˘ a arborii de parcurgere ¸si s˘ a se pun˘a ˆın evident¸a˘ muchiile de ˆıntoarcere. 4. Compania TLC (Telephone Line Company) a stabilit o nou˘ a ret¸ea de cablu telefonic. Ea leag˘ a mai multe locat¸ii numerotate cu numere ˆıntregi de la 1 la N . Nu exist˘a dou˘ a locat¸ii numerotate cu acela¸si num˘ ar. Liniile sunt bidirect¸ionale ¸si conecteaz˘a dou˘ a locat¸ii; fiecare locat¸ie are un post telefonic. Din orice locat ¸ie poate fi apelat˘ a oricare 55
Fig. 2.20: Un exemplu de graf neorientat cu 8 vˆarfuri
alt˘a locat¸ie, prin leg˘atura direct˘ a sau conexiuni intermediare. Uneori ret ¸eaua cade ˆın unele locat¸ii ¸si conexiunea aferent˘ a nu mai este posibil˘a. Tehnicienii de la TLC au realizat c˘ a, ˆın acest caz, nu numai c˘ a locat¸ia respectiv˘ a nu mai poate fi apelat˘a, dar ea ˆıntrerupe leg˘ atura ¸si ˆıntre alte locat¸ii pentru care asigur˘ a conexiunea. ˆIn aceast˘ a situat¸ie spunem c˘ a locat¸ia (unde a ap˘ arut c˘ aderea) este critic˘ a . Se cere s˘ a se elaboreze un algoritm care s˘ a determine num˘ arul tuturor acestor puncte critice. (ACM Europa Central˘a, 1996)
5. Fiind dat un graf G = (V, E ), s˘a se verifice dac˘ a este aciclic. 6. Fiind dat un graf G = (V, E ), s˘a se verifice dac˘ a este bipartit. 7. ˆIntr-un ora¸s, intersect¸iile sunt numerotate de la 1 la n (se consider˘ a intersect¸ie nu numai locul unde se intersecteaz˘ a mai multe str˘ azi ci ¸si punctele terminale, capetele de str˘ azi). Edilii ora¸sului doresc s˘ a numeroteze ¸si str˘ azile ora¸sului, dar ˆıntr-un mod care s˘ a ¸tin˘a seama de numerotarea intersect¸iilor, ¸si anume: dou˘ a str˘ azi diferite vor avea numere diferite ¸si ˆın fiecare intersect¸ie trebuie s˘ a soseasc˘ a o strad˘ a care s˘ a aib˘a num˘arul intersect¸iei. Se cere s˘ a se realizeze un algoritm care s˘ a determine o astfel de numerotare, dac˘ a exist˘a. Datele de intrare constau dintr-un num˘ ar n reprezentˆ and num˘ arul de puncte de intersect¸ie; fiecare punct este identificat prin num˘ arul cu care este numerotat, ˆıntre 1 ¸si n, a se urmat de lista punctelor cu care acesta comunic˘ a direct printr-o strad˘ a. Prin strad˘ ˆınt¸elege o port¸iune de drum aflat˘ a ˆıntre dou˘a puncte de intersect¸ie, neglijˆand faptul c˘a ˆın practic˘a str˘ azile cuprind mai multe intersect¸ii. 8. Se consider˘ a o tabl˘a de ¸sah de dimensiune n n (n 100) pe care sunt dispuse obstacole. Se cere s˘ a se determine num˘ arul minim de mut˘ari necesare unui nebun pentru a se deplasa, respectˆ and regulile jocului de ¸sah ¸si ocolind obstacolele, dintr-o pozit¸ie init¸ial˘ a dat˘ a ˆıntr-o pozit¸ie final˘a dat˘ a.
×
≤
Se consider˘ a c˘a obstacolele nu coincid cu pozit ¸ia init¸ial˘a ¸si nici cu cea final˘ a a nebunului. (Concurs student¸esc, 1993)
9. Pe o tarla agricol˘ a sunt mai multe parcele ce trebuie cosite. Parcelele de diferite culturi (lucern˘a, trifoi, iarb˘a, furaje) vor fi cosite de muncitori diferit¸i ce lucreaz˘ a numai la 56
un anumit fel de cultur˘a (de exemplu un muncitor este specializat numai pe cositul lucernei). Terenul agricol se reprezint˘ a printr-o matrice n m (n linii ¸si m coloane). Fiecare element al matricii corespunde unei mult¸imi de un anumit tip. Dou˘ a elemente ale matricii sunt vecine dac˘ a au o latur˘ a comun˘ a. O parcel˘ a este o mult¸ime maximal˘a de elemente astfel ˆıncˆ at un muncitor se poate deplasa ˆıntre oricare dou˘ a elemente de-a lungul a dou˘a elemente vecine.
×
Se cere s˘ a se determine num˘ arul de muncitori care pot s˘ a coseasc˘ a o cultur˘ a dat˘ a.
57
Capitolul 3 Grafuri euleriene ¸si hamiltoniene 3.1
Grafuri Euleriene
ˆIn anul 1736, matematicianul Leonhard Euler publica o lucrare asupra problemei podurilor din K¨onigsberg1 (a se vedea figura 3.1) ˆın care se prezint˘ a un studiu teoretic asupra lant¸urilor ¸si ciclurilor Euleriene.
Fig. 3.1: Podurile din K¨onigsberg
S˘a consider˘ am un graf neorientat G = (V, E ).
Definit¸ia 3.1 Un lant ¸ L ce cont ¸ine fiecare muchie a unui graf G o singur˘ a dat˘ a se nume¸ste lant ¸ Euler sau lant ¸ eulerian . Dac˘ a extremit˘ at ¸ile lant ¸ului sunt identice ¸si lant ¸ul este eulerian atunci ciclul se nume¸ste ciclu Euler sau ciclu eulerian . Un graf ce cont ¸ine un ciclu eulerian se nume¸ste graf eulerian . O problem˘ a mai cunoscut˘ a legat˘ a de not¸iunile de ciclu ¸si lant¸ eulerian este aceea de a desena cu o singur˘a linie neˆıntrerupt˘ a o anumit˘ a figur˘ a (a se vedea figura 3.2).
Teorema 3.1 (Euler) Un graf G conex ( V fiec˘ arui vˆ arf este par.
| | ≥ 3 ) este eulerian dac˘ a ¸si numai dac˘ a gradul
1
http://en.wikipedia.org/wiki/Seven Bridges of K¨onigsberg
58
Fig. 3.2: Dou˘a figuri geometrice ce pot fi desenate folosind o singur˘a linie
un ciclu eulerian C . Acesta Demonstrat¸ie: ” ” Fie G un graf conex eulerian trece o singur˘ a dat˘ a prin toate muchiile grafului. Gradul fiec˘ arui vˆarf u este par, deoarece pentru fiecare muchie incident˘ a cu u prin intermediul c˘ areia se ajunge ˆın vˆ arful respectiv, exist˘a o alt˘a muchie prin intermediul c˘areia se p˘ ar˘ase¸ste vˆarful u. ” ” Presupunem c˘ a u V , d G (u) este par. Fie L un lant¸ de lungime maxim˘a ˆın graful G ¸si fie u ¸si v extremit˘ a¸t ile lant¸ului. Presupunem c˘ a u = v. Deoarece dG(v) este par, [v, w] E astfel ˆıncˆat [v, w] / L. Lant¸ul L [v, w] va avea lungimea mai mare decˆa t lant¸ul init¸ial L, contradict¸ie. Prin urmare u = v. Rezult˘ a c˘a L = C este un ciclu de lungime maxim˘a. Presupunem c˘ a ciclul C nu este ciclu eulerian. Atunci x, y V , [x, y] E , [x, y] / C ¸si x C (exist˘a o muchie a grafului G ce nu apart¸ine ciclului C cu proprietatea c˘ a cel put¸in o extremitate apart¸ine ciclului). Fie L1 = [x , . . . , u , v , . . . , x] un lant¸ ˆın graful G ce cont¸ine exact muchiile ciclului C . Atunci L2 = [x , . . . , u , v , . . . , x , y ] este un lant¸ ˆın G ce are lungimea mai mare decˆ a t cea a lant¸ului L, contradict¸ie. Rezult˘ a atunci c˘ a ciclul C este eulerian G este un graf eulerian.
⇒
⇐
⇒ ∃
∀ ∈
∪ {
}
∃
∈
∈
∃ ∈
∈
∈
∈
⇒
Teorema 3.2 (Euler) Un graf G conex are un lant ¸ eulerian dac˘ a ¸si numai dac˘ a exist˘ a exact dou˘ a vˆ arfuri ˆın G al c˘ aror grad s˘ a fie impar. Demonstrat¸ie: ” ” Se demonstreaz˘ a la fel ca la teorema 3.1. ” ” Fie G = (V, E ) un graf conex ce are exact dou˘ a vˆ arfuri de grad impar, u ¸si v. S˘a consider˘a un nod w / V ˆımpreun˘a 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 ad˘augarea nodului w ¸si a muchiilor [u, w], [v, w]. Atunci dG (u) este par, u V ′ exist˘a un ciclu eulerian C ˆın graful G′ . Dac˘a elimin˘am muchiile [u, w] ¸si [v, w] obt¸inem un lant¸ eulerian ˆın graful G.
⇒
⇐
∪{
∈
′
}
∪{ }
∀ ∈
⇒
Corolarul 3.3 Un graf G conex ( V 3 ) este eulerian dac˘ a ¸si numai dac˘ a mult ¸imea muchiilor sale poate fi descompus˘ a ˆın cicluri disjuncte.
| |≥
Teorema 3.4 Pentru un graf conex cu 2k vˆ arfuri de grad impar, k 1 , exist˘ a k lant ¸uri ˆın aror mult ¸imi de muchii formeaz˘ a o partit ¸ie a mult ¸imii E , ¸si din care cel mult unul G ale c˘ are lungimea impar˘ a.
≥
3.1.1
Algoritm pentru determinarea unui ciclu eulerian
Pornind de la Teorema 3.1 construim un ciclu eulerian. Se consider˘ a drept punct de plecare un vˆarf oarecare al grafului. La fiecare pas se alege pentru vˆ arful curent u, un vˆarf adiacent 59
Fig. 3.3: Exemplu de graf eulerian
v, diferit de vˆarful din care s–a ajuns ˆın u. Parcurgerea se continu˘ a pˆan˘a ˆın momentul ˆın care ajungem ˆıntr–un vˆ arf ce a fost deja vizitat ¸si se ˆınchide ciclul C . Dac˘a ciclul C nu este eulerian, atunci se elimin˘ a din graful G toate muchiile ciclului C , rezultˆ and mai multe 1 1 1 componente conexe G1, G2 , . . . , Gk . Pentru fiecare astfel de component˘ a G1i , ce are cel put¸in dou˘ a vˆarfuri, aplic˘am recursiv 1 algoritmul. Conform teoremei 3.1, fiecare component˘ a Gi este eulerian˘ a. Fie C i1 ciclul eulerian corespunz˘ ator componentei G1i . Ciclul C are cel put¸in cˆate un vˆ arf comun cu fiecare ciclu C i1 . Fie acest vˆ arf ui . Definim un ciclu C 0 astfel: pentru fiecare i, i = 1, k, ad˘aug˘am la ciclul C , ˆın vˆarful ui, ciclul C i1 . Ciclul C 0 cont¸ine toate muchiile grafului G, prin urmare este eulerian. Funct¸ia IsEulerian() verific˘a dac˘ a un graf este eulerian conform teoremei 3.1 (a se vedea algoritmul 23). a un graf conex este eulerian Algoritm 23 Algoritm de verificare dac˘ 1: function IsEulerian(G = (V, E )) 2: if ( V < 3) then 3: return f alse 4: end if 5: for i 1, n do s 0 6: for j 1, n do 7: 8: s s + vecini,j 9: end for 10: if (s mod 2 = 1) then 11: return false end if 12: 13: end for 14: return true 15: end function
| |
← ←
← ←
Dup˘a cum se poate vedea din funct¸ia IsEulerian() numai componentele conexe ce au ordinul mai mare sau egal cu 3 vor fi luate ˆın considerare.
60
ˆIn algoritmul 24 este prezentat˘ a funct¸ia EulerRec() pentru determinarea unui ciclu eulerian. Procedura FindCycle(G) (a se vedea algoritmul 24) construie¸ste un ciclu: se alege un vˆarf u0 ¸si se caut˘ a prima muchie [u0, u1 ]. ˆIn vˆarful u1 se caut˘ a o muchie [u1 , u2] astfel ˆıncˆ at vˆarful u 2 s˘a fie distinct de vˆ arful din care s-a ajuns ˆın u 1 (u2 = u 0) ¸si s˘a nu mai fi fost vizitat. Dac˘a u 2 a fost vizitat atunci funct¸ia se termin˘a. Se continu˘ a procedeul de c˘ autare pˆan˘ a cˆand se ajunge la un vˆ arf ce a fost vizitat anterior ¸si se returneaz˘ a ciclul cuprins ˆıntre cele dou˘ a aparit¸ii ale aceluia¸si vˆ arf. Procedura FindComponents(G′ ; k, G11, G12, . . . , G1k ) determin˘ a componentele conexe ale ′ grafului G = G E (C ) , obt¸inut prin eliminarea din graful G a muchiilor ciclului C , unde G11, G12, . . . , G1k sunt cele k componente conexe returnate de aceasta. Procedura MergeCycles(C, C 11, . . . , Ck1 ; C 0) construie¸ste un ciclul C 0 obt¸inut prin ad˘ augarea 1 1 la ciclul C , succesiv, a ciclurilor C 1 , . . . , Ck .
−
Algoritm 24 Algoritm de determinare a unui ciclu eulerian ˆıntr–un graf conex 1: function EulerRec(G = (V, E )) 2: if (IsEulerian(G) = f alse) then 3: return 4: end if 5: call F indCycle(G; C ) 6: if (E (G) E (C ) = ) then 7: return C 8: end if G′ G E (C ) 9: 10: call FindComponents(G′ ; k, G11 , G12 , . . . , G1k ) 11: for i 1, k do 1 12: C i EulerRec(G1i ) 13: end for 14: call MergeCycles(C, C 11 , . . . , Ck1 ; C 0 ) 15: return C 0 16: end function
∅
\
∅
← − ← ←
Fig. 3.4: Grafurile part¸iale obt¸inute ˆın urma primelor dou˘a etape ale algoritmului 24
Exemplul 3.5 S˘ a aplic˘ am algoritmul 24 pentru graful din figura 3.3. Presupunem c˘ a primul element (elementul de start) este vˆ arful u0 = 1. Procedura FindCycle construie¸ste lant ¸ul 61
Fig. 3.5: Grafurile part¸iale obt¸inute ˆın urma etapelor 3 ¸si 4 ale algoritmului 24
ˆ urma elimin˘ L = [1, 2, 3, 4, 5, 3]. Se observ˘ a prezent ¸a ciclului C 1 = [3, 4, 5, 3]. In arii acestuia r˘ amˆ ane graful din figura 3.4 a) care este un graf conex. Continu˘ am procesul de construire a unui ciclu eulerian, prin apelul procedurii FindCycle pentru noul graf. Aceasta returneaz˘ a ciclul C 2 = [1, 2, 3, 9, 1]. Prin eliminarea muchiilor ˆ continuare se construie¸ste lant acestuia, E (C 2 ), se obt ¸ine graful din figura 3.4 b). In ¸ul ¸ine ciclul C 3 = [2, 8, 7, 5, 6, 4, 10, 2]. Graful obt ¸inut prin L = [2, 8, 7, 5, 6, 4, 10, 2], ce cont eliminarea muchiilor ciclului este cel din figura 3.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 formeaz˘ a prin reuniunea ciclurilor intermediare determinate: C = (((C 4 C 3 ) C 2 ) C 1) adic˘ a 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]).
∪
3.1.2
∪
∪
∪
Algoritmul lui Rosenstiehl
Algoritmul lui Rosenstiehl [51] construie¸ste un lant¸ L care la final va deveni un ciclu eulerian, folosind pentru aceasta o stiv˘ a drept structur˘ a de date auxiliar˘a. Se porne¸ste de la un nod oarecare. Se ˆınainteaz˘ a atˆ a ta timp cˆ at este posibil: pentru nodul curent u se caut˘ a o muchie incident˘a cu el, ¸si care s˘ a nu mai fi fost parcurs˘ a la unul din pa¸sii anteriori. Dac˘ a exist˘a o astfel de muchie (e = [u, v]), atunci se salveaz˘ a pe stiv˘a ˆ nodul u, iar nodul v devine nodul curent. In momentul ˆın care nodul curent u nu mai are muchii nevizitate, se adaug˘a lant¸ului L, ¸si se extrage de pe stiv˘ a nodul anterior (din care s-a ajuns ˆın u) (a se vedea algoritmul 25). Vectorul vizit are drept scop s˘ a p˘astreze situat¸ia muchiilor: 1 , dac˘ a e E a fost parcurs˘ a vizite = 0 , dac˘ a e E nu a fost ˆınc˘a parcurs˘ a.
∈ ∈
Exemplul 3.6 S˘ a aplic˘ am algoritmul 25 pentru graful din figura 3.3. S˘ a presupunem c˘ a vˆ arful u de la care porne¸ste algoritmul este vˆ arful 1. La sfˆ ar¸situl 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 urm˘ ator, se extrage valoarea 9 de pe stiv˘ a ¸si astfel u devine 9. La sfˆ ar¸situl acestuia 62
Algoritm 25 Algoritm lui Rosenstiehl de determinare a unui ciclu eulerian ˆıntr–un graf conex 1: function Rosenstiehl(u, G) 2: for e E do 3: vizite 0 4: end for 5: S u 6: while (S = ) do 7: S u 8: while ( e = [u, v] E ) vizite 1 9: 10: S u 11: u v 12: end while L u 13: end while 14: 15: return L 16: end function
∈
⇐
←
∅ ⇒ ∃ ← ⇐ ← ⇐
⊲ Se insereaz˘a pe stiv˘a nodul u ⊲ Se extrage din stiv˘a nodul curent
∈ ∧ (vizite = 0)) do
⊲ Se marcheaz˘a muchia e ca fiind utilizat˘a ⊲ Se salveaz˘a pe stiv˘a nodul curent u ⊲ Nodul curent devine nodul v ⊲ Se adaug˘a la lista L nodul curent
avem: S = [1, 2, 3, 4, 5, 3, 9, 7, 5, 6, 4, 10, 2, 8, 7, 6, 10, 8], L = [1, 9], u = 9.
ˆ In continuare, deoarece nu mai exist˘ a muchii nevizitate, se vor extrage elementele aflate pe stiva S cˆ ate unul la fiecare pas, formˆ and 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: P as 20 : S = [], L = [1, 9, 8, 10, 6, 7, 8, 2, 10, 4, 6, 5, 7, 9, 3, 5, 4, 3, 2, 1], u = 1.
Ciclul eulerian obt ¸inut se afl˘ a ˆın lista L. ˆIn continuare se prezint˘ a implementarea ˆın limbajul C++ a algoritmului lui Rosenstiehl : Listing 3.1: rosenstiehl.cpp #include #include #include #include #include #include #include
< l i s t >
u s i n g n a me s pa c e s t d ; #define INPUT FILE ” gr af 1 . txt ” typedef v e c t o r L i n i e ; typedef v e c t o r Matrice ; in t rea dIn put ( Matr ice& ma) in t n , i , j , v al ue ; i f s t r e a m f i n ( INPUT FILE ) ;
{
f i n >> n ; ma = M a t ri ce ( n , L i n i e ( n , 0 ) ) ;
63
fo r ( i = 0 ; i < n ; i ++) fo r ( j = 0 ; j < n ; j ++) f i n >> ma[ i ] [ j ] ;
{
} fin . clo se (); return n ;
} void p r i n t ( l i s t & l ) l i s t : : i t e r a t o r i t ;
{
cout << ” C i c l u e u l e r i a n : [ ” ; fo r ( i t = l . b e gi n ( ) ; i t != l . end ( ) ; i t ++) cout << i t << ” , ” ; cout << ” ] n” ;
∗ \
}
void r o s e n s t i e h l ( i nt n , M a t r i c e& ma , i nt u , l i s t & l ) s t a c k s ; in t v ; s . push( u ) ; while ( ! s . empty ( ) ) u = s . top () ; s . pop ( ) ; v = 0; while ( v < n ) i f (ma[ u ] [ v ] == 1) ma [ u ] [ v ] = 0 ; ma[ v ] [ u ] = 0; s . push( u ) ; u = v; v = 0;
{
{
{
{
}
else v++;
}
l . p u sh b a c k ( u ) ;
}
}
void main( void ) Matrice ma; l i s t l = l i s t () ; in t n = r e a d I n p u t ( ma ) ;
{
r o s e n s t i e h l ( n , ma , 0 , l ) ; pri nt ( l );
} Datele de intrare vor fi preluate dintr-un fi¸sier. Cont ¸inutul fi¸sierului de intrare graf1.txt corespunz˘ator grafului din figura 3.6 este urm˘ atorul: 6 0 1 1 1 1 0 1 0 1 1 1 0 1 1 0 1 0 1
64
1 1 1 0 0 1 1 1 0 0 0 0 0 0 1 1 0 0
Pe prima linie avem num˘arul de vˆ arfuri al grafului (cardinalul mult¸imii V ), ¸si ˆıncepˆ and cu cea de-a doua linie avem valorile matricei de adiacent¸a˘, separate prin spat¸ii, cˆate un rˆ and al acesteia pe fiecare pe linie.
Fig. 3.6: Un alt exemplu de graf eulerian
Pentru a reduce timpul de implementare, am utilizat cˆ ateva structuri de date deja existente ˆın limba jul C++, mai exact structuri de date implementate cu ajutorul template-urilor din cadrul libr˘ ariei Standard Template Library - STL2 3: 4
• stiva - stack . Am folosit o stiv˘a de numere ˆıntregi: stack s;. • lista - list . Pentru a p˘astra elementele ciclului eulerian am folosit o list˘a liniar˘a, 5
informat¸ia util˘a din cadrul nodurilor fiind constituit˘ a din valori ˆıntregi: list l = list();. 6
• vector - vector .
Matricea de adiacent ¸˘ a am implementat-o sub forma unui vector de vectori cu elemente numere ˆıntregi:
typedef v e c t o r L i n i e ; typedef v e c t o r Matrice ; .. . Matrice ma; ma = M at ri ce ( n , L i n i e ( n , 0 ) ) ;
Dup˘a cum se poate observa din programul anterior, pentru reprezentarea intern˘ a a grafului s-a folosit matricea de adiacent ¸˘ a . 7
• Parcurgerea elementelor listei se realizeaz˘a cu a jutorul unui iterator - iterator . Declararea unui obiect de tip list::iterator se poate face astfel: list::iterator it;.
2
http://en.wikipedia.org/wiki/Standard_Template_Library, http://www.sgi.com/tech/stl/ 4 http://www.sgi.com/tech/stl/stack.html 5 http://www.sgi.com/tech/stl/List.html 6 http://www.sgi.com/tech/stl/Vector.html 7 http://www.sgi.com/tech/stl/Iterators.html 3
65
void p r i n t ( l i s t & l ) l i s t : : i t e r a t o r i t ;
{
cout << ” C i c l u e u l e r i a n : [ ” ; fo r ( i t = l . b e gi n ( ) ; i t != l . end ( ) ; i t ++) cout << i t << ” , ” ; cout << ” ] n” ;
∗ \
}
Deoarece muchiile sunt parcurse o singur˘ a dat˘ a ˆın cadrul acestui algoritm, ¸si pentru a nu mai p˘astra o structur˘ a auxiliar˘a care s˘ a marcheze faptul c˘ a o muchie a fost vizitat˘a, vom marca parcurgerea unei muchii prin ¸stergerea acesteia din matricea de adiacent ¸˘ a astfel: ma [ u ] [ v ] = 0 ; ma[ v ] [ u ] = 0;
Ciclul eulerian obt¸inut pentru graful din figura 3.6 este: L = [1, 5, 2, 4, 6, 3, 4, 1, 3, 2, 1].
3.1.3
Algoritmul lui Fleury
Un alt algoritm pentru determinarea unui ciclu eulerian este algoritmul lui Fleury.
Fig. 3.7: Exemplu de graf eulerian pentru algoritmul lui Fleury
Se porne¸ste cu un vˆ arf oarecare al grafului G (G = (V, E ), V = n, E = m). Ideea const˘a ˆın a construi un lant ¸ prin alegerea la fiecare pas a unei muchii nealeas˘ a la pa¸sii anteriori , ¸si care, de preferat, s˘ a nu fie muchie critic˘ a ˆın graful part¸ial obt¸inut prin eliminarea muchiilor deja alese. S˘a consider˘ a m c˘a, la un moment dat, avem construit un lant¸ Lk = [v0 , [v0, v1 ], v1, . . . , [vk−1 , vk ], vk ]. C˘aut˘ am o muchie e k+1 = [vk , vk+1 ] astfel ˆıncˆat ek+1 / Lk ¸si care nu este muchie critic˘a ˆın graful part¸ial Gk = G e1 , e2 , . . . , ek (graful obt¸inut prin eliminarea tuturor muchiilor lant¸ului Lk ). Dac˘a nu exist˘a decˆ at muchii critice ˆın graful part¸ial Gk incidente cu vk atunci alegem una dintre ele. Dac˘a exist˘a o muchie ek+1 cu aceste propriet˘ a¸ti atunci construim lant¸ul Lk+1 = [Lk , ek+1, vk+1] (Lk+1 = [v0 , [v0 , v1 ], v1 , . . . , [vk−1, vk ], vk , [vk , vk+1 ], vk+1]). ˆIn momentul ˆın care E (Lk ) = E (G) algoritmul lui Fleury se opre¸ste (vezi algoritmul 26).
| |
− {
}
66
| |
∈
3.2 3. 2
Graf Gr afur urii Ha Hami milt lton onie iene ne
Definit¸ia ¸ia 3.2 Se S e num nume¸ e¸ste st e lant ¸ Hamilton sau sau lant ¸ hamiltonian hamiltonian un lant ¸ L ce trece o singur˘ a dat˘ a prin toate vˆ arfurile unui graf. Definit¸ia ¸ia 3.3 Se nu nume me¸¸ste st e ciclu un ciclu elementar ce trece prin toate vˆ arfurile ciclu hamiltonian un grafului. grafulu i. Un graf ce admite un ciclu hamiltonian se nume¸ste ste graf graf hamiltonian . Problema determin˘ arii dac˘ arii a un graf oarecare G oarecare G este este hamiltonian este o problem˘ a dificil˘a, a, atent¸ia ¸ia cercet˘ ato rilor atoril or ˆınd ındrep reptˆ tˆ andu–se c˘ andu–se atre enunt¸area atre ¸area unor condit¸ii ¸ii suficiente de existent¸˘ ¸a˘ a unui ciclu hamilt hamiltonian. onian.
Lema 3.8 Fie G = G = (V, E ) un graf neorie u ¸si v dou˘ neorienta ntatt ¸si si fie u si v a vˆ arfuri neadiacente ale grafulu grafului i u, v V , u = v v,, [u, v ] / E ), ast + dG (v ) n . At ( u, astfel fel ˆınc ıncˆ at ˆ dG(u) + d Atunc unci i G este hamiltonian G + [u, [u, v ] este hamiltonian.
∈
∈
≥
⇔ ⇔
at mai mult, graful G+[ +[u, Demonstrat¸ie: ¸ie: ” ” Dac˘a G este hamiltonian, atunci cu atˆat u, v ] este hamil hamiltonian. tonian. ” ” Presupunem c˘ a G + [u, [ u, v ] este un graf hamiltonian. Atunci exist˘a un ciclu hamiltonia to nian n ˆın ın gra graful ful G + [u, [u, v ] pe care ˆıl not˘am am cu C .
⇒
⇐
1. dac˘ a [u, v ] / C C atunci C este un ciclu hamiltonian ¸si si ˆın graful G hamiltonian.
∈
⇒ G este un graf
2. dac˘ a [u, v ] C C atunci C = [u,v,x3, x4 , . . . , xn−1 , xn, u]. Pentru fiecare muchie [u, [u, xk ] E putem putem avea urm˘ atoarele situat¸ii: atoarele ¸ii:
∈
∈
(a) [[v, v, xk+1] E . Atunci ciclul C ciclul C 1 = [u, xk , xk−1, . . . , x3 , v , xk+1, . . . , xn , u] este hamiltonia to nian n ˆın ın gra graful ful G G graful graful G G este hamiltonian.
∈
⇒
(b) [[v, v, xk+1] / E . Not˘am am dG+[ k.. Atunci dG+[ n k 1. ¸si si De aici, +[u,v u,v]] (u) = k +[u,v u,v]] (v ) rezult˘a c˘a dG (u) + dG(v ) < dG+[ n k + k 1 = n 1 < n. +[u,v u,v]] (u) + dG+[ +[u,v u,v]] (v ) Contradict¸ie ¸ie cu dG (u) + dG(v ) n n,, u, v V V,, u = v v,, [u, v ] / E .
∈
≥ ∀ ∈
≤ − − ≤ − − ∈
−
Prin urmare lema este demonstrat˘ a. a.
Teorema 3.9 (Dirac, 1952) Un graf G = (V, E ) ( V 3 ) este hamiltonian dac˘ a u V avem dG(u) n2 (orice vˆ arf al grafului are gradul mai mare decˆ at jum˘ atate din num˘ arul de vˆ arfuri din graf).
| |≥
≥
∀ ∈ ∀
Teorema 3.10 (Ore, 1961) Un graf G = a u, v V G = (V, E ) ( V 3 ) este hamiltonian dac˘ n unde u = v, [u, v ] / E (pentru oricare dou˘ avem dG(u) + d G (v ) a vˆ arfurii distinc arfur distincte, te, neadiacente, ale grafului suma gradelor lor este mai mare decˆ at num˘ arul de vˆ arfuri din graf).
≥
∈
| |≥
∀ ∈
Teorema 3.11 (Chvatal, 1972) Fie un graf G = (V, E ) ( V secvent ¸˘ a grafic˘ a. Dac˘ a este satisf˘ acut˘ a relat ¸ia
| |≥
∀k a.i. d ≤ k ≤ n2 ⇒ d ≥ n − k k
n−k
atunci graful este hamiltonian.
68
3) ¸si d1 , d2 , . . . , dn o
Definit¸ia ¸ia 3.4 Pentru un graf G construim un ¸sir sir de grafuri G = G1 , G2, . . . astfel: astfel: gr graful aful Gk+1 se obt ¸ine din graful Gk prin ad˘ augarea muchiei [uk , vk ], unde vˆ arfurile uk , vk V nu n. Proc suntt adi sun adiacente acente ˆın Gk ¸si si dG (uk ) + d G(vk ) Procesu esull se ˆınc ınchei heiee ˆın mom momentu entull ˆın care nu mai exist˘ a dou˘ a vˆ arfuri neadiacente disti distincte ncte astfel ˆıncˆ at dG(u p) + dG(v p ) n . Graful G p se num ume¸ e¸ste ˆın ınch chid iderea erea lui G ¸si si se no noteaz teaz˘ a˘ cu cl cl((G).
∈
≥
≥
Lema 3.12 Orice graf prezint˘ a o singur˘ a ˆın ınch chid idere. ere. Corolarul 3.13 Un graf G = (V, E ) ( V a cl 3) este hamiltonian dac˘ cl((G) ˆın ınch chid iderea erea lui G este izomorf˘ a cu graful complet de ordinul n).
| |≥
≅
a K n (dac˘
Definim δ (G) = min dG(u) u V ¸si ∆(G) = max dG(u) u V .
{
| ∈ }
{
| ∈ }
Corolarul 3.14 Un graf G = (V, E ) ( V
| | ≥ 3) este hamiltonian dac˘ a δ (G) ≥
n . 2
Definit¸ia ¸ia 3.5 O mult ¸ime de vˆ arfuri A a unui graf G se spune c˘ a este independent˘ a dac˘ a oricare dou˘ a elemente distincte din A sunt independente. Num˘ arul de independent ¸˘ a al lui G, notat cu β (G), reprezint˘ a num˘ arul maxim de vˆ arfuri dintr–o mult ¸ime independent˘ a. a ¸si si numa numaii dac dac˘ a˘ graful G este complet. β (G) = 1 dac˘
Definit¸ia ¸ia 3.6 Pentr Pentru u un gr graf af G se num nume¸ e¸ste ste conectivitatea lui G, ¸si si not˘ am cu κ(G), num˘ arul minim de vˆ arfuri ale unei t˘ aieturi aiet uri.. O t˘ aietur˘ a este o submult ¸ime U a lui V astfel ˆıncat ˆ graful G U s˘ a fie neconex.
−
Teorema 3.15 Un graf G = (V, E ) avˆ and ordinul n β (G).
≥ 3, este hamiltonian dac˘ a κ(G) ≥
G = (V 1 , V 2 , E ) condit Observat¸ia ¸ia 3.16 Pentru un graf bipartit G = ¸ia necesar˘ a pentru a fi hamiltonian este V 1 = V 2 .
| | | | |
Problema comis–voiajorului include problema determin˘ arii exist arii existent ent¸ei ¸ei unui ciclu hamiltonian nia n ˆıntr–un ıntr –un gra graf. f. C˘autarea autarea optimului printre toate variantele de cicluri nu este o solut¸ie ¸ie fezabil˘ a deoarece pentru un graf G arul de cicluri poate fi foarte mare. De exemplu pentru graful complet G num˘arul (n−1)! K n avem 2 cicluri hamiltoniene distincte.
Exemplul 3.17 Graful din figura 2.20 nu este hamiltonian (nu admite un ciclu hamiltonian).
• Graful din figura 3.6 are mai multe cicluri hamiltoniene: de exemplu ciclurile C = [1, 5, 1
2, 3, 6, 4, 1] ¸si si C 2 = [1, 3, 6, 4, 2, 5, 1] .
Nod 1 2 3 4 5 6 dG 4 4 4 4 2 2 Teorema lui Dirac (1952) Teorema (1 952) nu se s e poate aplica deoar deoarec ecee nu este ˆındeplinit˘ ındep linit˘ a condit ¸ia ”orice vˆ arf al grafului are gradul mai mare decˆ at jum˘ atate din num˘ arul de vˆ arfuri din graf”: n 6 = = 3. 2 2 De asemenea, a semenea, pentru teorema teorema lui Ore (1961) nu este ˆındeplinit˘ a condit ¸ia ”pentru oricare dou˘ a vˆ arfuri dinstincte, neadiacente, ale grafului suma gradelor lor este mai mare decˆ at num˘ arul de vˆ arfuri din graf”: fie vˆ arfurile 5 ¸si si 6, neadiacente, pentru care avem dG (5) = 2 < 2 <
4 < 6 6.. dG(5) + dG(6) = 2 + 2 = 4 < 69
• Pentru graful din figura 3.8 avem: Nod 1 2 3 4 5 6 7 8 dG 3 4 3 4 4 5 3 4 3 < 4 4 = n2 ; – condit ¸ia te teoremei oremei lui Dirac (1952), (19 52), nu este ˆındeplinit˘ a: dG (1) = 3 <
– condit 7 < 8 8 ; ¸ia te teoremei oremei lui Ore (1961), (19 61), nu este ˆındeplinit˘ a: dG(2) + dG (7) = 4 + 3 = 7 < – observ˘ am c˘ a nici condit ¸iile teoremei teoremei lui Chvatal (1972) (197 2) nu sunt ˆındeplinite: fie d d1, d2, . . . , dn o secvent ¸˘ a grafic˘ a. Nod 1 3 7 2 4 5 8 6 dG 3 3 3 4 4 4 4 5 Relat ¸ia urm˘ atoare nu este satisf˘ acut˘ a:
∀k a.i. d ≤ k ≤ n2 ⇒ d ≥ n − k. n−k
k
3.2.1 3.2 .1
Proble Pro blema ma co comis mis–v –voiajo oiajorul rului ui
Un comis-voiajor trebuie s˘ a viziteze n ora¸se se etichetate cu numer numerele ele de la 1 la n. Pe Pent ntru ru a simplifica simpl ifica problema, acesta va pleca ˆıntotdeauna ıntotd eauna din ora¸sul sul num˘ arul 1 ¸si se va ˆın ıntoa toarce rce to tott ˆın 1, trecˆ and prin fiec fiecare are ora¸ o ra¸s o singur˘ a dat˘ a (cu except ¸ia ora ora¸¸sulu su lui i 1 care va fi vizitat de dou˘ a ori). Cunosc Cunoscˆ and ˆ distant ¸ele ˆın ıntre tre ora ora¸¸se, se , s˘ a se determine o parcurgere (ciclu hamiltonian) de cost minim. Pentru a reprezenta drumurile directe direct e dintre ora¸ or a¸se se (existent ( existent ¸a unei leg˘ atur i direc aturi directe te ˆıntre acestea) utili utiliz˘ z˘ am matricea costurilor A = (aij ), i , j = 1, n: am ai,j =
∞
0 , daca˘ x i = = x x j + , dac˘ a [xi , x j ] / E . d > 0 , daca˘ [xi , x j ] E
∈ ∈ ) ∈ V × unde x × . . . × V V unde x
(3.1)
Vectorul solut¸iilor ¸iilor rezultat va fi X fi X = (x1 , x2 , . . . , xn+1 1 = x n+1 = 1: la pasul i, i = 2, n + 1, se viziteaz˘ a or ora¸ a¸sul su l xi V V .. Primul ora¸s, s, ora¸sul sul din care se pleac˘ a, a, este fixat la 1 (vezi algoritmul 27). La pasul k (2 k n + 1) se ˆıncea ıncearc˘ rc˘ a vizita vizitarea rea ora¸sului sului xk + 1 dac˘a nu a fost vizitat la un pas anterior, ¸si si dac˘ a costul part¸ial ¸ial al lant¸ului ¸ului este mai mic decˆat at costul celui mai bun ciclu hamiltonian obt¸inut ¸inut pˆ an˘a ˆın ace an˘ acell mom moment ent ˆın gra graf: f:
∈
≤ ≤
1
≤ x
k
< n ¸si vizitat si vizitatxk +1 = 0 ¸si cost + axk
−1
optim
≤ cost
,xk +1
(3.2)
(pentru k = = n + 1 trebuie verificat˘ a ¸si si co cond ndit it¸ia ¸ia xk = 1). n + La ˆınceput ınceput costul celui mai scurt ciclu hamiltonian hamiltonian poate fi determi determinat nat cu un algoritm de tip Greedy sau i se poate atribui valoarea + (o valoare suficient de mare, de exemplu n max ai,j i, j = 1, n , i = j, ai,j = ). Dac˘a condit¸iile ¸iile anterioa a nterioare re sunt ˆındeplinite, ındepli nite, atunc atuncii x k x k + 1. Se march marchea eaz˘ z˘ a ora ora¸¸sul sul ale aless la pasul k ca fiind vizitat (vizitat (vizitatk 1), se actualizeaz˘a costul drumului pˆ an˘a ˆın mo an˘ mome mentu ntull curent (cost (cost cost cost + + axk ,xk ), ¸si si se trece la pasul k + 1. 1. ˆIn caz contrar, nu exist˘ a nici o alegere convenabil˘ a pentru ora¸sul sul de pe pozit¸ia k ¸si va trebui s˘ a ne ˆıntoarc ıntoarcem em pentru p entru o alt˘ alta˘ alegere aleg ere pentru p entru ora¸ o ra¸sul sul vizitat vizit at la pasul pasul k k 1. Pentru aceasta se marcheaz˘ a ora¸sul sul ales la pasul anterior ca nefiind vizitat (vizitatxk 0), ¸si si se scade din costul drumului pˆan˘ an˘a ˆın momentul curent, costul muchi muchiei ei [xk−1 , xk ] (cost cost axk ,xk ).
·
{ |∀
←
∞
∞} ←
←
−1
− ← ←
70
−
−1
Fig. 3.8: Exemplu de graf pentru problema comis-voiajorului
Exemplul 3.18 Matricea costurilor corespunz˘ atoare grafului din figura 3.8 are urm˘ atoarele valori:
A =
∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞∞ ∞ ∞ ∞∞ ∞ ∞ ∞ ∞∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞ 0 14 6 5 14 0 12 16 20 6 0 12 12 12 0 21 24 10 5 16 21 0 16 20 12 16 0 14 6 24 14 0 10 12 10 6 10 0
Graful respectiv are mai multe cicluri hamiltoniene:
• C = [1, 2, 4, 5, 6, 7, 8, 3, 1] . • C = [1, 2, 5, 4, 8, 7, 6, 3, 1] . • C = [1, 5, 6, 2, 4, 7, 8, 3, 1] . • C = [1, 3, 6, 7, 8, 4, 2, 5, 1] . 1 2 3 4
Fig. 3.9: Rezultatul rul˘arii programului pentru determinarea ciclului hamiltonian optim
Programul corespunz˘ ator scris ˆın limbajul C este urm˘atorul: 71
Algoritm 27 Algoritm pentru problema comis-voiajorului
A - matricea de costuri ce cont¸ine distant¸ele dintre ora¸se n - num˘ arul de ora¸se 1: function CanContinue(A, X, k, cost) 2: if (vizitatxk = 1) (cost + axk ,xk > costoptim) ((k = n + 1) 3: return f alse
Input:
∨
∨
−1
4: else 5: return true 6: end if 7: end function 8: procedure Evaluare Solutie(X, cost) 9: X optim X 10: costoptim cost 11: end procedure
← ←
12: procedure ComisVoiajorBacktracking(A, n) 13: x1 1, cost 0, costoptim 14: k 2, x k 1 15: while (k > 1) do 16: gasit f alse 17: while ((xk + 1 n) (gasit = f alse)) do 18: xk x k + 1 19: gasit CanContinue(A,X,k,cost) 20: end while 21: if (gasit = true) then 22: if (k = n + 1) then 23: call Evaluare Solutie(X,cost + axk 1 ,xk ) 24: else vizitatxk 1 25: 26: cost cost + axk 1 ,xk 27: k k + 1 28: xk 0 29: end if else 30: 31: k k 1 32: vizitatxk 0 33: if (k > 1) then cost cost axk 1 ,xk 34: end if 35: 36: end if 37: end while optim optim 38: Output ’Solutia optima: ’, x 1 , . . . , xn+1 ,costoptim 39: end procedure
← ←
← ← ←
←
←∞
≤ ∧
←
−
←
← ← ←
−
← −
← ←
−
−
{
}
Listing 3.2: hamilton.c #include #include #define NMAX 1 00
/ / nu ma ru l maxim de n o d u ri d i n g r a f
72
∧ (xk = x1 ))) then
in t n = r e a d I n pu t ( a ) ; C o m i s Vo i a j o r Ba c k t r ac k i n g ( n , a ) ;
} Cont¸inutul fi¸sierului graf1.txt este: 8 0 14 6 -1 5 -1 -1 -1
14 0 -1 12 16 20 -1 -1
6 -1 0 -1 -1 12 -1 12
-1 12 -1 0 21 -1 24 10
5 16 -1 21 0 16 -1 -1
-1 20 12 -1 16 0 14 6
-1 -1 -1 24 -1 14 0 10
-1 -1 12 10 -1 6 10 0
−1. ˆIn timpul
∞
Corespunz˘ ator valorii am utilizat ˆın fi¸sierul de test valoarea negativ˘ a citirii datelor aceasta a fost ˆınlocuit˘ a cu o constant˘ a suficient de mare: .. . sc a n f (”%d” , &a [ i ] [ j ] ) ; i f (max < a [ i ] [ j ] ) max = a [ i ] [ j ] ; i f ( a [ i ] [ j ] < 0 ) a [ i ] [ j ] = LIMIT;
ˆIn timpul citirii se determin˘a valoarea maxim˘a, max ai,j i, j = 1, n , i = j, ai,j = a se init¸ializa mai apoi costul optim cu valoarea n max. Funct¸ia ˆın care se verific˘ a condit¸iile de continuare este urm˘ atoarea:
·
{ |∀
∞}, pentru
in t CanContinue( in t n , M at ri ce a , in t k , long c o s t , V ec to r v i z i t a t , V ec to r x ) i f ( ( v i z i t a t [ x [ k ] ] == 1 ) (mai mare( cost , a [ x [k 1]][ x [k ]] , cost optim )) ( ( k != n +1) && ( x [ k ] == 1 ) ) ) return 0 ; else return 1 ;
||
||
−
{
} Folosim o funct¸ ie (mai mare()) cu scopul de a realiza verificarea inegalit˘ at¸ii cost + optim axk ,xk +1 > cost : −1
in t mai mare( long a , long b , long c ) i f ( a + b > c ) return 1 ; else return 0 ;
{
} ¸si a preveni eventualele dep˘a¸siri (overflow )8 ˆın timpul calculelor. Nu am afi¸sat numai solut¸ia optim˘ a, ci am ales s˘a afi¸sa˘m toate ciclurile hamiltoniene care ˆımbun˘at˘a¸t eau solut¸ia anterioar˘ a: void E v a l u a r e S o l u t i e ( i nt n , long c o s t , V ec to r x ) in t i ; cost optim = cost ; 8
http://en.wikipedia.org/wiki/Arithmetic_overflow
75
{
Fig. 3.10: Pasii efectuati de algoritm pentru determinarea ciclului hamiltonian asociat grafului din figura 3.8
78
Fig. 3.11: Pasii efectuati de algoritm pentru determinarea ciclului hamiltonian asociat grafului din figura 3.8 (continuare)
79
Capitolul 4 Arbori. Arbori binari Definit¸ia 4.1 Fiind dat˘ a o mult ¸ime M de elemente denumite noduri, vom numi arbore o submult ¸ime finit˘ a de noduri astfel ˆıncˆ at: 1. exist˘ a un nod cu destinat ¸ie special˘ a, numit r˘ ad˘ acina arborelui; 2. celelalte noduri sunt repartizate ˆın n mult ¸imi distincte, disjuncte dou˘ a cˆ ate dou˘ a, A 1, A2 , . . . , An, fiecare mult ¸ime Ai constituind la rˆ andul ei un arbore. Aceast˘a definit¸ie este una recursiv˘ a, construct¸ia unui arbore depinzˆand de alt ¸i arbori. Descendent¸ii direct¸i ai r˘ad˘acinii arborelui sunt r˘ ad˘acinile subarborilor A 1 , A2 , . . . , An, n 1. Orice nod al unui arbore cu r˘ad˘acin˘a constituie r˘ ad˘acina unui subarbore compus din nodul respectiv ¸si tot¸i descendent¸ii s˘ai. Se observ˘ a c˘a un arbore impune o structur˘ a de organizare ierarhic˘ a asupra elementelor unei mult¸imi.
≥
Definit¸ia 4.2 Se nume¸ste arbore liber (”free tree”) un graf G = (V, E ) neorientat, conex ¸si aciclic. Teorema 4.1 (Propriet˘ at ¸ile arborilor liberi) Fie G = (V, E ) un graf neorientat. Urm˘ atoarele afirmat ¸ii sunt echivalente: 1. G este un arbore liber. 2. Oricare dou˘ a vˆ arfuri din G sunt conectate printr-un drum elementar unic. 3. G este conex, dar, dac˘ a elimin˘ am o muchie oarecare din E , graful obt ¸inut nu mai este conex. 4. G este conex, ¸si E = V
| | | | − 1. 5. G este aciclic, ¸si |E | = |V | − 1.
6. G este aciclic, dar dac˘ a ad˘ aug˘ am o muchie oarecare ˆın E , graful obt ¸inut cont ¸ine un ciclu. Dac˘a se alege un vˆa rf sau nod drept r˘ ad˘ acina arborelui , atunci un arbore liber devine arbore cu r˘ ad˘ acin˘ a . ˆIn general vom folosi termenul de arbore ˆın lo c de termenul corect arbore cu r˘ ad˘ acin˘ a . arf terminal (frunz˘ a) este un vˆarf f˘ar˘a descendent¸i. Vˆarfurile care nu sunt terminale Un vˆ sunt neterminale (sau noduri interioare ) . De obicei, se consider˘ a c˘a exist˘a o ordonare a descendent¸ilor aceluia¸si p˘arinte.
80
1
2
3
4
5
8
9
10
6
12
11
7
13
Fig. 4.1: Exemplu de arbore binar plin cu 2 4
14
15
− 1 vˆarfuri
Definit¸ia 4.3 Adˆ ancimea unui vˆ arf este dat˘ a de lungimea drumului de la r˘ ad˘ acin˘ a la acel ˆ alt vˆ arf. In˘ ¸imea unui vˆ arf se determin˘ a ca fiind lungimea celui mai lung drum dintre acel vˆ arf ˆ alt ¸si un vˆ arf terminal. In˘ ¸imea r˘ ad˘ acinii determin˘ a ˆın˘ alt ¸imea arborelui. Nivelul unui vˆ arf se calculeaz˘ a ca diferent ¸a dintre ˆın˘ alt ¸imea arborelui ¸si adˆ ancimea acestui vˆ arf. Toate vˆ arfurile unui arbore ce au aceea¸si adˆ ancime se spune c˘ a sunt pe acela¸si nivel . Un arbore oarecare cu r˘ ad˘acin˘a este n-ar dac˘ a fiecare vˆ arf are pˆ an˘a la n descendent¸i direct¸i. O mult¸ime de arbori disjunct¸i formeaz˘ a o p˘ adure . Din punct de vedere grafic, un arbore se poate reprezenta descriind nodurile cu ajutorul unor cercuri sau p˘ atrate ˆın care se afl˘ a informat¸ia aferent˘ a, iar relat¸ia de descendent¸˘a prin leg˘aturile ce unesc nodurile cu fii lor.
4.1
Arbori binari
Definit¸ia 4.4 Un arbore binar este un arbore oarecare cu r˘ ad˘ acin˘ a, fiecare vˆ arf al s˘ au avˆ and cel mult doi descendent ¸i, indicˆ andu-se cine este descendentul stˆ ang al r˘ ad˘ acinii ¸si cine este cel drept. Trebuie ment¸ionat c˘ a un arbore binar nu este un caz particular de arbore oarecare, deoarece ˆın cazul unui arbore oarecare este suficient s˘ a spunem c˘ a c˘a un vˆarf are un singur decendent spre deosebire de cazul arborelui binar cˆ and trebuie precizat dac˘ a descendentul este drept sau stˆ a ng. Arborele binar ce nu cont ¸ine nici un nod se nume¸ste arbore vid sau arbore nul .
Definit¸ia 4.5 Se nume¸ste arbore binar plin un arbore binar cu 2k k nivele astfel ˆıncˆ at pe fiecare nivel i avem 2i−1 vˆ arfuri.
− 1 vˆ arfuri a¸sezate pe
Se observ˘ a c˘a fiecare vˆ arf al arborelui are doi descendent¸i, descendentul stˆ ang ¸si descendentul drept, cu except¸ia nodurilor terminale. ˆIn figura 4.1 este prezentat un arbore binar plin ˆın care vˆarfurile sunt a¸sezate pe 4 niveluri (k = 4). De regul˘a vˆ arfurile unui arbore binar plin se numeroteaz˘ a ˆın ordinea nivelurilor, iar ˆın cadrul unui nivel, de la stˆ anga la dreapta.
Definit¸ia 4.6 Se nume¸ste arbore binar complet cu n vˆ arfuri un arbore binar plin avˆ and k −1 k k a vˆ arfurile numerotate n +1, n +2, . . . , 2 1. k nivele, unde 2 n < 2 , din care se elimin˘
≤
−
81
1
2
3
4
5
8
9
6
10
7
12
11
Fig. 4.2: Exemplu de arbore binar complet cu 12 vˆarfuri
ˆIn figura 4.2 este ilustrat un exemplu de arbore binar complet. Un arbore binar poate fi transformat ˆıntr–un arbore binar plin ˆın modul urm˘ ator: complet˘am arborele cu noi vˆarfuri astfel ˆıncˆat fiecare vˆ arf s˘a aib˘a doi descendent¸i sau nici unul, renumerot˘ am vˆarfurile iar vˆarfurilor nou introduse li se asociaz˘ a o valoare ce va indica faptul c˘a ele sunt fictive.
4.1.1
Moduri de reprezentare
Cele mai cunoscute modalit˘ a¸t i de reprezentare a arborilor binari sunt: 1. expresii cu paranteze ; 2. forma standard ; 3. reprezentarea tip tat˘ a . 1
2
8
3
5
4
9
6
7
Fig. 4.3: Exemplu de arbore binar
1. expresii cu paranteze – expresia ˆıncepe cu r˘ ad˘acina ¸si dup˘a fiecare vˆ arf k urmeaz˘ a expresiile subarborilor ce au ca r˘ad˘acini descendent¸ii vˆarfului respectiv, separate prin virgul˘ a ¸si incluse ˆıntre paranteze. Dac˘ a un descendent al unui vˆarf nu exist˘a atunci expresia respectiv˘a este ˆınlocuit˘a cu 0 sau NULL. Un arbore cu un singur vˆ a rf (r˘ ad˘acina), etichetat cu a1 , se reprezint˘ a astfel: a1 (0, 0). Un arbore format din r˘ ad˘acin˘a a1 ¸si doi descendent¸i, a2 ¸si a3 , se reprezint˘ a astfel: a1(a2 (0, 0), a3(0, 0)). Pentru arborele din figura 4.3 avem: 1(2(3(0, 4(0, 0)), 5(6(0, 0), 7(0, 0))), 8(0, 9(0, 0))) 82
Fig. 4.5: Exemplu de arbore binar cu 8 noduri
⌊ ⌋ ·
Tatai = Stangi =
4.1.2
i 2
, i 2 nu exist˘ a , i = 1
≥
2 i , 2 i n nu exist˘ a , 2 i > n
· ≤ ·
Drepti =
·
2 i+1 , 2 i + 1 n nu exist˘ a , 2 i + 1 > n
· ·
≤
Metode de parcurgere
Exist˘a mai multe moduri de parcurgere a unui arbore binar. Indiferent de metoda de parcurgere aleas˘ a se parcurge mai ˆıntˆ ai subarborele stˆ ang ¸si apoi subarborele drept. Dup˘ a momentul ˆın care un nod k este vizitat fat¸a˘ de subarborii s˘ai stˆ ang ¸si drept, avem:
• parcurgere ˆın preordine (r˘ ad˘ acin˘ a, subarbore stˆ ang, subarbore drept).
ˆIn urma parcurgerii ˆın preordine a arborelui din figura 4.3 rezult˘ a urm˘atoarea ordine pentru noduri: 1, 2, 3, 4, 5, 6, 7, 8, 9. ˆIn figura 4.6 este prezentat˘ a parcurgerea ˆın preordine a unui arbore binar, descris˘ a ˆın mai mult¸i pa¸si, conform descrierii recursive a acestei metode.
Vom da exemplu de o procedur˘ a nerecursiv˘ a de vizitare ˆın preordine (vezi algoritmul 28). Se pleac˘ a de la r˘ ad˘acin˘a ¸si se merge spre stˆ anga atˆ ata timp cˆ at este posibil (liniile 5–8). Dac˘ a nu se mai poate merge spre stˆ anga se ˆıncearc˘ a continuarea algorimului din descendentul drept, dac˘ a este posibil. Dac˘ a nu se poate face un pas spre dreapta (descendentul drept nu exist˘ a – linia 10), se va urca ˆın arbore cˆ ate un nivel (liniile 12–22), pˆ an˘a cˆand se ajunge ˆın situat¸ia de a se veni din descendentul stˆ ang (linia 19) ¸si se reia algoritmul trecˆ and ˆın descendentul drept, dac˘a exist˘ a (linia 25). ˆIn momentul ˆın care s–a ajuns ˆın nodul r˘ ad˘acin˘a venind din dreapta, algoritmul se ˆıncheie. Datorit˘ a faptului c˘ a atunci cˆ and se urc˘ a ˆın arbore trebuie s˘ a ¸stim din ce descendent venim, vom utiliza ¸si vectorul tata. Aceast˘a procedur˘a poate fi utilizat˘ a ¸si pentru celelalte moduri de parcurgere, modific˘ arile necesare referindu–se la momentul cˆ and trebuie vizitat nodul curent (prin apelul procedurii V izit - instruct¸iunea call V izit(k)). 84
Fig. 4.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 #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]); }
85
Algoritm 28 Algoritm de parcurgere ˆın preordine 1: procedure Preordine(Rad, Stang, Drept, T ata) 2: k rad 3: q 0 4: while (q = 0) do while (stangk = 0) do 5: 6: call V izit(k) 7: k stangk 8: end while 9: call V izit(k) while (q = 0) (dreptk = 0) do 10: 11: found 0 12: while (q = 0) (found = 0) do 13: j k k tatak 14: if (k = 0) then 15: 16: q 1 17: else 18: if ( j = stangk ) then found 1 19: end if 20: 21: end if 22: end while 23: end while 24: if (q = 0) then 25: k dreptk 26: end if 27: end while 28: return 29: end procedure
← ←
⊲ prelucrarea informat¸iei din nodul vizitat
←
∧
← ∧ ← ← ←
←
←
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];
86
} vizit(i); while ((q == 0) && (drept[i] == 0)) { found = 0; while ((q == 0) && (found == 0)) { j = i; i = tata[i]; if (i == 0) q = 1; else if (j == stang[i]) found = 1; } } if (q == 0) i = drept[i]; } } void main(void){ readInput(); preord(rad); }
• parcurgere ˆın inordine (arbore stˆ ang, r˘ ad˘ acin˘ a, arbore drept): 3, 4, 2, 6, 5, 7, 1, 8, 9.
Metoda de parcurgere ˆın inordine este ilustrat˘ a de algoritmul 29 [93], [123]. Vom utiliza o stiv˘a S unde se vor introduce nodurile din care se coboar˘ a spre stˆanga (liniile 6–9). Atunci cˆand nu se mai poate merge spre stˆ anga se ˆıncearc˘ a continuarea algoritmului din descendentul drept al nodului curent. Dac˘ a acesta nu exist˘ a, se reface drumul ˆınapoi spre r˘ ad˘acin˘a, compus numai din descendent¸ii stˆangi (liniile 11–18). Parcurgerea se termin˘a ˆın momentul ˆın care stiva este vid˘ a (vezi algoritmul 29). Implementarea ˆın limbajul C a algoritmului 29 este: #include #define MAX 100 /** stang[k] - descendentul stang al nodului k; 0 daca nu exista */ char stang[MAX]; /** drept[k] - descendentul drept al nodului k */ char drept[MAX]; /** Numarul de noduri din arbore */ int n; /** Nodul radacina */ int rad; void vizit(int nod) { printf("%2d ", nod); } void readInput(void) {
87
Algoritm 29 Algoritm de parcurgere ˆın inordine 1: procedure Inordine(Rad, Stang, Drept) 2: k rad 3: q 0 S 4: 5: while (q = 0) do 6: while (stangk = 0) do 7: S k 8: k stangk 9: end while call V izit(k) 10: 11: while (q = 0) (dreptk = 0) do 12: if (S = ) then 13: q 1 14: else S k 15: 16: call V izit(k) 17: end if 18: end while 19: if (q = 0) then k dreptk 20: 21: end if 22: end while 23: return 24: end procedure
← ← ← ∅
⊲ init¸ializare stiv˘a vid˘a
⇐ ←
∅
⊲ S.push(k)
∧
⊲ verificare dac˘a stiva este vid˘a
← ⇒
⊲ S.pop(k)
←
int k; printf("n = "); scanf("%d", &n); printf("rad = "); scanf("%d", &rad); for (k = 1; k <= n; k++) { printf("stang[%d] = ", k); scanf("%d", &stang[k]); } for (k = 1; k <= n; k++) { printf("drept[%d] = ", k); scanf("%d", &drept[k]); } } void inord(int rad) { int i, cap; int q; int stack[MAX]; i = rad; q = 0; cap = -1; while (q == 0) { while (stang[i] > 0) {
88
cap++; stack[cap] = i; i = stang[i]; } vizit(i); while ((q == 0) && (drept[i] == 0)) { if (cap < 0) q = 1; else { i = stack[cap]; cap--; vizit(i); } } if (q == 0) i = drept[i]; } } void main(void) { readInput(); inord(rad); }
• parcurgere ˆın postordine (subarbore stˆ ang, subarbore drept, r˘ ad˘ acin˘ a): 4, 3, 6, 7, 5, 2, 9, 8, 1 (vezi algoritmul 30).
Algoritm 30 Algoritm de parcurgere ˆın postordine 1: procedure Postordine(K, Stang, Drept) if (k = 0) then 2: 3: call P ostordine(stangk , stang, drept) 4: call P ostordine(dreptk , stang, drept) 5: call V izit(k) 6: end if 7: return 8: end procedure
Modalitatea de vizitare recursiv˘ a a arborilor binari este exemplificat˘a la arborii binari de sortare. Dac˘a presupunem realizarea unei act¸iuni de vizitare pe exteriorul frontierei arborelui, deplasarea efectuˆ andu-se ˆın sens trigonometric ¸si avˆ and drept punct de plecare r˘ ad˘acina acestuia, vom ajunge s˘ a vizit˘am cel put¸in o dat˘ a toate nodurile arborelui (vezi figura 4.7). T ¸ inˆand cont de momentul vizit˘ a rii se poate obt¸ine oricare dintre metodele de parcurgere (preordine, inordine, postordine ): pentru preordine vom marca un nod ˆın momentul ˆın care ˆıl vizit˘am pentru prima dat˘ a, ˆın cazul metodei de vizitare ˆın inordine vom marca un nod ˆın momentul ˆın care ˆıl vizit˘ am pentru prima dat˘ a dac˘ a este frunz˘ a, respectiv a doua oar˘ a dac˘a este un nod interior, iar la postordine vom marca un nod ˆın momentul ˆın care ˆıl vizit˘ am pentru ultima oar˘ a.
89
1
2
8
3
9
5
6
7
4
Fig. 4.7: Vizitarea unui arbore pe frontier˘a
·
Fig. 4.8: Arbore asociat expresiei aritmetice 2 ((a + b)
− (c + b))
Arborele asociat unei expresii aritmetice Oric˘arei expresii aritmetice i se poate asocia un arbore binar astfel:
• fiec˘arui operator ˆıi corespunde un nod neterminal avˆand drept subarbori stˆang ¸si drept cei doi operanzi corespunz˘ atori.
• fiecare nod terminal este etichetat cu o variabil˘a sau o constant˘a. Arborele din figura 4.8 corespunde expresiei matematice: 2 · ((a + b) − (c + b)). ˆIn urma vizit˘arii ˆın postordine a acestui arbore se obt ¸ine forma polonez˘ a expresiei a postfixat˘ a asociat˘ anterioare: 2ab + cb + −∗. Pentru a obt¸ine valoarea expresiei respective, arborele poate fi parcurs ˆın postordine.
4.2
Arbori binari de c˘ autare
Definit¸ia 4.7 [93] Se nume¸ste arbore binar de c˘ autare un arbore binar ce verific˘ a urm˘ atoarea proprietate:
90
pentru orice vˆ arf i apart ¸inˆ and arborelui, avem
Inf i Inf i
≥ I nf ≤ I nf
j j
, pentru toate vˆ arfurile j ce apart ¸in descendentului stˆ ang al vˆ arfului i , pentru toate vˆ arfurile j ce apart ¸in descendentului drept al vˆ arfului i
unde I nf i este informat ¸ia asociat˘ a vˆ arfului i . Aceast˘ a 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 c˘ autare mai este cunoscut ¸si sub numele de arbore binar de sortare deoarece parcurgerea ˆın inordine a unui arbore binar de c˘ autare ne conduce la obt¸inerea elementelor vectorului Inf ˆın ordine cresc˘ atoare. ˆIn principal aceast˘ a structur˘ a de date este utilizat˘a pentru reg˘ asirea eficient˘ a a unor informat¸ii. Operat¸iile de creare a arborelui, ¸stergere a unui nod, modificare a informat¸iei asociate unui nod sau inserare a unui nod se pot realiza ˆıntr-un mod optim astfel ˆıncˆ at s˘ a nu se autare . distrug˘a proprietatea de arbore de c˘ Dac˘a dorim ca arborele s˘a nu cont¸in˘a chei duplicate, vom modifica definit¸ia de mai sus astfel: pentru orice vˆ arf i apart¸inˆand arborelui, avem
Inf i < Inf j Inf i > Inf j
, pentru toate vˆ arfurile j ce apart¸in descendentului stˆang al vˆ arfului i , pentru toate vˆ arfurile j ce apart¸in descendentului drept al vˆ arfului i.
ˆIn continuare vom utiliza aceast˘ a definit¸ie pentru descrierea algoritmilor ce urmeaz˘ a.
Crearea unui arbore de c˘ autare Crearea (construirea) unui arbore de c˘ autare se face aplicˆand ˆın mod repetat operat ¸ia de inserare.
C˘ autarea unei informatii ˆıntr-un arbore de c˘ autare Informat¸ia asociat˘ a unui nod mai poart˘ a numele de cheie . S˘a presupunem c˘ a dorim s˘ a c˘aut˘ am o cheie x ˆıntr-un arbore de c˘ autare. Vom compara mai ˆıntˆai cheia x cu informat¸ia asociat˘ a r˘ad˘acinii arborelui (vezi algoritmul 31): 1. dac˘ a cheia x este mai mic˘a decˆ a t cheia r˘ ad˘acinii, atunci se va continua c˘ autarea ˆın subarborele stˆ ang; 2. dac˘ a cheia x este mai mare decˆ a t cheia r˘ ad˘acinii, atunci se va continua c˘ autarea ˆın subarborele drept; 3. dac˘ a cheia x este egal˘ a cu cheia r˘ad˘acinii, atunci c˘autarea se ˆıncheie cu succes.
Inserarea unui nod ˆıntr-un arbore de c˘ autare Aceasta operat¸ie prezint˘ a urm˘atoarele particularit˘ at¸i (vezi algoritmul 32):
• se verific˘a prin intermediul operat¸iei de c˘autare dac˘a cheia x a nodului care se dore¸ste s˘a fie inserat mai exist˘a ˆın arbore;
• ˆın cazul ˆın care c˘autarea se ˆıncheie cu succes, inserarea nu va mai avea loc; • ˆın cazul ˆın care operat¸ia de c˘autare se termin˘a f˘ar˘a succes, atunci se va crea un nod nou ˆın locul subarborelui vid unde s-a terminat c˘ autarea.
ˆIn figura 4.9 este ilustrat˘ a inserarea unui nod ˆıntr–un arbore binar. 91
Algoritm 31 Algoritm de c˘autare a unei valori ˆıntr-un arbore binar de c˘ autare 1: function SearchNode( p, Key) 2: if ( p = NU LL) then 3: if ( p.info > key) then 4: return SearchNode( p.lef t, key) else 5: 6: if ( p.info < key) then 7: return SearchNode( p.right, key) 8: else 9: return p end if 10: 11: end if 12: else 13: return NU LL 14: end if 15: end function
autare Algoritm 32 Algoritm de inserare ˆıntr-un arbore binar de c˘ 1: function InsertNode( p, Key) 2: if ( p = NU LL) then 3: call CreateNode( p, key) 4: else 5: if ( p.info > key) then 6: p.left InsertNode( p.lef t, 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
←
←
S ¸ tergerea unui nod dintr-un arbore de c˘ autare Mai ˆıntˆai se va c˘auta nodul ce se dore¸ste s˘ a fie eliminat. S˘a presupunem c˘ a am g˘ asit nodul ce urmeaz˘ a a fi ¸sters. Avem urm˘atoarele situat¸ii (vezi algoritmul 34): 1. nodul ce urmeaz˘ a s˘ a fie ¸sters este un nod terminal - se face ¸stergerea normal avˆ and grij˘a s˘a se ˆınlo cuiasc˘a leg˘atura din nodul p˘ arinte c˘ atre el cu null; 2. nodul ce urmeaz˘ a s˘ a fie ¸sters are un singur descendent - nodul respectiv se ¸sterge iar p˘arintele va cont¸ine acum noua leg˘ atur˘ a c˘atre descendentul fostului fiu; 3. nodul ce urmeaz˘ a a fi ¸sters (notat A) are doi descendent¸i:
• se determin˘a cel mai din stˆanga nod (notat B) din subarborele drept al nodului
ce trebuie ¸sters (vezi algoritmul 33); acesta va fi ¸sters ˆın mod fizic, dar la un pas ulterior; 92
Fig. 4.9: Arbore binar de c˘autare. Inserarea unui nod.
• toate informat¸iile legate de datele cont¸inute ˆın nodul B vor fi copiate ˆın nodul A; • subarborele drept al nodului B se va lega fie ca descendent drept al nodului A
(dac˘ a nodul B este descendent direct al nodului A), fie ca descendent stˆ a ng al tat˘ alui nodului B;
• se va ¸sterge fizic nodul B. Exemplul 4.3 Fie un arbore binar, eticheta fiec˘ arui nod fiind un ¸sir de caractere (un cuvˆ ant). Se cere s˘ a se realizeze un program care s˘ a creeze un arbore binar de sortare, s˘ a-l parcurg˘ a ˆın inordine ¸si s˘ a permit˘ a inserarea ¸si ¸stergerea unui nod specificat. #include #include #include #include
typedef struct nod {
93
Fig. 4.10: Arbore binar de c˘ autare. S ¸tergerea unui nod. a) Nod f˘ar˘a descendent¸ i b) Nod cu un singur descendent c) Nod cu doi descendent¸i.
94
Algoritm 33 Algoritm pentru determinarea celui mai din stˆ anga nod 1: function LeftmostNode(Parent,Curent) 2: while (curent.left = NULL) do 3: parent curent curent curent.left 4: end while 5: 6: if ( parent.right = curent) then 7: parent.right curent.right 8: else parent.left curent.right 9: end if 10: 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;
95
Algoritm 34 Algoritm de ¸stergere a unui nod ˆıntr-un arbore binar de c˘ autare 1: function DeleteNode( p, Key) 2: if ( p = NU LL) then 3: if ( p.info > key) then p.left DeleteNode( p.lef t, key) 4: else 5: 6: if ( p.info < key) then 7: p.right DeleteNode( p.right, key) 8: else 9: if ( p.lef t = NU LL) ( p.right = NU LL) then if ( p.lef t = NULL) then 10: 11: tmp p.left 12: else 13: tmp p.right 14: end if call DisposeNode( p) 15: 16: p tmp 17: else 18: tmp p.right tmp Lef tmostNode( p, tmp) 19: tmp.left p.left 20: 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);
96
VisitInord(p->right); } } NOD* LeftmostNod(NOD* parent, NOD* curent) { while (curent->left != NULL) { parent = curent; curent = curent->left; } if (parent->right == curent) parent->right = curent->right; else parent ->left = curent->right; return curent; } NOD* ElibNod(NOD *p) { free(p->cuvint); free(p); return NULL; } NOD* DeleteNod(NOD* p, char *sir) { NOD *tmp; int ind; if (p != NULL){ ind = strcmp(p->cuvint, sir); if (ind < 0) p->right = DeleteNod(p->right, sir); else if (ind > 0) p->left = DeleteNod(p->left, sir); else if (p->left == NULL || p->right == NULL) { if (p->left != NULL) tmp = p->left; else tmp = p->right; ElibNod(p); p = tmp; } else{ tmp = p->right; tmp = LeftmostNod(p, tmp); tmp->left = p->left; tmp->right = p->right; ElibNod(p); p = tmp; }
97
} return p; } void main(void) { char cuvint[100]; char ch; NOD *p; rad = NULL; while (1) { printf("***********************************\n"); printf("1. Inserare \n"); printf("2. Cautare\n"); printf("3. Stergere\n"); printf("4. Afisare arbore\n"); printf("0. Exit \n"); ch = getch(); if (ch != ’0’ && ch != ’4’) { printf("Cuvint: "); scanf("%s", cuvint); } switch (ch) { case ’1’: rad = Insert(rad, cuvint); break; case ’2’: p = Search(rad, cuvint); if (!p) printf("Cuvintul %s nu a fost gasit! \n", cuvint); else printf("Cuvintul %s exista in arbore! \n", cuvint); break; case ’3’: rad = DeleteNod(rad, cuvint); break; case ’4’: VisitInord(rad); break; case ’0’: exit(1); } } }
4.3 1.
Exercit¸ii (a) S˘ a se realizeze o subrutin˘ a ce determin˘ a cea mai mare valoare p˘astrat˘a ˆıntr–un arbore binar de c˘ autare; (b) Aceea¸si cerint¸a˘ pentru cea mai mic˘ a valoare.
2. S˘a se realizeze o subrutin˘ a ce determin˘ a toate nodurile unui arbore binar de c˘ autare cu proprietatea c˘ a informat¸ia k asociat˘a unui nod verific˘ a relat¸ia a k b, unde a ¸si b sunt date.
≤ ≤
3.
(a) S˘ a se realizeze un algoritm care determin˘ a num˘arul arborilor binari distinct¸i cu n noduri, unde n este un num˘ar natural dat. 98
Fig. 4.11: Exemplu de arbore binar complet cu 7 vˆarfuri
(b) Aceea¸si problem˘a pentru arbori binari de c˘ autare. 4. S˘a consider˘ am o secvent¸˘a de intrare compus˘ a din termeni definit¸i recursiv astfel: (a) X este un termen; (b) dac˘ a A ¸si B sunt termeni, atunci (A B) este un termen; (c) orice termen se construie¸ste cu ajutorul regulilor (i) ¸si (ii). Un termen este transformat cu ajutorul urm˘ atoarei 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 ˆınseamn˘ a aplicarea acestei reguli. Un termen se nume¸ste redus dac˘a nu cont¸ine nici un subtermen care s˘ a poat˘ a fi redus. Un termen poate fi considerat ca subtermen pentru el ˆınsu¸si. S˘a se realizeze un algoritm care determin˘ a pentru un termen de intrare, termenul redus corespunz˘ator. Un termen cont¸ine doar simbolurile ’X ’, ’(’ ¸si ’)’, f˘ar˘a spat¸ii ˆıntre ele. Lungimea unui termen de intrare este 100.
Observat¸ia 4.4 O expresie va avea maxim 9 nivele de parantezare. Num˘ arul de reduceri ce pot fi efectuate este finit. Liniile de ie¸sire cont ¸in maxim 80 caractere. Caracterele posibile din termenii de ie¸sire sunt tot ’ X ’, ’ (’ ¸si ’ )’. Intrare (((XX)X)X) (((XX)(XX)X) (XX)
Ie¸sire ((XX)(XX)) ((XX)((XX)X)) (XX)
(ACM, Bucure¸sti, 1996)
5. Prin arbore binar complet ˆınt¸elegem un arbore binar ˆın care un nod are fie doi descendent ¸i, fie nu are nici unul. Un arbore binar complet poate fi reprezentat prin codificarea drumurilor de la r˘ ad˘acin˘a la fiecare frunz˘ a utilizˆand numai valorile 0 ¸si 1: atunci cˆ and coborˆ am ˆın arbore spre stˆanga se adaug˘ a valoarea 0 iar atunci cˆ and coborˆ am spre dreapta se adaug˘ a valoarea 1.
99
Pentru arborele din figura 4.11 drumurile de astfel: ABC : ABDE : ABDF : AG :
la r˘ ad˘acin˘a la fiecare frunz˘ a se codific˘a 00 010 011 1
Concatenˆand toate drumurile de la r˘ ad˘acin˘a c˘atre frunze, arborele anterior poate fi reprezentat prin secvent¸a ce poate fi interpretat˘ a ca reprezentarea ˆın baza 2 a unui num˘ar (000100111). Fiind date m ¸si n dou˘a numere naturale, se cere s˘ a se determine dac˘ a exist˘a un arbore binar complet a c˘arui reprezentare va cont ¸ine exact m cifre 0 ¸si n cifre 1 (0 < m, n 100).
≤
(Timi¸soara-preg˘atire, 1996)
6. Scriet¸i o subrutin˘ a nerecursiv˘ a care num˘ ar˘a nodurile frunz˘ a ale unui arbore binar. 7. Realizat¸i o subrutin˘ a care s˘ a determine nivelul cu cele mai multe noduri frunze dintr–un arbore binar. 8. Determinat¸i ˆın˘a¸t imea unui arbore binar printr–o funct¸ie nerecursiv˘ a. 9. Se consider˘ a un dict¸ionar ce este implementat sub forma unei structuri de arbore. Pentru fiecare cuvˆ ant se cunoa¸ste traducerea acestuia precum ¸si probabilitatea lui de aparit¸ie ˆın texte. Se cere s˘ a se realizeze un algoritm care s˘ a conduc˘ a la o c˘autare optim˘ a a cuvintelor ˆın dict¸ionar. Se ment¸ioneaz˘ a faptul c˘ a probabilitatea de a c˘ auta un cuvˆ ant care nu se g˘ase¸ste ˆın dict¸ionar este zero. Datele de ie¸sire constau din afi¸sarea arborelui optimal de c˘ autare compus din cuvintele introduse. Spre exemplu, un set de date de intrare poate fi: 5 1 4 2 1 2
abroad in_strainatate baker brutar calf gamba dice zaruri ear ureche
10. O cas˘ a de comenzi este interesat˘ a de crearea unui program pe calculator care s˘ a ¸tin˘a evident¸a comenzilor ˆın ordinea datelor la care au fost onorate, iar ˆın cadrul fiec˘ arei date de livrare, ˆın ordine lexicografic˘ a dup˘ a numele produsului. S˘a se scrie un program care folose¸ste structuri de date de tip arbore. Intrarea este format˘a din mai multe linii. Pe fiecare linie avem cˆ ate o comand˘ a dat˘ a sub forma: [nume p rodus] [zi] [luna] [an]. Intrarea se termin˘ a cu o linie pe care avem doar ’&’. Ie¸sirea este format˘ a prin afi¸sarea comenzilor ˆın ordinea datei la care au fost onorate, iar ˆın cadrul unei date, ˆın ordine lexicografic˘ a ˆın funct¸ie de numele comenzii. autare dup˘a data de livrare ¸si ˆın fiecare nod va fi Indicat¸ie: Se va crea un arbore de c˘ un arbore de c˘ autare dup˘a numele comenzii. 100
11. Se consider˘ a o expresie logic˘ a formata din n variabile logice reprezentate printr–o singur˘ a liter˘a ¸si operatorii & (AND), (OR), ! (NOT) (nu avem paranteze). Expresia este reprezentat˘ a sub forma unui arbore binar.
|
Se cere s˘ a se realizeze un algoritm care pentru o expresie logic˘ a dat˘a cerceteaz˘ a existent¸a unei combinat¸ii de valori logice (true / false ), pentru care expresia dat˘ a ia valoarea logic˘ a true . De exemplu pentru expresia a !b c&d solut¸ia este a = false, b = false, c = false, d = f alse, iar pentru expresia !a&a nu avem solut¸ie.
||
12. Se consider˘ a o expresie matematic˘ a reprezentat˘ a 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 identificat ¸i printr–o singur˘ a liter˘a.
− ∗
Se cere s˘ a se realizeze un algoritm ce creaz˘ a structura de date corespunz˘ atoare unei expresii date ¸si s˘ a 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 evalu˘arii expresiei a b/c?a cu a = 2, b = 9 ¸si c = 3 obt¸inem valoarea 1.
−
∗
−
101
−
Capitolul 5 Arbori oarecare Reamintim definit¸ia unui arbore oarecare :
Definit¸ia 5.1 Fiind dat˘ a o mult ¸ime M de elemente denumite noduri, vom numi arbore o submult ¸ime finit˘ a de noduri astfel ˆıncˆ at: 1. exist˘ a un nod cu destinat ¸ie special˘ a, numit r˘ ad˘ acina arborelui; 2. celelalte noduri sunt repartizate ˆın n mult ¸imi disjuncte dou˘ a cˆ ate dou˘ a, A 1 , A2, mult ¸ime Ai constituind la rˆ andul ei un arbore.
5.1
· · · , A , fiecare n
Moduri de reprezentare
Pentru reprezentarea arborilor oarecare pot fi utilizate urm˘ atoarele metode:
• leg˘ aturi fiu-frate : fiu - este primul descendent al vˆarfului k, frate - urm˘atorul descenk
k
dent al tat˘ alui nodului k, ce urmeaz˘ a dup˘ a k. ˆIntre descendent¸ii unui vˆarf se presupune c˘a definim o relat¸ie de ordine. R˘ad˘acina arborelui din figura 5.1 este Rad = 1. 1
2
3
5
4
6
7
8
9
10
Fig. 5.1: Exemplu de arbore oarecare
Table 5.1: Reprezentarea cu leg˘aturi fiu–frate a arborelui din figura 5.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
102
Dac˘a identific˘ am F iu cu Stang ¸si Frate cu Drept, unui arbore oarecare i se poate asocia un arbore binar . Pentru reprezentarea unui arbore oarecare, modelul de reprezentare fiu-frate ˆın varianta de alocare static˘ a poate fi u¸sor extins la o variant˘ a de alocare dinamic˘ a, folosind pointeri pentru leg˘ aturile c˘ atre primul descendent respectiv pentru urm˘ atorul frate . Astfel un nod al arborelui poate avea urm˘ atoarea configurat¸ie: typedef struct nod { TipOarecare data; struct nod* fiu; struct nod* frate; }Nod;
Asem˘an˘ator cu modelul construit la arbori binari, rad (Nod rad;) este o variabil˘a de tip pointer la N od (variabila p˘ astreaz˘a adresa unei zone de memorie). rad desemneaz˘ a adresa r˘ ad˘acinii arborelui.
∗
rad 1
2
NULL
3
4
NULL
NULL
5
NULL
6
NULL
7
NULL
NULL
9
8
NULL
NULL
10
NULL
NULL
Fig. 5.2: Exemplu de arbore oarecare cu 10 noduri reprezentat prin leg˘aturi fiu-frate
ˆIn figura 5.2 este reprezentat arborele din figura 5.1 prin leg˘ aturi fiu-frate .
• lista descendent ¸ilor . ˆIn cadrul acestui mod de reprezentare fiecare vˆarf este descris prin lista descendent¸ilor s˘ai. Pentru memorare se va utiliza un vector cu n componente: C k =
0 , vˆarful respectiv nu are descendent¸i j , j indic˘a adresa (coloana) unde ˆıncepe lista descendent ¸ilor vˆarfului k.
Listele de descendent¸i se p˘astreaz˘a prin intermediul unei matrice L cu 2 linii ¸si N coloane:
− 1
L1,k - un descendent al vˆ arfului a c˘arui list˘a cont¸ine coloana k a matricei date. L2,k =
0 , dac˘ a descendentul respectiv este ultimul j , j indic˘a coloana unde se afl˘ a urm˘atorul descendent
Exploatˆand mai departe aceast˘ a idee, ˆıntr-un nod al arborelui oarecare se poate p˘ astra o list˘a cu adresele descendent ¸ilor s˘ai, lista fiind reprezentat˘ a sub forma unui tablou alocat 103
Table 5.2: Reprezentarea arborelui din figura 5.1 folosind liste cu descendent¸i. Nod 1 2 3 4 C 1 0 4 6 2 3 4 5 L = 2 3 0 5
5 0 6 0
6 0 7 7
7 0 8 8
8 9 10 0 0 0 9 10 9 0
static sau dinamic. Oricum modelul se recomand˘ a atunci cˆ and num˘ arul descendent¸ilor unui nod este limitat superior, sau cˆ and acesta este cunoscut/stabilit ˆın momentul ˆın care se construie¸ste arborele. Modific˘ arile efectuate asupra arborelui, cum ar fi inser˘ ari de descendent¸i noi, atunci cˆand intr˘ arile alocate pentru ace¸sti descendent ¸i sunt deja alocate nu poate conduce decˆ at la realocarea spat¸iului de memorie cu un cost reflectat ˆın complexitatea algoritmului. Astfel dac˘ a num˘arul de descendent¸i este limitat superior putem defini #define NMAX 100 typedef struct nod { TipOarecare data; struct nod* children[NMAX]; }Nod;
sau typedef struct nod { TipOarecare data; int no_of_children; struct nod** children; }Nod;
atunci cˆ and num˘ arul maxim de descendent¸i nu poate fi cunoscut decˆ at ˆın momentul construirii arborelui. Astfel, ˆın acel moment, se aloc˘ a spat¸ iu pentru un tablou de pointeri c˘ atre structura de tip Nod definit˘ a (children = malloc(sizeof(Nod*) * no of children)). rad
1
2
0
3
5
3
2
4
0
6
0
7
0
4
8
0
9
0
10
0
Fig. 5.3: Exemplu de arbore oarecare cu 10 noduri reprezentat prin liste cu descendent¸i
ˆIn figura 5.3 este reprezentat arborele din figura 5.1 folosind liste cu descendent ¸i . 104
• Tata - fiec˘arui nod k se indic˘a nodul p˘arinte. Table 5.3: Reprezentarea arborelui din figura 5.1 folosind vectorul tata. Nod Tata
1 2 3 4 5 6 7 8 9 10 0 1 1 1 3 3 4 4 4 4
Acest mod de reprezentare are dezavantajul c˘ a nu p˘ astreaz˘a ˆın mod explicit ordinea descendent¸ilor unui nod. Aceast˘ a ordine poate fi dedus˘ a printr–o numerotare adecvat˘ aa nodurilor. De obicei reprezentarea cu vectorul tata nu este folosit˘a singur˘ a ci ˆımpreun˘a cu alte moduri de reprezentare, ca o completare. De exemplu, reprezentarea unui nod propus˘ a la primul punct, poate fi modifcat˘a astfel ˆıncˆat s˘a incorporeze ¸si informat¸ii despre nodul p˘ arinte: typedef struct nod { TipOarecare struct nod* struct nod* struct nod* }Nod;
rad
data; fiu; tata; frate;
NULL 1
NULL
NULL 2
3
4
NULL
NULL
5
6
NULL
NULL
7
8
NULL
NULL
NULL
9
10
NULL
NULL
NULL
Fig. 5.4: Exemplu de arbore oarecare cu 10 noduri reprezentat prin leg˘aturi fiu-frate-tata
ˆIn figura 5.4, reprezentarea din figura 5.2 este ˆımbun˘ at˘a¸tit˘a prin leg˘ atura tata .
5.2
Metode de parcurgere
Pentru parcurgerea unui arbore oarecare se pot folosi metodele generale pentru parcurgerea arborelui binar asociat . Arborele binar asociat unui arbore oarecare se obt ¸ine ˆın urma realiz˘arii corespondent¸ei F iu Stang ¸si F rate Drept. ˆIn plus fat¸a˘ de acestea, avem ¸si dou˘ a metode de parcurgere pe care putem s˘ a le numim specifice unui arbore oarecare. Acestea se pot clasifica dup˘ a momentul ˆın care se realizeaz˘ a vizitarea nodului p˘ arinte astfel:
⇒
⇒
105
• A–preordine :
se viziteaz˘ a mai ˆıntˆa i r˘ad˘acina, ¸si apoi, ˆın ordine, subarborii s˘ ai [93], [123]. Metoda este identic˘ a cu metoda de parcurgere ˆın preordine a arborelui binar ata¸sat: 1, 2, 3, 5, 6, 4, 7, 8, 9, 10 (vezi algoritmul 35). Parcurgerea ˆın A–preordine este exemplificat˘ a pe arborele oarecare din figura 5.1.
Algoritm 35 Algoritm de parcurgere ˆın A-preordine 1: procedure APreordine(Rad,Fiu,Frate) 2: k rad 3: q 0 4: S 5: while (q = 0) do 6: if (k = 0) then 7: call V izit(k) 8: S k k f iuk 9: 10: else 11: if (S = ) then 12: q 1 13: else 14: S k k f ratek 15: 16: end if 17: end if 18: end while 19: return 20: end procedure
← ← ← ∅
⇐ ←
∅
← ⇒ ←
⊲ init¸ializare stiv˘a vid˘a
⊲ S.push(k)
⊲ verificare dac˘a stiva este vid˘a
⊲ S.pop(k)
• A–postordine : se viziteaz˘a mai ˆıntˆai tot¸i subarborii ce au drept r˘ad˘acini, descendent¸ii
r˘ad˘acinii arborelui ¸si apoi r˘ ad˘acina arborelui [93], [123]. ˆIn urma parcurgerii ˆın A– postordine a arborelui din figura 5.1 se obt¸ine 2, 5, 6, 3, 7, 8, 9, 10, 4, 1.
ˆ continuare se prezint˘ Exemplul 5.1 [31] In a implementarea ˆın limbajul de programare C a variantelor recursive pentru fiecare dintre cele dou˘ a metode de vizitare ale unui arbore oarecare. #include #include #include /** * Definitii de tipuri necesare cozii */ typedef struct nod { int id; float info; struct nod *down, *next; }NOD; typedef NOD* TINFO;
106
/** * Definitii de tipuri pentru reprezentarea arborelui */ typedef struct cnod { TINFO info; //informatia memorata in nod struct cnod *next; //adresa urmatorului element }CNOD; CNOD *prim = NULL, *ultim = NULL; NOD* citListaDescendenti(NOD *up); /** * Functie ce testeaza daca coada e vida * @return 1 daca coada e vida * 0 altfel */ int isEmpty(void) { if (prim == NULL) return 1; else return 0; } /** * Functia adauga un element la sfarsitul unei cozi * unde n reprezinta elementul ce se adauga. */ void add(TINFO n) { CNOD *curent; curent = (CNOD *) malloc(sizeof(CNOD)); curent->info = n; curent->next = NULL; if (!prim) { prim = ultim = curent; } else { ultim->next = curent; ultim = curent; } } /** * Functia extrage un element din coada. Returneaza elementul scos. */ TINFO get(void) { CNOD *curent; TINFO n; if (prim == ultim) { n = prim->info; free(ultim);
107
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; printf("\nLista de descendenti pt %d (CTRL+Z) daca nu are\n", up->id); prim = ultim = NULL; while (citInfo(&n)) {
108
p = (NOD *)malloc(sizeof(NOD)); *p = n; if (prim == NULL) prim = ultim = p; else { ultim->next = p; ultim = p; } add(p); } return prim; } /** * Parcurgerea in A-preordine a arborelui cu radacina p. */ void aPreordine(NOD *p) { NOD *desc; printf("%d ", p->id); desc = p->down; while (desc != NULL) { aPreordine(desc); desc = desc->next; } } /** * Parcurgerea in A-postordine a arborelui cu radacina p. */ void aPostordine(NOD *p) { NOD *desc; desc = p->down; while (desc != NULL) { aPostordine(desc); desc = desc->next; } printf("%d ",p->id); } void main(void) { NOD *rad; rad = creare(); printf("\n Parcurgerea in A-Preordine este:\n"); aPreordine(rad); printf("\n Parcurgerea in A-Postordine este:\n"); aPostordine(rad); }
109
5.3
Arbori de acoperire de cost minim
Fie G = (V, E ) un graf neorientat ¸si fie c : E ¸ie de cost ce asocieaz˘ a o valoare R o funct real˘a fiec˘ a rei muchii. Not˘ a m cu G mult¸imea arborilor part¸iali ai grafului G (un arbore part¸ial este un graf part¸ial conex ¸si f˘ar˘a cicluri al grafului init¸ial). Cerint¸a problemei este aceea de a determina un arbore T ′ and costul cel mai mic G avˆ dintre tot¸i arborii ce apart¸in mult¸imii G :
−→
T
∈ T
T
c(T ′) = min c(T ) T
{
| ∈ T } G
unde costul unui arbore T se define¸ste ca c(T ) = e∈E T c(e). Arborele T ′ ce posed˘ a aceast˘a proprietate se nume¸ste arbore de acoperire de cost minim (eng. minimum spanning tree MST ). Se mai ˆıntˆalne¸ste ¸si sub denumirea de arbore part ¸ial de cost minim sau arbore part ¸ial minim . ¸ial conex G1 = (V, E 1 ) (E 1 Altfel spus, se cere s˘a se determine un graf part E ) cu proprietatea c˘ a suma costurilor tuturor muchiilor este minim˘ a, graful part¸ial de cost minim fiind chiar un arbore. Determinarea arborelui de acoperire de cost minim are multe aplicat ¸ii practice: de exemplu, dac˘a se dau n ora¸se precum ¸si costul leg˘ aturilor ˆıntre acestea, se cere s˘ a se determine o conectare a tuturor ora¸selor astfel ˆıncˆ at oricare dou˘ a ora¸se s˘a fie conectate direct sau indirect iar costul conect˘arii s˘ a fie minim.
⊆
Lema 5.2 (Proprietatea t˘ aieturii) Fie S o submult ¸ime de noduri a lui V ¸si e muchia ce are costul minim dintre toate muchiile ce au o singur˘ a extremitate ˆın S . Atunci arborele part ¸ial de cost minim va cont ¸ine muchia e. Demonstrat¸ie: Fie T ′ un arbore part¸ial de cost minim. Presupunem prin reducere la absurd c˘ a e / E T . Dac˘a ad˘ aug˘am muchia e la T ′ se obt¸ine un ciclu C . ˆIn acest ciclu exist˘a o alt˘a muchie f ce are exact o extremitate ˆın mult ¸imea S . ′ ˆInlocuind muchia f cu muchia e ˆın arborele T , obt¸inem un alt arbore de acoperire T ′′ = T ′ e f . Deoarece c(e) < c(f ) vom avea c˘a c(T ′′ ) = c(T ′ ) + c(e) c(f ) c(T ′′ ) < c(T ′ ) adic˘ a am
∈
′
∪{ }\{ }
− ⇒ <0
′′
obt¸inut un arbore de acoperire T ce are costul mai mic decˆ at T ′ , contradict¸ie cu faptul c˘a T ′ este un arbore de acoperire de cost minim.
Definit¸ia 5.2 Se nume¸ste t˘ aietur˘ a a grafului G o partit ¸ie de dou˘ a submult ¸imi a mult ¸imii nodurilor V , notat˘ a astfel: < S, T > (unde S T = V ¸si S T = ).
∪
∩
∅
Lema 5.3 (Proprietatea ciclului) Fie C un ciclu ¸si f muchia ce are costul maxim dintre toate muchiile ce apart ¸in lui C . Atunci arborele part ¸ial de cost minim T ′ nu cont ¸ine muchia f . Demonstrat¸ie: Fie T ′ un arbore part¸ial de cost minim. Presupunem prin reducere la absurd c˘ a f E T . Dac˘a ¸stergem muchia f din arborele T ′ , se obt¸ine o t˘aietur˘a < S, V S >. Avem c˘a f C ¸si o extremitate a lui f apart¸ine lui S . Exist˘ a atunci o alt˘a muchie e cu proprietatea c˘ a e C ¸si e are o extremitate ce apart¸ine lui S . ˆInlocuind muchia f cu muchia e ˆın arborele T ′ , obt¸inem un alt arbore de acoperire T ′′ = T ′ e f . Deoarece c(e) < c(f ) c(T ′′ ) < c(T ′ ) adic˘ a am obt¸inut un arbore de acoperire T ′′ ce are costul mai mic decˆ at T ′ , contradict¸ie.
∈ ∈
∪{ }\{ }
\
′
∈
⇒
110
Majoritatea algoritmilor ce calculeaz˘ a arborele de acoperire de cost minim prezint˘ a aceea¸si 0 0 0 tehnic˘a general˘ a de calcul. La ˆınceput se porne¸ste cu o p˘ adure de arbori, = T 1 , T 2 , . . . , Tn0 unde T i0 = xi , i = 1, n este un arbore format dintr–un singur nod. La pasul k vom avea mult¸imea k compus˘ a din n k arbori: k = T 1k , T 2k , . . . , Tnk −k . La fiecare pas, se alege un arbore T ik ¸si o muchie (u′, v ′ ) avˆand costul minim dintre toate muchiile (u, v) cu proprietatea c˘ a o extremitate apart¸ine mult¸imii de noduri a arborelui T ik (u′ T ik ) ¸si cealalt˘a extremitate apart¸ine mult¸imii V V T ik (v ′ V V T ik ). Prin urmare, mult¸imea k+1 se obt¸ine din mult¸imea k prin reuniunea arborilor T ik ¸si T jk (i = j), unde u ′ T ik ¸si v ′ T jk ( k+1 = k T ik , T jk T ik T jk ). ˆIn final, la pasul n 1, mult¸imea n−1 = T n−1 va fi compus˘ a dintr–un singur element, 1 acesta fiind arborele de acoperire de cost minim . Algoritmii cei mai cunoscut¸i pentru determinarea arborilor de acoperire de cost minim sunt:
T {
{ } T
−
∈
∈
T
−
T {
}
\
∈
T T
T \ { { }
}
∈ \ T }∪ { ∪ }
1. Algoritmul lui Boruvka (vezi algoritmul 36)
Algoritm 36 Algoritmul lui Boruvka (varianta schematica) 1: procedure Boruvka1(G,C,n; L) a p˘adurea de arbori compus˘a din n arbori, fiecare arbore fiind compus dintr–un 2: init¸ializeaz˘
singur nod 3: 4: 5: 6: 7:
L
←∅
P
|P| ∈ P alege e muchia de cost minim de la T la G \ T L ⇐ e ⊲ adaug˘ a muchia e la lista de muchii alese ce vor forma arborele de acoperire
while ( > 1) do for T do
de cost minim
8: end for adaug˘ a toate muchiile selectate ˆın cadrul f or-ului anterior la 9: end while 10: 11: end procedure
2. Algoritmul lui Prim (vezi algoritmul 37)
Algoritm 37 Algoritmul lui Prim (varianta schematica) 1: procedure Prim1(n,C,u; L) 2: S u ,L 1, n do 3: for i 4: di c u,i 5: end for 6: for i 1, n 1 do 7: k min dk k V S L (tatak , k) 8: 9: S S k 10: for each j V S do 11: d j min d j , ck,j 12: end for 13: end for 14: end procedure
← { } ← ∅ ← ← ← − ← { | ∈ \ } ⇐ ← ∪ { } ∈ \ ← { }
111
P
3. Algoritmul lui Kruskal (vezi algoritmul 38)
Algoritm 38 Algoritmul lui Kruskal (varianta schematica) 1: procedure Kruskal1(G,C,n; L) 2: ordoneaz˘ a muchiile ˆın ordine cresc˘atoare dup˘ a cost L 3: for each u V do 4: 5: creaz˘a o mult¸ime compus˘a 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) reune¸ste mult¸imile ce cont¸in pe u ¸si v 12: count count + 1 13: 14: end if 15: end while 16: end procedure
←∅
∈
{ }
←
−
⇐
←
Algoritmul lui Prim implementat simplu are o complexitate O(n2 )[30] ¸si atinge o complexitate de O(m log n) dac˘ a se folosesc heap–uri Fibonacci [54], sau pairing heaps [115]. ˆIn tabelul 5.4 sunt prezentat¸i mai mult¸i algoritmi dezvoltat¸i de–a lungul timpului pentru determinarea arborelui de acoperire minimal ¸si complexit˘ at¸ile lor. Karger, Klein ¸si Tarjan [79] pornind de la algoritmul lui Boruvka au realizat un algoritm randomizat pentru determinarea arborelui de acoperire minimal, avˆ and o complexitate liniar˘ a, iar Chazelle [26] a dezvoltat un algoritm avˆ and complexitatea O(nα(m, n)) (α(m, n) este inversa funct¸iei lui Ackerman). Pe de alt˘a parte, Pettie ¸si Ramachandran [105] au propus un algoritm demonstrat ca fiind optimal, avˆand complexitatea cuprins˘ a ˆıntre O(n + m) ¸si O(nα(m, n)). Table 5.4: Algoritmi pentru determinarea arborelui de acoperire minim Anul 1975 1976 1984 1986 1997 2000 2002
Complexitate Autori E log log V Yao E log log V Cheriton-Tarjan ∗ E log V, E + V log V Friedman-Tarjan ∗ Gabow-Galil-Spencer-Tarjan E log log V Eα(V )log α(V ) Chazelle Eα(V ) Chazelle [26] optimal Pettie-Ramachandran [105]
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 utilizeaz˘ a matricea costurilor C : 0 , dac˘ a i = j ci,j = , dac˘ a (i, j) / E d > 0 , dac˘ a (i, j) E
{
⊆ ×
∞
}
∈ ∈
112
5.3.1
Algoritmul lui Boruvka
Algoritmul lui Boruvka [77] a fost descoperit de c˘atre matematicianul ceh Otakar Boruvka ˆın 1926 [21], ¸si redescoperit apoi de c˘ atre alt¸i cercet˘ atori. Dintre ace¸stia, Sollin (1961) este cel care a mai dat numele algoritmului, acesta fiind cunoscut ˆın literatura de specialitate ¸si sub numele de algoritmul lui Sollin . Pentru ca acest algoritm s˘ a poat˘ a fi aplicat, trebuie ca muchiile grafului s˘ a aib˘a costuri distincte (vezi algoritmul 39). Algoritm 39 Algoritmul lui Boruvka 1: procedure Boruvka2(G,C,n; L) 1, n do 2: for i V i i 3: 4: end for 5: L , M V 1 , . . . , Vn 6: while ( T < n 1) do 7: for U M do fie (u, v) muchia pentru care se obt¸ine valoarea minim˘a min c(u′ , v ′ ) (u′ , v ′ ) 8:
← ← { } ← ∅ ← { } | | − ∈ U, v ′ ∈ / V \ U } determin˘a componenta U ′ ce cont¸ine pe v L ⇐ (u, v) end for for U ∈ M do
{
|
∈ E, u′ ∈
9: 10: 11: 12: 13: reune¸ste mult¸imile ce cont¸in pe u ¸si v , U ¸si U ′ 14: end for 15: end while 16: end procedure
Exemplul 5.4 S˘ a consider˘ am graful din figura 5.5: G = (V, E ), V = 1, 2, 3, 4, 5, 6, 7, 8
{
}
Aplicˆ and algoritmul lui Boruvka, la pasul ˆıntˆ ai vor fi selectate muchiile (1, 2), (3, 6), (4, 5), ˆ (4, 7) ¸si (7, 8). In urma operat ¸iilor de reuniune a componentelor conexe pe baza muchiilor selectate, vor mai r˘ amˆ ane trei componente conexe ˆın mult ¸imea M . La pasul al doilea sunt alese muchiile (2, 5) ¸si (1, 3) ce conduc, ˆın urma operat ¸iilor de reuniune, la o singur˘ a component˘ a conex˘ a.
5.3.2
Algoritmul lui Prim
Algoritmul a fost descoperit mai ˆıntˆ ai de V. Jarnik (1930) [76], ¸si apoi independent de Prim (1957) [106] ¸si Djikstra (1959) [38]. Se porne¸ste cu o mult ¸ime S format˘ a dintr-un singur nod (S = v0 ). La fiecare pas se alege muchia de cost minim ce are numai o extremitate ˆın mult ¸imea S . Procesul se ˆıncheie dup˘a n 1 pa¸si, rezultˆ a nd un graf part¸ial aciclic. Din teorema 4.1 rezult˘ a faptul c˘a acest graf part¸ial aciclic cu n 1 muchii este un arbore (de acoperire). at ¸ii t˘ aieturii , toate muchiile alese apart¸in arborelui part¸ial de cost minim Conform Propriet˘ de unde rezult˘ a c˘a acest arbore de acoperire este un arbore part ¸ial minim . Vom utiliza trei vectori de dimensiune n, unde n reprezint˘ a num˘arul de vˆarfuri al grafului (vezi algoritmul 40):
{ }
−
−
113
Fig. 5.5: Exemplu de graf ponderat - aplicat¸ie algoritmul lui Boruvka
• vizitat - vector caracteristic vizitatk =
1 , dac˘ a nodul k 0 , dac˘ a nodul k
∈ S ∈ V \ S
• d - pentru un nod k ∈/ S , d va cont¸ine distant¸a minim˘a de la k la un nod j ∈ S . La ˆınceput, d = c . Pentru un nod k ales la un moment dat, d ( j ∈ S ) se modific˘a k
j
v0 ,j
j
numai dac˘ a ck,j < d j astfel d j = c k,j .
• tata - cont¸ine pentru fiecare nod k ∈/ S nodul j ∈ S astfel ˆıncˆat c
k,j =
La ˆınceput,
tatak =
0 v0
min ck,i i S .
{ |∈ }
, dac˘ a nodul k = v 0 , dac˘ a nodul k = v 0
ˆIn momentul ˆın care se modific˘ a d j , se va modifica ¸si valoarea lui tata j = k.
Fig. 5.6: Exemplu de graf ponderat - aplicat¸ie algoritmul lui Prim
114
Algoritm 40 Algoritmul Prim (varianta detaliata) 1: function DistantaMinima(n,vizitat,d) 2: min 3: for j 1, n do 4: if (vizitat j = 1) (d j < min) then min d j 5: 6: j0 j 7: end if 8: end for 9: if (min = ) then return 1 10: 11: else 12: return j 0 13: end if 14: end function
←∞ ←
← ←
∧
∞ −
15: procedure Prim2(n,C,v0 ; d, tata) 16: vizitatv0 1 0 17: tatav0 1, n do 18: for i 19: if (i = v 0 ) then 20: vizitati 0 21: di c v0 ,i 22: tatai v 0 23: end if 24: end for 25: for i 1, n 1 do 26: k DistantaMinima(n, vizitat, d) 27: if (k < 0) then Output ’Graful nu este conex!’ 28: 29: return 30: end if 31: vizitatk 1 ⊲ (k, tatak ) este o muchie ce apart¸ine arborelui part¸ial minim 1, n do 32: for j if (vizitat j = 1) (d j > ck,j ) then 33: 34: tata j k 35: d j c k,j 36: end if 37: end for 38: end for 39: end procedure
← ← ← ← ← ←
← ←
−
← ←
∧ ← ←
Exemplul 5.5 Fie graful din figura 5.6: G = (V, E ), V = 1, 2, 3, 4, 5, 6, 7, 8
{
Vom lua nodul init ¸ial v0 = 1. La ˆınceput, 1 2 3 4 5 6 7 d 14 6 5 tata 0 1 1 1 1 1 1 vizitat 1 0 0 0 0 0 0
∞
}
dup˘ a etapa de init ¸ializare avem: 8
∞ ∞ ∞
1 0 115
Algoritm 41 Algoritmul lui Prim folosind structuri de date avansate 1: procedure Prim3(n,C,u) 2: for fiecare v V do 3: dv 4: end for Q ⊲ init¸ializeaz˘a coada cu prioritate Q cu mult¸imea vid˘ a 5: 6: for fiecare v V do 7: Q v 8: end for S ⊲ init¸ializeaz˘a mult¸imea S cu mult¸imea vid˘ a 9: 10: while Q = do 11: u deleteMin(Q) ⊲ extrage nodul de prioritate minim˘a din Q 12: S S u 13: for fiecare muchie e = (u, v) E do 14: if (v / S ) (c(e) < dv ) then actualizeaza prioritatea lui v : dv c(e) 15: 16: end if 17: end for 18: end while 19: end procedure
∈
← ∞ ←∅ ∈ ⇐ ← ∅ ∅ ← ← ∪ { } ∈ ∧
∈
←
O structur˘ a o colect¸ie de mult¸imi disjuncte a de date pentru mult ¸imi disjuncte memoreaz˘ dinamice. Fiecare mult¸ime este identificat˘ a printr–un reprezentant [30]. Operat¸iile de baz˘ a ale acestei structuri de date sunt [2]:
• init(x, B) - procedura creaz˘a o mult¸ime B format˘a dintr–un singur element x; • find(x) - ˆıntoarce reprezentantul mult¸imii c˘areia ˆıi apart¸ine x; • merge(A, B) - reune¸ste mult¸imile distincte A ¸si B (x ∈ A, y ∈ B) ˆıntr–o nou˘a mult¸ime ce cont¸ine elementele celor dou˘ a mult¸imi.
O astfel de structur˘ a de date pentru mult ¸imi disjuncte se mai nume¸ste ¸si structur˘ a de date union–find (eng. union–find data structure ).
Reprezentarea folosind vectori Vom folosi un vector C de dimensiune n, unde n reprezint˘ a num˘arul de elemente, iar c k = u indic˘a faptul c˘ a elementul k apart¸ine mult¸imii u. Init const˘ a din init¸ializarea lui cx cu identificatorul mult¸imii, u. Find ˆıntoarce valoarea lui cx (valoarea identificatorului mult¸imii). ˆIn funct¸ia Merge se caut˘ a toate elementele ce fac parte din mult¸imea de identificator cy ¸si se trec ˆın mult¸imea al c˘arui identificator este cx (vezi algoritmul 42).
Exemplul 5.6 Pentru o putea opera cu modul de reprezentare ales, cel cu vectori, trebuie ca elementele mult ¸imii s˘ a ia valori naturale ˆın intervalul [ 1, . . . ,n ]. Dac˘ a acest lucru nu este posibil atunci folosim un vector auxiliar A ce p˘ astreaz˘ a valorile elementelor, ˆın cadrul reprezent˘ arii utilizˆ andu–se indicele acestora. S˘ a presupunem c˘ a avem mult ¸imea de elemente ¸ia a,x,y , b,z,v , e, u . Atunci conform modului de A = a,b,e,x,y,z,u,v ¸si partit reprezentare descris avem:
{
}
{
}{
117
}{ }
ai) Algoritm 42 Algoritmi pentru operat¸iile init, find, merge (varianta ˆıntˆ 1: procedure Init(x, u) 2: cx 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 for k 1, n do 10: 11: if (ck = sety) then 12: ck setx 13: end if 14: end for 15: return setx 16: end function
←
← ← ←
←
1 2 3 4 5 6 7 8 A ’a’ ’b’ ’e’ ’x’ ’y’ ’z’ ’u’ ’v’ C 1 2 3 1 1 2 2 3 a2 =′ b′ are semnificat ¸ia urm˘ atoare: elementul de pe pozit ¸ia 2 are valoarea ′ b′ . c2 = 2 elementul de pe pozit ¸ia 2 face parte din mult ¸imea de identificator 2 . Sau a4 = ′ x′ - elementul de pe pozit ¸ia 4 are valoarea ′ x′ , ¸si c4 = 1 - elementul de pe pozit ¸ia 4 face parte din mult ¸imea de identificator 1.
Reprezentarea folosind liste ˆınl˘ant¸uite ˆIn cadrul acestei metode fiecare mult¸ime este reprezentat˘ a printr–o list˘ a simplu ˆınl˘ant¸uit˘ a, elementul reprezentant fiind elementul aflat ˆın capul listei. Un nod al listei va avea un cˆ amp ce p˘ astreaz˘a informat¸ ia cu privire la un element al unei mult¸imi, ¸si un cˆamp ce cont¸ine leg˘atura c˘ atre urm˘ atorul nod al listei. Vom folosi un vector de adrese (List), ce cont¸ine adresa primului element din fiecare list˘ a (Listi ). Procedura Init aloc˘a un nod ¸si init¸ializeaz˘ a cˆampurile acestuia. Adresa nodului alocat este p˘ astrat˘ a ˆın Listk (vezi algoritmul 43). Funct¸ia Find caut˘ a un element printre elementele p˘ astrate de fiecare list˘ a ˆın parte. Se parcurge vectorul List ¸si se obt¸ine reprezentantul fiec˘ arei submult¸imi p˘astrate. Se parcurge lista (c˘autare liniar˘ a) ¸si se caut˘a un element cu valoarea x. Dac˘a elementul este g˘ asit atunci se ˆıntoarce capul listei ˆın care a fost g˘ asit. Dac˘a pˆan˘ a la sfˆar¸sit nu a fost identificat elementul x, atunci funct¸ia returneaz˘ a valoarea NU LL. Funct¸ia M erge reune¸ste dou˘ a liste: practic se adaug˘ a o list˘a la sfˆar¸situl celeilalte. Pentru a realiza aceast˘ a operat¸ie pe lˆang˘ a 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, fie direct dac˘ a ˆın momentul cre˘arii p˘astr˘am pentru fiecare list˘ a ˆınc˘ a o variabil˘a ce cont¸ine adresa ultimului element (last).
118
Algoritm 43 Algoritmi pentru operat¸iile init, find, merge (varianta a II-a) 1: procedure Init(x, k) 2: Listk new node 3: Listk .data x 4: Listk .next NU LL 5: end procedure 6: function Find(x) 7: for i 1, m do 8: p Listi , reprezentant p 9: while ( p = NULL) ( p.data = x) do p p.next 10: 11: end while 12: if ( p = NU LL) then 13: return reprezentant 14: end if end for 15: 16: return NULL 17: end function 18: function Merge(x, y) capx Find(x) 19: 20: capy F ind(y) 21: curent cap x 22: while (curent.next = NU LL) do 23: curent curent.next 24: end while 25: curent.next cap y 26: return cap x 27: end function
←
⊲ aloc˘a spat¸iu pentru un nod al listei
← ←
← ←
←
← ← ←
← ←
∧
←
Reprezentarea folosind o p˘ adure de arbori ˆIn cadrul acestei modalit˘ a¸t i de reprezentare fiecare mult¸ime este p˘astrat˘a sub forma unui arbore. Pentru fiecare nod p˘ astr˘am informat¸ia despre p˘ arintele s˘ au: tatak reprezint˘ a indicele nodului p˘arinte al nodului k. Se poate observa foarte u¸sor (vezi algoritmul 44) faptul c˘ a arborele obt¸inut ˆın urma unor operat¸ii repetate Merge poate deveni degenerat (o list˘a liniar˘a). Pentru a putea s˘ a evit˘am o astfel de situat¸ie, reuniunea se poate realiza ¸tinˆand cont de una din urm˘ atoarele caracteristici:
arul de elemente din fiecare arbore - r˘ad˘acina arborelui ce are mai put¸ine elemente 1. num˘ (not˘ am arborele cu T B ) va deveni descendentul direct al r˘ ad˘acinii arborelui ce posed˘ a mai multe elemente (notat cu T A ). Astfel toate nodurile din arborele T A vor r˘ amˆane cu aceea¸si adˆ ancime, pe cˆand nodurile din arborele T B vor avea adˆancimea incrementat˘ a cu 1. De asemenea, arborele rezultat ˆın urma reuniunii va avea cel put ¸in de dou˘a ori mai multe noduri decˆ at arborele T B .
119
Algoritm 44 Algoritmi pentru operat¸iile init, find, merge (varianta a III-a) 1: procedure Init(x) 2: tatax x 3: end procedure 4: function Find(x) 5: while (x = tatax ) do 6: x tatax 7: end while 8: return x 9: end function 10: function Merge(x, y) 11: radx F ind(x) 12: rady Find(y) 13: tatarady radx 14: return radx 15: end function
← ←
← ← ←
1: function Merge(x, y) 2: radx F ind(x) 3: rady F ind(y) 4: size tataradx + tatarady 5: if ( tataradx > tatarady ) then 6: tatarady radx tataradx size 7: return radx 8: 9: else 10: tataradx rady 11: tatarady size 12: return rady end if 13: 14: end function
1: procedure Init(x) 2: tatax 1 3: end procedure 4: function Find(x) 5: while (tatax > 0) do 6: x tatax 7: end while 8: return x 9: end function
← −
|
←
← ← ←|
| | | | | | ← ← − ← ← −
Prin aceast˘ a euristic˘ a simpl˘a se urm˘are¸ste o echilibrare a arborelui rezultat pentru a se evita cazurile degenerate. Complexitatea timp a procedurii Find este O(log n).
alt ¸imea fiec˘ arui arbore - r˘ad˘acina arborelui ce are ˆın˘ 2. ˆın˘ alt¸imea mai mic˘a (not˘ am arborele cu T B ) va deveni descendentul direct al r˘ ad˘acinii arborelui cu ˆın˘ alt¸imea mai mare (notat cu T A ). Dac˘a ˆın˘alt¸imile lui T A ¸si T B sunt egale ¸si T A devine subarbore al lui T B atunci ˆın˘alt¸imea lui T B cre¸ste cu o unitate. ˆIn˘alt¸imea fiec˘ arui subarbore de r˘ ad˘acin˘a u se p˘astreaz˘a ˆıntr–o variabil˘a nou˘ a hu . Pentru a economisi cantitatea de memorie utilizat˘ a , se poate p˘ astra ˆın˘ alt¸imea unui arbore ˆın vectorul tata: tatax = valoarea ˆın˘alt¸imii arborelui, atunci cˆ and x reprezint˘ a nodul r˘ad˘acin˘a al unui arbore.
−
120
1: function Merge(x, y) 2: radx F ind(x) 3: rady F ind(y) 4: if (hradx > h rady ) then tatarady radx 5: 6: return radx 7: else 8: tataradx rady 9: if (hradx = h rady ) then hrady h rady + 1 10: 11: end if 12: return rady 13: end if 14: end function
← ←
1: procedure Init(x) 2: tatax x hx 0 3: 4: end procedure 5: function Find(x) 6: while (tatax = x) do x tatax 7: end while 8: 9: return x 10: end function
← ← ←
←
← ←
Cea de–a doua tehnic˘ a utilizat˘ a pentru a reduce complexitatea timp a operat¸iilor Find ¸si Merge este reprezentat˘ a de comprimarea drumului . Aceasta const˘ a ˆın a apropia fiecare nod de r˘ ad˘acina arborelui c˘ aruia ˆıi apart¸ine. Astfel ˆın timpul operat¸iei F ind(x) se determin˘ a mai ˆıntˆai r˘ad˘acina arborelui c˘aruia ˆıi apart¸ine (rad) ¸si apoi se mai parcurge o dat˘ a drumul de la nodul x la r˘ad˘acin˘a, astfel tatay rad, y lant¸ul de la x la rad. 1: function Find(x) 2: y x 3: while (tatay = y) do y tatay 4: end while 5: 6: rad y 7: y x 8: while (tatay = y) do y tatay 9: tatax rad 10: 11: x y 12: end while 13: return rad 14: end function
←
←
∀ ∈
← ← ← ← ← ←
5.3.4
Algoritmul lui Kruskal
Algoritmul lui Kruskal [88] este o ilustrare foarte bun˘ a a metodei generale Greedy (vezi algoritmul 45). La ˆınceput se pleac˘ a cu o p˘ a dure de arbori, fiecare arbore fiind alc˘ atuit dintr–un singur nod. La fiecare pas se alege o muchie: dac˘ a cele dou˘ a extremit˘ a¸t i (noduri) fac parte din acela¸si arbore atunci nu se va ad˘ auga muchia curent˘a la arborele part¸ial respectiv deoarece ar conduce la un ciclu, ceea ce ar strica proprietatea de graf aciclic. Altfel, dac˘ a cele dou˘a noduri fac parte din arbori part¸iali distinct¸i, se adaug˘a muchia curent˘ a la mult¸imea de muchii alese anterior, iar cei doi arbori part¸iali devin unul singur. La fiecare pas, num˘ arul de arbori part¸iali scade cu 1. Dup˘a n 1 alegeri p˘ adurea init¸ial˘a compus˘ a din n arbori s–a transformat ˆıntr–un singur arbore. Pentru a p˘ astra muchiile grafului vom utiliza o matrice A cu m coloane ¸si 3 linii: primele dou˘a linii cont¸in extremit˘ a¸t ile muchiilor, iar linia a 3-a costul muchiei respective. Pentru a se aplica o strategie Greedy, datele de intrare – muchiile – se vor ordona cresc˘ ator ˆın funct¸ie de costul asociat. Deoarece graful part¸ial respectiv este un arbore cu n noduri el va avea n 1 muchii.
−
−
121
Fig. 5.7: Algoritmului lui Kruskal exemplificat pe graful din figura 5.6
La pasul ˆıntˆ ai evalu˘ am muchia (1, 5), ¸si deoarece cele dou˘ a extremit˘ at ¸i fac parte din arbori distinct ¸i, vom selecta aceast˘ a muchie ( L = (1, 5) ). Reunim arborii din care fac parte cele dou˘ a extremit˘ at ¸i, (vezi figura 5.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 figura 5.7 (2)). La pasul al patrulea, ajungem la muchia (4, 8) ce are costul 10. Nodul 4 face parte din arborele de r˘ ad˘ acin˘ a 4 iar nodul 8 face parte din arborele de r˘ ad˘ acin˘ a 6. Deoarece extremit˘ at ¸ile muchiei sunt amplasate ˆın arbori distinct ¸i select˘ am muchia curent˘ a pentru arborele part ¸ial ˆ de cost minim, L = (1, 5), (1, 3), (6, 8), (4, 8) . In urma reuniunii arborilor corespunz˘ atori celor dou˘ a noduri se obt ¸ine configurat ¸ia din figura 5.7 (4).
{
{
}
}
{
}
123
Subliniem faptul c˘ a arborii reprezentat ¸i ˆın figura 5.7 sunt diferit ¸i de arborii ce conduc la obt ¸inerea solut ¸iei problemei: arborii din figur˘ a constituie suportul necesar pentru reprezentarea structurii de date pentru mult ¸imi disjuncte, ce este utilizat˘ a pentru efectuarea eficient˘ a a operat ¸iilor Find ¸si M erge. Acest lucru justific˘ a faptul c˘ a de¸si la pasul al patrulea select˘ am muchia (4, 8) pentru arborele part ¸ial de cost minim, ea nu se reg˘ ase¸ste ˆın configurat ¸ia atur˘ a dintre un arbore (4) (nodul 4 este unit direct cu nodul 6, vezi figura 5.7). Singura leg˘ 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 comun˘ a. La pa¸sii urm˘ atori, 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 evalueaz˘ a muchia (3, 6). Nodurile 3 ¸si 6 fac parte din arbori diferit ¸i, astfel ˆıncˆ at muchia curent˘ a poate fi selectat˘ a pentru solut ¸ia problemei, L = (1, 5), (1, 3), (6, 8), (4, 8), (7, 8), (2, 4), (3, 6) .
{
}
{
}
5.4
Exercit¸ii
1. S˘a se realizeze o subrutin˘ a ce ˆıntoarce num˘ arul de noduri dintr–un arbore oarecare. 2. Se d˘a o secvent¸˘a format˘ a din n numere naturale d1 , d2 , . . . , dn. S˘a se realizeze un algoritm prin care s˘ a se verifice dac˘ a exist˘a un arbore cu n noduri ale c˘ aror grade sunt d1 , d 2 , . . . , dn . Dac˘a exist˘a un astfel de arbore, acesta va fi reprezentat prin liste ale nodurilor. O list˘ a a unui nod cont¸ine num˘ arul nodului urmat de fii s˘ai. Intrare 1123113
Ie¸sire DA 1 2 3 4 2 5 6 3 7 4 5 6 7
(Timi¸soara-preg˘atire, 1996)
3. Fie un graf G cu n vˆarfuri ¸si m muchii de cost pozitiv. Alegˆ and un nod, numit nod central , s˘a se determine un subarbore al lui G astfel ˆıncˆ at drumurile de la nodul central la toate celelalte noduri s˘ a aib˘a lungimea minim˘a. 4. Fiind date n puncte ˆın spat¸iul 3 determinate prin coordonatele (x,y,z ), s˘a se elaboreze un algoritm ce determin˘ a sfera de raz˘ a minim˘a cu centrul ˆıntr–unul din punctele date ¸si care cont¸ine ˆın interiorul ei toate cele n puncte.
R
5. Se consider˘ a procesul de proiectare a unei pl˘ aci electronice cu N componente (1 N 100). Pentru fiecare component˘ a electronic˘a C se cunoa¸ste num˘arul de interconexiuni. Se consider˘ a graful determinat de mult¸imea pinilor ¸si mult¸imea interconect˘ arilor posibile ale tuturor componentelor, precum ¸si lungimile lor. Dintre toate modalit˘ at¸ile de interconectare posibile se cere cea corespunz˘ atoare arborelui de acoperire minim (interconectarea pentru care suma tuturor circuitelor imprimate are lungimea minim˘ a).
≤ ≤
124
Datele de intrare sunt alc˘ atuite din descrierile mai multor pl˘ aci electronice. Descrierea fiec˘arei pl˘aci cont¸ine num˘ arul N de componente ¸si num˘ arul M de interconexiuni. O conexiune se caracterizeaz˘ a prin trei valori: dou˘ a vˆ arfuri, u ¸si v, precum ¸si lungimea acesteia l. Datele de ie¸sire constau dintr-un r˘ aspuns pentru fiecare mult¸ime de date de test, compus din num˘arul testului (se ˆıncepe numerotarea cu 1), precum ¸si costul interconect˘ arii de lungime minim˘a.
Exemplul 5.8 Pentru datele de intrare 5 7 1 2 40 0 0
2 3 35
1 5 27
1 4 37
4 5 30
2 4 58
3 4 60
vom obt ¸ine rezultatul Cazul 1: [1 5] [4 5] [2 3] [1 2] Interconectarea de cost minim are valoarea 132.
ˆ In figura 5.8 este reprezentat graful corespunz˘ ator datelor de intrare.
Fig. 5.8: Descrierea conexiunilor posibile dintre componentele unei pl˘ aci electronice
6. Realizat¸i o subrutin˘ a nerecursiv˘a care s˘ a determine cheia minim˘ a dintr–un arbore oarecare. 7. Determinat¸i ˆın˘alt¸imea unui arbore oarecare printr–o funct¸ie nerecursiv˘ a. 8. Pentru asigurarea securit˘ at¸ii activit˘a¸tilor dintr–un combinat chimic s–a apelat la o companie de pompieri. Desf˘ a¸surarea activit˘ a¸t ilor companiei presupune stabilirea locurilor de instalare a comandamentului precum ¸si a posturilor de supraveghere. Pentru aceasta sunt disponibile ˆın cadrul combinatului n puncte de control. Pentru fiecare pereche de puncte de control se cunoa¸ste dac˘ a exist˘a o leg˘ atur˘ a direct˘ a ˆıntre ele ¸si, ˆın caz afirmativ, distant¸a dintre ele. Se cunoa¸ste, de asemenea, faptul c˘ a ˆıntre oricare dou˘ a puncte de control exist˘ a un drum direct sau indirect. Odat˘ a stabilite amplasamentele comandamentului ¸si ale punctelor de supraveghere ˆın cˆate unul dintre cele n puncte de control este posibil s˘a se ajung˘ a de la comandament 125
la fiecare punct de supraveghere parcurgˆ and un anumit drum. Evident este de dorit ca lungimea acestui drum s˘ a fie minim˘a ¸si valoarea maxim˘a dintre lungimile drumurilor minime pentru fiecare punct de supraveghere ˆın parte s˘ a fie cˆat mai mic˘a posibil˘a. Se cere s˘ a se determine punctul de control ˆın care trebuie instalat comandamentul cˆ at ¸si drumurile ce vor fi parcurse de la comandament la fiecare punct de supraveghere, astfel ˆıncˆat aceste drumuri s˘ a aib˘a valoarea lungimii minim˘a ¸si cel mai lung dintre ele s˘a fie cˆ at mai scurt posibil.
126
Capitolul 6 Grafuri orientate 6.1
Not¸iuni de baz˘ a
Fie o mult¸ime finit˘a V = x1 , x2 , . . . , xn . Fie E V V (unde V V este produsul cartezian al mult¸imii V cu ea ˆıns˘a¸si). ˆIn cazul unui graf orientat, not ¸iunea de muchie este ˆınlocuit˘a cu not¸iunea de arc (o pereche de noduri (x, y) devine ordonat˘ a, adic˘a (x, y) = (y, x)). Pentru un arc (x, y) E , vˆarful x reprezint˘ a extremitatea init¸ial˘a a arcului, iar vˆarful y reprezint˘ a extremitatea final˘ a. Vom spune c˘ a vˆ arfurile x ¸si y sunt adiacente.
{
}
⊆ ×
×
∈
Definit¸ia 6.1 Un graf orientat este o pereche ordonat˘ a G = (V, E ), unde V este o mult ¸ime de vˆ arfuri sau noduri, iar E este o mult ¸ime de arce. Exemplul 6.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 figura 6.1.
{
{
}
}
Definit¸ia 6.2 Un graf part ¸ial al unui graf orientat G = (V, E ) este un graf orientat G1 = (V, E 1 ) unde E 1 E .
⊂
Exemplul 6.2 Pentru graful G din exemplul anterior ( G = (V, E )), consider˘ am graful part ¸ial G1 = (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 .
{
{
}
}
Definit¸ia 6.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 extremit˘ at ¸i ˆın mult ¸imea V 1 ( E1 = E V ×V ).
⊂
|
1
1
Exemplul 6.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) .
{
{
}
}
{
{
}
}
Definit¸ia 6.4 Gradul exterior al unui vˆ arf d + (x) este egal cu num˘ arul arcelor ce au ca ex+ tremitate init ¸ial˘ a pe x ( d (x) = (x, u) (x, u) E, u V ). Gradul interior al unui vˆ arf arul arcelor ce au ca extremitate final˘ a pe x ( d−(x) = (u, x) (u, x) d− (x) este egal cu num˘ E, u V ).
|{
|
∈
∈ }|
127
∈ }|
|{
|
∈
Fig. 6.1: Un exemplu de graf orientat
Definit¸ia 6.5 Se nume¸ste lant ¸ o secvent ¸˘ a de arce L = l1, . . . , l p cu proprietatea c˘ a oricare dou˘ a arce consecutive ale secvent ¸ei au o extremitate comun˘ a. ( L = [v0 , v1 , . . . , v p ] unde (vi , vi+1) E sau (vi+1, vi ) E , i = 0, p 1).
{
∈
∈ ∀
}
−
Definit¸ia 6.6 Un drum D = [v0 , v1 , . . . , v p ] este o succesiune de vˆ arfuri cu proprietatea c˘ a oricare dou˘ a vˆ arfuri vecine sunt adiacente ( (vi , vi+1 ) E, i = 0, p 1).
∈
−
Dac˘a vˆarfurile v0, v1 , . . . , v p sunt distincte dou˘ a cˆate dou˘ a, drumul se nume¸ste elementar.
Exemplul 6.4 L1 = (6, 8), (7, 6), (7, 5), (3, 5), (1, 3) ¸si L2 = (6, 8), (6, 4), (1, 4) sunt lant ¸uri de la vˆ arful 8 la vˆ arful 1, iar D1 = (1, 3), (3, 5), (5, 7), (7, 6), (6, 8) ¸si D2 = (1, 4), (4, 6), (6, 8) sunt drumuri elementare de la vˆ arful 1 la vˆ arful 8.
{
{
}
}
{
{
}
}
Definit¸ia 6.7 Un drum D pentru care v0 = v p se nume¸ste circuit . Definit¸ia 6.8 Se nume¸ste circuit hamiltonian un circuit elementar ce trece prin toate vˆ arfurile grafului. Un graf ce admite un circuit hamiltonian se nume¸ste graf hamiltonian orientat . Definit¸ia 6.9 Un drum D ce cont ¸ine fiecare arc exact o singur˘ a dat˘ a se nume¸ste drum eulerian . Dac˘ a v 0 = v p ¸si drumul este eulerian atunci circuitul se nume¸ste circuit eulerian . Un graf ce cont ¸ine un circuit eulerian se nume¸ste graf eulerian orientat . Definit¸ia 6.10 Un graf se nume¸ste conex dac˘ a pentru orice pereche de vˆ arfuri x ¸si y exist˘ a un lant ¸ Lxy de la x la y . Definit¸ia 6.11 Un graf orientat este complet dac˘ a oricare dou˘ a vˆ arfuri sunt adiacente. Metodele utilizate pentru reprezentarea unui graf neorientat se pot adapta ˆın mod natural pentru reprezentarea unui graf orientat, ˆınlocuind not ¸iunea de muchie cu cea de arc.
128
Fig. 6.2: Arbore de acoperire ˆın l˘a¸t ime pentru graful orientat din figura 6.1
6.2
Parcurgerea grafurilor
ˆIn urma vizit˘arii unui graf neorientat, de obicei nu rezult˘a numai un arbore de acoperire ci o p˘adure de arbori de acoperire. ˆIn urma unei parcurgeri ˆın l˘ a¸time a unui graf orientat, se ˆıntˆ alnesc urm˘ atoarele tipuri de arce:
• arc al arborelui de acoperire - arcul (u, v) este un arc al arborelui de acoperire ˆın l˘at¸ime. • arc de ˆıntoarcere - arcul (u, v) se nume¸ste arc de ˆıntoarcere dac˘a are sensul contrar unui drum de la v la u ˆın arborele de acoperire (v al lui v ¸si v este un str˘ amo¸s al lui u).
u)
(se spune c˘a u este un descendent
• arc de traversare - arcul (u, v) este un arc de traversare dac˘a v nu este nici descendent direct, nici str˘amo¸s al lui u.
ˆIn figura 6.2 este prezentat arborele de acoperire ˆın l˘ at¸ime corespunz˘ ator grafului din figura 6.1 avˆ a nd r˘ ad˘acina 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 (a se vedea algoritmul 46). Pentru aceasta se utilizeaz˘ a dou˘ a mult¸imi de noduri, V izitat ¸si Neexplorat, unde V izitat reprezint˘ a mult¸imea nodurilor vizitate iar Neexplorat (Neexplorat V izitat) reprezint˘ a mult¸imea nodurilor vizitate dar neexplorate (noduri ce prezint˘ a vecini ˆınc˘ a nevizitat¸i). ˆIn urma unei parcurgeri ˆın adˆ ancime a unui graf orientat, fiecare arc din mult¸imea arcelor poate fi ˆıncadrat ˆıntr-unul dintre tipurile urm˘ atoare:
⊂
• arc al arborelui de acoperire - arcul (u, v) este un arc al arborelui de acoperire dac˘a df s(u) apeleaz˘ a df s(v) (df s(u)
call
df s(v)).
• arc de ˆınaintare - arcul (u, v) este un arc de ˆınaintare dac˘a este paralel cu un drum de la u la v din arborele de acoperire (u v) (nu face parte din arborele de acoperire). 129
Algoritm 46 Algoritm de vizitare a unui graf (model general) 1: procedure ParcurgereGraf(u, G)
u - vˆarful de unde se porne¸ste vizitarea G - graful 2: V izitat u 3: Neexplorat V izitat 4: while (Neexplorat = ) do 5: extrage un nod x din Neexplorat 6: determin˘a y, urm˘atorul vecin al lui x ce nu a fost vizitat 7: if (y = NULL) then elimin˘a x din Neexplorat 8:
Input:
←{ } ←
∅
else 9: 10: if (y / V izitat) then 11: V izitat V izitat y 12: Neexplorat Neexplorat 13: end if end if 14: 15: end while 16: end procedure
∈
←
←
∪{ }
∪ {y}
• arc de ˆıntoarcere - arcul (u, v) se nume¸ste arc de ˆıntoarcere dac˘a 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 dac˘a dfs(v) a fost apelat ¸si s-a terminat ˆınainte de apelul lui df s(u).
Fig. 6.3: Arbore de acoperire ˆın adˆancime pentru graful orientat din figura 6.1
Pentru fiecare nod v al unui graf vom introduce dou˘ a numere, prenumv ¸si postnumv , numere ce depind de ordinea ˆın care sunt ˆıntˆ alnite nodurile acestuia ˆın timpul vizit˘ arii ˆın adˆancime: prenumv marcheaz˘ a momentul cˆ and este ˆıntˆ alnit nodul v pentru prima oar˘a 130
iar postnumv momentul ˆın care prelucrarea nodului v s-a ˆıncheiat. Variabila counter este init¸ializat˘ a cu valoarea 1: 1: procedure Prenum(v) 2: prenumv counter 3: counter counter + 1 4: end procedure
← ←
¸si 1: procedure Postnum(v) 2: postnumv counter 3: counter counter + 1 4: end procedure
← ←
Un arc (u, v) va avea urm˘atoarele propriet˘ a¸ti, ˆın funct¸ie de una din cele patru categorii de arce introduse anterior ˆın care se ˆıncadreaz˘ a: 1. arc al arborelui de acoperire : prenumu < prenumv ¸si postnumu > postnumv ; 2. arc de ˆınaintare : prenumu < prenumv ¸si postnumu > postnumv ; 3. arc de ˆıntoarcere : prenumu > prenumv ¸si postnumu < postnumv ; 4. arc de traversare : prenumu > prenumv ¸si postnumu > postnumv . Arborele de acoperire ˆın adˆ ancime corespunz˘ ator grafului din figura 6.1 ilustreaz˘ a aceste tipuri de arce: arc de ˆınaintare – (1, 4), arce de ˆıntoarcere - (2, 1), (7, 5), (4, 6) arc de traversare - (5, 2), arce ale arborelui de acoperire - (1, 2), (1, 3), (3, 5), (5, 7), (7, 6), (6, 8), (6, 4) (a se vedea figura 6.3).
Algoritm 47 Algoritm de vizitare ˆın adˆancime pentru un graf orientat 1: procedure DFSNum(k, n,V ecin)
k - nodul curent vizitat Input: - num˘ arul de noduri din graf n V ecin - matricea de adiacent¸˘a a grafului 1 ⊲ marcarea nodului curent ca fiind vizitat 2: vizitatk 3: call Prenum(k) 4: call V izitare(k) ⊲ vizitarea nodului curent 5: for i 1, n do 6: if (vizitati = 0) (vecink,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 6.5 Fiind dat un graf orientat G ¸si dou˘ a noduri oarecare u, v G ce apart ¸in aceluia¸si arbore de acoperire ˆın adˆ ancime rezultat ˆın urma parcurgerii cu metoda DF S a grafului G avem:
∈
1. dac˘ a (u, v) este un arc al arborelui de acoperire sau un arc de ˆınaintare sau un arc de traversare postnumu > postnumv ;
⇒
131
2. dac˘ a (u, v) este un arc de ˆıntoarcere
⇒ postnum < postnum . u
v
Lema 6.6 Fiind dat un graf orientat G pentru oricare dou˘ a noduri u, v
∈ G avem:
1. v este un descendent al lui u ˆın p˘ adurea de arbori de acoperire rezultat ¸i ˆın urma vizit˘ arii ˆın adˆ ancime a grafului G intervalul [ prenumv , postnumv ] este inclus ˆın intervalul [ prenumu , postnumu ];
⇔
2. nu exist˘ a nici o leg˘ atur˘ a ˆıntre u ¸si v ˆın p˘ adurea de arbori de acoperire rezultat ¸i ˆın urma vizit˘ arii ˆın adˆ ancime a grafului G intervalele [ prenumu , postnumu ] ¸si [ prenumv , postnumv ] sunt disjuncte;
⇔
3. situat ¸ii prenumu < prenumv < postnumu < postnumv sau prenumv < prenumu < postnumv < postnumu nu sunt posibile. Lema 6.7 Fiind dat un graf neorientat G, dac˘ a pentru dou˘ a noduri oarecare u, v G avem relat ¸ia prenumu < prenumv < postnumu atunci:
∈
• prenum < prenum < postnum < postnum ; • exist˘ a un drum de la u la v ˆın G. u
v
v
u
ˆ urma apel˘ Exemplul 6.8 In arii procedurii DFSNum(1, 8, Vecin) (a se vedea algoritmul 47), secvent ¸a de apeluri recursive ale procedurii DFSNum este ilustrat˘ a prin arborele de acoperire ˆın adˆ ancime din figura 6.3. Numerotarea nodurilor ˆın preordine ¸si postordine, rezultat˘ a ˆın urma vizit˘ arii este urm˘ atoarea: 1 2 3 4 prenum 1 2 4 8 postnum 16 3 15 9
5 6 7 8 5 6 7 10 14 13 12 11
Pentru arcul (1, 4) avem prenum1 = 1, postnum1 = 16, prenum4 = 8 ¸si postnum4 = 9. Deoarece relat ¸ia prenumu < prenumv < postnumu este adev˘ arat˘ a conform lemei 6.7 ar trebui s˘ a avem prenumu < prenumv < postnumv < postnumu ¸si s˘ a existe un drum de la u la v , lucruri care sunt adev˘ arate ( prenum1 < prenum4 < postnum4 < postnum1 ¸si exist˘ a un drum de la 1 la 4 ˆın arborele de acoperire ˆın adˆ ancime).
6.3
Sortarea topologic˘ a
Fig. 6.4: Un graf orientat aciclic
132
Definit¸ia 6.12 Un graf orientat ¸si care nu posed˘ a circuite se nume¸ste graf orientat aciclic (directed acyclic graph - DAG). ˆ Lema 6.9 Intr-un graf orientat aciclic, dac˘ a prenumu < prenumv ¸si exist˘ a un drum de la u la v ˆın G, atunci prenumu < prenumv < postnumv < postnumu. ˆIn practic˘ a, exist˘a mai multe situat¸ii ˆın care o mult¸ime de activit˘ a¸t i sau sarcini, trebuie organizate ˆıntr-o anumit˘ a ordine, ˆıntre acestea existˆ and, de obicei, o mult¸ime de restrict¸ii sau dependent¸e. ˆIn activitatea de construire a unei cl˘ adiri, anumite activit˘ a¸ti nu pot fi ˆıncepute decˆat dup˘ a finalizarea altor activit˘ a¸t i: spre exemplu, nu se poate ˆıncepe ridicarea peret ¸ilor unei construct¸ii decˆat dup˘ a turnarea fundat¸iei ¸si finalizarea structurii de rezistent¸˘a, nu se poate realiza instalat¸ia electric˘a dac˘ a nu au fost ridicate zidurile, ¸s.a.m.d. Sau dac˘ a un student dore¸ste s˘ a ˆı¸si alc˘ atuiasc˘a un plan de studiu individual ce va cont¸ine pe lˆang˘ a cursurile obligatorii, ¸si o serie de cursuri opt¸ionale, va trebui s˘ a ¸tin˘a cont de anul de studiu ˆın care se pred˘ a fiecare materie, precum ¸si de cerint ¸ele obligatorii ale acestora: un curs nu poate fi inclus ˆın planul de studiu individual al unui student decˆ at dac˘ a acesta a parcurs ¸si a obt¸inut creditele la toate materiile anterioare cerute explicit ˆın programa cursului.
Algoritm 48 Algoritm de sortare topologic˘ a a unui graf orientat (prima variant˘ a) 1: procedure SortTop1(n,V ecin)
n - num˘ arul de noduri din graf V ecin - vector ce cont¸ine listele cu vecini ai fiec˘arui nod 1, n do for k dminusk 0
Input:
←
2: 3: 4: end for 5: for (u, v) E do 6: dminusv = dminusv + 1 7: end for Q ⊲ se init¸ializeaz˘a coada 8: 9: for k 1, n do 10: if (dminusk = 0) then 11: Q k ⊲ se insereaz˘a ˆıntr–o coad˘ a nodurile cu gradul interior 0 12: end if end for 13: 14: while (Q = ) do 15: Q k ⊲ se extrage din coad˘a un nod 16: L k ⊲ se insereaz˘a nodul ˆıntr-o list˘a w V ecink ⊲ v ia valoarea capului listei de vecini a nodului k 17: 18: while (w = NULL) do 19: dminusw.nodeIndex dminusw.nodeIndex 1 20: if (dminusw.nodeIndex = 0) then Q w.nodeIndex ⊲ se insereaz˘a ˆın coad˘a nodul w.nodeIndex 21: 22: end if 23: w w.next ⊲ se trece la urm˘atorul vecin 24: end while 25: end while 26: end procedure
∀
← ∈
←∅ ←
⇐
⇒ ⇐ ←
∅
←
−
⇐
←
Dependent¸a dintre dou˘ a activit˘ a¸ti A ¸si B o putem modela prin introducerea a dou˘ a noduri ˆın graf xi ¸si x j , asociate celor dou˘ a activit˘ a¸t i. Dac˘a activitatea A trebuie realizat˘ a ˆınaintea activit˘a¸tii B, atunci se adaug˘ a arcul (xi , x j ). 133
Definit¸ia 6.13 Se nume¸ste sortare topologic˘ a pentru un graf orientat G = (V, E ) o ordonare x1 , x2 , . . . , xn a nodurilor grafului astfel ˆıncˆ at pentru orice arc (xi , x j ) s˘ a avem i < j.
{
}
a presupune aranjarea liniar˘ Prin urmare o sortare topologic˘ a a vˆarfurilor unui graf astfel ˆıncˆat toate arcele sale s˘a fie orientate de la stˆ anga la dreapta. Lema 6.10 Dac˘ a un graf orientat G admite o sortare topologic˘ a atunci G este aciclic. Lema 6.11 Dac˘ a un graf orientat G este aciclic atunci el admite o sortare topologic˘ a. ˆ Observat¸ia 6.12 Intr-un graf orientat aciclic exist˘ a cel put ¸in un nod al c˘ arui grad interior este 0 (graful nu posed˘ a arce care s˘ a aib˘ a nodul v drept extremitate final˘ a). Pornind de la aceast˘ a observat¸ie se schit¸eaz˘ a urm˘atorul algoritm [78]: ˆıntr-un graf G se determin˘a un nod v astfel ˆıncˆ at gradul s˘au interior s˘ a fie zero (d− (v) = 0); se adaug˘a acest nod la o list˘a ce va cont¸ine ordonarea topologic˘ a, se ¸sterge nodul din graf ˆımpreun˘ a cu toate ′ ′ arcele ce ˆıl au ca extremitate init ¸ial˘ a, ¸si se reia algoritmul pentru graful G = (V , E ′), unde V ′ = V v , E ′ = E V ×V . Algoritmul 48 se termin˘ a ˆın cel mult n pa¸si ( V = n). Dac˘ a se termin˘a mai devreme, atunci graful G nu este aciclic (la un moment dat nu mai exist˘a nici un nod v astfel ˆıncˆat d− (v) = 0).
\ { }
|
′
′
| |
Fig. 6.5: Sortare topologic˘a cu algoritmul 48 pentru graful orientat din figura 6.4
Exemplul 6.13 Fie graful orientat din figura 6.5. Vˆ arful 3 are d− (v) = 0. Se adaug˘ a acest nod la lista final˘ a ce va cont ¸ine nodurile ordonate, se ¸sterge nodul ˆımpreun˘ a cu arcele care ˆıl ˆ au drept extremitate init ¸ial˘ a. In graful rezultat, vˆ arful 1 are proprietatea c˘ a d− (v) = 0. Se adaug˘ a la lista de rezultate, ¸si se elimin˘ a din graf ˆımpreun˘ a cu arcele ce pleac˘ a din el. Se continu˘ a procedeul pˆ an˘ a cˆ and graful devine vid (ˆıntregul proces poate fi urm˘ arit ˆın figura 6.5). 134
postnum 14 12 3 1
11 10 9 8 7 2 4 5 7 6
Astfel, sortarea topologic˘ a a nodurilor grafului obt ¸inut˘ a ˆın urma aplic˘ arii algoritmului 49 este: 3, 1, 2, 4, 5, 7, 6. Observat¸ia 6.16 O optimizare a algoritmului 49 se refer˘ a la ad˘ augarea nodului v ˆıntr-o stiv˘ a atunci cˆ and se termin˘ a vizitarea acestui nod v ¸si se calculeaz˘ a postnumv . Aceast˘ a stiv˘ a va cont ¸ine la final nodurile grafului ˆın ordinea descresc˘ atoare a valorilor postnumv . Prin urmare, nu mai este nevoie s˘ a ordon˘ am descresc˘ ator valorile vectorului postnum pentru a obt ¸ine o sortare topologic˘ a a nodurilor grafului G, iar complexitatea algoritmului 49 devine O( V + E ).
| | | |
6.4
Componente tare conexe
Fig. 6.6: Graf orientat. Componente tare conexe.
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 ).
⊆ ×
{
}
Definit¸ia 6.14 O component˘ ¸ime maxia tare conex˘ a a unui graf orientat G este o mult mal˘ a de vˆ arfuri U V , astfel ˆıncˆ at, pentru fiecare pereche de vˆ arfuri u, v ( u, v U ) exist˘ a atˆ at un drum de la u la v cˆ at ¸si un drum de la v la u. Prin urmare se spune c˘ a vˆ arfurile u ¸si v sunt accesibile unul din cel˘ alalt.
⊆
{ }
∈
Un graf orientat ce are o singur˘ a component˘ a tare conex˘ a astfel ˆıncˆat U = V este un graf tare conex . ˆIn cazul ˆın care un graf orientat nu este tare conex atunci el se poate descompune ˆın mai multe componente tare conexe . O astfel de component˘ a este determinat˘ a de un subgraf al grafului init¸ial.
Definit¸ia 6.15 Graful orientat G se nume¸ste tare conex dac˘ a pentru orice pereche de vˆ arfuri u = v , exist˘ a un drum de la nodul u la nodul v ¸si un drum de la nodul v la nodul u.
Se poate ar˘ ata c˘ a relat¸ia de tare conexitate este o relat¸ie de echivalent¸a˘. O relat¸ie (notat˘ a cu ) este o relat a propriet˘ a¸t ile de reflexivitate , simetrie ¸si ¸ie de echivalent ¸˘ a dac˘a prezint˘ tranzitivitate :
∼
136
• reflexivitate: a ∼ a; • simetrie: a ∼ b ⇒ b ∼ a; • tranzitivitate: a ∼ b, b ∼ c ⇒ a ∼ c. ∼
O clas˘a de echivalent¸a˘ este mult¸imea tuturor elementelor care se afl˘ a ˆın relat¸ia ([x]∼ = yx y ). Relat¸ia de echivalent¸a˘ determin˘ a o partit¸ie ˆın clase de echivalent¸a˘ a mult¸imii peste care a fost definit˘ a . Prin urmare relat¸ia de tare conexitate determin˘ a o partit¸ ie a mult¸imii V . Clasele de echivalent¸a˘ determinate de relat¸ia de tare conexitate sunt componentele tare conexe. Pentru determinarea componentelor tare conexe exist˘ a mai mult¸i algoritmi, dintre care amintim algoritmul lui Tarjan , algoritmul lui Kosaraju ¸si algoritmul lui Gabow .
{ | ∼ }
6.4.1
Algoritmul lui Kosaraju
Algoritmul lui Kosaraju-Sharir a fost prezentat de Aho, Hopcroft ¸si Ullman ˆın lucrarea lor [2], fiind preluat dintr-un manuscris al lui S. Rao Kosaraju (M. Sharir l-a prezentat ˆın lucrarea [110]). Algoritmul folose¸ste graful transpus GT asociat grafului init¸ial G ([11], [23], [30]), fiind compus din urm˘ atorii pa¸si: Pas 1. Se realizeaz˘ a parcugerea grafului cu algoritmul de vizitare ˆın adˆ ancime, pornind de la ˆ un nod arbitrar. In timpul vizit˘arii, se realizeaz˘ a numerotarea ˆın postordine a nodurilor ˆın cadrul vectorului postnum. Pas 2. Se obt¸ine un nou graf GT = (V, E T ) prin inversarea sensului arcelor grafului G (E T = (u, v) (v, u) E,u, v V ).
{
|
∈
∈ }
Pas 3. Se caut˘ a nodul nevizitat din graful GT ce are cel mai mare num˘ ar atribuit ˆın urma parcurgerii de la Pasul 1. Din acest nod se init¸iaz˘ a parcugerea grafului cu algoritmul de vizitare ˆın adˆancime. Pas 4. Dac˘a mai r˘amˆan noduri nevizitate atunci se reia Pasul 3 , altfel algoritmul se termin˘a. Fiecare arbore rezultat ˆın urma parcurgerii de la Pasul 3 constituie o component˘ a tare conex˘ a a grafului G. ˆIn continuare se prezint˘ a implementarea ˆın limbajul C a algoritmului anterior: Listing 6.1: kosaraju.c #include #include #define MAX 100 #define TRUE 1 #define FALSE 0 char v eci n [MAX] [MAX] ; char v i z i t a t [MAX] ; in t n ;
// Matrice a de adi ace nta // Ve ct or c e p a st r ea z a s t a r ea u nu i nod : v i z i t a t sa u n e v i z i t a t . // Numarul de v a r f u r i d i n g r a f
in t nump ; in t postnum [MAX] ;
// Numarul a s o c i at f i e c a r u i v a rf l a v i z i t a r e a i n p o st o rd i ne .
137
}
i f (maxim == 0) break ; k++; p r i n t f ( ” C omp onen ta %d : ” , k ) ; dfs 1 (nod ) ; p r i n t f ( ” n” ) ;
}
\
}
Exemplul 6.17 Dup˘ a parcurgerea ˆın adˆ ancime a grafului 6.6, valorile vectorilor vizitat ¸si postnum sunt urm˘ atoarele: 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. 6.7: Graful transpus G T corespunz˘ ator grafului din figura 6.6
Se construie¸ste graful transpus, GT (a se vedea figura 6.7). Se caut˘ a primul nod u ˆınc˘ a ˆ acest mod, nevizitat ( vizitatu = 0), c˘ aruia ˆıi corespunde cea mai mare valoare postnumu . In se identific˘ a componenta tare conex˘ a compus˘ a numai din nodul 1: 1 . Urm˘ atorul nod, ca valoarea a vectorului postnum ordonat descresc˘ ator, este 2. Din nodul 2 se parcurg nodurile 3 ¸si 4, rezultˆ and o alt˘ a component˘ a tare conex˘ a: 2, 3, 4 . Urm˘ atorul nod nevizitat u ce are valoarea prenumu maxim˘ a este nodul 8. Se identific˘ a mai ˆıntˆ ai componenta tare conex˘ a 7, 8 ¸si apoi 5, 6 .
{ } {
{ }
}
{ }
Analiza algoritmului Algoritmul lui Kosaraju realizeaz˘ a dou˘ a parcurgeri ale tuturor elementelor grafului G: prima dat˘ a parcurge graful G ¸si a doua oar˘ a parcurge graful GT . Prin urmare, complexitatea timp a algoritmului este Ω( V + E ) ˆın cazul ˆın care graful este reprezentat prin liste de adiacent¸a˘ ¸si este p˘atratic˘a ˆın V 2 (O( V 2)) atunci cˆ and graful este reprezentat prin matricea de adiacent¸a˘.
| | | | | | | |
6.4.2
Algoritmul lui Tarjan
Algoritmul lui Tarjan [117] este considerat drept o ˆımbun˘ at˘a¸t ire a algoritmului lui Kosaraju prin aceea c˘ a nu mai este nevoie de parcurgerea grafului G de dou˘ a ori. 139
Algoritm 50 Algoritmul lui Tarjan pentru determinarea componentelor tare conexe 1: procedure Tarjan(n,V ecin)
n - num˘ arul de noduri din graf V ecin - matricea de adiacent¸a˘ for i 1, n do vizitati 0
Input:
←
2: 3: 4: end for 5: counter 0 S 6: 7: for i 1, n do 8: if (vizitati = 0) then 9: call DF ST arjan(i, n, V ecin) 10: end if end for 11: 12: end procedure 13: procedure DFSTarjan(k,n,V ecin) 14: S k 1 15: vizitatk 16: counter counter + 1 17: prenumk counter, lowk counter 18: for i 1, n do 19: if (vecink,i = 1) then 20: if (vizitati = 0) then call DF ST arjan(i,n,V ecin) 21: 22: lowk M in(lowk ,lowi ) 23: else 24: if (i S ) then lowk M in(lowk , prenumi ) 25: end if 26: 27: end if 28: end if 29: end for 30: if (lowk = prenumk ) then 31: repeat 32: S u 33: Output ”u” 34: until (u = k) 35: end if 36: end procedure
← ∅ ←
⇐
←
←
←
← ← ←
⊲ vizitarea ˆın adˆancime a grafului
←
← ∈
←
⇒
low5 = min prenum6,low5 = min 4, 5 = 4 (deoarece de¸si vizitat6 = 1, avem 6 S , adic˘ a nodul 6 se afl˘ a pe stiv˘ a). Astfel ˆın momentul termin˘ arii apelului DFSTarjan(5, 8, Vecin), low5 = 4. Acum la nivelul lui DFSTarjan(6, 8, Vecin), ˆın urma revenirii din DFSTarjan(5, 8, a low6 = min low6 ,low5 = min 4, 4 = 4 (linia 22). Vecin), se calculeaz˘ Nodul 6 este un nod-cap ( low6 = prenum6) (vezi linia 30), ¸si prin urmare se extrag de pe stiv˘ a toate elementele dintre vˆ arful stivei ¸si elementul 6 inclusiv, rezultˆ and prima component˘ a tare conex˘ a: 5, 6 . Stiva r˘ amˆ ane cu elementele (vˆ arful stivei fiind ˆın dreapta) 1, 2, 3 . Din nodul 3 se continu˘ a cu vizitarea nodului 8, ˆınc˘ a nevizitat:
{
}
{ }
{
}
{ }
∈
{ }
{
141
}
Fig. 6.8: Drumul P ˆın algoritmul lui Gabow pentru graful din figura 6.6
- dac˘a w / P , atunci se adaug˘ a nodul w la drumul P (P = [v1 , . . . , vk , w]);
∈ - dac˘a w ∈ P , fie w = v . Contract˘ am circuitul { v , v , . . . , v , w} ˆın graful H : ˆın locul mult¸imii de vˆarfuri {v , v , . . . , v } va r˘amˆane doar reprezentantul acesteia, e.g. j
j
j
j+1
j+1
k
k
nodul ce are valoarea minim˘ a ˆın urma numerot˘ arii ˆın preordine;
- dac˘a nu avem nici un arc neverificat care s˘ a aib˘a nodul vk ca extremitate init¸ial˘ a , se marcheaz˘ a nodul vk ca apart¸inˆand unei componente conexe. Se ¸sterge nodul vk din graful H ¸si din drumul P , ˆımpreun˘ a cu toate arcele ce ˆıl au ca extremitate. Dac˘ a P = atunci se continu˘ a algoritmul ˆıncercˆ andu-se augmentarea drumului P . ˆIn caz contrar, se ˆıncearc˘ a init¸ializarea unui drum nou P ˆın H .
∅
S¸i acest algoritm are la baz˘ a metoda de parcurgere ˆın adˆ ancime a unui graf (DFS). ˆIn timpul vizit˘arii, algoritmul utilizeaz˘ a dou˘ a stive S ¸si P : S cont¸ine toate nodurile ce nu au fost atribuite ˆınc˘ a unei componente tare conexe, ˆın ordinea ˆın care au fost ˆıntˆalnite, iar P cont¸ine toate nodurile despre care ˆınc˘ a nu se poate spune nimic cu privire la apartenent ¸a lor la componente conexe diferite (cont¸ine nodurile drumului P din graful H ) . Altfel spus, se observ˘a faptul c˘ a stiva P p˘astreaz˘a nodurile r˘ a o subsecvent¸˘a a secvent¸ei ad˘ acin˘ a ce formeaz˘ nodurilor ce se afl˘ a la un moment dat ˆın cealalt˘ a stiv˘a S . ˆIn cadrul secvent¸ei urm˘ atoare, pentru un nod i adiacent cu nodul curent k, se verific˘a dac˘a nu a mai fost vizitat. Dac˘ a vizitati = 0 atunci se continu˘ a vizitarea ˆın adˆ ancime (call DFSGabow(i, n, Vecin)). Altfel, se verific˘ a dac˘ a nodul i nu a fost deja asignat unei componente tare conexe, ˆın caz afirmativ eliminˆ andu-se din drumul P circuitul vi , . . . , vk , vi : 1: if (vizitati = 0) then 2: call DFSGabow(i, n, V ecin) 3: else 4: if (i S ) then 5: while ( prenum i < prenum peek(P )) do P u 6: 7: end while 8: end if 9: end if
∈
{
}
⊲i
⇒
La finalul procedurii DFSGabow, se verific˘a dac˘ a nodul curent este identic cu cel aflat ˆın vˆarful stivei P . ˆIn caz afirmativ, se extrag de pe stiv˘ a toate nodurile dintre vˆ arful curent 143
al stivei S ¸si nodul k, ¸si se marcheaz˘ a ca fiind elemente ale componentei tare conexe a c˘ arei r˘ad˘acin˘a este nodul k: if (k = peek(P )) then
⇒ u repeat S ⇒ u P
Output ”u” until (u = k) end if
Algoritm 51 Algoritmul lui Gabow pentru determinarea componentelor tare conexe 1: procedure Gabow(n, V ecin) 2: for i 1, n do 3: vizitati 0 4: end for 5: counter 0 6: S , P 7: for i 1, n do 8: if (vizitati = 0) then call DFSGabow(i, n, V ecin) 9: 10: end if 11: end for 12: end procedure 13: procedure DFSGabow(k, n,V ecin) 1 14: vizitatk 15: S k, P k 16: counter counter + 1 17: prenumk counter 1, n do 18: for i 19: if (vecink,i = 1) then 20: if (vizitati = 0) then 21: call DFSGabow(i,n,V ecin) 22: else ⊲ i nu a fost asignat ˆınc˘a unei componente tare conexe 23: if (i S ) then 24: while ( prenum i < prenum peek(P )) do 25: P u 26: end while 27: end if end if 28: 29: end if 30: end for 31: if (k = peek(P )) then P u 32: repeat 33: 34: S u 35: Output ”u” 36: until (u = k) 37: end if 38: end procedure
←
←
← ← ∅ ← ∅ ←
← ⇐ ⇐ ← ← ←
∈
⇒
⇒
⇒
144
6.5
Exercit¸ii
1. (Compilator ) ˆIn trecut compilatoarele erau foarte simple. ˆIn acele vremuri oamenii preferau s˘ a includ˘a tot programul ˆıntr-un singur fi¸sier. Dac˘ a cineva f˘ acea o modificare ˆın program, trebuia recompilat tot codul surs˘ a. Cre¸sterea lungimii programelor conducea la timpi de compilare tot mai mari, ceea ce constituia o piedic˘a ˆın ceea ce prive¸ste cre¸sterea complexit˘a¸tii algoritmilor implementat¸i. De aceea programatorii au dezvoltat o tehnic˘ a pentru eliminarea compil˘ arilor redundante. Ei au ˆınceput prin a sparge programele ˆın mai multe module mici pe care le compilau separat. Astfel, pentru orice modificare care se opera ˆıntr-un anumit modul, se compila numai acesta, ¸si nu ˆıntreaga aplicat¸ie. Fiecare modul cont¸ine la ˆınceput lista celorlaltor module pe care le folose¸ste. Modulul A trebuie recompilat numai dac˘ a a fost modificat sau are ˆın lista sa un modul B care a fost recompilat la rˆ andul s˘au. ˆIn celelalte cazuri nu este necesar˘ a recompilarea modulului A. Problema cere s˘ a se realizeze un algoritm ¸si pe baza acestuia un program, care s˘a decid˘ a ce module trebuie recompilate ¸si care nu. Pentru a avea un timp de recompilare minim, va trebui s˘a c˘autam compilarea unui num˘ a r cˆ at mai mic de linii. Prima linie a datelor de intrare cont¸ine num˘ arul de module N (1 N 100). Urmeaz˘ a descrierea modulelor. Prima linie a descrierii cont ¸ine numele modulului. A doua linie cont¸ine num˘ arul liniilor din codul surs˘ a. A treia linie cont¸ine num˘ arul M (0 M < N ) de module de care depinde modulul actual. Linia urm˘ atoare cont¸ine numele acestor module, separate printr–un spat¸iu. Numele unui modul nu dep˘a¸se¸ste 20 de caractere.
≤ ≤
≤
Descrierea modulelor este urmat˘ a de mai multe blocuri, cˆate unul pentru fiecare versiune a programului. Prima linie a fiec˘ arui bloc cont¸ine num˘ arul k (1 k N ) de module ce au suferit modific˘ ari de la recompilarea versiunii precedente.
≤ ≤
Linia urm˘atoare cont¸ine numele modulelor, separate prin spat¸iu, ˆın care au survenit modific˘ ari. Dup˘a ultimul bloc exist˘ a o singur˘ a linie ce cont¸ine doar num˘ arul 0. Pentru fiecare versiune a programului se scrie o linie ce cont¸ine num˘ arul liniilor codului surs˘a care au trebuit s˘ a fie recompilate. Intrare 3 MATH 20 0 MAIN 100 2 MATH IO IO 7 0 3 MATH IO MAIN 0
Ie¸sire 127
(Propusa la CEOI 1997)
2. Se d˘a un num˘ a r de k (k < 1000) reguli, numerotate de la 1 la k. O regul˘ a are forma x y unde x ¸si y sunt propozit¸ii cu urm˘ atoarea semnificat¸ie: dac˘ a propozit¸ia x este adevarat˘ a, atunci proprozit¸ia y este adev˘arat˘ a.
→
145
S˘a consider˘ am pentru k = 5 urm˘ atoarele reguli:
→ 5 (5) 1 → 5 Profesorul cere s˘ a se demonstreze regula 1 → 5. Demonstrat¸ia optim˘ a const˘ a ˆın apli(1) 1
→2
(2) 1
→3
→ 4
(3) 3
(4) 4
carea direct˘ a a regulii (5).
(2)
(1)
(3)
(4)
→ 3, 1 → 2, 3 → 4, 4 → 5). Aceasta este o demonstrat¸ie corect˘ a , ce cont¸ine ˆın plus regula 1 → 2. Profesorul ar fi fost mult¸umit cu demonstrat¸ia 2 3 4 (1 → 3, 3 → 4, 4 → 5). Un elev demonstreaz˘ a regula (5) astfel: 2 1 3 4 (1
(1)
(2)
(3)
(4)
S˘a se realizeze un algoritm ce utilizeaz˘ a drept date de intrare k, x (propozit¸ia care se consider˘a adev˘ arat˘ a), y (propozit¸ia ce trebuie demonstrat˘ a), ¸sirul celor k reguli cˆ at ¸si ¸sirul numerelor de ordine ale regulilor ce constituie demonstrat ¸ia elevului.
Algoritmul verific˘ a dac˘ a ¸sirul numerelor de ordine ale regulilor constituie o demonstrat ¸ie. ′ ′ ′ ′ ˆIn caz negativ va afi¸sa NU , iar ˆın caz afirmativ va afi¸sa DA , urmat pe linia urm˘ atoare de demonstrat¸ia elevului din care au fost eliminate afirmat¸iile inutile. Se consider˘ a c˘a un ¸sir de reguli f˘ ar˘a reguli ˆın plus constituie o demonstrat ¸ie corect˘ a a propozit¸iei y pornind de la propozitia x, dac˘a renunt¸area la orice regul˘ a ar conduce la o secvent¸a˘ prin care y nu se mai poate deduce din x. (Marele premiu PACO, 1997)
3. Dup˘a cum se poate observa prin reclamele de la televizor, multe companii cheltuiesc foarte mult¸i bani pentru a convinge oamenii c˘ a ofer˘ a cele mai bune servicii la cel mai sc˘azut pret¸. O companie de telefoane ofer˘ a cercuri de apel (calling circles). Un abonat poate s˘ a fac˘ a o list˘a cu persoanele pe care le sun˘ a cel mai frecvent (¸si care constituie cercul s˘ au de prieteni). Dac˘ a acesta sun˘ a pe cineva inclus ˆın aceast˘ a list˘a, ¸si persoana respectiv˘a este, de asemenea, abonat˘ a la aceea¸si companie, va beneficia de un discount mai mare decˆ at pentru o convorbire telefonic˘ a cu cineva din afara listei. O alt˘a companie a aflat de aceast˘ a init¸iativ˘ a ¸si se ofer˘ a s˘a determine ea lista de cuno¸stint¸e cu care un abonat vorbe¸ste cel mai frecvent la telefon. LibertyBellPhone este o companie nou˘ a de telefoane ce se gˆ ande¸ste s˘a ofere un plan de apeluri mult mai avantajos decˆ at alte companii. Ea ofer˘ a nu numai reduceri pentru ”cercul de apel”, cˆ at ¸si determin˘a pentru un abonat acest cerc. Iat˘ a cum procedeaz˘ a: compania p˘ astreaz˘a numerele tuturor persoanelor participante la fiecare apel telefonic. ˆIn afara unui abonat, cercul s˘ au de apel const˘ a din toate persoanele pe care le sun˘ a ¸si care ˆıl sun˘a, direct sau indirect. De exemplu, dac˘ a Ben ˆıl sun˘a pe Alexander, Alexander o sun˘ a pe Dolly ¸si Dolly ˆıl suna pe Ben, atunci ei tot¸i fac parte din acela¸si cerc. Dac˘ a Dolly ˆıl mai sun˘a ¸si pe Benedict iar Benedict o sun˘ a pe Dolly, atunci Benedict este ˆın acela¸si cerc cu Dolly, Ben ¸si Alexander. ˆIn fine, dac˘ a Alexander ˆıl sun˘a pe Aron dar Aaron nu ˆıl sun˘ a pe Alexander, Ben, Dolly sau Benedict, atunci Aaron nu este ˆın cerc. S˘a se realizeze un algoritm ce determin˘ a cercurile de apel, cunoscˆandu–se lista apelurilor telefonice dintre abonat¸i. (Finala ACM 1995, Calling Circles)
4. La facultatea X exist˘ a dou˘ a alternative pentru ca student¸ii s˘a aib˘a timp s˘a asimileze cuno¸stint¸ele: 146
(a) M˘ arirea zilei la 30 de ore, ¸si (b) Reducerea programei ¸scolare. Optˆand pentru a doua variant˘ a, Liga Student¸ilor introduce o platform˘ a program care: (a) Stabile¸ste care sunt materiile necesare pentru a putea studia o nou˘ a materie. De exemplu, pentru a studia cursul ”Management” este nevoie de cursul ”Teorie Economic˘a” ¸si de cursul ”Marketing”. (b) Stabile¸ste care sunt materiile cu adev˘ arat utile dintre toate cele studiate ˆın facultate. De exemplu, cursul de ”M˘ asur˘ari electrice” nu este util la absolut nimic. (c) Cere s˘ a se elimine din program˘ a materiile care nu sunt nici folositoare, nici nu servesc (direct sau indirect) la ˆınv˘ at¸area unor materii folositoare. (d) Cere s˘ a se indice care dintre materii nu pot fi predate ˆın nici o ordine. De exemplu, cursul ”Mecanic˘ a” se bazeaz˘ a pe cursul ”Ecuat¸ii Diferent¸iale”, dar cursul de ”Ecuat¸ii Diferent¸iale” ˆı¸si preia exemplele din ”Mecanic˘ a”. Prin urmare nu exist˘a nici o ordine ˆın care aceste materii s˘ a fie predate f˘ ar˘a a introduce cuno¸stint¸e nedemonstrate. 5. Distribuirea c˘ art¸ilor cerute de c˘ atre cititorii de la sala de lectur˘ a a unei biblioteci este f˘acut˘a de c˘ atre un robot ce are posibilitatea s˘ a ajung˘ a la orice carte ce poate fi solicitat˘ a. Din p˘acate, rafturile unde sunt depozitate c˘ art¸ile sunt dispuse astfel ˆıncˆ at robotul nu poate lua c˘ art¸ile ˆıntr–un singur drum. Dup˘a recept¸ionarea comenzilor de la mai mult¸i cititori, robotul cunoa¸ste pozit¸ia c˘art¸ilor ˆın rafturi ¸si drumurile c˘ a tre acestea. Din p˘acate, de la pozit¸ia unei c˘art¸i, robotul nu se poate deplasa decˆ at spre anumite c˘ art¸i. Acesta porne¸ste ¸si culege c˘ art¸ile ce sunt accesibile ˆıntr–un drum, apoi porne¸ste de la o alt˘a pozitie de carte ¸si culege acele c˘ art¸i ce sunt accesibile din acel punct ¸si a¸sa mai departe. Datele de intrare constau din num˘ a rul de c˘art¸i n precum ¸si num˘arul m de leg˘ aturi dintre pozit¸iile acestora (1 100, 11 n m 10000), urmate de m perechi de numere naturale, ce semnific˘ a leg˘aturile directe ˆıntre pozit¸iile c˘art¸ilor.
≤ ≤
≤
≤
Datele de ie¸sire constau din drumurile pe care le face robotul pentru a culege c˘ art¸ile cerute. 6. Se consider˘ a o mult¸ime de n elevi dintr-o clas˘ a. Fiecare elev are cuno¸stint¸e mai avansate ˆıntr-un anumit domeniu. Pentru ridicarea nivelului clasei, dirigintele vrea s˘ a-i aranjeze ˆın grupuri astfel ˆıncˆa t tot¸i elevii dintr-un grup s˘ a ajung˘ a s˘ a cunoasc˘ a, ˆıntr-o anumit˘a perioad˘ a de timp, toate cuno¸stint¸ele tuturor celorlalt¸i colegi din acela¸si grup. Grupurile de elevi nu ˆı¸si vor schimba cuno¸stint¸ele ˆıntre ele. Se ¸stie c˘ a un elev nu se poate face ˆınt¸ eles de c˘ a tre oricine. El are o list˘ a de preferint¸ e fat¸a˘ de care el le va ˆımp˘art˘ a¸si cuno¸stintele sale. Relat¸ia de preferint¸a˘ nu este simetric˘ a. S˘a se determine num˘ arul minim de grupuri ˆın care va fi ˆımp˘ art¸it˘a clasa precum ¸si component¸a acestora.
147
Fig. 6.9: Pozit¸iile c˘art¸ilor ˆıntr-o bibliotec˘a precum ¸si posibilit˘a¸t ile de deplasare ale robotului
148
Capitolul 7 Heap-uri Un heap (eng. heap = movil˘a) reprezint˘ a o structur˘ a de date abstract˘ a organizat˘ a sub forma unei structuri ierarhice (de arbore) ¸si care respect˘ a urm˘atoarea proprietate: pentru oricare dou˘a noduri ale arborelui, A ¸si B, astfel ˆıncˆat tataB = A, avem cheieA cheieB (sau cheieA cheieB ). Fiecare nod va avea o valoare asociat˘ a numit˘a cheie . Se observ˘ a faptul c˘a, dac˘a este ˆındeplinit˘a proprietatea c˘ a A, B, tataB = A, cheieA cheieB , atunci ˆıntotdeauna cheia de valoare maxim˘ a se va afla ˆın r˘ ad˘acina arborelui. Structura de date heap poate fi implementat˘ a sub mai multe forme: 2 3 heap [116], heap binar [9], heap binomial [125], heap Fibonacci [54], heap ternar , treap 1 [7], [109]. Principalele operat¸ii definite pentru un heap H sunt:
≤
≥
∀
≥
−
1. FindMin(H; p) - determin˘ a cheia avˆ and cea mai mic˘a valoare ¸si ˆıntoarce o referint˘ a c˘atre nodul ce o cont¸ine; 2. DeleteMin(H) - ¸sterge nodul ce cont¸ine cheia de valoare minim˘ a ¸si reorganizeaz˘ a structura prin refacerea propriet˘ a¸t ii de heap; 3. Insert(H,p) - insereaz˘a nodul p ˆın heap -ul H ; 4. Delete(H,p) - ¸sterge nodul p din heap -ul H ; 5. DecreaseKey(H,p,v) - modific˘a valoarea cheii nodului p din heap -ul H , atribuindu-i valoarea v ¸si reorganizeaz˘a aceast˘ a structur˘ a, p˘astrˆandu-i proprietatea de heap; 6. MergeHeaps(H 1 , H 2 , H ) - unific˘a dou˘ a heap-uri, desemnate prin H 1 ¸si H 2 , ˆıntr-una nou˘a H , ce va cont¸ine elementele celor dou˘ a heap-uri. Tabelul 7.1 ([30]) ilustreaz˘ a complexitatea-timp a acestor operat¸ii pentru diferite tipuri de implement˘ari ale structurii de heap. Structura de date de tip heap prezint˘ a multiple aplicat¸ii dintre care amintim algoritmul de ordonare heapsort a unei secvent¸e de elemente, diferit¸i algoritmi din teoria grafurilor (algoritmi pentru determinarea drumurilor de cost minim) sau algoritmi de select ¸ie (pentru determinarea valorii minime, maxime, mediane dintr-un vector).
7.1
Heap-uri binare (Min -heapuri sau Max -heapuri)
Definit¸ia 7.1 Un min-heap este un arbore binar complet ˆın care valoarea memorat˘ a ˆın orice nod al s˘ au este mai mic˘ a sau egal˘ a decˆ at valorile memorate ˆın nodurile fii ai acestuia. 1
http://acs.lbl.gov/~aragon/treaps.html
149
ˆ figura 7.2 pot fi urm˘ Exemplul 7.2 In arite operat ¸iile necesare pentru inserarea unui nod avˆ and cheia asociat˘ a de valoare 3. Se adaug˘ a elementul avˆ and valoarea cheii 3, pe ultima pozit ¸ie a heap-ului. Deoarece valoarea cheii ultimului element 3 este mai mic˘ a decˆ at valoarea cheii p˘ arintelui s˘ au 8, se interschimb˘ a valorile cheilor. Se compar˘ a din nou, valoarea cheii nodului curent (cheia cu valoarea 3) cu valoarea cheii asociat˘ a p˘ arintelui s˘ au (nodul 1). Se interschimb˘ a din nou valorile cheilor. La final, heap-ul arat˘ a ca ˆın ultima configurat ¸ie din figura 7.2.
7.1.2
S ¸ tergerea elementului minim
Procedura DeleteMin (a se vedea algoritmul 53) va elimina nodul r˘ad˘acin˘a din heap, ˆıntorcˆand valoarea acestuia prin intermediul variabilei minim, ¸si va reorganiza heap-ul . ˆIn linia 6 se p˘astreaz˘a valoarea r˘ ad˘acinii ˆın variabila minim. Instruct¸iunea urm˘ atoare ia ultimul element ¸si ˆıl aduce pe prima pozit¸ie. ˆIn linia 8, se decrementeaz˘ a num˘arul de elemente existente la momentul respectiv ˆın heap. Apoi se p˘astreaz˘a ˆın variabila j indicele elementului ce cont¸ine cea mai mic˘ a valoare a cheii, dintre cheile corespunz˘ atoare descendent¸ilor nodului curent (liniile 11-15). ˆIn linia 17 se interschimb˘ a pozit¸ia p˘arintelui cu pozit¸ia celui mai mic dintre descendent¸ii s˘ai, dac˘a este necesar. Instruct¸inea de ciclare while (liniile 10 - 22) se p˘ar˘ase¸ste atunci cˆ and s-a ajuns la o frunz˘a sau cˆ and nu se mai poate ˆınainta ˆın jos ˆın arbore (linia 20).
Algoritm 53 Algoritm de ¸stergere a elementului minim dintr-un heap 1: procedure DeleteMin(A,last,N ; minim) 2: if (last = 0) then 3: Output ′ Heap vid!′ 4: return 5: end if 6: minim a 1 7: a1 a last 8: last last 1 9: i 1 last 10: while (i 2 ) do if ((2 i = last) (a2·i < a2·i+1 )) then 11: 12: j 2 i 13: else 14: j 2 i + 1 15: end if 16: if (ai > a j ) then 17: ai a j 18: i j 19: else return 20: 21: end if 22: end while 23: end procedure
{
← ← ←
}
←
− ≤⌊ ⌋ · ∨ ← · ← ·
↔ ←
ˆIn figura 7.3 pot fi urm˘ arit¸i pa¸sii necesari pentru ¸stergerea nodului de valoare minim˘ a din heap-ul considerat drept exemplu.
152
Fig. 7.3: S ¸tergerea valorii minime dintr-un heap
7.1.3
Crearea unui heap
a) Algoritm 54 Algoritm de creare a unui heap (prima variant˘ 1: procedure NewHeap1(A, n) 2: for i 2, n do 3: call Insert(A, i 1, n , ai ) end for 4: 5: end procedure
←
−
Prima metod˘a de construire a unui heap binar se bazeaz˘ a pe urm˘ atoarea tehnic˘ a: se pleac˘ a init¸ial cu heap-ul format dintr-un singur element ¸si se insereaz˘ a pe rˆand, toate elementele ˆın heap-ul nou creat (a se vedea algoritmul 54).
Exemplul 7.3 S˘ a construim o structur˘ a de heap din ¸sirul de elemente 10, 7, 9, 5, 7 . . .. Se pleac˘ a cu heap-ul format dintr-un singur nod, 10. Apoi, se insereaz˘ a elementul cu valoarea 7 ˆın heap. Deoarece 7 > 10 , se interschimb˘ a valorile ˆıntre ele (a se vedea figura 7.4). Urmeaz˘ a s˘ a se insereze nodul a c˘ arui valoare asociat˘ a este 9 (acest nod va fi descendentul drept al r˘ ad˘ acinii, dup˘ a cum se poate observa din figura 7.4). La final, se adaug˘ a nodul a c˘ arui cheie are valoarea 5, ¸si se restabile¸ste proprietatea de heap prin interschimb˘ ari succesive. Analizˆand num˘ arul de operat¸ii efectuate de procedura Insert, se observ˘a c˘a o valoare nou introdus˘ a poate s˘ a ajung˘ a pˆan˘a ˆın r˘ad˘a cina arborelui. Astfel, num˘ a rul de operat¸ii (interschimb˘ari) ˆın cazul cel mai defavorabil va fi egal cu ˆın˘ alt¸imea arborelui, h. Deoarece avem un arbore binar complet cu cel mult n no duri, ˆın˘ alt¸imea acestuia este cel mult [log n] (h [log n]). Complexitatea timp ˆın cazul cel mai defavorabil al procedurii NewHeap este egal˘ a cu suma timpilor necesari inser˘ arii tuturor celor n valori, ˆın cazul cel mai defavorabil. Vom ¸tine cont de faptul c˘a, ˆın cazul unui arbore binar complet, num˘ arul nodurilor de pe nivelul i este 2i , cu except¸ia ultimului nivel unde num˘arul de noduri este mai mic.
≤
[log n]
n
T (n) =
k=1
T Insert (i)
· ≤
i 2i
i=1
153
[log n]
≤ log n
i=1
2i = O(n log n)
(7.1)
Fig. 7.4: Crearea unui heap prin inser˘ari succesive
Cea de-a doua metod˘ a se bazeaz˘ a pe ideea de a construi un heap prin combin˘ ari (uniuni) repetate de heap-uri, strategie cu o eficient¸˘a superioar˘a metodei anterioare. Init¸ial, se porne¸ste cu o p˘ adure de arbori, compu¸si fiecare numai dintr-un singur nod, r˘ ad˘a cina. La fiecare pas i, se construie¸ste un heap nou din ai ¸si dou˘a heap-uri de dimensiunile apropiate: heap-ul avˆand r˘ ad˘acina a 2·i ¸si cel cu r˘ad˘acina a2·i+1 (a se vedea algoritmul 55). a) Algoritm 55 Algoritm de creare a unui heap (a doua variant˘ 1: procedure NewHeap2(A, n) n 2: for i 1 do 2 , 1 STEP 3: call Push(A,i,n) end for 4: 5: end procedure
←⌊ ⌋
−
Se observa faptul c˘ a subrutina Push (a se vedea algoritmul 56) este asem˘ an˘atoare cu subrutina DeleteMin, scopul lui Push fiind acela de a reorganiza un heap ˆıntre limitele first ¸si last ale tabloului A.
Exemplul 7.4 S˘ a construim o structur˘ a de heap avˆ and drept date de intrare elementele secvent ¸ei: 1 2 3 4 5 6 7 10 7 9 5 7 8 6 Dup˘ a cum se poate observa din figura 7.5, pentru i = 3 se va construi heap-ul compus din elementele de valori 9, 8 ¸si 6, pentru i = 2 se va construi heap-ul compus din elementele de valori 7, 5 ¸si 7, iar pentru i = 1 se va construi heap-ul ce are drept r˘ ad˘ acin˘ a elementul de valoare 10, descendentul stˆ ang fiind heap-ul construit la pasul i = 2 iar descendentul drept fiind heap-ul construit la pasul i = 3. Fie x un nod aflat pe nivelul i (i = 0, h 1, h = log n). Acest nod va suferi cel mult h i deplas˘ari ˆın cadrul structurii de heap, migrˆ and c˘ atre frunzele arborelui. Num˘ arul de
−
−
154
Algoritm 56 Algoritm de reorganizare a unui heap 1: procedure Push(A, first, last) 2: i f irst last 3: while (i 2 ) do 4: if ((last = 2 i) (a2·i < a2·i+1 )) then j 2 i 5: 6: else 7: j 2 i + 1 8: end if 9: if (ai > a j ) then ai a j 10: 11: i j 12: else 13: i last 14: end if end while 15: 16: end procedure
←
≤⌊ ⌋ · ∨ ← · ← ·
↔ ← ←
Fig. 7.5: Crearea unui heap prin reorganiz˘ari succesive
operat¸ii (deplas˘ari) efectuate de algoritmul NewHeap2 pentru a construi o structur˘ a de heap cu n elemente este: h−1
T (n)
≤ i=0
7.2
i
2 (h
− i)
j=h−i
=
h
j2
j=1
h
h− j
j = 2 j 2 j=1
h
h
≤ n
j=1
j < 2n = O(n) 2 j
(7.2)
Ordonare prin metoda HeapSort
Metoda de ordonare HeapSort [129] are la baz˘ a algoritmul general prezentat ˆın subrutina SortX (a se vedea algoritmul 57). Vom presupune c˘ a elementele ¸sirului init¸ial se afl˘ a ˆıntr-o structur˘a de date de tip list˘a L, iar rezultatul va fi obt¸inut ˆın structura de date de tip heap S . Init¸ial structura S nu cont¸ine nici un element. Subrutina Insert(x, S; S) va ad˘auga elementul x structurii de date S . Subrutina 155
a) Algoritm 57 Algoritm de ordonare folosind heap-uri (prima variant˘ 1: procedure SortX(A, n) 2: for fiecare x L do 3: call Insert(x, S ; S ) 4: end for 5: while (S = ) do 6: call M in(S ; y) 7: ouput y 8: call Delete(y, S ; S ) 9: end while 10: end procedure
∈
∅ { }
a a lui S ˆın y, iar Delete(y, S; S) ¸sterge elementul y Min(S; y) ˆıntoarce valoarea minim˘ din S . Dup˘a cˆateva modific˘ ari ale algoritmului init¸ial de ordonare se obt¸ine algoritmul HeapSort (a se vedea algoritmul 58): instruct¸iunile din liniile 2-4 formeaz˘a heap-ul ˆıntr-o manier˘ a incremental˘ a; pentru toate elementele, ˆın linia 6 se elimin˘a cel mai mic element din fat ¸a heap-ului ¸si se reface proprietatea de arbore part ¸ial ordonat (linia 7) ˆıntre limitele 1 ¸si i 1.
−
a) Algoritm 58 Algoritm de ordonare folosind heap-uri (a doua variant˘ 1: procedure HeapSort(A, n) i 2: for i 1 do 2 , 1 STEP 3: call Push(A,i,n) 4: end for n, 2 STEP 1 do 5: for i a1 a i 6: 7: call Push(A, 1, i 1) 8: end for 9: end procedure
←⌊ ⌋
−
← ↔
− −
Implementarea ˆın limbajul C a algoritmului de ordonare a unei secvent ¸e de numere folosind heap-uri este urm˘ atoarea: Listing 7.1: sortheap.c #include #define MAXN 100 /
∗∗ ∗ Fu nc ti e ∗/
p en tr u c i t i r e a v a l o r i l o r d a t el o r de i n t r a r e .
in t readI nput ( in t in t i , n ;
∗a ) {
pr in tf (”n=” ) ; sca nf (”%d” , &n ) ; fo r ( i = 1 ; i <= n ; i ++) pr in tf (”a[%d]=” , i ); sc an f (”%d” , &a [ i ] ) ;
{
}
return n ;
} 156
/
∗∗ ∗ F u nc ti e ∗/
p en tr u a f i s a r e a r e z u l t a t e l o r .
void l i s t ( in t i nt i ;
∗a ,
in t n )
{
for ( i = 1 ; i <= n ; i ++) pr in tf (”%d , ” , a [ i ] ) ; p r i n t f ( ” n” ) ;
\
} /
∗∗ ∗ Reorganizarea unei movile . ∗ @param st ar t − i n d i c e l e p ri mu lu i e le me nt a l s e c v en t e i ∗ @param f i n i s h − i n d i c e l e l u l t im u l u i e le me nt a l s e c v en t e i ∗ @param a − v e c to r u l c e p a s tr e az a v a l o r i l e m o vi l ei ∗/ void push( in t s t a r t , i nt f i n i s h , i nt ∗ a ) { in t i , j , aux ;
i = start ; while ( i <= f i n i s h / 2 ) i f ( 2 i == f i n i s h a[2 i ] < a[2 i + 1]) j = 2 i; else j = 2 i + 1 ; i f ( a [ j ] < a [ i ]) aux = a [ i ] ; a [ i ] = a [ j ] ; a [ j ] = aux ; i = j;
∗
{
||
∗ ∗
∗
∗
{
}
else return ;
}
}
in t a [MAXN] ; void main( void ) in t n = r e a d I n pu t ( a ) ; in t i , j , aux ;
{
/ / o r g a n i z a r e a u n ui h ea p fo r ( i = n / 2 ; i > 0 ; i p us h ( i , n , a ) ;
−−)
/ / d e t er m in a r ea m in im u lu i s i r e o r g a n i z a r e a hea p u l u i j = n ; while ( j > 1 ) a ux = a [ 1 ] ; a [ 1 ] = a [ j ] ; a [ j ] = a ux ; j ; p us h ( 1 , j , a ) ;
−
{
−−
}
l i s t (a , n ) ;
}
157
7.3
Aplicatie - Coad˘ a cu prioritate
Definit¸ia 7.2 O coad˘ a cu prioritate este o structur˘ a de date abstract˘ a format˘ a din elemente ce au asociat˘ a o valoare numit˘ a cheie sau prioritate ¸si care suport˘ a urm˘ atoarele opera¸ii: t
• Insert(Q, X) - insereaz˘ aelementul x ˆın coada cu prioritate denumit˘ a Q ; • ExtractMax(Q) - extrage din coada cu prioritate Q , elementul de valoare maxim˘ a. a prin intermediul c˘ areia mai multe procese utilizeaz˘ a ˆın coMultitasking -ul este o metod˘ ˆ mun resursele calculatorului (inclusiv procesorul). In situat¸ia unui calculator cu un singur procesor, se spune c˘ a ˆın orice moment ruleaz˘ a cel mult un proces, ceea ce ˆınseamn˘ a c˘a procesorul execut˘ a instruct¸iunile unui singur proces la un moment dat. Sarcina alegerii procesului care s˘ a se afle ˆın execut¸ie la un moment dat este o problem˘ a de planificare . Operat¸ia de a opri un proces aflat ˆın execut¸ie, pentru a-i aloca altui proces, aflat ˆın a¸steptare, un timp procesor se nume¸ste schimbare de context (eng. context switch ). Realizarea frecvent˘ a a acestor schimb˘ari de context creaz˘a iluzia execut ¸iei ˆın paralel a mai multor programe. ˆIn cazul unui calculator cu mai multe procesoare, multitasking-ul permite execut¸ia unui num˘ar de procese mai mare decˆ at num˘ arul de procesoare. Sistemul de operare este cel care se ocup˘ a de planificarea proceselor, planificare ce se ˆıncadreaz˘ a ˆıntr-una din urm˘atoarele strategii:
• ˆın cazul sistemelor cu multiprogramare , procesul curent se afl˘a ˆın execut¸ie pˆan˘a ˆın
momentul ˆın care realizeaz˘ a o operat¸ie ce presupune a¸steptarea dup˘ a un eveniment extern (o operat¸ie de I/O), sau pˆan˘a cˆand planificatorul de procese fort¸eaz˘ a eliberarea procesorului.
• ˆıntr-un sistem de tip time-sharing , fiecare proces va elibera procesorul ˆın mod voluntar, dup˘a expirarea cuantei de timp alocate acestuia, sau ˆın urma aparit ¸iei unui eveniment hardware cum ar fi o ˆıntrerupere.
• ˆın cadrul sistemelor real-time , unui pro ces aflat ˆın stare de a¸steptare i se garanteaz˘a accesul la procesor ˆın cazul aparit ¸iei unui eveniment extern. Astfel de sisteme sunt proiectate pentru controlul unor dispozitive mecanice cum ar fi robot ¸ii industriali.
Comutarea ˆıntre procese consum˘ a timp procesor pentru ca planificatorul de procese s˘ a ˆınghet¸e starea unui proces ¸si s˘ a dezghet¸e starea altui proces (schimbare de context ) . Dac˘ a mai multe procese concurente sunt executate pe acela¸si procesor ¸si toate efectueaz˘ a diverse calcule, atunci timpul total de execut¸ie va fi mai mare decˆ at timpul de execut¸ie al unui program secvent¸ial echivalent. Cre¸sterea vitezei sistemului se poate realiza prin ˆıntret ¸eserea (intercalarea) diferitelor faze ale mai multor procese. ˆIn cadrul unui proces se pot identifica dou˘ a tipuri de faze din punct de vedere logic: a exclusiv la nivelul procesorului faza de calcul ¸si faza de I/O . Faza de calcul se realizeaz˘ utilizˆandu-se la maxim funct¸iunile acestuia. Faza de I/O (intrare/ie¸sire) presupune un aport mai mare din partea perifericelor (imprimante, hard discuri, pl˘ a ci de ret¸ea etc), procesul ˆ a de a¸steptˆ and ca un periferic s˘a-¸si termine sarcina. In timp ce un proces se afl˘a ˆıntr-o faz˘ I/O a¸steptˆ and dup˘ a finalizarea unei operat¸ii de c˘atre un dispozitiv periferic, un proces aflat ˆın faza de calcul poate ocupa procesorul ¸si efectua calculele programate. ˆIn cazul multitasking-ului preemtiv , planificatorul de procese aloc˘ a cuante de timp procesor egale fiec˘ arui proces, asigurˆ andu-le astfel un tratament echitabil. De asemenea, sistemul poate r˘aspunde rapid unui eveniment extern (cum ar fi sosirea unor date) ce presupune procesarea 158
de c˘atre procesul curent sau de c˘ atre un altul. ˆIn cazul multitasking-ului non-preemtiv , planificatorul ˆıi d˘ a controlul unui proces pˆ an˘a cˆand acesta se termin˘ a, sau elibereaz˘ a singur procesorul. Sistemele de operare bazate pe kernelul Windows NT 4.0 folosesc un planificator de procese preemtiv cu priorit˘ a¸ti. ˆIn momentul ˆın care trebuie s˘a aleag˘ a un proces pentru a-i da controlul procesorului, planificatorul de procese va alege procesul ce are prioritatea cea mai mare ¸si este gata de execut¸ie, utilizˆand o strategie de tip round rubin : dac˘a avem trei procese avˆ and aceea¸si prioritate A, B ¸si C , ¸si un proces D de prioritate mai mic˘ a, planificatorul va alege mai ˆıntˆai procesul A, apoi procesul B, urmat de procesul C , procesului D alocˆandu-i procesorul numai ˆın situat¸ia ˆın care celelalte trei procese nu sunt ˆın starea gata de execut ¸ie (de exemplu sunt blocate ˆın a¸steptarea efectu˘ arii unei operat¸ii I/O). Pentru procesele de prioritate mic˘a, ¸si care nu au fost executate deloc ˆın ultimele, s˘ a zicem, 3 secunde, planificatorul de procese aplic˘ a urm˘atorul algoritm pentru a evita fenomenul de ˆınfometare : le atribuie o prioritate temporar˘ a foarte mare, ridicˆandu-le la ˆınceputul cozii de procese, ¸si le aloc˘ a o cuant˘ a de timp mai lung˘a decˆat ˆın mod normal. Sistemele de operare bazate pe un kernel de tip Unix folosesc un planificator bazat pe mai multe cozi de priorit˘ a¸ti ce folose¸ste acea¸si strategie de tip round rubin ˆın cadrul fiec˘ arei cozi. Procesele noi sunt inserate ˆıntr-o coad˘ a corespunz˘ atoare unor priorit˘ a¸ti mai mari, ¸si pe m˘asur˘a ce petrec un timp procesor mai ˆındelungat, sunt inserate ˆıntr-o coad˘a corespunz˘ atoare unor priorit˘ a¸t i mai mici. Sistemele de operare de generat ¸ie mai nou˘a, bazate pe un kernel a prioritatea unui proces aflat ˆın stare de ˆınfometare (starvation ) pˆan˘a Unix , incrementeaz˘ cˆand acesta este executat, dup˘ a care prioritatea acestuia este resetat˘ a la valoarea pe care procesul o avea ˆınainte de a ˆıncepe ˆınfometarea . Vom simula un planificator de procese bazat pe o coad˘ a de priorit˘ a¸t i: la un moment dat, planificatorul alege procesul de prioritate minim˘ a, ˆıi d˘a controlul procesorului pentru o perioad˘ a de timp variabil˘ a ˆıns˘a limitat˘a. Dup˘a trecerea acelei perioade de timp, prioritatea procesului este incrementat˘ a cu o valoare ce depinde direct proport¸ional de timpul procesor petrecut de acesta, ¸si este inserat ˆın coada de priorit˘ at¸i pe pozit¸ia corespunz˘ atoare. ˆIn cadrul fi¸sierului task.h definim structura de date Process precum ¸si dou˘ a funct¸ii ce au leg˘ atur˘ a cu prelucrarea informat¸iilor asociate unui proces:
• long p(Process a); - ˆıntoarce prioritatea unui proces; • void execute(int id); - simuleaz˘a execut¸ia unui proces pentru o anumit˘a perioad˘a de timp.
Listing 7.2: task.h
∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ Task .h ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ /
/ #ifndef TASK #define TASK
#include #include < s t d l i b . h> #include t y pe de f s t r u c t p r o c e s s i nt i d ; long p r i o r i t y ; Proces s ;
{
}
long p ( P r o c e s s a ) ; void exe cute ( i nt i d ) ;
159
#endif
ˆIn fi¸sierul heap.h se define¸ste o coad˘ a de priorit˘ a¸ti (PriorityQueue ) implementat˘ a sub forma unui heap :
• Process* deleteMin(PriorityQueue*); - ˆıntoarce procesul de prioritate minim˘a avˆand grij˘a s˘a-l ¸stearg˘ a din coada de priorit˘ a¸ti;
• void makeNull(PriorityQueue*); - gole¸ste (videaz˘a) o coad˘a de priorit˘a¸ti; • void insertTask(PriorityQueue*, Process); - adaug˘a un nou proces ˆın coada de priorit˘a¸ti.
Coada de priorit˘ a¸t i se comport˘ a ca un container de obiecte . Listing 7.3: heap.h
∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ Heap .h ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ /
/ #ifndef HEAP #define HEAP
#include #include ” t a s k . h ” #define MAX SIZE 10 0 /
∗∗ ∗ Definitia ∗/
s t r u c t u r i i P ri or it yQ ue ue
{
t y pe de f s t r u c t p r i o r i t y q u e u e Pro ces s elemen ts [MAX SIZE+1]; i nt l a s t ; PriorityQueue ;
}
∗
∗
P r o c e s s deleteMi n ( Prior ityQue ue ) ; void makeNull( Prior ityQueu e ) ; void i n s e r t T a s k ( P r i o r i ty Q u e u e , P r o c e ss ) ;
∗
∗
#endif
Implementarea funct¸iilor declarate ˆın fi¸sierul header heap.h, se realizeaz˘a ˆın cadrul fi¸sierului heap.c:
• Process* deleteMin(PriorityQueue*); - ˆıntoarce procesul de prioritate minim˘a. Se mut˘a
elementul de pe ultima pozit¸ie pe prima pozit¸ie ¸si se reorganizeaz˘ a structura de date de tip heap (prin reorganizare ˆınt¸elegem rearanjarea elementelor cu un num˘ ar minim de operat¸ii astfel ˆıncˆ at s˘a se p˘astreze proprietatea de heap ).
• void makeNull(PriorityQueue*); - videaz˘a structura de date de tip heap. • void insertTask(PriorityQueue*, Process); - adaug˘a un nou proces ˆın heap la sfˆar¸sit, ¸si caut˘ a pozit¸ia corespunz˘ atoare acestui proces nou astfel ˆıncˆ at proprietatea de heap s˘ a fie p˘astrat˘a.
Fi¸sierul manager.c cont¸ine funct¸ii ce implementeaz˘ a comportamentul planificatorului de procese. 160
a un proces cu identificatorul p ¸si prioritatea egal˘ a cu momentul void init(int p) - creaz˘ de timp ˆın care a fost creat, pe care ˆıl insereaz˘ a ˆın coada de priorit˘ a¸ti. a procesul aflat ˆın vˆ arful cozii, ˆıi d˘a convoid select(void) - la un moment dat, selecteaz˘ trolul procesorului pentru o perioad˘a de timp, actualizeaz˘ a prioritatea acestuia ¸si-l insereaz˘ a ˆın coada de priorit˘ a¸ti. Dac˘a dorim ca managementul cozii de priorit˘a¸i t s˘a fie realizat prin intermediul unei liste ˆın locul structurii de tip heap, este suficient s˘ a ˆınlocuim linia #include "heap.h" cu linia #include "list.h".
7.4
Exercit¸ii
1. S˘a se implementeze structura de date abstract˘ a coad˘ a cu prioritate cu ajutorul: a) unui vector ordonat, b) unei liste simplu ˆınl˘ ant¸uit˘ a, ¸si s˘a se elaboreze algoritmii pentru operat¸iile Insert ¸si Extract. Analizat¸i ˆın aceste cazuri complexitatea operat¸iilor. 2. S˘a se descrie algoritmi avˆ and complexitate logaritmic˘ a ce implementeaz˘ a urm˘atoarele operat¸ii efectuate asupra unei cozi cu prioritate.
• DecreaseKey(H, k, x) - valoarea elementului k din heap-ul H se mic¸soreaz˘a (h ← min {h , x}), operat¸ia fiind, eventual, urmat˘ a de restaurarea heap-ului. • IncreaseKey(H, k, x) - valoarea elementului k din heap-ul H este m˘arit˘a (h ← max {h , x}), operat¸ia fiind urmat˘ a de restaurarea heap-ului. k
k
k
k
Coada cu prioritate este implementat˘ a cu ajutorul unui heap. 3. Implementare urm˘atoare a algoritmului Heapsort este foarte scurt˘ a ¸si rapid˘a, avˆand avantajul c˘ a nu utilizeaz˘ a un vector suplimentar pentru ordonare2 : Listing 7.4: heapsortv2.c void h e a p s o r t ( in t a r r [ ] , unsigned int N) in t t ; unsigned int n = N, p a re n t = N/ 2 , i nd ex , c h i l d ;
{
fo r ( ; ; ) i f ( parent > 0 ) t = arr[ par ent ] ; else n ; i f (n == 0 ) return ; t = arr [n ] ; arr [n ] = arr [ 0 ] ;
{
}
−−
{
−−
{
} index = parent ; 2
http://en.wikibooks.org/wiki/Algorithm_Implementation/Sorting/Heapsort
161
∗
child = index 2 + 1; while ( c h i l d < n ) i f ( ( c h i l d + 1 < n ) && child++;
{
( a r r [ c h i l d + 1 ] > arr [ chil d ]) )
{
} i f ( ar r [ ch il d ] > t ) arr [ index ] = arr [ chi ld ] ; i n de x = c h i l d ; child = index 2 + 1; else break ;
{
}
} }
{
∗
arr [ index ] = t ;
}
}
Ordonat¸i cresc˘ ator urm˘atoarea secvent¸˘a de numere, pe baza funct¸iei anterioare, utilizˆand numai o coal˘ a de hˆartie ¸si un creion: 5, 100, 25, 20, 10, 5, 7, 80, 90, 1. 4. S˘a se elaboreze un algoritm de timp O(n lg k) pentru a interclasa k liste ordonate, unde n este num˘arul total de elemente din listele de intrare.
·
5. Adriana este o mare colect¸ionar˘a de timbre. ˆIn fiecare zi se duce la magazinul de pe strada ei pentru a-¸si m˘ ari colect¸ia. ˆIntr-o zi, vˆanz˘atorul (nimeni altul decˆ at Balaurul Arhirel) s-a gˆandit s˘a-i fac˘a o surpriz˘ a. A scos dintr-un dulap vechi ni¸ste timbre foarte valoroase pe care erau scrise cu fir de aur ¸si de argint numere naturale. S¸tiind c˘ a fetit¸a nu are bani prea mult¸i, Balaurul i-a spus urm˘atoarele: ”Eu pot s˘ a ˆımpart timbrele ˆın m intervale de forma [1, . . . , mi ]. Tu pot ¸i s˘ a iei din orice interval o singur˘ a subsecvent ¸˘ a de maxim k elemente. Desigur, dac˘ a ai ales o subsecvent ¸˘ a din intervalul i vei pl˘ ati o anumit˘ a sum˘ a . . . ” Adriana s-a gˆ andit c˘ a ar fi frumos s˘ a-¸si numeroteze toate cele n pagini ale clasorului ei cu astfel de timbre. Fiind ¸si o fetit ¸˘a pofticioas˘ a ¸si-a zis: ”Tare a¸s vrea s˘ a m˘ anˆ anc o ˆınghet ¸at˘ a din banii pe care ˆıi am la mine, dar nu ¸stiu dac˘ a o s˘ a-mi ajung˘ a s˘ a pl˘ atesc timbrele. Cum s˘ a fac? ” Fiind cunoscute cele m intervale, precum ¸si costurile acestora, ajutat ¸i-o pe Adriana s˘a cumpere timbrele necesare numerot˘ arii clasorului, pl˘atind o sum˘ a cˆat mai mic˘a.
Date de intrare Pe prima linie a fi¸sierului timbre.in se afl˘ a n m k. n reprezint˘ a num˘arul de pagini ale clasorului, m reprezint˘ a num˘arul de intervale, iar k lungimea maxim˘a a unei subsecvent¸e. Pe urm˘ atoarele m linii se afl˘a dou˘ a numere separate printr-un spat¸iu, mi ci , unde mi reprezint˘ a marginea superioar˘ a a intervalului i, iar c i costul acestuia.
Date de ie¸sire Pe prima linie a fi¸sierului timbre.out se va afla Smin, reprezentˆ and suma minim˘a pe care trebuie s˘ a o pl˘ateasc˘a Adriana pentru a cump˘ ara timbrele necesare numerot˘ arii clasorului.
Restrict¸ii: 0 < N < 1001, 0 < M < 10001, 0 < K < 1001, 0 < mi < 100000, 0 < ci < 10000.
162
Pentru a numerota toate cele n pagini ale clasorului, Adriana are nevoie de timbre cu numerele de la 1 la n.
Exemplu timbre.in 4 3 2 5 3 2 1 6 2
timbre.out 3
{ }
{ }
Lu˘am subsecvent¸a 1, 2 din al doilea interval ¸si subsecvent¸a 3, 4 din al treilea interval. Obt¸inem astfel costul minim 3. (InfoArena, Timbre)
6. Paftenie barbarul a fost capturat de c˘ atre du¸smanii s˘ ai cei mai temut¸i ¸si aruncat ˆıntr-o temnit¸a˘. Temnit¸a este, de fapt, un grid de dimensiune R C . ˆIn anumite celule exist˘a dragoni, unele sunt ocupate de peret¸i, iar altele sunt libere. Paftenie trebuie s˘a ias˘a din temnit¸a˘ mergˆand numai prin celule libere (o celul˘ a are maxim 4 vecini), ¸si asta stˆ and cˆat mai departe de fioro¸sii dragoni ale c˘ aror fl˘ac˘ ari ˆıi pot deteriora vestimentat¸ia (astfel ˆıncˆat valoarea minim˘ a dintre distant¸ele pˆ an˘a la cel mai apropiat dragon din fiecare din celulele traseului s˘ au s˘a fie maxim).
×
S˘a se determine un traseu al lui Paftenie pentru a ie¸si din temnit ¸˘ a astfel ˆıncˆat distant¸a minim˘a pˆan˘a la cel mai apropiat dragon din fiecare dintre celulele traseului s˘ au s˘a fie maxim˘a.
Date de intrare Pe prima linie a fi¸sierului de intrare barbar.in sunt date dou˘ a numere ˆıntregi R ¸si C , reprezentˆ and num˘ arul liniilor, respectiv al coloanelor temnit¸ ei. Pe urm˘ atoarele R linii se afl˘a cˆate C caractere, neseparate prin spat ¸ii, cu urm˘atoarele semnificat¸ ii: ’.’ celul˘a liber˘a, ’ ’ perete, ’D’ dragon, ’I’ punctul de plecare al lui Paftenie, ’O’ ie¸sirea din temnit¸a˘.
∗
Date de ie¸sire Fi¸sierul de ie¸sire barbar.out va cont¸ine pe prima linie un singur num˘ ar, reprezentˆ and valoarea maxim˘ a pentru minima dintre distant¸ele pˆ an˘a la cel mai apropiat dragon din ˆ fiecare din celulele traseului. In caz c˘ a nu exist˘a solut¸ie se va afi¸sa valoarea 1.
−
≤ R, C ≤ 1.000.
Restrict¸ii: 1
Se garanteaz˘ a c˘a ˆın temnit¸a˘ exist˘a cel put¸in un dragon. Exemplu Pentru datele de intrare 10 10 .......... .I....D... .......... ..D...D... .*........ D*........ *...D..... ..****.... ...O...... ..........
163
o solut¸ie posibil˘a este urm˘ atoarea, unde valoarea maxim˘ a dintre distant¸ele minime este 2: .......... .Iooo.D... ....o..... ..D.o.D... .*..oo.... D*...ooooo *...D....o ..****...o ...Ooooooo .......... (preONI 2005, Barbar)
7. Se d˘a un ¸sir de numere ˆıntregi ¸si dou˘ a numere x ¸si y (x, y N). S˘ a se determine subsecvent¸a de sum˘ a maxim˘a a c˘ arei lungime se afl˘a ˆın intervalul [x, y].
∈
164
Capitolul 8 Distant¸e ˆın grafuri Ret¸eaua de drumuri europene, nat¸ionale ¸si judet¸ene dintr-o ¸tar˘a, ret¸eaua feroviar˘ a reprezentˆ and infrastructura c˘ ailor ferate absolut necesar˘ a pentru realizarea transportului de m˘ arfuri ¸si c˘al˘atori cu trenul, ret¸eaua de fibr˘ a optic˘ a folosit˘a pentru traficul de date ˆın Internet, ret ¸eaua de transport a energiei electrice folosit˘ a pentru alimentarea cu energie electric˘ a a consumatorilor casnici ¸si a celor industriali, ret¸eaua de canalizare dintr-un ora¸s folosit˘ a pentru evacuarea de¸seurilor, ret¸eaua CAT V a unui operator de televiziune prin cablu, ret ¸eaua de linii de autobuz ce face parte din sistemul public de transport al unui ora¸s sunt exemple tipice de grafuri cu care interact¸ion˘am ˆın viat¸a de zi cu zi. Putem spune c˘ a ret¸elele de transport ¸si cele de comunicat ¸ii de date ne influent¸eaz˘ a ˆın mod direct modul de viat¸a˘, devenind elemente indispensabile ale omului modern, ¸si astfel, problemele referitoare la studiul c˘ ailor de comunicat¸ie, drumurilor, conexiunilor, cap˘ at˘a 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 aflarea r˘ aspunsului la multe astfel de ˆıntreb˘ari. ¸, ciclu , circuit au fost prezentate ˆın capitolele anterioare, Definit¸iile pentru drum , lant ¸ este determinat˘ ˆımpreun˘a cu multe alte concepte teoretice. Lungimea unui lant a de num˘ arul muchiilor sale. ˆIn mod analog, lungimea unui drum este egal˘ a cu num˘arul arcelor sale. ˆIntr-un graf G se introduce o funct¸ie de cost ce asocieaz˘ a o valoare real˘ a fiec˘ arei muchii sau arc, dup˘ a caz: c : E R sau c : V V R
−→
× −→
Not˘am costul unui drum ca fiind suma costurilor arcelor componente. Astfel pentru un drum D = [v0, v1 , . . . , vm ], v i V , de lungime m, costul acestuia va fi dat de c˘ atre urm˘ atoarea formul˘ a:
∈
m−1
c(D) =
c([vi , vi+1]).
(8.1)
i=0
Not˘am cu s,t mult¸imea drumurilor dintre nodul surs˘ a x s ¸si nodul destinat¸ie x t , x s , xt V . Pentru dou˘ a noduri oarecare xs ¸si xt , se dore¸ste s˘a se determine un drum δ s,t de la xs la xt a c˘ arui valoare s˘ a fie optim˘ a (minim˘a sau maxim˘a):
M
opt c(δ s,t )=
min c(δ s,t ).
δs,t ∈Ms,t
165
∈
(8.2)
8.1
Drumul minim de la un vˆ arf la celelalte vˆ arfuri
8.1.1
Algoritmul lui Moore
Fie G = (V, E ) un graf orientat unde V = x1 , x2 , . . . , xn este mult¸imea nodurilor ¸si E este mult¸imea arcelor (E V V ). Pentru reprezentarea grafului vom utiliza, ca metod˘ a de reprezentare, listele de adiacent¸a˘ (liste de vecini). Fie u un vˆarf al grafului numit surs˘ am pentru fiecare vˆ arf w a . Dorim s˘a determin˘ V, w = u, dac˘a exist˘a, un drum de lungime minim˘a de la u la w. Lungimea unui drum se define¸ste ca fiind num˘ arul arcelor ce ˆıl compun. Algoritmul lui Moore (a se vedea algoritmul 59) se bazeaz˘ a pe structura algoritmului de parcurgere ˆın l˘ a¸time al unui graf (Breadth First Search - a se vedea algoritmul 16). ˆIn lucrarea [98], Moore ˆı¸si prezint˘ a algoritmul astfel:
{
⊆ ×
}
∈
Write 0 on the source. Then look at all the neighbors of the source and write 1 on them. Then look at all the neighbors of nodes with 1 on them and write 2 on them. And so on. Vom utiliza un vector de distant¸e D , unde d k reprezint˘ a costul drumului minim de la vˆarful u la vˆarful xk , iar tatak p˘astreaz˘a vˆ arful anterior lui xk , pe drumul de cost minim de la nodul u la nodul xk .
Algoritm 59 Algoritmul lui Moore (prima versiune) 1: procedure Moore1(k, n, V ecin; D,Tata) Input:
Output:
k - nodul surs˘ a n - num˘ arul de noduri din graf V ecin - vector ce cont¸ine capetele listelor de vecini D - vectorul distant¸elor de la nodul surs˘a la celelalte noduri Tata - vector ce cont¸ine pentru fiecare nod k, predecesorul acestuia pe drumul de cost minim de la nodul surs˘a la nodul k 1, n do +
← ← ∞
for i 2: 3: di 4: end for 5: dk 0 ⊲ distant¸a de la un nod la el ˆınsu¸si este 0 Q k ⊲ inserarea nodului curent k ˆın coad˘ a 6: 7: while (Q = ) do ⊲ cˆat timp coada nu este vid˘a 8: Q k ⊲ extrage nodul curent din coad˘a 9: v vecink ⊲ se pleac˘a cu primul vecin din lista de vecini 10: while (v = NULL) do ⊲ v.nodeIndex este indicele nodului vecin 11: if (dv.nodeIndex = + ) then 12: dv.nodeIndex d k + 1, tatav.nodeIndex k 13: Q v.nodeIndex ⊲ inserarea nodului v.nodeIndex ˆın coada 14: end if v v.next ⊲ se trece la urm˘atorul vecin 15: end while 16: 17: end while 18: end procedure
← ⇐
⇒ ←
∅
⇐
←
∞
←
←
166
Exemplul 8.1 Fie un graf orientat reprezentat prin intermediul urm˘ atoarelor liste cu vecini: 1: 2: 3: 4: 5: 6:
(2, 4) (3, 6) (2) (1, 5) (4, 6, 9) (7, 8)
7: (8) 8: (6, 7) 9: () 10: (11) 11: (10)
ˆ In urma aplic˘ arii algoritmului lui Moore pentru acest graf ¸si pentru nodul 1 drept surs˘ a, se obt ¸in urm˘ atoarele valori pentru vectorii D ¸si Tata : ˆ In urma aplic˘ arii algoritmului lui Moore pentru acest graf ¸si avˆ and nodul 1 drept surs˘ a, se obt ¸in urm˘ atoarele valori pentru vectorii d ¸si tata: D Tata
1 0
2 1 1
3 2 2
4 1 1
5 2 4
6 2 2
7 3 6
8 3 6
9 10 3 5
11
∞ ∞
Urm˘ arind valorile calculate, putem trage concluzia c˘ a drumul de lungime minim˘ a de la nodul 1 la nodul 9, are costul 3 ¸si este compus din nodurile [1, 4, 5, 9]. Algoritmul 60 calculeaz˘ a lungimea drumurilor minime de la un nod surs˘ a la toate nodurile accesibile din acesta, ˆın cazul ˆın care lungimea unui drum se define¸ste ca fiind suma costurilor asociate arcelor ce ˆıl compun .
Algoritm 60 Algoritm lui Moore (a doua versiune) 1: procedure Moore2(k,n,C ; D,Tata)
k - nodul surs˘ a Input: n - num˘ arul de noduri din graf C - matricea costurilor 1, n do 2: for i 3: di +
← ← ∞
4: end for 5: dk 0 Q k 6: 7: while (Q = ) do 8: Q k 9: for fiecare vecin (v = x i ) al lui x k do 10: if (dk + ck,i < di ) then 11: di d k + ck,i , tatai k Q i 12: 13: end if 14: end for 15: end while 16: end procedure
← ⇐
⇒
∅
← ⇐
8.1.2
⊲ distant¸a de la un nod la el ˆınsu¸si este 0 ⊲ inserarea nodului curent k ˆın coad˘ a ⊲ cˆat timp coada nu este vid˘a ⊲ extrage nodul curent din coad˘a
←
⊲ inserarea nodului i ˆın coada
Algoritmul lui Dijkstra
S˘a consider˘ am G = (V, E ) un graf orientat unde V = 1, 2, . . . , n este mult¸imea nodurilor iar E este mult¸imea arcelor (E V V ). Pentru reprezentarea grafului vom utiliza matricea
{
⊆ ×
167
}
costurilor C:
∞
, dac˘ a i = j , dac˘ a (i, j) / E, i = j d > 0 , dac˘ a (i, j) E 0
ci,j =
∈ ∈
Fie u un vˆarf al grafului numit surs˘ am pentru fiecare vˆ arf w a . Dorim s˘a determin˘ V, w = u, dac˘a exist˘a, un drum de lungime minim˘a de la u la w. Lungimea unui drum se define¸ste ca fiind suma costurilor asociate arcelor ce ˆıl compun . arf intermediar orice vˆarf u Fie p = [x1 , x2 , . . . , xk ] un drum elementar. Se nume¸ste vˆ arful intermediar este diferit de extremit˘ x2, . . . , xk−1 unde u = x 1 , u = x k (vˆ a¸tile drumului p). a de elaborare a algoritmilor Greedy , Algoritmul lui Dijkstra [38] utilizeaz˘a metoda general˘ drumurile de lungime minim˘a fiind generate ˆın ordinea cresc˘ atoare a lungimii lor. Vom nota cu S mult¸imea nodurilor grafului G pentru care se cunoa¸ste (s-a calculat) drumul de lungime minim˘a de la surs˘a la un astfel de nod. La ˆınceput, mult ¸imea S este compus˘ a doar din nodul surs˘ a. La fiecare pas, se adaug˘ a la mult¸imea S un nod xk V S cu proprietatea c˘ a, drumul de la surs˘ a la acest nod xk , este cel mai mic dintre toate drumurile posibile de la surs˘a la noduri din mult¸imea V S , cu proprietatea c˘ a, un astfel de drum are drept noduri intermediare numai elemente din mult¸imea S. Vom utiliza un tablou D, ce va p˘ astra pentru fiecare nod xk , lungimea drumului cel mai scurt de la surs˘ a ce trece numai prin noduri din mult¸imea S (a se vedea algoritmul 61).
{
}
∈
∈
∈ \
\
Algoritm 61 Algoritmul lui Dijkstra (schema general˘ a) 1: procedure Dijkstra1(u,n,C ) S u 2: 1, n do 3: for i 4: di c u,i 5: end for 6: for i 1, n 1 do k min dk xk V S 7: S S xk 8: 9: for each x j V S do 10: d j min d j , dk + ck,j 11: end for 12: end for 13: end procedure
← { } ← ← ← − ← { | ∈ \ } ← ∪ { } ∈ \ ← { }
Dup˘a ce am identificat un astfel de nod x k , mult¸imea S se modific˘a astfel: S = S xk . Este posibil ca lungimea unui drum de la surs˘a la un nod x j V S , ce are drept noduri intermediare noduri din mult¸imea S, s˘a se modifice datorit˘ a faptului c˘ a, mai ˆınainte, nodul xk nu fusese luat ˆın considerare, deoarece nu apart ¸inea mult¸imii S. Astfel, se poate ca un drum de la surs˘ a la nodul x j , ce trece prin nodul x k , s˘a fie mai mic decˆ at drumul anterior de la surs˘a la nodul x j , ce nu avea printre nodurile sale intermediare vˆ arful xk :
∈ \
dk + ck,j < d j . Vom utiliza trei vectori (a se vedea algoritmul 62):
• Vizitat - vector caracteristic pentru mult¸imea S, unde: 1 , dac˘ a nodul x ∈ S vizitat = 0 , dac˘ a nodul x ∈ V \ S j
j j
168
∪{ }
Algoritm 62 Algoritmul lui Dijkstra 1: function DistantaMinima(n,V iz izit ita at,D t,D)) 2: min 3: for j 1,, n do 1 ((vizitat vizitat j = 0) (d j < min)) min )) then 4: if (( min d j 5: 6: j0 j 7: end if 8: end for min = = ) then 9: if (min return 1 10: 11: else 12: return j 0 13: end if 14: end function
←∞ ←
∧
← ← ∞ −
15: procedure Dijkstra2(u,n,C ) 16: vizitatu 1 0, tata 17: du 0, tatau 0 1,, n do 1 18: for i if (i = u u)) then 19: 20: vizitati 0 21: di c u,i , tatai u 22: end if 23: end for 24: while ( (true true)) do 25: k D Distan istantaM taMinim inima a(n, vizi vizita tat, t, d) 26: if (k < 0) then ⊲ forteaza iesirea dintr-o instructiune de ciclare 27: break else 28: 29: vizitatk 1 30: for i 1 1,, n do 31: if (( ((vizitat vizitati = 0) (dk + ck,i < di )) then tatai k 32: di d k + ck,i 33: 34: end if 35: end for 36: end if 37: end while 38: end procedure
←
←
←
←
← ←
←
←
←
←
← ←
∧
¸ elor de la nodul u la cel celela elalte lte nodur nodurii ale grafului. grafului. • D - vectorul distant¸elor
A¸sa sa cum am precizat anteri anterior, or, d j p˘astreaz˘ astreaz˘a costul drumului de lungime minim˘ a de la nodul surs˘ au la nodul x j , drum ce are drept noduri intermediare numai elemente din mult¸imea ¸imea S. ˆIn momentul init¸ial ¸ial vom avea d j = c u,j . Not˘am am cu x cu x k nodul ales la un moment dat. Atunci costul drumului de lungime minim˘a d j de la nodul surs˘a u la un nod x j , se modific˘a numai dac˘a dk + c k,j < d j , fiind actualizat actualiz at astfel: d j = d k + ck,j .
• tata
j
- p˘astreaz˘ astreaz˘a pentru fiecare nod x j , nodul anterior xk (xk 169
∈ S ) pe drumul de cost
minim de la u la x j . La ˆınc ıncepu eput, t, tata j =
0 , daca˘ nodul j = u sau cu,j = u , ˆın rest r est ( j = u u ¸ ¸si si cu,j = ).
∞
∞
Un element al acestui vector se poate modifica atunci cˆ and se modific˘ a valoarea elementului d j as astf tfel el ˆın ıncˆ cˆat d at d j = d k + ck,j , caz ˆın car caree tata j = k k..
Observat¸ia ¸ia 8.2 Algorit Algoritmul mul lui Dijkstr Dijkstraa pr prezint˘ ezint˘ a ase asem˘ m˘ an˘ arii cu al ar algo gori ritm tmul ul de c˘ auta au tare re ˆın l˘ at ¸ime, vect vectorul orul Tata p˘ astrˆ and la sfˆ ar¸situl situl pro procesulu cesuluii de calcu calcull al distant ¸elor un arbore al drumurilor minime ce are drept r˘ ad˘ acin˘ a nodul surs˘ a. Demonstrarea corectitudinii algoritmului
Fig. 8.1: O cale special˘a mai scurt˘a
ˆIn momentul ˆın care alegem nodul no dul x xk V S avˆ and proprietatea c˘ and a drumul de la surs˘a la acel nod x nod xk este cel mai mic dintre toate drumurile drumurile posibile de la surs˘ a la noduri din mult¸imea ¸imea V S , ce trec numai prin noduri din mult¸imea ¸imea S , acel drum este cel mai mic drum de la surs˘a la xk dintre dintre toate drumurile posibile. posibile. Den Denumim umim drum special un drum de la surs˘a la un nod x nod x k ce are drept noduri intermediare numai elemente din mult¸imea ¸imea S . S˘a presupunem prin reducere la absurd, c˘ a exist˘a un drum de lungime mai mic˘a de la nodul surs˘ a la nodul x nodul xk , ce nu are toate noduril no durilee intermediare intermediare din mult ¸imea S ¸imea S . Fie x Fie x j V S , primul nod ce nu apart¸ine ¸ine mult¸imii S ¸imii S pe pe drumul de la nodul surs˘ a la nodul x nodul xk . Atunci, drumul de la nodul surs˘ a u u la la nodul x nodul xk se compune dintr-un drum de la u la u la la x x j , ¸si si un dru drum m de d e la l a x j la xk . Datorit˘ a modului de alegere al lui x lui x j , drumul de la u la u la la x x j are drept noduri intermediare numai elemente din mult¸imea ¸imea S (x j este primul nod pe drumul de la nodul surs˘ a la nodul xk , care nu apart¸ine ¸ine mult¸imii ¸imii S ), ), deci este un drum special ¸ ¸si si are o lungime mai mic˘ a decˆ at at nodul ul xk . Pri Prin n urmare, am identifi identificat cat un alt drum special de drumul special de la surs˘a la nod lungime mai mic˘a, a, ceea ce contrazice modul de alegere al nodului xk . Trebuie s˘ a demonstr˘ am c˘a, am a, ˆın ori orice ce mom moment, ent, d astreaz˘a lungimea celui mai scurt drum d k p˘astreaz˘ ˆ special de de la nodul surs˘a u la nodul xk V S . In momentu momentull ˆın ın care no nodul dul xk e est stee ad ad˘˘auga au gatt mult¸imii ¸imii S , avem grij˘a s˘a verific˘ am dac˘ am a nu exist˘a un drum special de de la nodul u la un nod x j V S care care s˘ a aib˘a o lungime mai mic˘a. a . S˘a presupunem c˘ a, pentru un nod x a, nod x j fixat, exist˘a un nod w nod w S S a ast stfe fell ˆın ıncˆ cˆat at drumul special u xk xk w (w, x j ) s˘ a aib˘a o lungime mai mic˘a (a se vedea vedea figura 8.1). Deoa Deoarece rece nodul w a fost ad˘augat augat mult¸imii ¸imii S ˆ ˆınainte ına inteaa no nodulu duluii xk , avem dw d k .
∈ \ \
\
∈ \
∈ \ \
∈ \ \
∈
∪
≤
170
∪
in t c [MAXN] [MAXN] ; / n od ul f a t a de c a re s e c a l c u l e a z a d ru mu ri le de l un gi me minima in t u ; / tata [ k ] pa r i n te l e v a r f ul u i k i n a r bo r el e r e z u lt a t / in t t a t a [MAXN] ; / d is ta nt a de l a u l a f i e c a r e nod / in t d [MAXN] ;
∗ ∗ ∗
−
∗/
∗
∗
r e a d I n p u t (&n , c , &u ) ; d i j k st r a ( u , n , c , d , t at a ) ; p r i n tS o l u ti o n ( u , n , d , t at a ) ;
} Utilizarea structurii coad˘ a cu prioritate ˆın algoritmul lui Dijkstra Complexitatea-timp a algoritmului lui Dijkstra poate fi ˆımbun˘ at˘a¸tit˘a prin utilizarea structurii a cu prioritate (a se vedea algoritmul 63). de date coad˘ a cu prioritate) Algoritm 63 Algoritmul lui Dijkstra (varianta ce folose¸ste o coad˘ 1: procedure Dijkstra3(u,n,C ) 2: for i 1, n do 3: di tatai NULL 4: 5: call Insert(Q,i,di ) ⊲ insereaz˘a ˆın coad˘a elementul x i cu prioritatea di 6: end for 7: du 0 ⊲ actualizeaz˘a prioritatea vˆarfului u la d u 8: call DecreaseKey(Q,u,du ) S 9: 10: for i 1, n 1 do 11: k DeleteMin(Q) ⊲ ¸sterge elementul de prioritate minim˘a din coad˘a 12: S S xk at (xk , x j ) E do 13: for fiecare x j V S, astfel ˆıncˆ if (d j > d k + ck,j ) then 14: 15: d j dk + ck,j 16: tata j k 17: call DecreaseKey(Q,j,d j ) ⊲ actualizeaz˘a prioritatea vˆarfului x j la d j 18: end if end for 19: 20: end for 21: end procedure
← ← ∞ ←
← ← ∅ ← − ← ← ∪ { }
∈ \
∈
← ←
Analizˆand algoritmul 63, obt¸inem c˘ a timpul de lucru al acestuia depinde de formula: T (n, m) = O(n T Insert + n T DeleteMin + m T DecreaseKey)
·
·
·
(8.4)
unde n reprezint˘ a num˘arul de noduri ale grafului G iar m reprezint˘ a num˘arul de muchii ale aceluia¸si graf. Cele trei operat¸ii ˆıntˆalnite ˆın cadrul algoritmlui sunt (a se vedea ¸si capitolul 7): 1. DeleteMin(Q) - ¸sterge nodul ce cont¸ine cheia de valoare minim˘ a ¸si reorganizeaz˘ a structura prin refacerea propriet˘ a¸t ii de coad˘ a cu prioritate . 174
2. Insert(Q,p,x) - insereaz˘ a nodul p ˆın coada cu prioritate Q, unde x reprezint˘ a prioritatea nodului p. 3. DecreaseKey(Q,p,v) - modific˘a valoarea priorit˘ a¸t ii nodului p din coada cu prioritate Q, atribuindu-i valoarea v ¸si reorganizeaz˘ a aceast˘ a structur˘ a. Astfel, complexitatea algoritmului pentru diferite implement˘ ari ale cozii cu prioritate Q va fi [30]: 1. List˘a liniar˘a dublu ˆınl˘ant¸uit˘a neordonat˘ a: T (n, m) = O(n 1 + n n + m 1) = O(n2 )
(8.5)
T (n, m) = O(n 1 + n n + m 1) = O(n2 )
(8.6)
·
2. Arbore 2
− 3:
·
·
·
·
·
3. Heap-uri Fibonacci: T (n, m) = O(n 1 + n n + m 1) = O(n2 ).
·
8.2
·
·
(8.7)
Drumuri minime ˆıntre toate perechile de vˆ arfuri
Fie G = (V, E ) un graf orientat, unde V = x1 , x2, . . . , xn reprezint˘ a mult¸imea nodurilor ¸si E reprezint˘ a mult¸imea arcelor (E V V ). Fiec˘arui arc din E i se asociaz˘a o valoare pozitiv˘a ci,j (ci,j 0) ce reprezint˘ a lungimea arcului. Se dore¸ste calcularea drumurilor de lungime minim˘a pentru oricare pereche de noduri xi , x j . Pentru a determina drumurile minime ˆıntre toate perechile de vˆ arfuri, putem alege un algoritm ce determin˘ a drumurile minime de surs˘a unic˘a (drumurile minime de la acel vˆarf la toate celelalte), ¸si pe care s˘ a-l aplic˘am pentru fiecare vˆ arf al grafului. Un astfel de algoritm este algoritmul lui Dijkstra , algoritm ce a cunoscut multiple ˆımbun˘ at˘a¸t iri fat¸a˘ de varianta orginal˘ a, ce se bazeaz˘ a pe utilizarea unor structuri de date avansate (de exemplu heap-uri Fibonacci), menite s˘ a-i optimizeze timpul de execut¸ie. Astfel, complexitateatimp ˆın cazul cel mai defavorabil pentru calculul celor mai scurte drumuri, pornind dintr-un vˆarf al unui graf cu n noduri ¸si m arce, este O(m + n log n). Dac˘ a aplic˘am acest algoritm pentru toate cele n vˆarfuri vom obt¸ine un timp de lucru de O(mn + n2 log n). Algoritmul 64 prezint˘ a o variant˘ a a algoritmului lui Dijkstra ˆın care drumurile de lungime minim˘a ˆıntre oricare dou˘ a vˆarfuri sunt calculate incremental ¸si intercalat [35]. Matricea D va p˘astra pentru fiecare pereche [xi , x j ] lungimea di,j a drumului minim dintre cele explorate pˆan˘a la momentul curent. Coada de priorit˘ at¸i p˘astreaz˘a toate perechile (i, j), ordonate cresc˘ator ˆın funct¸ie de prioritatea di,j a fiec˘ areia. La fiecare pas, se extrage din coad˘ a perechea (i, j) avˆand prioritatea cea mai mic˘ a ¸si se ˆıncearc˘a extinderea drumului [xi , x j ] cu exact un arc la fiecare extremitate a drumului. Tehnica utilizat˘ a este cea a relax˘ arii, ˆıntˆalnit˘a ¸si ˆın cadrul algoritmului lui Bellman , cu scopul de a mic¸sora costul drumului de la xi la x j , prin parcurgerea tuturor arcelor ce p˘ ar˘asesc vˆ arful x j ¸si a celor ce sosesc ˆın vˆ arful xi .
⊆ ×
≥
{
}
C
8.2.1
Algoritmul lui Roy-Floyd-Warshall
Fie p = [x1 , x2 , . . . , xk ] un drum elementar. Definim un vˆ arf intermediar orice vˆarf u x2, . . . , xk−1 , u = x1 , u = x k (vˆarful intermediar este diferit de extremit˘ a¸tile drumului p).
{
}
∈
175
Algoritm 64 Algoritmul lui Dijkstra pentru toate perechile de vˆ arfuri 1: procedure DijkstraAll(n, C ; D) 2: for i 1, n do 3: for j 1, n do 4: if ((i, j) E ) then di,j c i,j 5: 6: else 7: di,j 8: end if (i, j) cu prioritatea d i,j 9: end for 10: 11: end for 12: while ( = ) do 13: (i, j) 14: for k 1, n do if (di,j + c j,k < d i,k ) then 15: 16: di,k di,j + c j,k 17: actualizeaz˘a prioritatea perechii (i, k) din 18: end if 19: end for for k 1, n do 20: 21: if (ck,i + di,j < dk,j ) then 22: dk,j c k,i + di,j actualizeaz˘a prioritatea perechii (k, j) din 23: 24: end if 25: end for 26: end while 27: end procedure
←
←
⊲ sau (ci,j > 0)
∈ ← ← ∞
∧ (ci,j < ∞)
C⇐
C∅ C⇒ ←
←
C la d i,k
←
←
C la d k,j
U Fie U = x1 , x2 , . . . , xk o mult¸ime de noduri. Not˘ am cu ¸imea drumurilor de i,j mult la xi la x j ce au drept noduri intermediare elemente din mult¸imea U . Fie p drumul de cost U minim de la x i la x j ( p a i,j ). Un astfel de drum este elementar deoarece am presupus c˘ nu exist˘a cicluri al c˘aror cost s˘ a fie negativ. (k) Not˘am di,j costul drumului minim de la xi la x j ce are noduri intermediare numai din mult¸imea x1 , x2 , . . . , xk . Dac˘a xk / p (xk nu apart¸ine drumului p) atunci drumul de cost minim de la x i la x j avˆ and toate nodurile intermediare din mult¸imea x1 , x2, . . . , xk−1 va fi ¸si drum de cost minim de la xi la x j cu toate nodurile intermediare din mult¸imea x1, x2 , . . . , xk : (k) (k −1) di,j = d i,j (8.8)
{
} ∈M
{
{
}
M
∈
{
}
}
Dac˘a x k este un vˆarf intermediar al drumului p, fie p 1 ¸si p2 subdrumuri ale lui p, p = p 1 p2 ( p1 drumul de la xi la xk avˆ and nodurile intermediare din mult¸imea x1 , x2 , . . . , xk ¸si p2 drumul de la xk la x j avˆ and nodurile intermediare din mult¸imea x1, x2 , . . . , xk ). Deoarece p1 ¸si p2 sunt drumuri de cost minim ¸si xk / x1, x2 , . . . , xk−1 vom avea:
∈{
}
−1)
= d (k) i,k ,
(k −1)
= d k,j .
d(k i,k
{
{
}
}
⊕
(k)
dk,j Prin urmare
c( p) = c( p1 ) + c( p2 )
⇒ d 176
(k) i,j
−1)
= d(k i,k
−1)
+ d(k k,j
(8.9)
Algoritmul Roy-Floyd-Warshall va construi un ¸sir de matrici D (0), D (1), . . . , D (k) , . . . , D (n) . Pentru fiecare k, 1 k n, d (k) ¸ine lungimea drumului minim de la nodul xi la nodul i,j va cont x j ce are drept noduri intermediare numai noduri din mult¸imea x1 , x2, . . . , xk . Matricea D (0) se va init¸ializa astfel:
≤ ≤
{
d(0) i,j
=
∞
}
0 , dac˘ a i = j + , dac˘ a (xi , x j ) / E, i = j d > 0 , dac˘ a (xi , x j ) E
∈ ∈
(8.10)
(un drum ce nu are nici un vˆ arf intermediar este determinat de costul leg˘ aturii directe dintre xi ¸si x j ). Drumul de lungime minim˘a de la xi la x j ce trece numai prin nodurile x1 , x2 , . . . , xk (k −1) fie nu cont¸ine nodul xk , caz ˆın care d(k) , fie ˆıl cont¸ine, ¸si atunci are loc relat ¸ia i,j = di,j (k) (k −1) (k−1) (k) di,j = d i,k + dk,j . Prin urmare, valoarea elementului di,j se va calcula astfel:
{
−1)
(k) (k di,j = min di,j
{
(k −1)
, di,k
−1)
+ d(k k,j
}
}
(8.11)
De¸si aceast˘ a ultim˘a relat¸ie sugereaz˘ a un algoritm recursiv, o abordare iterativ˘ a este mai eficient˘ a atˆ at ˆın ceea ce prive¸ste complexitatea timp, cˆ at ¸si din punctul de vedere al necesarului de memorie. Se observ˘ a c˘a, matricea D (k) nu mai este necesar˘a de ˆındat˘ a ce matricea (k+1) D a fost calculat˘ a. (n)
(n)
D (n) = (di,j ), di,j = δ i,j ,
∀x , x ∈ V. i
j
(8.12)
Algoritm 65 Algoritmul Floyd-Warshall 1: procedure FloydWarshall1(n, C ; D (n)) 1, n do 2: for i for j 1, n do 3: (0) 4: di,j c i,j 5: end for 6: end for 1, n do 7: for k for i 1, n do 8: 9: for j 1, n do (k) (k−1) (k−1) (k−1) 10: di,j min di,j , di,k + dk,j 11: end for 12: end for end for 13: 14: end procedure
←
←
← ← ←
← ←
{
}
ˆIn algoritmul 65 se poate renunt¸a la indicii superiori, obt¸inˆandu-se astfel o economie de spat¸iu de la Θ(n3 ) la Θ(n2) (a se vedea algoritmul 66). Algoritmul 66 ne permite s˘ a calcul˘ am distant¸a minim˘a ˆıntre oricare pereche de noduri apat¸inˆand unui graf G. Se pune problema de a determina ¸si component ¸a drumului minim (mult¸imea vˆarfurilor intermediare) dintre dou˘ a noduri, nu numai valoarea distant¸ei minime. Acest lucru se poate realiza prin intermediul unei alte matrici P (k) asociat˘ a matricii D (k) . (k) Un element P i,j va p˘astra nodul intermediar k ce conduce la cea mai mic˘ a valoare pentru 177
a) Algoritm 66 Algoritmul Floyd-Warshall (varianta optimizat˘ 1: procedure FloydWarshall2(n, C ; D) 2: for i 1, n do 3: for j 1, n do di,j c i,j 4: end for 5: 6: end for 7: for k 1, n do 8: for i 1, n do 1, n do 9: for j di,j min di,j , di,k + dk,j 10: 11: end for 12: end for 13: end for 14: end procedure
←
←
← ← ←
← ←
{
}
(k) d(k) si x j i,j conform formulei 8.11. Daca pi,j = 0 atunci cel mai scurt drum dintre nodurile xi ¸ este cel direct. (0) Valoarea elementelor matricii init¸iale P (0) este pi,j = 0. Valoarea unui element p(k) atoare, ¸tinˆand cont de i,j se va calcula conform formulei urm˘ relat¸ia 8.11: −1) (k −1) (k−1) (k −1) , dac˘ a d(k pi,j d i,k + dk,j (k) i,j pi,j = (8.13) −1) (k−1) (k −1) k , dac˘ a d(k > d + d i,j i,k k,j
≤
Se va calcula secvent¸a de matrici P (0), P (1), . . ., P (n) ˆımpreun˘a cu secvent¸a D(0) , D (1), . . ., D(n) . La final vom avea: P = P (n) , D = D (n) . Printr-un rat¸ionament analog, se poate demonstra c˘ a nu este nevoie s˘ a construim efectiv ¸sirul de matrici P (0) , P (1), . . ., P (n) . Algoritmul 67 prezint˘ a varianta extins˘ a a algoritmului 66, ˆın cadrul acestuia fiind ad˘ augat ¸si suportul pentru determinarea drumului de cost minim dintre oricare pereche de noduri a grafului.
Exemplul 8.4 Fie graful din figura 8.2. Matricea costurilor asociat˘ a acestui graf este urm˘ atorul:
C =
∞ ∞∞ 0 + 3 + +
1 0 + + +
∞
∞ ∞ ∞
+ 8 0 9 3
∞ ∞ ∞ ∞ ∞
21 + 12 0 +
+ 4 + + 0
S¸irul de matrice D k , rezultat al aplic˘ arii algoritmului Roy-Floyd-Warshall pentru acest graf, este:
178
Algoritm 67 Algoritmul Floyd-Warshall (reconstruirea drumului optim) 1: procedure FloydWarshall3(n, C ; D, P ) 2: for i 1, n do 3: for j 1, n do di,j c i,j 4: pi,j 0 5: 6: end for 7: end for 8: for k 1, n do 9: for i 1, n do for j 1, n do 10: 11: if (di,j > di,k + dk,j ) then 12: di,j d i,k + dk,j 13: pi,j k 14: end if end for 15: 16: end for 17: end for 18: end procedure 19: procedure PrintDrum(i,j,P ) 20: if ( pi,j = 0) then 21: Output (i,j) 22: else 23: call PrintDrum(i, pi,j , P ) 24: call PrintDrum( pi,j , j , P ) 25: end if 26: end procedure
←
← ← ←
←
←
←
← ←
}
{ }
∞ ∞ ∞ ∞ ∞ ∞ ∞∞ ∞∞ ∞ ∞ ∞ ∞ ∞∞ ∞∞ ∞ ∞ 0
D0
=
3
0
D2 =
D4 =
3
1 0
21
8 0 12 9 0 3
4
1 9 21 0 8 4 0 12 9 0 3
5 4 8
0 1 9 11 0 8 3 4 0 12 13 9 6 7 3
0
D1
=
0
D3 =
0
21 5 20 4 12 8 0 17 15 0
∞ ∞ ∞ ∞ ∞ ∞∞ ∞∞ ∞ ∞
D5 =
3
1 0 4
21
8 0 12 9 0 3
4
0
0 1 9 11 0 8 3 4 0 12 13 9 6 7 3
21 5 20 4 12 8 0 17 15 0
0 1 8 10 0 7 3 4 0 12 13 9 6 7 3
20 5 19 4 12 8 0 17 15 0
ˆInchiderea tranzitiv˘ a Definit¸ia 8.1 Fie G = (V, E ) un graf orientat, simplu ¸si finit, V = x1 , x2 , . . . , xn . ˆ Inchiderea tranzitiv˘ a a grafului G este un graf G+ = (V, E + ), unde E + = (xi , x j )
{
179
{
}
|
∃ un drum de la x
i
la x j ˆın G .
}
Definit¸ia 8.2 O relat ¸ie binar˘ a ρ peste o mult ¸ime A = 1, 2, . . . , n se define¸ste ca fiind o submult ¸ime a produsului cartezian A A ( ρ A A). (a, b) ρ se mai poate scrie aρb.
×
⊆ ×
{
}
∈
ˆ Definit¸ia 8.3 Inchiderea tranzitiv˘ a a relat ¸iei binare ρ este o relat ¸ie binar˘ a ρ ∗ cu proprietatea 1, . . . , n , (i, j) ρ ∗ i1 , . . . , im 1, 2, . . . , n astfel ˆıncˆ c˘ a i, j at (i1, i2 ) ρ , (i2 , i3 ) ρ, . . ., (im−1 , im ) ρ ¸si i = i 1, j = im .
∀ ∈ {
Not˘am ρn ∗
} ∈ ⇔∃ ∈ = ρ ◦ ρ ◦ . . . ρ, unde ρ
n
∈ {
}
∈
∈
reprezint˘ a compunerea relat¸iei ρ cu ea ˆıns˘a¸si de n ori.
n ori
Atunci ρ se poate defini ˆın modul urm˘ ator: ρ∗ =
ρn = ρ
n∈N
∪ (ρ ◦ ρ) ∪ . . . ∪ (ρ ◦ ρ ◦ . . . ◦ ρ) ∪ . . .
(8.14)
O relat¸ie binar˘a ρ peste mult¸imea 1, 2, . . . , n se poate reprezenta printr-o matrice R, ale c˘arei elemente sunt definite astfel:
{
}
1 , dac˘ a (i, j) ρ 0 , dac˘ a (i, j) / ρ
∈ (8.15) ∈ Pentru relat¸ia ρ = ρ ◦ ρ ˆıi corespunde matricea R × R, pentru relat¸ia ρ = ρ ◦ ρ ◦ ρ ˆıi corespunde matricea R × R × R etc. Dac˘a not˘ a m cu R matricea corespunz˘ atoare relat¸iei ρ , atunci avem R = R = R × . . . × R. Conform formulei 8.14, pentru determinarea ρ este nevoie de calculul unei serii de matrici R , R , . . . , R , unde un element r ∈ R se calculeaz˘ a dup˘ a formula: 1 , dac˘ a r = 1 sau (r = 1∧r = 1) r = (8.16) ri,j =
2
3
(k)
k
(k)
k
∗
k ori
0
1
k i,j
n
k i,j
k −1 i,j
k
k−1 i,k
k −1 k,j
0 , altfel .
ˆInchiderea tranzitiv˘ a a unei relat¸ii binare prezint˘ a multiple aplicat¸ii, ca subproblem˘a ˆın cazul unor probleme computat¸ionale, cum ar fi: construirea unui automat de parsare utilizat la realizarea unui compilator, evaluarea interog˘ arilor recursive efectuate asupra unei baze de date sau analiza elementelor accesibile ˆıntr-o ret ¸ea de tranzit¸ie asociat˘ a unui sistem paralel sau distribuit. Prima variant˘ a de a determina ˆınchiderea tranzitiv˘ a presupune atribuirea unui cost unitar (= 1) arcelor mult¸imii E , urmat˘a de aplicarea algoritmului Roy-Floyd-Warshall . Vom obt¸ine o matrice, ˆın care, di,j = + dac˘ a nu exist˘ a un drum de la xi la x j sau di,j = c < n dac˘ a exist˘a. ˆIn cadrul celei de-a doua variante de calcul (a se vedea algoritmul 68), se ˆınlocuiesc ˆın algoritmul 65 operat¸iile min cu (sau logic ) ¸si respectiv + cu (¸si logic ). Semnificat¸ia valorii unui element al matricii D (k) este urm˘ atoarea: dki,j = 1 dac˘a exist˘a un drum de la xi la x j ˆın graful G avˆand vˆarfurile intermediare numai din mult¸imea x1 , . . . , xk .
∞
∨
∧
{
d0i,j =
1 , dac˘ a i = j sau (xi , x j ) E 0 , dac˘ a i = j ¸si (xi , x j ) / E
∈
∈
}
(8.17)
Exist˘ a astfel un drum de la xi la x j ˆın graful G avˆ a nd vˆarfurile intermediare numai k din mult¸imea x1, . . . , xk (di,j = 1) dac˘ a exist˘a un drum de la xi la x j avˆ a nd vˆarfurile
{
}
180
Algoritm 68 Algoritm de calcul a ˆınchiderii tranzitive 1: procedure InchidereTranzitiva(n, C ; D) 2: for i 1, n do 3: for j 1, n do 4: if ((i = j) (ci,j = + )) then d0i,j 1 5: 6: else 7: d0i,j 0 8: end if 9: end for end for 10: 11: for k 1, n do 12: for i 1, n do 13: for j 1, n do −1 k di,j dki,j−1 (dki,k−1 dkk,j ) 14: end for 15: 16: end for 17: end for 18: end procedure
←
←
← ←
←
←
∨
← ←
∞
∨
∧
intermediare numai din mult¸imea x1 , . . . , xk−1 sau dac˘a exist˘a un drum de la xi la xk avˆ and vˆarfurile intermediare numai din mult¸imea x1, . . . , xk−1 ¸si dac˘a exist˘a un drum de la xk la x j avˆ and vˆarfurile intermediare numai din mult¸imea x1, . . . , xk−1 :
{
dki,j = d ki,j−1
}
{
k −1 i,k
{
}
k −1 k,j )
∨ (d ∧ d
Algoritm 69 Algoritm de calcul a ˆınchiderii tranzitive (variant˘a) 1: procedure InchidereTranzitiva(n, C ; D) 1, n do 2: for i 1, n do 3: for j 4: if ((i = j) (ci,j = + )) then 5: di,j 1 6: else di,j 0 7: end if 8: 9: end for 10: end for 11: for k 1, n do 12: for i 1, n do if (di,k = 1) then 13: 14: for j 1, n do 15: di,j d i,j dk,j 16: end for 17: end if 18: end for 19: end for 20: end procedure
←
←
← ←
←
∨
∞
←
← ← ∨
181
}
(8.18)
Exemplul 8.5 Fie graful din figura 8.2, din care am eliminat arcul (1, 2). Matricea costurilor asociat˘ a acestui graf modificat este:
∞ ∞ ∞ ∞ ∞ ∞ ∞ ∞∞ ∞∞ ∞ ∞ 0
21
0
C =
D0 =
D2 =
D4 =
8.3
1 0 1 0 0
0 1 0 0 0
0 1 1 1 1
1 0 1 1 0
0 1 0 0 1
1 0 1 0 0
0 1 0 0 0 1 1 1 1 1
0 1 1 1 1 0 1 0 0 0
1 0 1 1 0 1 1 1 1 1
0 1 0 0 1 1 1 1 1 1
0 1 0 0 1
D1 =
D3 =
8 0 12 9 0 3
3
1 0 1 0 0
0 1 0 0 0
0 1 1 1 1
1 0 1 1 0
0 1 0 0 1
1 1 1 1 1
0 1 0 0 0 1 1 1 1 1
0 1 1 1 1 0 1 0 0 0
1 1 1 1 1 1 1 1 1 1
0 1 0 0 1 1 1 1 1 1
D5 =
0 1 0 0 1
4
0
Exercit¸ii
1. (Cutii n-dimensionale) S˘a consider˘ a m o cutie n-dimensional˘ a , dat˘ a prin lungimea fiec˘arei laturi (o cutie bidimensional˘ a este un dreptunghi, o cutie tridimensional˘ a este un paralelipiped etc.). Problema cere analizarea unui grup de k cutii n-dimensionale: s˘a se elaboreze un algoritm ce determin˘ a o secvent¸a˘ de cutii de lungime maxim˘a (b1, b2 , . . .) din grupul de k cutii, astfel ˆıncˆat fiecare cutie bi s˘a poat˘ a fi introdus˘ a ˆın cutia bi+1 . O cutie D = (d1, d2 , . . . , dn) poate fi inclus˘ a ˆıntr-o cutie E = (e1, e2 , . . . , en ) numai dac˘a exist˘a o permutare a mult¸imii valorilor laturilor cutiei D astfel ˆıncˆat lungimea fiec˘ arei laturi a cutiei D, dup˘a reorganizare, s˘ a nu dep˘ a¸seasc˘ a lungimea laturii corespunz˘ atoare din cutia E .
⊂
De exemplu cutia (2, 6) poate fi introdus˘ a ˆın cutia (7, 3) ((6, 2) (7, 3)), iar cutia (1, 6, 13) poate fi introdus˘a ˆın cutia (11, 15, 3) ((6, 13, 1) (11, 15, 3)).
⊂
Cutiile egale pot fi introduse una ˆıntr-alta. M˘ asurile laturilor sunt numere reale mai mici sau egale cu 1000. Num˘arul de cutii nu dep˘ a¸seste 100, iar num˘ arul de dimensiuni nu dep˘ a¸se¸ste 20. (Tuymaada, 1997)
2. Suntet¸i angajat la o companie ce asigur˘ a servicii de transport m˘ arfuri en-gross. Client¸ii au magazine ˆın ora¸se din toat˘ a ¸tara ¸si ei sunt aprovizionat¸i din depozitele locale ale companiei.
182
Pentru o aprovizionare optim˘ a, compania vrea s˘ a investeasc˘ a ˆın construirea unui depozit central din care s˘ a se aprovizioneze toate zonele; va fi, deci, necesar ca acest depozit s˘ a fie plasat ˆıntr-unul din ora¸se, ˆın a¸sa fel ˆıncˆ at timpul total de livrare din acest depozit c˘atre toate ora¸sele s˘ a fie minim. Camioanele companiei transport˘ a 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 ora¸s ¸si ˆınapoi (se presupune c˘ a ¸soferul va urma de fiecare dat˘ a calea cea mai rapid˘ a, iar ˆın fiecare ora¸s depozitul local se afl˘ a amplasat la intrare) iar timpul de desc˘ arcare al camionului la destinat¸ie este de exact 30 de minute. Drumurile ce leag˘ a ora¸sele sunt de aceea¸si calitate, dar pot exista drumuri ce iau mai mult timp ˆıntr-un sens decˆ at ˆın sens invers. Pot fi, de asemenea, drumuri cu sens unic. Pentru a simplifica modelul, s-a stabilit pentru fiecare ora¸s o list˘ a cu toate drumurile ce pleac˘ a din ora¸s spre celelalte ora¸se ¸si cˆ at timp ia parcurgerea fiec˘ arui drum. (Concurs ACM Zona Pacificul de Sud)
3. Se d˘ a ret¸eaua hidrografic˘ a a unei ¸ta˘ri constituit˘ a din mult¸imea rˆ aurilor ¸si afluent¸ii lor. Cele N rˆauri (2 n 1000) sunt numerotate de la 1 la N . Leg˘atura dintre un rˆ au v ¸si un afluent al s˘ au u este specificat˘ a prin perechea (u, v).
≤ ≤
Pentru a se putea face estim˘ ari cu privire la potent¸ialul risc de inundat¸ie pe cursul unui rˆau trebuie s˘ a se calculeze debitul fiec˘arui rˆ au ˆın parte. Debitul unui izvor se define¸ste ca fiind cantitatea de ap˘ a ce trece prin sect¸iunea izvorului ˆın unitatea de timp. Debitul rˆ aului u la v˘arsare va fi egal cu debitul izvorului rˆaului u plus suma debitelor afluent¸ilor la v˘arsare ˆın rˆaul u. Se cere s˘ a se realizeze un algoritm care s˘ a calculeze debitul la v˘ arsare al fiec˘ arui rˆ au. 4. S˘a se determine drumul de lungime minim˘ a necesar deplas˘ arii unui cal pe o tabl˘a de ¸sah (avˆand dimensiunile 8 8), de la un punct de plecare la unui de sosire, ¸stiind c˘ a pe tabl˘a exist˘a ¸si c˘asut¸e-capcan˘a. Calul ˆın drumul s˘ au nu poate trece printr-o c˘ asut¸a˘-capcan˘ a.
×
(ACM, Eastern Europe, 1994)
5. Un traduc˘ ator g˘ ase¸ste o carte scris˘a cu litere latine, ˆın care ˆıns˘ a ordinea literelor din alfabet este schimbat˘ a . La sfˆar¸situl c˘art¸ii, se afl˘a un index de cuvinte complet ¸si necontradictoriu. Se cere s˘ a se elaboreze un algoritm ce determin˘ a ordinea literelor din noul alfabet. (ONI, 1991)
6. Profesorul Heif realizeaz˘ a ni¸ste experimente cu o specie de albine din America de Sud pe care le-a descoperit ˆın timpul unei expedit¸ii ˆın jungla Amazonului. Mierea produs˘ a de aceste albine este superioar˘ a din punct de vedere calitativ mierii produse de albinele din Europa sau din America de Nord. Din p˘ acate, aceste albine nu se ˆınmult ¸esc ˆın captivitate. Profesorul Heiff crede c˘ a pozit¸iile larvelor (albine lucr˘ atoare, regina) din fagure depind de condit¸iile de mediu, care sunt diferite ˆın laborator fat ¸˘a de p˘adurea tropical˘a. Ca un prim pas pentru a-¸si verifica teoria, profesorul Heiff dore¸ste s˘ a calculeze distant¸a dintre pozit¸iile larvelor. Pentru aceasta el m˘ asoar˘a distant¸a dintre celulele fagurelui ˆın care sunt plasate larvele. El a numerotat celulele alegˆ and ˆın mod arbitrat una, c˘ areia i-a atribuit valoarea 1, celelalte celule r˘ amase fiind apoi numerotate circular, ˆın sensul acelor de ceas, ca ˆın figur˘ a. 183
__ __ __ __ __/ \__/ \__/ \__/ \__ __/ \__/ \__/53\__/ \__/ \__ / \__/ \__/52\__/54\__/ \__/ \ \__/ \__/51\__/31\__/55\__/ \__/ / \__/50\__/30\__/32\__/56\__/ \ \__/49\__/29\__/15\__/33\__/57\__/ / \__/28\__/14\__/16\__/34\__/ \ \__/48\__/13\__/ 5\__/17\__/58\__/ /..\__/27\__/ 4\__/ 6\__/35\__/ \ \__/47\__/12\__/ 1\__/18\__/59\__/ /..\__/26\__/ 3\__/ 7\__/36\__/ \ \__/46\__/11\__/ 2\__/19\__/60\__/ /..\__/25\__/10\__/ 8\__/37\__/ \ \__/45\__/24\__/ 9\__/20\__/61\__/ /..\__/44\__/23\__/21\__/38\__/ \ \__/70\__/43\__/22\__/39\__/62\__/ / \__/69\__/42\__/40\__/63\__/ \ \__/ \__/68\__/41\__/64\__/ \__/ / \__/ \__/67\__/65\__/ \__/ \ \__/ \__/ \__/66\__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/
De exemplu, dou˘a larve pozit¸ionate ˆın celulele 19 ¸si 30 se afl˘ a la distant¸a de 5 celule. Unul din drumurile minime ce unesc cele dou˘a celule trece prin celulele 19-7-6-5-15-30. S˘a se scrie un algoritm ce calculeaz˘ a distant¸a minim˘a dintre oricare dou˘ a celule. Datele de intrare constau din dou˘ a numere naturale u ¸si v (u, v 10.000) reprezentˆand num˘arul celulelor ˆıntre care se dore¸ste s˘ a se determine distant¸a minim˘a.
≤
(ACM Final, 1999)
7. ˆIntr-o ¸tar˘ a exist˘a N orase, unele dintre ele legate prin autostr˘ a zi cu dublu sens. Se presupune c˘ a din orice ora¸s se poate ajunge ˆın oricare altul. Taxa este aceea¸si pentru orice autostrad˘ a (1 Ron), dar trebuie pl˘ atit˘ a din nou la intrarea pe o nou˘ a autostrad˘ a. Distant¸a dintre dou˘ a ora¸se este considerat˘ a egal˘ a cu taxa minim˘ a pentru a ajunge dintr-unul ˆın altul. Se dore¸ste introducerea unui abonament a c˘ arui plat˘ a permite circulat¸ia pe autostr˘ azi f˘ar˘a nici o tax˘a suplimentar˘ a. Costul abonamentului este de 100 de ori mai mare decˆ at distant¸a maxim˘a ˆıntre dou˘a perechi de ora¸se. S˘a se scrie un algoritm ce calculeaz˘ a costul abonamentului. (CEOI, 1996)
8. ˆIn fiecare noapte, un print¸ intr˘ a ˆın subsolurile unui castel unde locuie¸ste o print ¸es˘a. Drumul c˘atre print¸es˘a este ca un labirint. Sarcina print ¸ului este s˘a se gr˘abeasc˘ a ¸si s˘a g˘aseasc˘a drumul prin labirint c˘ atre print¸es˘ a, deoarece trebuie s˘ a se ˆıntoarc˘ a ˆın zori. Acesta are unelte potrivite cu ajutorul c˘ arora poate s˘ a sparg˘ a numai un singur perete pentru a-¸si scurta drumul c˘ atre destinat¸ie. S˘a se elaboreze un algoritm care: 184
(a) determin˘ a lungimea celui mai scurt drum din labirint din locul ˆın care print ¸ul intr˘a ˆın labirint pˆan˘a ˆın locul ˆın care tr˘aie¸ste print¸esa; (b) determin˘ a lungimea celui mai scurt drum ˆın condit¸iile de mai sus, dac˘a se sparge un perete ¸si numai unul. Castelul are o form˘ a dreptunghiular˘ a cu m n camere (1 n, m 100). Fiecare camer˘a poate avea peret¸i spre E , S , V ¸si N codificat¸i cu un num˘ ar ˆıntre 1 ¸si 14: 1, 2, 4, 8. Se ¸stie c˘ a nu exist˘a camere f˘ ar˘a nici un perete (codul > 0) ¸si nici camere care s˘ a aib˘a peret¸i ˆın toate direct¸iile (codul < 15).
×
≤
≤
Lungimea celui mai scurt drum se define¸ste ca fiind num˘ arul de camere dintre surs˘ a ¸si destinat¸ie inclusiv. Datele de intrare constau din M linii ce cont¸in fiecare N numere ˆıntregi reprezentˆ and codificarea peret¸ilor din fiecare camer˘ a, urmate de 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 Bucure¸sti, 1999)
185
Appendix A Metoda Backtracking Metoda Backtracking este una dintre cele mai cunoscute metode de elaborare a algoritmilor. Metoda se aplic˘ a numai ˆın situat¸ia ˆın care nu exist˘ a nici o alt˘a modalitate de rezolvare a problemei propuse deoarece timpul de execut¸ie depinde exponent¸ial de dimensiunea datelor de intrare. Se utilizeaz˘ a o structur˘ a de tip stiv˘a iar metoda poate fi implementat˘ a atˆ at iterativ cˆ at ¸si recursiv. Metoda se aplic˘ a acelor probleme ˆın care solut¸ia se poate reprezenta sub forma unui vector x = (x1 , x2, . . . , xn ) A 1 A2 . . . An , unde mult¸imile A i , (i = 1, n) sunt finite ¸si nevide ( Ai = n i > 0). ˆIn plus, pentru fiecare problem˘ a ˆın parte este necesar ca solut¸ia x1 , x2, . . . , xn s˘ a satisfac˘ a anumite condit¸ii interne ρ(x1 , x2 , . . . , xn ) (vezi algoritmul 70).
∈ × × ×
| |
a) Algoritm 70 Algoritm Backtracking (varianta general˘ 1: procedure Backtracking(n, A) 2: k 1 3: xk prima valoare din afara domeniului de valori 4: while (k > 0) do gasit f alse 5: while ( valori neverificate pentru x k ) (gasit = true) do 6: 7: xk urmatoarea valoare neverificata 8: if (ρk (x1 , x2 , . . . , xk ) = true) then 9: gasit true 10: end if end while 11: 12: if (gasit = true) then 13: if (k = n) then 14: call Afis Solutie(x1 , . . . , xn ) 15: else 16: k k + 1 17: xk prima valoare din afara domeniului de valori 18: end if 19: else k k 1 20: 21: end if 22: end while 23: end procedure
← ←
← ∃ ←
∧
←
← ←
← −
De exemplu, s˘a consider˘ am dou˘ a mult¸imi, S 1 = a,b,c ¸si S 2 = m, n . Se dore¸ste s˘ a se determine perechile (x1 , x2 ), cu x1 A 1 ¸si x2 A 2, cu proprietatea c˘ a dac˘ a x1 este a sau b
∈
∈
186
{
}
{
}
atunci x2 nu poate fi n. Rezolvarea conduce la urm˘atoarele 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 = A1 A 2 ... A n se nume¸ste spat ¸iul solut ¸iilor posibile , iar elementele ¸ii rezultat . Ne propunem determinarea x A ce satisfac condit¸iile interne se numesc solut ¸iilor rezultat , eventual pentru a alege dintre ele pe aceea ce minimizeaz˘ tuturor solut a sau maximizeaz˘a o funct¸ie obiectiv. Metoda Backtracking evit˘a generarea tuturor solut¸iilor posibile (toate elementele produsului cartezian A1 A 2 ... A n ). Construirea unei solut¸ii se face ˆın mai mult¸i pa¸si, elementele vectorului X primind valori pe rˆand: elementului xk A k , k = 1, n, i se atribuie o valoare numai dup˘ a ce au fost atribuite valori pentru componentele x1 A1 , x2 A2 , . . ., xk−1 Ak−1 . Metoda trece la atribuirea unei valori pentru xk+1 Ak+1 doar dac˘ a ¸iile de continuare , notate cu ρ k (x1 , x2, . . . , xk ). xk ˆımpreun˘a cu x 1 , x2, . . . , xk−1 verific˘a condit Dac˘a aceste condit¸ii ρ k (x1 , x2 , . . . , xk ) sunt ˆındeplinite se trece la c˘ autarea unei valori pentru elementul xk+1 A k+1 al solut¸iei. Neˆındeplinirea condit¸iilor are urm˘ atoarea semnificat¸ie: pentru orice alegere x k+1 A k+1, . . . , xn An , nu vom putea ajunge la o solut ¸ie rezultat ˆın care condit ¸iile interne s˘a fie ˆındeplinite. ˆIn aceast˘ a situat¸ie va trebui s˘a ˆıncerc˘am o alt˘a alegere pentru xk , acest lucru fiind posibil doar dac˘ a nu am epuizat toate valorile disponibile din mult¸imea Ak . Dac˘a mult¸imea Ak a fost epuizat˘ a va trebui s˘ a mic¸sor˘am valoarea variabilei k cu o unitate (k k 1), ¸si s˘a trecem la alegerea unei alte valori pentru elementul xk−1 A k−1. Mic¸sorarea valorii curente a variabilei k cu o unitate d˘ a numele metodei ¸si semnific˘ a faptul c˘a atunci cˆ and nu putem avansa, vom urm˘ari ˆınapoi secvent¸a curent˘ a din solut¸ie. Faptul c˘ a valorile v1, v2, . . . , vk−1, ¸iile de continuare , nu este suficient pentru ale componentelor x1 , x2 , . . . , xk−1, satisfac condit a garanta obt¸inerea unei solut¸ii ale c˘arei prime k 1 componente coincid cu aceste valori. O alegere bun˘ a a condit arului de calcule, ¸iilor de continuare conduce la reducerea num˘ fiind de dorit ca aceste condit ¸ii de continuare s˘a fie nu numai necesare, dar ¸si suficiente 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 ˆıncepˆand cu prima component˘a ¸si deplasˆ andune c˘ atre ultima, cu eventuale reveniri asupra valorilor atribuite anterior. Anumite valori sunt consumate ˆın urma unor atribuiri sau ˆıncerc˘ ari de atribuire e¸suate din cauza neˆındeplinirii condit¸iilor de continuare. Exist˘ a dou˘ a variante fat¸a˘ de metoda standard:
× × ×
∈
× × ×
∈
∈
∈
∈
∈
∈
∈
∈
← −
∈
−
• solut¸iile pot avea num˘ar variabil de componente; • dintre solut¸ii se alege cea care minimizeaz˘a sau maximizeaz˘a o funct ¸ie cost sau o funct ¸ie obiectiv dat˘a.
Exemplul A.1 Enunt ¸ul problemei : Se cere s˘ a se genereze permut˘ ari de n elemente. n Rezolvare: Spat ¸iul solut ¸iilor este A = i=1 Ai , Ai = 1, 2, . . . , n , Ai = n . Solut ¸ia va fi obt ¸inut˘ a ˆın vectorul x = (x1, x2 , . . . , xn ) A 1 A2 . . . An . La pasul k se ˆıncearc˘ a atribuirea unui alt element din mult ¸imea Ak lui xk . Vom trece la pasul k + 1 doar dac˘ a sunt ˆındeplinite condit ¸iile de continuare: (x1 , x2, . . . , xk ) nu cont ¸ine elemente care se repet˘ a. Deoarece aceast˘ a verificare se face la fiecare pas, este suficient s˘ a verific˘ am dac˘ a valoarea elementului xk nu se reg˘ ase¸ste printre valorile x1 , x2 , . . . , xk−1 (vezi funct ¸ia CanContinue() din algoritmul 71).
× { ∈ × ×
} | |
Exemplul A.2 Fie n = 4. Atunci Ai va fi compus din elementele Ai = 1, 2, 3, 4 . Pentru k = 1, x1 va primi valuarea 1 (vezi linia 15): 1
{
187
}
Pentru k = 2, se verific˘ a x2 = 1, ˆıns˘ a nu se respect˘ a condit ¸iile de continuare (linia 16). Astfel se trece la urm˘ atoarea valoare, x2 = x 1 + 1 = 2 (linia 15). Pentru aceast˘ a configurat ¸ie condit ¸iile de continuare sunt ˆındeplinite, ¸si se trece la pasul urm˘ ator, k = k + 1 = 3 (linia 24): 1 2 Se verific˘ a valorile 1, 2 ¸si 3 pentru x3 (linia 16), dintre care, numai ultima satisface condit ¸iile de continuare: 1 2 3 ˆ urma verific˘ Ajun¸si la ultimul pas, k = 4, se verific˘ a valorile 1 , 2 , 3 ¸si 4 pentru x 4 . In arii condit ¸iilor de continuare pentru x 4 = 4, se urm˘ are¸ste 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 afi¸seaz˘ a: 1 2 3 4 Mai departe, nu mai exist˘ a valori netestate pentru x4 (linia 14), ¸si revenim la nivelul anterior k = k 1 = 3 (linia 28). Aici urm˘ atoarea valoare din domeniul de valori neverificat˘ a este x 3 = 4 (linia 15). Solut ¸ia part ¸ial˘ a respect˘ a condit ¸iile de continuare (linia 16), astfel ˆıncˆ at se trece la nivelul urm˘ ator (linia 24), k = k + 1 = 4: 1 2 4 Pentru x4 sunt verificate valorile, 1, 2, 3, 4 (liniile 15–16), dintre acestea numai valoarea 3, conducˆ and la o solut ¸ie final˘ a (linia 21): 1 2 4 3 Algoritmul se continu˘ a ˆın acela¸si mod pˆ an˘ a 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 variant˘ a de implementare a algoritmului 71 ˆın limbajul C este: #include #define TRUE 1 #define FALSE 0 #define NN 100 int n; int a[NN]; /** * Functia citeste valorile datelor de intrare. */ void readInput(void) { //se citeste numarul de elemente n printf("n="); scanf("%d", &n); }
188
ari (varianta backtracking) Algoritm 71 Algoritm pentru generare permut˘ Input: n - num˘ arul de elemente din mult¸imea init¸ial˘ a 1: function CanContinue(A, k) 2: for i 1, k 1 do 3: if (ai = a k ) then 4: return f alse 5: end if 6: end for 7: return true 8: end function
←
−
9: procedure Permutari(n) k 1 10: 11: xk 0 12: while (k > 0) do 13: gasit f alse 14: while (xk + 1 n) (gasit = true) do xk x k + 1 15: 16: if ((CanContinue(X, k)) = true) then 17: gasit true 18: end if 19: end while if (gasit = true) then 20: 21: if (k = n) then 22: Output x1 , . . . , xn 23: else k k + 1 24: 25: xk 0 26: end if 27: else k k 1 28: 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. */
189
int canContinue(int k) { int i; for (i = 1; i <= k - 1; i++) if (a[k] == a[i]) return FALSE; return TRUE; } void run(void) { int i; int gata; //initializare i = 1; a[i] = 0; while (i > 0) { gata = FALSE; while ((a[i] + 1 <= n) && !gata) { /** * cat timp exista elementul urmator si nu sunt verificate * conditiile de continuare */ //treci la elementul urmator a[i]++; //verifica conditiile de continuare if (canContinue(i)) gata = TRUE; //sau: gata = canContinue(i); } if (gata) //daca s-au verificat conditiile de continuare if (i == n) //daca suntem la ultimul element afisam solutia list(); else { //altfel trecem la elementul urmator i++; a[i] = 0; } else //altfel revenim la un element anterior si alegem o alta valoare i--; } } void main(void) { read_data(); run(); }
Exemplul A.3 Enunt ¸ul problemei : S˘ a se genereze combin˘ ari de n luate cˆ ate m ( 0 190
≤
m
≤ n).
Rezolvare: Spat ¸iul solut ¸iilor este A = m ¸ia va i=1 Ai , Ai = 1, 2, . . . , n , Ai = n . Solut fi obt ¸inut˘ a ˆın vectorul x = (x1, x2 , . . . , xm) A 1 A2 . . . Am (vezi algoritmul 72). La pasul k se ˆıncearc˘ a atribuirea unui alt element din mult ¸imea Ak lui xk . Condit ¸ia de continuare se refer˘ a la proprietatea c˘ a elementele vectorului solut ¸ie trebuie s˘ a respecte relat ¸ia ¸ine foarte u¸sor prin urm˘ atorul procedeu: la trecerea la x1 < x2 < .. . < xk . Acest lucru se obt pasul k + 1, variabila x k+1 va primi, ca valoare init ¸ial˘ a, valoarea curent˘ a a variabilei x k (vezi linia 15 din algorimul 72). Pentru n = 5 ¸si m = 3 avem: A = 3i=1Ai = A1 A 2 A 3 , unde A1 = A2 = A3 = 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)
ari (varianta backtracking) Algoritm 72 Algoritm pentru generare combin˘ 1: procedure Combinari(n, m) 2: k 1 3: xk 0 4: while (k > 0) do gasit f alse 5: 6: while (xk + 1 n) (gasit = true) do 7: xk x k + 1 8: gasit true 9: end while 10: if (gasit = true) then 11: if (k = m) then 12: Output x1 , . . . , xm 13: else k k + 1 14: 15: xk x k−1 16: end if 17: else k k 1 18: end if 19: 20: end while 21: end procedure
← ←
← ←
≤ ∧
{
}
←
← ←
← −
ˆ In continuare prezent˘ am o implementare a algoritmului 72 ˆın limbajul C : 191
#include #define TRUE 1 #define FALSE 0 #define NN 100 int n, m; int a[NN]; /** * Functia citeste valorile datelor de intrare. */ void readInput(void) { printf("n="); scanf("%d", &n); printf("m="); scanf("%d", &m); } /** * Functia afiseaza solutia calculata de functia run(). */ void list(void) { int i; for (i = 1; i <= m; i++) printf("%d ", a[i]); printf("\n"); } void run(void) { int i; int gata; //initializare i = 1; a[i] = 0; while (i > 0) { gata = FALSE; while ((a[i] + 1 <= n) && !gata) { /** * cat timp exista elementul urmator si nu sunt verificate * conditiile de continuare, treci la elementul urmator */ a[i]++; gata = TRUE; } if (gata) //daca s-au verificat conditiile de continuare if (i == m) //daca suntem la ultimul element afisam solutia list(); else { //altfel trecem la elementul urmator i++;
192
a[i] = a[i - 1]; } else //altfel revenim la un element anterior si alegem o alta valoare i--; } } void main(void) { readInput(); run(); }
Exemplul A.4 Problema damelor Enunt ¸ul problemei : Pe o tabl˘ a de ¸sah cu n linii ¸si n coloane ( n n p˘ atrate), s˘ a se pozit ¸ioneze n dame astfel ˆıncˆ at s˘ a nu se atace reciproc. Rezolvare: Tabla de ¸sah va fi reprezentat˘ a prin matricea A = (aij ), i , j = 1, n. Dou˘ a dame aflate ˆın pozit ¸iile aij ¸si akl se vor ataca reciproc dac˘ a:
×
i = k sau j = l sau i
| − k| = | j − l|
Prima observat ¸ie care se poate deduce se refer˘ a la faptul c˘ a dou˘ a dame nu trebuie s˘ a se afle pe aceea¸si linie sau pe aceea¸si coloan˘ a. Not˘ am cu V = 1, 2, . . . , n mult ¸imea celor n coloane ale tablei de ¸sah. Vectorul solut ¸iei rezultat X = (x1 , x2 , . . . , xn ) atoarea semnificat ¸ie: componenta i a V ... V are urm˘ vectorului ( i = 1, n) reprezint˘ a linia i de pe tabla de ¸sah. Valoarea xi a acestei componente reprezint˘ a coloana pe care se a¸seaz˘ a dama de pe linia i (vezi algoritmul 73). La ˆınceput, toate damele sunt ˆın afara tablei de ¸sah, prin urmare xi = 0, i = 1, n. La pasul k vom ˆıncerca a¸sezarea damei k (de pe linia k) pe coloana urm˘ atoare ( xk + 1). Init ¸ial dama a k-a este pozit ¸ionat˘ a ˆın afara tablei ( xk = 0 / V ). O dam˘ a poate fi a¸sezat˘ a pe o alt˘ a coloan˘ a dac˘ a vechea pozit ¸ie satisface condit ¸ia xk < n. Noua pozit ¸ie ( xk + 1) trebuie s˘ a satisfac˘ a condit ¸iile interne ρk :
{
} ∈ × ×
∈
• ∀i, 1 ≤ i < k, x = x + 1 (nu sunt pe aceea¸si coloan˘ a); • |i − k| = |x − (x + 1)| (nu sunt pe aceea¸si diagonal˘ a). i
i
k
k
Dac˘ a xk < n ¸si ρk sunt adevar˘ ate, dama de pe linia k va fi pozit ¸ionat˘ a pe coloana xk + 1 ˆ caz contrar, dama k nu ¸si se trece la pasul k + 1 , corespunz˘ ator pozit ¸ion˘ arii damei k + 1. In poate fi a¸sezat˘ a pe tabl˘ a ¸si va trebui s˘ a relu˘ am pozit ¸ionarea damei k 1.
−
Exemplul A.5 Problema color˘ arii h˘ art ¸ilor Enunt ¸ul problemei : S˘ a se coloreze o hart˘ a reprezentˆ and n ¸˘ t ari folosind m culori etichetate 1, . . . , m. Rezolvare: Datele necesare pentru descrierea h˘ art ¸ii vor fi reprezentate de o matrice arei elemente au urm˘ atoarea semnificat ¸ie: A = (aij ), i , j = 1, n, ale c˘ aij =
Matricea A aij = a ji ¸si aii = 0.
1 , dac˘ a ¸ara t i are frontier˘ a comun˘ a cu ¸ara t j 0 , ˆın caz contrar
n×n este
∈ M
simetric˘ a ¸si elementele de pe diagonala principal˘ a sunt nule:
193
Algoritm 73 Algoritm Problema damelor Input: n - num˘ arul de linii/coloane de pe tabla de ¸sah 1: function CanContinue(A, k) 2: for j 1, k 1 do 3: if (a j = a k ) ((k j) = ak a j ) then return f alse 4: 5: end if 6: end for 7: return true 8: end function
←
−
∨
−
| − |
9: procedure DameBacktracking(n) 10: i 1 11: xi 0 12: while (i > 1) do gasit f alse 13: 14: while (xi + 1 n) (gasit = true) do 15: xi x i + 1 16: if (CanContinue(X, i) = true) then gasit true 17: end if 18: 19: end while 20: if (gasit = true) then 21: if (i = n) then 22: call Afis Solutie(x1 , . . . , xn ) 23: else 24: i i + 1 25: xi 0 26: end if else 27: 28: i i 1 29: end if 30: end while 31: end procedure
← ←
← ←
≤ ∧
←
← ←
← −
Fie C = c1 , c2 , . . . , cm mult ¸imea culorilor. Vectorul solut ¸iei rezultat X = (x1 , x2 , . . . , xn) C ... C , are urm˘ atoarea semnificat ¸ie: ¸ara t i are repartizat˘ a culoarea xi C, i = 1, n. La ˆınceput, fiec˘ arei ¸˘ t ari i se va atribui valoarea 0. k , k = 1, n, dac˘ La pasul k ˆıncerc˘ am s˘ a atribuim o nou˘ a culoare ( xk + 1) pentru ¸ara t a sunt ˆındeplinite condit ¸iile:
{
}
∈ × ×
∈
1. xk < m (dac˘ a mai exist˘ a alte culori neatribuite pentru ¸ara t k); 2. pentru i, 1
∀ ≤ i < k cu a
i,k
= 1 avem xi = x k + 1.
Dac˘ a cele dou˘ a condit ¸ii anterioare sunt ˆındeplinite, atunci noua culoare pentru ¸ara t k va ˆ caz contrar, fi xk + 1, ¸si se va trece la pasul k + 1 (stabilirea unei culori pentru ¸ara t k + 1). In ¸˘ t arii k nu i se mai poate atribui o alt˘ a culoare ¸si ne ˆıntoarcem la pasul k 1, corespunz˘ ator alegerii unei noi culori pentru ¸ara t k 1. Algoritmul se termin˘ a atunci cˆ and nu mai exist˘ a nici o culoare neverificat˘ a pentru ¸ara t 1 (vezi algoritmul 74).
−
−
194
Algoritm 74 Algoritm Problema color˘ arii h˘art¸ilor
A - matricea de adiacent¸a˘/de vecin˘atate a ¸ta˘rilor Input: n - num˘ arul de ¸ta˘ri m - num˘ arul de culori disponibile 1: function CanContinue(A, X, k) 1, k 1 do 2: for j if (x j = x k ) (a j,k = 1) then 3: 4: return f alse
←
−
∧
5: end if 6: end for 7: return true 8: end function 9: procedure ColorBacktracking(A,n,m) 10: k 1 xk 0 11: 12: while (k > 0) do 13: gasit f alse 14: while (xk + 1 m) (gasit = true) do xk x k + 1 15: gasit CanContinue(A,X,k) 16: 17: end while 18: if (gasit = true) then 19: if (k = n) then 20: call Afis Solutie(x1 , . . . , xn ) else 21: 22: k k + 1 23: xk 0 24: end if 25: else 26: k k 1 27: end if 28: end while 29: end procedure
← ←
← ←
≤
∧
←
← ←
← −
Exemplul A.6 Enunt ¸ul problemei : Se d˘ a o sum˘ a s ¸si n tipuri de monede avˆ and valorile ¸i un algoritm care s˘ a determine o modalitate de plat˘ a a sumei s a1, a2 , . . . , an lei. Realizat utilizˆ and un num˘ ar minim de monede. Rezolvare: #include #define FALSE 0 #define TRUE 1 #define NN 100 #define MAX 10000 int suma_plata; //suma ce trebuie platita int n; //numarul de monezi
195
int int int int int
limit[NN]; val[NN]; taken[NN]; minim; keep[NN];
//limit[i] numarul maxim de monede de tipul val[i] //val[i] valoarea unei monede //cate monede de valoare val[i] au fost luate //numarul minim de monede cu care se poate face plata //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 * suma partiala sa * @param k - pasul */ int canContinue(int int i, suma_tmp;
sunt satisfacute conditiile de continuare: nu depaseasca valoarea totala de platit. curent k) {
suma_tmp = 0; for (i = 0; i <= k; i++) suma_tmp += val[i] * taken[i]; if ((k == n - 1 && suma_tmp == suma_plata) || (k != n - 1 && suma_tmp <= suma_plata)) return TRUE; else return FALSE; } /** * Se pastreaza solutia actuala (taken) numai daca este * mai buna decat cea anterioara (keep). */ void final(void) { int i; int monezi = 0; //se numara cate monede sunt in solutie for (i = 0; i < n; monezi += taken[i], i++); if (monezi < minim) { minim = monezi;
196
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;
197
} else //altfel revenim la un element anterior si alegem o alta valoare i--; } } void main(void) { readInput(); run(); print(); }
198
Bibliografie [1] A. Aho, J. Hopcroft, J. Ullman, On finding lowest common ancestors in trees , Proc. 5th ACM Symp. Theory of Computing (STOC), pp. 253-265, 1973. [2] A. V. Aho, J. E. Hopcroft, J. D. Ulmann, Data Structures and algorithms , AddisonWesley, 1983. [3] A. V. Aho, J. D. Ulmann, Foundation of Computer Science , Computer Science Press, 1992. [4] R. K. Ahuja, J. B. Orlin, A Fast and Simple Algorithm for the Maximum Flow Problem , Operations Research, vol. 37(5), pp. 748–759, 1989. [5] R. Ahuja, T. Magnanti, J. Orlin, Network Flows , Prentice Hall, 1993. [6] R. Andonie, I. Gˆarbacea, Algoritmi fundamentali, o perspectiv˘ a C++, Editura Libris, 1995. [7] C. Aragon, R. Seidel, Randomized Search Trees , in Proc. of 30th IEEE Symposium on Foundations of Computer Science, pp. 540–546, 1989. [8] A. Atanasiu, Concursuri de informatic˘ a. Probleme propuse (1994), Editura Petrion, Bucure¸sti, 1995. [9] M.D. Atkinson, J.-R. Sack, N. Santoro, T. Strothotte, Min-max heaps and generalized priority queues , Programming techniques and Data structures. Comm. ACM, vol. 29(10), pp. 996-1000, 1986. [10] M. Augenstein, A. Tenenbaum, Program efficiency and data structures , Proceedings of the eighth SIGCSE technical symposium on Computer science education, pp. 21–27, 1977. [11] S. Baase, Computer algorithms. Introduction to Design and Analysis , Addison-Wesley, 1992. [12] P. B˘az˘avan, Elemente de Teoria Algoritmilor , Editura Sitech, Craiova, 2007. [13] R. Bellman, On a routing problem , Quarterly Applied Mathematics, XVI(1), pp. 87-90, 1958. [14] M. A. Bender, M. Farach-Colton, The LCA problem revisited , Proceedings of the 4th Latin American Symposium on Theoretical Informatics, LNCS, vol. 1776, SpringerVerlag, pp. 88-94, 2000. [15] J. Bentley, R. Sedgewick, Fast Algorithms for Sorting and Searching Strings , Proceedings of the 8th Annual ACM-SIAM Symposium on Discrete Algorithms, 1997. 199
[16] J. Bentley, R. Sedgewick, Ternary Search Trees , Dr. Dobb’s 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.
aln´ım (About a certain minimal problem), Acta [21] O. Boruvka, O jist´em probl´emu minim´ Societ. Scient. Natur. Moravicae, 3, pp. 37-58, 1926. [22] R. P. Brent, An improved Monte Carlo factorization algorithm , BIT Numerical Mathematics, Springer, vol. 20(2), pp. 176–184, 1980. [23] D. D. Burdescu, Analiza complexit˘ a, Cluj–Napoca, at ¸ii algoritmilor , Editura Albastr˘ 1998.
¸ii [24] D. D. Burdescu, M. Brezovan, M. Co¸sulschi, Structuri de date arborescente cu aplicat ˆın Pascal ¸si C , Reprografia Universit˘ a¸tii din Craiova, 2000. [25] D. D. Burdescu, Liste, arbori, grafuri , Editura Sitech, Craiova, 2005. [26] B. Chazelle, A minimum spanning tree algorithm with inverse-Ackerman type complexity , J. ACM, 47, pp. 1028-1047, 2000. [27] D. Cherition, R. E. Tarjan, Finding minimum spanning trees , SIAM Journal on Computing, vol. 5, pp. 724-741, 1976. [28] J. Cheriyan, K. Mehlhorn, Algorithms for dense graphs and networks on the random access computer , Algorithmica, vol. 15, pp. 521-549, 1996. [29] B. V. Cherkassky, Algorithm for construction of maximal flows in networks with complexity of O(V 2 E ), Mathematical Methods of Solution of Economical Problems, vol. 7, pp. 112–125, 1977.
√
[30] T. H. Cormen, C. E. Leiserson, R. L. Rivest, Introducere ˆın Algoritmi , Computer Libris Agora, Cluj–Napoca, 1999.
a , Editia a 2-a, Universi[31] M. Co¸sulschi, M. Gabroveanu, Algoritmi o abordare pragmatic˘ taria, Craiova, 2004. a ˆın optimizarea combinatorie , Editura Univ. Al. I. Cuza Ia¸si, [32] C. Croitoru, Tehnici de baz˘ Ia¸si, 1992. [33] G. B. Dantzig, Linear programming and extensions , University Press, Princeton, 1962. [34] S. Dasgupta, C. H. Papadimitriou, U. V. Vazirani, Algorithms , McGraw–Hill, 2006. [35] C. Demetrescu, G. F. Italiano, Engineering Shortest Path Algorithms , WEA, Springer, LNCS 3059, pp. 191–198, 2004. 200
[36] R. B. Dial, Algorithm 360: shortest-path forest with topological ordering [H] , Communications of the ACM, vol. 12:11, pp. 632–633, 1969. [37] Dict a ¸ionarul explicativ al limbii romˆ ane , Academia Romˆan˘a, Institutul de Lingvistic˘ Iorgu Iordan, Editura Univers Enciclopedic, 1998. [38] E. W. Dijkstra, A note on two problems in connections with graphs , Numerische Mathematik, 1, pp. 269-271, 1959. [39] Y. Dinitz, Algorithm for solution of a problem of maximum flow in a network with power estimation , Doklady Akademii nauk SSSR, vol. 11, pp. 1277-1280, 1970. [40] Y. Dinitz, Dinitz’ Algorithm: The Original Version and Even’s Version , in Oded Goldreich, Arnold L. Rosenberg, and Alan L. Selman, Theoretical Computer Science: Essays in Memory of Shimon Even , Springer, pp. 218-240, 2006. [41] J. Edmonds, Paths, Trees and Flowers , Canadian J. Math, vol. 17, pp. 449-467, 1965. [42] J. Edmonds, R. M. Karp, Theoretical improvements in algorithmic efficiency for network flow problems , Journal of the ACM, vol. 19(2), pp. 248-264, 1972. [43] J. Egerv´ary, Matrixok kombinatorius tulajdons´ agair´ ol , (in Hungarian), Matematikai ´es Fizikai Lapok, vol. 38 pp. 16-28, 1931. [44] J. Farey, On a Curious Property of Vulgar Fractions , London, Edinburgh and Dublin Phil. Mag. 47, 385, 1816. [45] J. Feng, G. Li, J. Wang, L. Zhou, Finding and ranking compact connected trees for effective keyword proximity search in XML documents , Information Systems, vol. 35(2), pp. 186–203, 2010. [46] L. R. Ford, Network flow theory , Technical Report P-923, RAND, Santa Monica, CA, 1956. [47] L. R. Ford, Jr., D. R. Fulkerson, Maximal Flow Through a Network , Canadian Journal of Mathematics, 8, pp. 399–404, 1956. [48] L. R. Ford, Jr., D. R. Fulkerson, A Simple Algorithm for Finding Maximal Network Flows and an Application to the Hitchcock Problem , Canadian Journal of Mathematics, 9, pp. 210–218, 1957. [49] L. R. Ford, Jr., D. R. Fulkerson, Flows in Networks , Princeton University Press, Princeton, 1962. [50] C. L. Foster, The Design and Analysis of Algorithms , Springer Verlag, 1992. [51] J.-C. Fournier, Graph Theory and Applications , Wiley-Blackwell, 2009. [52] E. Fredkin, Trie Memory , Communications of the ACM, 3:(9), pp. 490, 1960. [53] M. Fredman, R. Sedgewick, R. Sleator, R. Tarjan, The pairing heap: A new form of self-adjusting heap, Algorithmica, 1, pp. 111–129, 1986. [54] M.L. Fredman, R.E. Tarjan, Fibonacci heaps and their use in improved network optimization algorithms , Journal of the ACM, vol. 34, pp. 596-615, 1987. 201
[55] H. N. Gabow, R. E. Tarjan, A linear-time algorithm for a special case of disjoint set union , Proceedings of the 15th ACM Symposium on Theory of Computing (STOC), pp. 246-251, 1983. [56] H. N. Gabow, Path-based depth-first search for strong and biconnected components , Information Processing Letters, pp. 107-114, 2000. [57] D. Gale, L. S. Shapley, College Admissions and the Stability of Marriage , American Mathematical Monthly, vol. 69, pp. 9–14, 1962. 5
[58] Z. Galil, An O(V E frac23 ) algorithm for the maximal flow problem , Acta Informatica, vol. 14, pp. 221–242, 1980. 3
[59] Z. Galil, A. Naamad, An O(EV (log V )2) algorithm for the maximal flow problem , Journal of Computer and System Sciences, vol. 21(2), pp. 203–217, 1980. [60] C. Giumale, Introducere ˆın analiza algoritmilor , Editura Polirom, Ia¸si, 2004. [61] A. V. Goldberg, A new max-flow algorithm , Technical Report MIT/LCS/TM-291, Laboratory for Computer Science, MIT, 1985. [62] A. V. Goldberg, R. E. Tarjan, A new approach to the maximum flow problem , in Proceedings of the 18th ACM Symposium on Theory of Computing, ACM, pp. 136–146, 1986. ´ Tardos, R. E. Tarjan, Network flow algorithms , Algorithms and [63] A. V. Goldberg, E. Combinatorics, vol. 9. in B. Korte, L. Lovsz, H. J. Prmel, A. Schrijver, Editors, Paths, Flows, and VLSI-Layout, Springer-Verlag, Berlin, pp.101-164, 1990. [64] R. L. Graham, D. E. Knuth, O. Patashnik, Concrete Mathematics: A Foundation for Computer Science , 2nd Edition, Addison-Wesley, 1994. [65] D. Gries, The Science of Programming , Springer Verlag, Heidelberg, New–York, 1981. [66] L. Guo, F. Shao, C. Botev, J. Shanmugasundaram, XRANK: ranked keyword search over XML documents , in Proceedings of the 2003 ACM SIGMOD International Conference on Management of Data, pp. 16–27, 2003. [67] D. Gusfield, Algorithms on Strings, Trees, and Sequences , Cambridge University Press, 1997. [68] G. H. Hardy, E. M. Wright, An Introduction to the Theory of Numbers , 5th Edition, Oxford University Press, 1979. [69] D. Harel, R. E. Tarjan, Fast algorithms for finding nearest common ancestors , SIAM Journal on Computing, vol. 13(2), pp. 338-355, 1984. [70] T. E. Harris, F. S. Ross, Fundamentals of a Method for Evaluating Rail Net Capacities , Research Memorandum RM-1573, The RAND Corporation, Santa Monica, California, 1955. [71] I. N. Herstein, I. Kaplansky, Matters mathematical , 2nd Edition, Chelsea Publishing Company, 1978.
202
5
[72] J. E. Hopcroft, R. Karp, An n algorithm for maximum matchings in bipartite graphs , SIAM Journal on Computing, vol. 2(4), pp.225-231, 1973. 2
[73] V. Hristidis , N. Koudas , Y. Papakonstantinou, D. Srivastava, Keyword Proximity Search in XML Trees , IEEE Transactions on Knowledge and Data Engineering, vol. 18(4), pp. 525–539, 2006. [74] D. A. Huffman, A Method for the Construction of Minimum-Redundancy Codes , Proceedings of the I.R.E., 1952, pp. 1098-1102. [75] C. Ionescu, I. Zsako, Structuri arborescente , Editura Tehnic˘ a, Bucure¸sti, 1990. [76] V. Jarn´ık, O jist´em probl´emu minim´ aln´ım (About a certain minimal problem), Pr´ace Moravsk´e Pr´ırodovedeck´e Spolecnosti, 6, pp. 57–63, 1930. [77] D. Jungnickel, Graphs, Networks and Algorithms , Algorithms and Computation in Mathematics, vol. 5, 3rd Edition, Springer, 2008. [78] A. B. Kahn, Topological sorting of large networks , Communications of the ACM, vol. 5(11), pp. 558-562, 1962. [79] D. Karger, P. Klein, R. Tarjan, A randomized linear-time algorithm to find minimum spanning trees , Journal of ACM, 42, pp. 321328, 1995. [80] A. V. Karzanov, Determining the maximum flow in the network by the method of pre flows , Doklady Akademii nauk SSSR 15, pp. 434-437, 1974. [81] B. W. Kernigham, D. M. Ritchie, The C Programming Language , 2nd Edition, Prentice Hall, 1988. [82] D. C. Kozen, The Design and Analysis of Algorithms . Texts and Monographs in Computer Science, Springer, 1993. [83] D. K˝onig, Theorie der endlichen und unendlichen Graphen , Leipzig: Akademische Verlagsgesellschaft, 1936. Translated from German by Richard McCoart, Theory of finite and infinite graphs , Birkh¨auser, 1990. [84] J. Kleinberg, E. Tardos, Algorithm Design , Addison-Wesley, 2005.
arii calculatoarelor , vol. 1 Algoritmi fundamentali , Teora, [85] D. E. Knuth, Arta program˘ Bucure¸sti, 1999. [86] D. E. Knuth, Arta program˘ arii calculatoarelor , vol. 2 Algoritmi seminumerici , Teora, Bucure¸sti, 2000. [87] D. E. Knuth, Arta program˘ arii calculatoarelor , vol. 3 Sortare ¸si C˘ autare , Teora, Bucure¸sti, 2001. [88] J. B. Kruskal, On the shortest spanning subtree of a graph and the traveling salesman problem , Proc. of the American Mathematical Society, 7, pp. 48-50, 1956. [89] H. W. Kuhn, On combinatorial properties of matrices , Logistics Papers (George Washington University), vol. 11, pp. 1-11, 1955.
203
[90] H. W. Kuhn, The Hungarian Method for the assignment problem , Naval Research Logistics Quarterly, vol. 2, pp. 83-97, 1955. [91] H. W. Kuhn, Variants of the Hungarian method for assignment problems , Naval Research Logistics Quarterly, vol. 3, pp. 253-258, 1956. [92] Y. Li , C. Yu , H. V. Jagadish, Schema-free XQuery , in Proceedings of the Thirtieth International Conference on Very Large Databases (VLDB), pp. 72–83, 2004. [93] L. Livovschi, H. Georgescu, Analiza ¸si sinteza algoritmilor , Ed. S¸tiint¸ific˘a ¸si Enciclopedic˘a, Bucure¸sti, 1986. [94] V. M. Malhotra, M. P. Kumar, S. N. Maheshwari, An O( V 3 ) algorithm for finding maximum flows in networks , Information Processing Letters, 7(6), pp.277–278, 1978.
| |
[95] K. Mehlhorn, Data Structures and Algorithms: Graph Algorithms and NP-Completeness , Springer Verlag, 1984.
| | · | |
[96] S. Micali, V. V. Vazirani, An O( V E ) algorithm for finding maximum matching in general graphs , Proc. 21st IEEE Symp. Foundations of Computer Science, pp. 17-27, 1980. [97] V. Mitrana, Provocarea algoritmilor , Editura Agni, Bucure¸sti, 1994. [98] E. F. Moore, The shortest path through a maze , in Proceedings of International Symposium on the Theory of Switching, Part II, pp. 285-292, 1959. (prezentat la simpozion la Universitatea Harvard in aprilie 1957) [99] R. Motwani, Average-case analysis of algorithms for matchings and related problems , Journal of the ACM, vol. 41(6), pp. 1329-1356, 1994. [100] J. Munkres, Algorithms for the Assignment and Transportation Problems , Journal of the Society for Industrial and Applied Mathematics, vol. 5(1), pp. 32-38, 1957. [101] G. Nivasch, Cycle detection using a stack , Information Processing Letters, vol. 90(3), pp. 135–140, 2004. [102] I. Od˘agescu, F. Furtun˘ a, Metode ¸si tehnici de programare , Computer Libris Agora, Cluj–Napoca, 1998. [103] S. Pettie, A faster all-pairs shortest path algorithm for real-weighted sparse graphs , in Proceedings of 29th International Colloquium on Automata, Languages, and Programming (ICALP02), LNCS Vol. 2380, pp. 85-97, 2002. [104] S. Pettie, V. Ramachandran, Computing shortest paths with comparisons and additions , in Proceedings of the 13th Annual ACM-SIAM Symposium on Discrete Algorithms (SODA02), SIAM, pp. 267-276, 2002. [105] S. Pettie, V. Ramachandran, An optimal minimum spanning tree algorithm , Journal of ACM, 49:1634, 2002. [106] R. C. Prim, Shortest connection networks and some generalizations , Bell System Technical Journal, 36, pp. 1389-1401, 1957.
204
[107] B. Schieber, U. Vishkin, On finding lowest common ancestors: Simplification and parallelization , SIAM J. Comput., vol. 17, pp. 1253-1262, 1988. [108] A. Schrijver, On the history of the transportation and maximum flowproblems , Mathematical Programming, vol. 91, issue 3, pp. 437-445, 2002. [109] R. Seidel, C. Aragon, Randomized Search Trees , Algorithmica, vol. 16, pp. 464–497, 1996. [110] M. Sharir, A strong-connectivity algorithm and its applications in data fow analysis , Computers and Mathematics with Applications, vol. 7(1), pp. 67–72, 1981. [111] Y. Shiloach, U. Vishkin, An O(n2 log n) parallel max-flow algorithm , Journal of Algorithms, vol. 3(2), pp. 128-146, 1982. [112] S. Skiena, The Algorithm Design Manual , 2nd Edition, Springer, 2008. [113] D. D. Sleator, An O(EV log V ) algorithm for maximum network flow , Technical Report, STAN-CS-80-831, 1980. [114] D. D. Sleator, R. E. Tarjan, A data structure for dynamic trees , Journal of Computer Sciences, vol. 26, pp. 362–391, 1983. [115] J. Stasko, J. Vitter, Pairing heaps: Experiments and analysis , Communications of the ACM, vol. 30(3), pp. 234-249, 1987. [116] T. Takaoka, Theory of 2-3 heaps , Discrete Applied Mathematics, Vol. 126(1), 5th Annual International Computing and Combinatories Conference (COCOON’99), pp. 115– 128, 2003. [117] R. Tarjan, Depth–first search and linear graph algorithms , SIAM Journal on Computing, vol. 1(2), pp. 146–160, 1972. [118] R. E. Tarjan, Edge-disjoint spanning trees and depth-first search , Algorithmica, vol. 6(2), pp. 171-185, 1976. [119] R. E. Tarjan, Applications of path compression on balanced trees , Journal of the ACM, vol. 26(4), pp. 690-715, 1979. [120] R. E. Tarjan, A simple version of Karzanov’s blocking flow algorithm , Operation Research Letters, vol. 2, pp.265–268, 1984. [121] I. Tomescu, Grafuri ¸si programare liniar˘ a, Bucure¸sti, 1975. a , Ed. Didactic˘a ¸si Pedagogic˘ [122] I. Tomescu, Probleme de combinatoric˘ a, a ¸si teoria grafurilor , Ed. Didactic˘a ¸si Pedagogic˘ Bucure¸sti, 1981.
a ¸si programare , Reprografia Universit˘ [123] I. Tutunea, Algoritmi, logic˘ a¸tii din Craiova, 1993. [124] I. Tutunea, S. Pesc˘ aru¸s, Algoritmi ¸si programe Pascal. (Culegere de probleme), Reprografia Universit˘ a¸t ii din Craiova, 1994. [125] J. Vuillemin, A data structure for manipulating priority queues , Communications of the ACM, vol. 21:4, pp. 309–315, 1978. 205