Differences

This shows you the differences between two versions of the page.

Link to this comparison view

sd-ca:laboratoare:lab-07 [2024/04/15 19:19]
melih.riza [Exerciții]
sd-ca:laboratoare:lab-07 [2026/05/12 18:39] (current)
roberto_giulio.pal [Exerciţii]
Line 1: Line 1:
-====== Laborator ​- Grafuri - Advanced  ​======+====== Laborator ​10 - Grafuri - Basics ​======
  
 Responsabili Responsabili
-  * [[stef.dascalu@gmail.com|Ștefan-Teodor Dascălu]] +  * [[alexiaops2014@gmail.com|Oprisan Alexia-Ioana]] 
-  * [[melih.riza03@gmail.com|Melih Riza]]+  * [[-@gmail.com|Macovei Nicolae-Cristian]] 
 +  * [[valentin.carauleanu@stud.fim.upb.ro|Cărăuleanu Valentin Gabriel]]
  
 ===== Obiective ===== ===== Obiective =====
  
 În urma parcurgerii acestui laborator, studentul va fi capabil să: În urma parcurgerii acestui laborator, studentul va fi capabil să:
-  * găsească soluțiile unor probleme folosind algoritmii de parcurgere 
-  * să folosească şi să adapteze algoritmii de parcurgere pentru implementarea soluţiilor găsite 
  
-===== Importanţă =====+  * înțeleagă operațiile de parcurgere a grafurilor și diferențele dintre ele. 
 +  * implementeze parcurgerile pe grafuri având la dispoziție structurile de date studiate. 
 +  * evalueze complexitatea parcurgerii grafurilor.
  
-Grafurile sunt utile pentru a modela diverse probleme şi se regăsesc implementaţi în multiple aplicaţii practice:+===== Ce este un graf? =====
  
-  * reţele ​de calculatoare ​(ex: stabilirea unei topologii fără bucle) +Un graf este o pereche ​de mulţimi ''​G = (V, E)''​. Mulțimea ''​V''​ conține nodurile grafului ​(**vertices**),​ iar mulțimea ''​E''​ conține muchiile sale (**edges**), fiecare muchie stabilind o relație de vecinătate între două noduri. Mulţimea ''​E''​ este inclusă în mulţimea ''​VxV''​.
-  * pagini Web (ex: Google PageRank) +
-  ​rețele sociale ​(ex: calcul centralitate) +
-  ​hărţi cu drumuri (ex: drum minim) +
-  * modelare grafică (ex: prefuse, graph-cut)+
  
-===== Aplicaţii parcurgeri =====+==== Diferenţa între graf orientat şi graf neorientat ​====
  
-==== Componente conexe ====+Dacă pentru orice element al mulţimii ''​E'',​ ''​e ​(u, v)'',​ elementul ''​e' ​(v, u)''​ aparţine de asemenea mulţimii ''​E'',​ atunci spunem că graful este **neorientat**. În caz contrar, graful este **orientat**. În cazul grafului orientat, muchiile se mai numesc şi arce.
  
-Se numeşte componentă conexă a unui graf neorientat ​''​G = (V, E)''​ un subgraf ''​G1 = (V1, E1)''​ în care pentru orice pereche de noduri ''​(A,​ B)''​ din V1 există un lanţ de la ''​A''​ la ''​B'',​ implicit şi de la ''​B''​ la ''​A''​.+{{sd-ca:​laboratoare:​neorientat.png}} {{sd-ca:​laboratoare:​orientat.png}}
  
-**Observaţie** Nu există un alt subgraf al lui ''​G'',​ ''​G2 ​(V2, E2)''​ care să îndeplinească această condiţie şi care să îl conţină pe ''​G1''​. În acest caz, ''​G2''​ va fi componenta conexă, iar ''​G1''​ nu.+===== Reprezentările grafurilor în memorie =====
  
-=== Algoritm ===+În funcţie de problemă şi de tipul grafurilor, avem 2 reprezentări:​ liste de adiacenţă sau matrice de adiacenţă.
  
-  * Atât o parcurgere ''​BFS'',​ cât şi una ''​DFS'',​ pornind dintr-un nod A, va determina componenta conexa din care face parte ''​A''​. +== Liste de adiacență ==
-  * Pentru a determina toate componentele conexe ale unui graf ''​G ​(V, E)'',​ se vor parcurge nodurile din ''​V''​. +
-  * Din fiecare nod care nu face parte dintr-o componentă conexă găsită anterior, se va porni o parcurgere ''​BFS''​ sau ''​DFS''​.+
  
-=== Pseudocod ===+Reprezentarea prin **liste de adiacență** folosește un tablou ''​Adj''​ cu ''​|V|''​ liste, câte una pentru fiecare vârf din ''​V''​. Pentru fiecare vârf ''​u''​ din ''​V'',​ lista de adiacență ''​Adj[u]''​ conține referințe către toate vârfurile ''​v''​ pentru care există muchia ''​(u,​ v)''​ în ''​E''​. ​
  
-<​code>​ +Cu alte cuvinte, ''​Adj[u]''​ conține totalitatea vârfurilor adiacente lui ''​u''​ în graful ''​G''​.
-// Inițializări +
-pentru fiecare nod u din V +
-+
-    stare[u] = nevizitat +
-+
-componente_conexe = 0+
  
-// Funcţie de vizitare a nodului +Această reprezentare este preferată pentru **grafurile rare**, adică atunci când ''​|E|''​ este mult mai mic decât ''​|V| × |V|''​.
-vizitare(nod) +
-+
-    stare[nod] = vizitat +
-    printeaza nod +
-}+
  
-// Parcurgerea în adâncime +{{sd-ca:​laboratoare:​undirected_graph.gif}}
-DFS(nod) +
-{ +
-    stiva s +
-      +
-    viziteaza nod  +
-    s.introdu(nod) +
-     +
-    cât timp stiva s nu este goală +
-    ​{ +
-        nodTop = nodul din vârful stivei  +
-                +
-        vecin = află primul vecin nevizitat al lui nodTop. +
-        dacă vecin există +
-        { +
-            viziteaza v +
-            s.introdu(v) +
-        }   +
-        altfel +
-        { +
-            s.scoate(nodTop) +
-        } +
-    ​} +
-}+
  
-// Parcurgerea nodurilor din V +Pentru graful de mai sus, lista de adiacență este:
-pentru fiecare nod u din V +
-+
-    dacă stare[u] == nevizitat +
-    { +
-        componente_componente = componente_conexe + 1 +
-        DFS(u) +
-    } +
-+
-</​code>​+
  
-=== Exemplu ​===+  * **0**: 1->2 
 +  * **1**: 0->​2->​3->​4 
 +  * **2**: 0->​1->​3 
 +  * **3**: 1->​2->​4 
 +  * **4**: 1->3 
 +==== Matrice de adiacență ====
  
-Graful din imagine are 4 componente conexe.+Reprezentarea prin **matrice de adiacență** a unui graf constă într-o matrice ''​A[i][j]''​ de dimensiune ''​|V| × |V|''​ astfel încât: 
 +  * ''​A[i][j] = 1'',​ dacă muchia ''​(i,​j)''​ aparține lui ''​E''​ 
 +  * ''​A[i][j] = 0'',​ în caz contrar.
  
-{{sd-ca:​laboratoare:​componenteConexe.png}}+Această reprezentare este preferată pentru **grafurile dense**, adică atunci când ''​|E|''​ este aproximativ egal cu ''​|V| × |V|''​.
  
-==== Aflarea distanţei minime între două noduri ====+Pentru graful de mai sus, matricea de adiacență este următoarea:​
  
-Dacă toate muchiile au același cost, putem afla distanța minimă între două noduri ''​A''​ și ''​B''​ efectuând o parcurgere ''​BFS''​ din nodul ''​A''​ și oprindu-ne atunci când nodul ''​B''​ a fost descoperit. Reamintindu-ne că nivelul unui nod este analog distanței, în muchii, față de sursă, și că ''​BFS''​ descoperă un nod de pe nivelul ''​N''​ numai după ce toate nodurile de pe nivele inferioare au fost descoperite,​ este ușor de văzut că nivelul nodului ''​B''​ în parcurgere corespunde distanței minime între ''​A''​ și ''​B''​.+^ ^0^1^2^3^4^ 
 +^0|0|1|1|0|0| 
 +^1|1|0|1|1|1| 
 +^2|1|1|0|1|0| 
 +^3|0|1|1|0|0| 
 +^4|0|1|0|1|0|
  
-Pentru a reține distanța și drumul exact de la ''​A''​ la ''​B'',​ se vor reține pentru fiecare nod ''​d[x]'' ​(distanța de la ''​sursă''​ la ''​x''​) și ''​p[x]''​ (părintele lui ''​x''​ în drumul ​de la sursă spre ''​x''​). În momentul descoperirii unui nod ''​y''​ al cărui părinte este ''​x'',​ se vor face următoarele atribuiri:+<note important>​ 
 +În general, preferăm reprezentarea prin **liste ​de adiacență** deoarece oferă o complexitate mai bună în cazul parcurgerilor ​(cea mai comună operație pe grafuri). Totuși, pentru **grafurile dense**, reprezentarea prin **matrice ​de adiacență** devine avantajoasă din mai multe motive:
  
-<​code>​ +  * **Acces direct**: verificarea existenței unei muchii ''​(i,​j)''​ se face în ''​O(1)'',​ față de ''​O(grad(i))''​ în cazul listelor de adiacență. 
-d[y] = d[x] + +  * **Simplitate**:​ mulți algoritmi pe grafuri dense sunt mai ușor de implementat și de înțeles cu o matrice de adiacență.
-p[y] = x +
-</​code>​+
  
-sursa având ''​d[A= 0'' ​și ''​p[A] = NULL''​.+Un exemplu concret este algoritmul ​[[https://​en.wikipedia.org/​wiki/​Floyd%E2%80%93Warshall_algorithm|Floyd-Warshall]], care calculează distanțele minime între toate perechile de noduri. Acesta se bazează pe accesul rapid la distanța dintre două noduri, reținând adițional ​și costurile muchiilor direct în matrice. Pe un graf dens, complexitatea ​''​O(|V|³)'' ​a algoritmului este dificil de îmbunătățit oricum, deci costul de memorie ''​O(|V|²)''​ al matricei devine acceptabil. 
 +</​note>​ 
 +===== Parcurgerea grafurilor =====
  
-Observații+==== Parcurgerea în lăţime ==== 
-  *dacă parcurgerea BFS se încheie fără ca nodul B să fi fost descoperitnu există drum între A și și deci distanța ​între acestea este infinită. + 
-  *Algoritmul funcționează corect numai în situații de cost uniform ​(toate muchiile au același cost). Pentru ​grafuri cu muchii de costuri diferitesunt necesari algoritmi mai avansați, cum ar fi: Dijkstra, Bellman-Ford sau Floyd-Warshall.+Parcurgerea în lățime (**Breadth-first Search - BFS**) presupune vizitarea nodurilor în următoarea ordine
 +  * nodul sursă (considerat a fi pe nivelul 0) 
 +  * vecinii nodului sursă (aceștia constituind nivelul 1) 
 +  * vecinii încă nevizitați ai nodurilor de pe nivelul 1 (aceștia constituind nivelul 2) 
 +  * vecinii încă nevizitați ai nodurilor de pe nivelul 2 
 +  * ş.a.m.d.  
 + 
 +Caracteristica esențială a acestui tip de parcurgere este, deci, că se preferă explorarea **în lățime**, a nodurilor de pe același nivel (aceeași depărtare față de sursă) în detrimentul celei **în adâncime**a nodurilor de pe nivelul următor. 
 + 
 +=== Pași de execuție === 
 + 
 +  * colorarea nodurilor. Pe parcurs ce algoritmul avansează, se colorează nodurile în felul următor: 
 +    ***alb** - nodul este nedescoperit încă 
 +    ***gri** - nodul a fost descoperit ​și este în curs de procesare 
 +    ***negru** - procesarea nodului s-a încheiat  
 +  * păstrarea informațiilor despre ​distanța ​până la nodul sursă. 
 +    pentru fiecare nod în ''​d[u]''​ se reține distanța până la nodul sursă (poate fi util în unele probleme)  
 +  * obținerea arborelui BFS. 
 +    * în urma aplicării algoritmului BFS se obține un arbore ​de acoperire ​(prin eliminarea muchiilor pe care nu le folosim la parcurgere). Pentru ​a putea reconstitui acest arborese păstrează pentru fiecare nod dat informația despre părintele său în ''​p[u]''​
  
 === Pseudocod === === Pseudocod ===
 +
 +Pentru implementarea BFS se utilizează o coadă (''​Q''​) în care inițial se află doar nodul sursă. Se vizitează pe rând vecinii acestui nod şi se pun și ei în coada. În momentul în care nu mai există vecini nevizitați,​ nodul sursă este scos din coadă.
  
 <​code>​ <​code>​
Line 117: Line 102:
 pentru fiecare nod u din V pentru fiecare nod u din V
 { {
-    ​stare[u] = nevizitat+    ​culoare[u] = alb
     d[u] = infinit     d[u] = infinit
     p[u] = null     p[u] = null
 } }
  
-// Distanța între sursă și destinație +culoare[sursa] = gri 
-distanța(sursă,​ destinație) +d[sursa] = 0 
-+enqueue(Q,sursa) // Punem nodul sursă în coada Q
- +
-    stare[sursă] = vizitat +
-    d[sursă] = 0 +
-    enqueue(Q,sursă               // Punem nodul sursă în coada Q+
    
-    ​// BFS +// Algoritmul propriu-zis 
-    cât timp coada Q nu este vidă +cât timp coada Q nu este vidă 
-    +
-        v = dequeue(Q) ​             // Extragem nodul v din coadă +    v = dequeue(Q) ​      ​// Extragem nodul v din coadă 
-        pentru fiecare u dintre vecinii lui v +    pentru fiecare u dintre vecinii lui v 
-            dacă stare[u] == nevizitat +        dacă culoare[u] == alb 
-            +        
-                stare[u] = vizitat +            ​culoare[u] = gri 
-                p[u] = v +            p[u] = v 
-                d[u] = d[v] + 1 +            d[u] = d[v] + 1 
-                enqueue(Q,​u) ​       // Adăugăm nodul u în coadă +            enqueue(Q,​u) // Adăugăm nodul u în coadă 
-            +        
-    ​+    ​culoare[v= negru   // Am terminat de explorat toți vecinii lui v
-    return d[destinație           // Dacă este infinit, nu există drum+
 } }
 </​code>​ </​code>​
  
-==== Sortarea topologică ====+Dacă graful are mai multe componente conexe, algoritmul, în forma dată, va parcurge doar componenta din care face parte nodul sursă. Pe grafuri cu mai multe componente conexe se va aplica în continuare algoritmul pentru fiecare nod rămas nevizitat și astfel se vor obține mai mulți arbori, câte unul pentru fiecare componentă.  
 + 
 +=== Exemplu ​=== 
 + 
 +{{sd-ca:​laboratoare:​bf1.jpg}} 
 +  
 +{{sd-ca:​laboratoare:​bf2.jpg}}
  
-Se dă un graf orientat aciclic. Orientarea muchiilor corespunde unei relații de ordine de la nodul sursă către cel destinație. O sortare topologică a unui astfel de graf este o ordonare liniară a vârfurilor sale astfel încât, dacă ''​(u,​v)''​ este una dintre muchiile grafului, ''​u''​ trebuie să apară înaintea lui ''​v''​ în înșiruire. Dacă graful ar fi ciclic, nu ar putea exista o astfel de înșiruire (nu se poate stabili o ordine între nodurile care alcătuiesc un ciclu).+Arborele obținut în urma execuției este următorul:
  
-Sortarea topologică poate fi văzută și ca plasarea nodurilor ​de-a lungul unei linii orizontale astfel încât toate muchiile ​să fie direcționate ​de la stânga la dreapta ​(să nu existe nici o muchie înapoispre rinte).+{{sd-ca:​laboratoare:​bf3.jpg}} 
 + 
 +==== Parcurgerea în adâncime ==== 
 + 
 +Parcurgerea în adâncime (**Depth-First Search - DFS**) presupune explorarea nodurilor în următoarea ordine: 
 +  * nodul sursă 
 +  * primul vecin nevizitat al nodului sursă (îl vom numi ''​V1''​) 
 +  * primul vecin nevizitat al lui ''​V1''​ (îl vom numi ''​V2''​) 
 +  * primul vecin nevizitat al lui ''​V2''​ 
 +  * s.a.m.d. 
 +  * în momentul în care am epuizat vecinii unui nod ''​Vn'',​ continuăm cu următorul vecin nevizitat al nodului anterior, ''​Vn-1''​  
 + 
 +Așadar, spre deosebire de BFS, acest tip de parcurgere pune prioritate pe explorarea **în adâncime** (la distanțe tot mai mari față de nodul sursă), în detrimentul celei **în lățime** (pe același nivel).  
 + 
 +=== Pași de execuție === 
 + 
 +  * colorarea nodurilor. Pe parcurs ce algoritmul avansează, se colorează nodurile in felul următor: 
 +    ***alb** - nodul este nedescoperit încă 
 +    ***gri** ​nodul fost descoperit și este în curs de procesare 
 +    ***negru** - procesarea nodului ​s-a încheiat  
 +  * păstrarea informațiilor despre timp. Fiecare nod are două momente ​de timp asociate: 
 +    *''​tDesc[u]''​ - momentul descoperirii nodului ​(și a schimbării culorii din alb în gri) 
 +    *''​tFin[u]''​ - momentul în care procesarea nodului ​s-a încheiat (și culoarea acestuia s-a schimbat din gri în negru)  
 +  * obținerea arborelui DFS. 
 +    * în urma aplicării algoritmului DFS asupra fiecărei componente conexe a grafului, se obține pentru fiecare dintre acestea câte un arbore de acoperire (prin eliminarea muchiilor pe care nu le folosim la parcurgere). Pentru a putea reconstitui acest arbore, păstram pentru fiecare nod dat informația despre părintele său în ''​p[u]''​.
  
 === Pseudocod === === Pseudocod ===
Line 159: Line 169:
 pentru fiecare nod u din V pentru fiecare nod u din V
 { {
-    ​stare[u] = nevizitat+    ​culoare[u] = alb
     p[u] = NULL     p[u] = NULL
     tDesc[u] = 0     tDesc[u] = 0
Line 171: Line 181:
     contor_timp = contor_timp + 1     contor_timp = contor_timp + 1
     tDesc[nod] = contor_timp     tDesc[nod] = contor_timp
-    ​stare[nod] = vizitat +    ​culoare[nod] = gri 
-    printeaza nod+    printeaza nod;
 } }
  
-// Parcurgere în adâncime+// Algoritmul propriu-zis
 DFS(nod) DFS(nod)
 { {
-    stiva s+    stiva s;
            
-    viziteaza nod  +    viziteaza nod 
-    s.introdu(nod)+    s.introdu(nod);
     ​     ​
     cât timp stiva s nu este goală     cât timp stiva s nu este goală
Line 191: Line 201:
         {         {
             p[v] = nodTop             p[v] = nodTop
-            viziteaza v +            viziteaza v; 
-            s.introdu(v)+            s.introdu(v);
         }  ​         }  ​
         altfel         altfel
Line 198: Line 208:
             contor_timp = contor_timp + 1             contor_timp = contor_timp + 1
             tFin[nodTop] = contor_timp             tFin[nodTop] = contor_timp
-            s.scoate(nodTop) ​  +            ​culoare[nodTop] = negru   
-        }+            ​s.scoate(nodTop);    
 +        }     ​
     }     }
 } }
- 
-// Parcurgere noduri și calculare tDesc și tFin pentru fiecare nod 
-pentru fiecare nod u din V 
-{ 
-    dacă u nu a fost vizitat 
-    { 
-         ​DFS(u) 
-    } 
-} 
- 
-// Sortare topologica 
-sortează nodurile din V descrescător în funcție de tFin[nod] 
 </​code>​ </​code>​
  
 === Exemplu === === Exemplu ===
  
-Profesorul Bumstead își sortează topologic hainele înainte de a se îmbrăca. +{{sd-ca:​laboratoare:​df1.jpg}}
-  * fiecare muchie ''​(u,​ v''​) înseamna că obiectul de îmbrăcăminte ''​u''​ trebuie îmbrăcat înaintea obiectului de îmbrăcaminte ''​v''​. Timpii de descoperire ''​(tDesc)''​ și de finalizare ''​(tFin)''​ obținuți în urma parcurgerii DFS sunt notați lângă noduri. +
-  * același graf, sortat topologic. Nodurile lui sunt aranjate de la stânga la dreapta în ordinea descrescătoare a ''​tFin''​. Observați că toate muchiile sunt orientate de la stânga la dreapta. Acum profesorul Bumstead se poate îmbrăca liniștit. ​+
  
-{{sd-ca:​laboratoare:​topologie.jpg}}+Nodul de pornire este I, iar pentru simplificare vecinii sunt aleși în ordine alfabeticăÎn stânga nodului este notat ''​tDesc'',​ iar în dreapta ''​tFin''​. Dacă se afișează nodurile, în urma parcurgerii se obține următorul output: **I, E, B, A, C, D, G, F, H**
  
-Așa cum se observă din imagine, sortarea topologică constă ​în sortarea nodurilor descrescător după timpii de finalizare. Demonstrația acestei afirmații se face simplu, arătând că nodul care se termină mai târziu trebuie să fie efectuat înaintea celorlalte noduri finalizate.+Arborele obținut ​în urma parcurgerii este următorul:
  
-==== Graf bipartit ====+{{sd-ca:​laboratoare:​df2.jpg}}
  
-Se numește graf bipartit un graf ''​G ​(V, E)''​ în care mulțimea nodurilor poate fi împărțită în două mulțimi disjuncte ''​A''​ și ''​B''​ astfel încât ''​V ​A U B''​ şi ''​E este inclus în A x B''​ (orice muchie leagă un nod din ''​A''​ cu un nod din ''​B''​). Dacă un graf conține noduri izolate, acesta nu este bipartit.+==== Complexitate ====
  
-=== Algoritm ===+Pentru ambele tipuri de parcurgeri, complexitatea este ''​O(|E|+|V|)''​ - unde ''​|E|''​ este numărul de muchii, iar ''​|V|''​ este numărul de noduri.
  
-  ​Pentru a determina dacă un graf este bipartit sau nu, una din metode constă în efectuarea de parcurgeri ''​BFS''​ și atribuirea de etichete nodurilor conform cu paritatea nivelului acestora în parcurgere (''​A''​ pentru nodurile de pe nivel par, ''​B''​ pentru nodurile de pe nivel impar).  +**Explicație**: în cazul cel mai defavorabil, vor fi explorate toate muchiile ​și toate nodurile ​(când graful este liniarizat).
-  ​Atunci când se adaugă vecinii nevizitați ai unui nod în coadăse vor verifica de asemenea etichetele vecinilor deja vizitați: dacă se descoperă că unul din aceștia are aceeași etichetă ca cea atribuită nodului curent, graful are o muchie între noduri de pe același nivel și deci nu poate fi bipartit.  +
-  * În caz contrar ​(s-a realizat parcurgerea ''​BFS''​ fără a apărea această situație), ​graful este bipartit și nodurile sunt etichetate cu mulțimea din care fac parte.+
  
-=== Pseudocod === 
- 
-<​code>​ 
-cât timp încă sunt noduri nevizitate 
-{ 
-    n = primul nod nevizitat. 
-    ​ 
-    dacă n este izolat ​ 
-    { 
-        return false 
-    } 
-    ​ 
-    nivel[n] = par 
-    enqueue(Q, n)  // Punem nodul sursă în coada Q 
- 
-    // BFS 
-    cât timp coada Q nu este vidă 
-    { 
-        v = dequeue(Q) ​          // Extragem nodul v din coadă 
-        pentru fiecare u dintre vecinii lui v 
-        dacă nivel[u] nedefinit 
-        { 
-            nivel[u] = (nivel[v] == par) ? impar : par 
-            enqueue(Q, u)    // Adăugăm nodul u în coadă 
-        } 
-        altfel dacă nivel[u] == nivel[v] 
-        { 
-            // Două noduri consecutive au acelaşi nivel 
-            // Graful nu este bipartit 
-            return false 
-        } 
-    } 
-} 
- 
-// S-a terminat parcurgerea BFS fără să apară două noduri consecutive pe acelaşi nivel 
-// Graful este bipartit 
-return true 
-</​code>​ 
- 
-=== Exemplu === 
- 
-{{sd-ca:​laboratoare:​bipartit.jpg}} 
- 
-==== Ciclu hamiltonian ==== 
- 
-Un lanţ hamiltonian într-un graf orientat sau neorientat ''​G = (V, E)'',​ este o cale ce trece prin fiecare nod din ''​V''​ o singură dată. Dacă nodul de început şi cel de sfârşit coincid (este vizitat de două ori) vom spune că lanţul formează un **ciclu hamiltonian**. 
- 
-Un graf ce conţine un ciclu hamiltonian se numeşte graf hamiltonian. 
- 
-=== Algoritm === 
- 
-În cadrul acestui laborator, vom folosi metoda backtracking pentru găsirea unui ciclu hamiltonian. Pentru contruirea soluţiei, se menţine o listă în care sunt adăugate nodurile parcurse: 
- 
-  * La fiecare pas, vom adăuga unul dintre nodurile care nu se află deja in listă 
-  * Se construieşte recursiv lanţul de lungime_lanţ + 1 
-  * Dacă dimensiunea listei este ''​n''​ (numărul de noduri din graf), se verifică dacă primul şi ultimul nod din listă sunt adiacente. În caz contrar, s-a găsit un lanţ hamiltonian,​ dar nu şi un ciclu hamiltonian. 
-  * Pentru a afla toate ciclurile hamiltoniene,​ la revenirea cu succes din apelul recursiv nu se iese din funcţie la găsirea primei potriviri, ci se încearcă în continuare alte posibilităţi. 
- 
-=== Pseudocod === 
- 
-<​code>​ 
-// Inițializări 
-număr_noduri = număr de noduri din V 
- 
-// Verifica dacă un nod este nou în lanţ 
-nouÎnLanţ(nod,​ lanţ) 
-{ 
-    return !lanţ.conţine(nod) 
-} 
- 
-// Construieste lanţul hamiltonian 
-construireLanţ(lanţ,​ lungime_lanţ) 
-{ 
-    dacă lungime_lanţ == număr_noduri 
-    { 
-        început = lanţ[0] 
-        sfârşit = ultimul element din lanţ 
- 
-        // Există muchie între cele 2 noduri 
-        dacă muchie(început,​ sfârşit) 
-        { 
-            // Lanţul este ciclu 
-            afişează ciclul 
-            return true 
-        } 
-    } 
-    altfel 
-    { 
-        pentru orice nod u din V 
-        { 
-            sfârşit = ultimul element din lanţ 
-            dacă muchie(u, sfârşit) şi nouÎnLanţ(u,​ lanţ) 
-            { 
-                addLast(lanţ,​ u)    // Adaugă u la lanţ 
-                ​ 
-                construireLanţ(lanţ,​ lungime_lanţ + 1) 
- 
-                // Pentru afişarea unui singur ciclu hamiltonian linia anterioară este inlocuită cu: 
-                // dacă construireLanţ(lanţ,​ lungime_lanţ + 1) == true 
-                //       ​return true 
-                ​ 
-                removeLast(lanţ,​ u) // Backtrack 
-            } 
-        } 
-    } 
-    return false 
-} 
- 
-// Apelează construirea ciclurilor hamiltoniene 
-cicluriHamiltoniene 
-{ 
-    // Din moment ce ar trebui să formeze un ciclu, lanţul poate incepe cu orice nod 
-    sursă = alegem un nod aleator din V 
-    addLast(lanţ,​ sursă) 
-    construireLanţ(lanţ,​ 1) 
-} 
-</​code>​ 
- 
- 
-=== Exemplu === 
- 
-{{sd-ca:​laboratoare:​hamilton.png}} 
- 
-===== Schelet ===== 
 <note important>​ <note important>​
-Daca folositi ​**Github Classroom**, va rugam sa va actualizati scheletul cu cel de mai josCel din repo-ul clonat initial nu este la cea mai recenta versiune.+De remarcat faptul că, pentru ambele tipuri de parcurgeri, complexitatea este cea menționată ''​O(|E|+|V|)'' ​**numai în cazul în care grafurile sunt reținute ca liste de adiacență**. În acest cazlista corespunzătoare nodului x reține numai vecinii nodului x.  
 +În cazul matricei ​de adiacență,​ pentru a parcurge vecinii unui nod x, trebuie să parcurgem toate nodurileAceastă limitare duce la o complexitate de ''​O(|V|^2)''​
 </​note>​ </​note>​
  
-{{:​sd-ca:​laboratoare:​lab07_2024.zip|Scheletul de laborator}} 
  
-===== Exerciții =====+===== Exerciţii =====
  
 <​note>​ <​note>​
-Trebuie să vă creați cont de [[https://​lambdachecker.io | Lambda Checker]], dacă nu v-ați creat deja, pe care îl veți folosi la SD pe toată durata semestrului. ​Aveti grija sa selectati contestul corect la submit, si anume **[[https://​beta.lambdachecker.io/​contest/​34 |SD-CA-LAB-07 Grafuri (Advanced) ]]**+Trebuie să vă creați cont de [[https://​lambdachecker.io | Devmind]], dacă nu v-ați creat deja, pe care îl veți folosi la SD pe toată durata semestrului.
 </​note>​ </​note>​
  
-1) [**3.5p**] ​Rezolvați o problemă **aleasă de catre asistent** din cele menționate mai sus care sunt prezente ​pe LambdaChecker (componente conexe, sortare topologică,​ drum minim, graf bipartit).+1) [**2.5p**] ​Implementați, plecând de la scheletul de cod, graful reprezentat prin matrice de adiacență (**Graph implementation using list** pe Devmind).
  
 +2) [**2.5p**] Implementați,​ plecând de la scheletul de cod, graful reprezentat prin liste de adiacență (**Graph implementation using matrix** pe Devmind).
  
-2) [**3.5p**] Rezolvați o altă problemă ,**aleasă de voi**, din cele menționate mai sus care sunt prezente ​pe LambdaChecker (componente conexe, sortare topologică,​ drum minim, graf bipartit).+3) [**1p**] Implementați parcurgerea in adâncime (DFS) a unui graf implementat cu matrice de adiacență (**DFS** pe Devmind).
  
 +4) [**1p**] Implementați parcurgerea in lățime (BFS) a unui graf implementat cu listă de adiacență (**BFS** pe Devmind).
  
-3) [**2p bonus**] Implementați Courses II pe LambdaChecker.+===== Interviu =====
  
 +Această secțiune nu este punctată și încearcă să vă ofere o idee despre tipurile de întrebări pe care le puteți întâlni la un job interview (internship,​ part-time, full-time, etc.) din materia prezentată în cadrul laboratorului.
  
-4[**Bonus**] Veți primi 1p bonus pentru fiecare problemă rezolvată în plus pe LambdaCheckersau pe care trimiteți asistentului din urmatoarele probleme: [[https://​leetcode.com/​problems/​number-of-islands/​description/​]];​ [[https://​leetcode.com/​problems/​number-of-provinces/​description/​]].+Multe dintre companiile mari folosesc date stocate sub formă de grafuri (Facebook Open Graph, Google Social Graph și Page Rank, etc.), astfel că la angajare vor dori să vadă că știți să lucrați cu grafuri: 
 +  ​cum se reprezintă grafurile (liste de adiacență vs. matrice de adiacență) 
 +  ​cum funcționează șcum se implementează parcurgerile (BFSDFS) 
 +  * când este preferată ​reprezentare față de cealaltă 
 +  * complexitatea timp șspațiu a algoritmilor de parcurgere
  
 +=== Probleme recomandate ===
  
-**Punctajul maxim care se poate obține incluzând bonusul pe exercițiile din acest laborator este de 12p.** +**Ușoare:** 
 +  * [[https://​leetcode.com/​problems/​find-if-path-exists-in-graph/​description|1971. Find if Path Exists in Graph]] - verificarea existenței unui drum între două noduri (BFS/DFS) 
 +  * [[https://​leetcode.com/​problems/​flood-fill/​description|733. Flood Fill]] - parcurgere BFS/DFS pe o matrice 
 +  * [[https://​leetcode.com/​problems/​number-of-islands/​description|200. Number of Islands]] - identificarea componentelor conexe
  
 +**Medii:**
 +  * [[https://​leetcode.com/​problems/​clone-graph/​description|133. Clone Graph]] - clonarea unui graf folosind BFS/DFS
 +  * [[https://​leetcode.com/​problems/​rotting-oranges/​description|994. Rotting Oranges]] - BFS multi-sursă
 +  * [[https://​leetcode.com/​problems/​course-schedule/​description|207. Course Schedule]] - detectarea ciclurilor într-un graf orientat
 +  * [[https://​leetcode.com/​problems/​number-of-provinces/​description|547. Number of Provinces]] - componente conexe cu BFS/DFS
 +  * [[https://​leetcode.com/​problems/​shortest-path-in-binary-matrix/​description|1091. Shortest Path in Binary Matrix]] - cel mai scurt drum cu BFS
  
 +**Grele:**
 +  * [[https://​leetcode.com/​problems/​word-ladder/​description|127. Word Ladder]] - BFS pe graf implicit
 +  * [[https://​leetcode.com/​problems/​find-the-city-with-the-smallest-number-of-neighbors-at-a-threshold-distance/​description|1334. Find the City with the Smallest Number of Neighbors at a Threshold Distance]] - Floyd-Warshall aplicat
  
 ===== Bibliografie ===== ===== Bibliografie =====
  
-    - [[http://​en.wikipedia.org/​wiki/​Connected_component_(graph_theory) | Componente conexe ]] +    - [[http://​en.wikipedia.org/​wiki/​Breadth-first_search ​BFS]] 
-    ​[[http://​en.wikipedia.org/​wiki/​Shortest_path ​Distanţa minimă ​]] +    - [[http://​en.wikipedia.org/​wiki/​Depth-first_search ​DFS ]] 
-    - [[http://​en.wikipedia.org/​wiki/​Topological_sorting | Sortare topologică ]] +    - [[https://​en.wikipedia.org/​wiki/​Floyd%E2%80%93Warshall_algorithm | Algoritmul ​Floyd-Warshall ]]
-    ​[[https://​en.wikipedia.org/​wiki/​Bipartite_graph ​Graf bipartit ​]] +
-    - [[https://​en.wikipedia.org/​wiki/​Hamiltonian_path | Lanţ hamiltonian si ciclu hamiltonian ]] +
-    - [[http://​en.wikipedia.org/​wiki/​Dijkstra%27s_algorithm | Dijkstra ]] +
-    - [[http://​en.wikipedia.org/​wiki/​Bellman-ford | Bellman-Ford ]] +
-    - [[http://​en.wikipedia.org/​wiki/​Floyd%E2%80%93Warshall_algorithm | Floyd-Warshall ]] +
-    - [[http://​en.wikipedia.org/​wiki/​A*_search_algorithm | A* ]] +
sd-ca/laboratoare/lab-07.1713197992.txt.gz · Last modified: 2024/04/15 19:19 by melih.riza
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0