This shows you the differences between two versions of the page.
|
sd-ca:laboratoare:lab-08 [2025/05/19 18:03] roberto_giulio.pal [Exerciții] |
sd-ca:laboratoare:lab-08 [2026/05/11 22:09] (current) valentin.carauleanu [Interviu] |
||
|---|---|---|---|
| Line 4: | Line 4: | ||
| * [[stef.dascalu@gmail.com|Ștefan-Teodor Dascălu]] | * [[stef.dascalu@gmail.com|Ștefan-Teodor Dascălu]] | ||
| * [[melih.riza03@gmail.com|Melih Riza]] | * [[melih.riza03@gmail.com|Melih Riza]] | ||
| + | * [[valentin.carauleanu@stud.fim.upb.ro|Cărăuleanu Valentin Gabriel]] | ||
| ===== Obiective ===== | ===== Obiective ===== | ||
| Line 25: | Line 26: | ||
| ==== Componente conexe ==== | ==== Componente conexe ==== | ||
| - | 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''. | + | 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'' și implicit de la ''B'' la ''A''. |
| - | **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. | + | <note> |
| + | **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'' ar fi componenta conexă, iar ''G1'' nu. | ||
| + | </note> | ||
| === Algoritm === | === Algoritm === | ||
| - | * Atât o parcurgere ''BFS'', cât şi una ''DFS'', pornind dintr-un nod A, va determina componenta conexa din care face parte ''A''. | + | * Atât o parcurgere **BFS**, cât și una **DFS**, pornind dintr-un nod ''A'', va determina componenta conexă din care face parte ''A''. |
| - | * Pentru a determina toate componentele conexe ale unui graf ''G = (V, E)'', se vor parcurge nodurile din ''V''. | + | * Pentru a determina toate componentele conexe ale unui graf ''G = (V, E)'', se parcurg toate 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''. | + | * Din fiecare nod care nu face parte dintr-o componentă conexă găsită anterior se pornește o nouă parcurgere BFS sau DFS. |
| + | |||
| + | === Complexitate === | ||
| + | |||
| + | ^ Complexitate ^ Reprezentare prin liste de adiacență ^ Reprezentare prin matrice de adiacență ^ | ||
| + | ^ **Timp** | ''O(|V| + |E|)'' | ''O(|V|²)'' | | ||
| + | ^ **Spațiu** | ''O(|V| + |E|)'' | ''O(|V|²)'' | | ||
| === Pseudocod === | === Pseudocod === | ||
| Line 45: | Line 54: | ||
| componente_conexe = 0 | componente_conexe = 0 | ||
| - | // Funcţie de vizitare a nodului | + | // Funcție de vizitare a nodului |
| vizitare(nod) | vizitare(nod) | ||
| { | { | ||
| Line 56: | Line 65: | ||
| { | { | ||
| stiva s | stiva s | ||
| - | + | ||
| - | viziteaza nod | + | vizitare(nod) |
| s.introdu(nod) | s.introdu(nod) | ||
| - | | + | |
| cât timp stiva s nu este goală | cât timp stiva s nu este goală | ||
| { | { | ||
| - | nodTop = nodul din vârful stivei | + | nodTop = nodul din vârful stivei |
| - | | + | |
| - | vecin = află primul vecin nevizitat al lui nodTop. | + | vecin = află primul vecin nevizitat al lui nodTop |
| dacă vecin există | dacă vecin există | ||
| { | { | ||
| - | viziteaza v | + | vizitare(vecin) |
| - | s.introdu(v) | + | s.introdu(vecin) |
| - | } | + | } |
| altfel | altfel | ||
| { | { | ||
| Line 82: | Line 91: | ||
| dacă stare[u] == nevizitat | dacă stare[u] == nevizitat | ||
| { | { | ||
| - | componente_componente = componente_conexe + 1 | + | componente_conexe = componente_conexe + 1 |
| DFS(u) | DFS(u) | ||
| } | } | ||
| Line 92: | Line 101: | ||
| Graful din imagine are 4 componente conexe. | Graful din imagine are 4 componente conexe. | ||
| - | {{sd-ca:laboratoare:componenteConexe.png}} | + | {{sd-ca:laboratoare:componenteConexe.png?400}} |
| + | ==== Aflarea distanței minime între două noduri ==== | ||
| - | ==== Aflarea distanţei minime între două noduri ==== | + | 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. Deoarece BFS descoperă nodurile în ordinea crescătoare a distanței față de sursă, nivelul nodului ''B'' în parcurgere corespunde distanței minime între ''A'' și ''B''. |
| - | 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''. | + | Pentru a reține distanța și drumul exact de la ''A'' la ''B'', se păstrează pentru fiecare nod: |
| + | * ''d[x]'' - distanța de la sursă la nodul ''x'' | ||
| + | * ''p[x]'' - părintele lui ''x'' în drumul de la sursă spre ''x'' | ||
| - | 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: | + | În momentul descoperirii unui nod ''y'' al cărui părinte este ''x'', se fac atribuirile: |
| <code> | <code> | ||
| Line 105: | Line 117: | ||
| </code> | </code> | ||
| - | sursa având ''d[A] = 0'' și ''p[A] = NULL''. | + | Sursa având ''d[A] = 0'' și ''p[A] = NULL''. |
| - | Observații: | + | <note important> |
| - | *dacă parcurgerea BFS se încheie fără ca nodul B să fi fost descoperit, nu există drum între A și B și deci distanța între acestea este infinită. | + | * Dacă parcurgerea BFS se încheie fără ca nodul ''B'' să fi fost descoperit, nu există drum între ''A'' și ''B'', deci distanța 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 diferite, sunt necesari algoritmi mai avansați, cum ar fi: Dijkstra, Bellman-Ford sau Floyd-Warshall. | + | * Algoritmul funcționează corect **numai pentru grafuri cu cost uniform** (toate muchiile au același cost). Pentru grafuri cu muchii de costuri diferite sunt necesari algoritmi mai avansați: **Dijkstra**, **Bellman-Ford** sau **Floyd-Warshall**. |
| + | </note> | ||
| + | |||
| + | === Complexitate === | ||
| + | |||
| + | ^ Complexitate ^ Reprezentare prin liste de adiacență ^ Reprezentare prin matrice de adiacență ^ | ||
| + | ^ **Timp** | ''O(|V| + |E|)'' | ''O(|V|²)'' | | ||
| + | ^ **Spațiu** | ''O(|V| + |E|)'' | ''O(|V|²)'' | | ||
| === Pseudocod === | === Pseudocod === | ||
| Line 123: | Line 142: | ||
| // Distanța între sursă și destinație | // Distanța între sursă și destinație | ||
| - | distanța(sursă, destinație) | + | distanta(sursa, destinatie) |
| { | { | ||
| + | stare[sursa] = vizitat | ||
| + | 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 | // BFS | ||
| cât timp coada Q nu este vidă | cât timp coada Q nu este vidă | ||
| Line 140: | Line 158: | ||
| 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ă |
| } | } | ||
| } | } | ||
| - | return d[destinație] // Dacă este infinit, nu există drum | + | return d[destinatie] // Dacă este infinit, nu există drum |
| } | } | ||
| </code> | </code> | ||
| - | |||
| ==== Sortarea topologică ==== | ==== Sortarea topologică ==== | ||
| - | 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). | + | Se dă un graf orientat aciclic (**DAG** - Directed Acyclic Graph). 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'' apare înaintea lui ''v'' în înșiruire. |
| + | |||
| + | <note important> | ||
| + | Dacă graful este ciclic, sortarea topologică **nu este posibilă**, deoarece nu se poate stabili o ordine între nodurile care alcătuiesc un ciclu. | ||
| + | </note> | ||
| + | |||
| + | Sortarea topologică poate fi vizualizată ca plasarea nodurilor de-a lungul unei linii orizontale astfel încât toate muchiile să fie orientate de la stânga la dreapta, fără nicio muchie îndreptată înapoi spre un părinte. | ||
| + | |||
| + | === Algoritm === | ||
| + | |||
| + | Sortarea topologică se realizează printr-o parcurgere **DFS**, în care se rețin pentru fiecare nod: | ||
| + | * ''tDesc[u]'' - momentul descoperirii nodului ''u'' | ||
| + | * ''tFin[u]'' - momentul finalizării procesării nodului ''u'' | ||
| + | |||
| + | La final, nodurile sunt sortate **descrescător** după ''tFin''. Nodul care se finalizează cel mai târziu trebuie să apară primul în sortare, deoarece nu depinde de niciun alt nod nedescoperit încă. | ||
| + | |||
| + | === Complexitate === | ||
| - | 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 înapoi, spre părinte). | + | ^ Complexitate ^ Reprezentare prin liste de adiacență ^ Reprezentare prin matrice de adiacență ^ |
| + | ^ **Timp** | ''O(|V| + |E|)'' | ''O(|V|²)'' | | ||
| + | ^ **Spațiu** | ''O(|V| + |E|)'' | ''O(|V|²)'' | | ||
| === Pseudocod === | === Pseudocod === | ||
| Line 166: | Line 201: | ||
| contor_timp = 0 | contor_timp = 0 | ||
| - | // Funcţie de vizitare a nodului | + | // Funcție de vizitare a nodului |
| vizitare(nod) | vizitare(nod) | ||
| { | { | ||
| Line 175: | Line 210: | ||
| } | } | ||
| - | // Parcurgere în adâncime | + | // Parcurgere în adâncime (recursiv) |
| DFS(nod) | DFS(nod) | ||
| { | { | ||
| - | stiva s | + | vizitare(nod) |
| - | + | pentru fiecare vecin al lui nod | |
| - | viziteaza nod | + | |
| - | s.introdu(nod) | + | |
| - | | + | |
| - | cât timp stiva s nu este goală | + | |
| { | { | ||
| - | nodTop = nodul din vârful stivei | + | dacă stare[vecin] == nevizitat |
| - | + | ||
| - | vecin = află primul vecin nevizitat al lui nodTop. | + | |
| - | dacă vecin există | + | |
| - | { | + | |
| - | p[v] = nodTop | + | |
| - | viziteaza v | + | |
| - | s.introdu(v) | + | |
| - | } | + | |
| - | altfel | + | |
| { | { | ||
| - | contor_timp = contor_timp + 1 | + | p[vecin] = nod |
| - | tFin[nodTop] = contor_timp | + | DFS(vecin) |
| - | s.scoate(nodTop) | + | |
| } | } | ||
| } | } | ||
| + | contor_timp = contor_timp + 1 | ||
| + | tFin[nod] = contor_timp | ||
| } | } | ||
| Line 206: | Line 229: | ||
| pentru fiecare nod u din V | pentru fiecare nod u din V | ||
| { | { | ||
| - | dacă u nu a fost vizitat | + | dacă stare[u] == nevizitat |
| { | { | ||
| - | DFS(u) | + | DFS(u) |
| } | } | ||
| } | } | ||
| - | // Sortare topologica | + | // Sortare topologică |
| sortează nodurile din V descrescător în funcție de tFin[nod] | sortează nodurile din V descrescător în funcție de tFin[nod] | ||
| </code> | </code> | ||
| Line 218: | Line 241: | ||
| === Exemplu === | === Exemplu === | ||
| - | Profesorul Bumstead își sortează topologic hainele înainte de a se îmbrăca. | + | Profesorul Bumstead își sortează topologic hainele înainte de a se îmbrăca: |
| - | * 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. | + | * Fiecare muchie ''(u, v)'' înseamnă că obiectul de îmbrăcăminte ''u'' trebuie îmbrăcat înaintea obiectului ''v''. Timpii de descoperire ''tDesc'' și de finalizare ''tFin'' sunt notați lângă fiecare nod. |
| - | * 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. | + | * Același graf, sortat topologic: nodurile sunt aranjate de la stânga la dreapta în ordinea descrescătoare a ''tFin'', astfel toate muchiile sunt orientate de la stânga la dreapta. |
| - | + | ||
| - | {{sd-ca:laboratoare:topologie.jpg}} | + | |
| - | 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. | + | {{sd-ca:laboratoare:topologie.jpg?500}} |
| + | Sortarea topologică constă în sortarea nodurilor **descrescător după timpii de finalizare**. Nodul care se finalizează cel mai târziu nu depinde de niciun alt nod rămas, deci trebuie plasat primul în ordine topologică. | ||
| ==== Graf bipartit ==== | ==== Graf bipartit ==== | ||
| - | 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. | + | 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 ∪ B'' și ''E ⊆ A × B''. |
| + | |||
| + | Altfel spus, nodurile grafului se pot împărți în 2 mulțimi astfel încât **nu** există muchii între noduri din aceeași mulțime. | ||
| + | |||
| + | <note info>Observație: Un graf nu este **bipartit** dacă conține un **ciclu de lungime impară**. | ||
| + | </note> | ||
| === Algoritm === | === Algoritm === | ||
| - | * 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). | + | * Pentru a determina dacă un graf este bipartit, una din metode constă în efectuarea unei parcurgeri **BFS** și atribuirea de etichete nodurilor în funcție de paritatea nivelului acestora: ''A'' pentru nodurile de pe nivel par, ''B'' pentru nodurile de pe nivel impar. |
| - | * 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. | + | * Atunci când se adaugă vecinii nevizitați ai unui nod în coadă, se verifică și etichetele vecinilor deja vizitați: dacă un vecin deja vizitat are **aceeași etichetă** ca nodul curent, înseamnă că există o muchie între noduri de pe același nivel, deci graful **nu este 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. | + | * În caz contrar, dacă parcurgerea BFS se finalizează fără a apărea această situație, graful este bipartit și nodurile sunt etichetate cu mulțimea din care fac parte. |
| + | * Nodurile izolate pot fi atribuite oricăreia dintre cele două mulțimi, deci **nu afectează bipartitivitatea** grafului. | ||
| + | |||
| + | === Complexitate === | ||
| + | |||
| + | ^ Complexitate ^ Reprezentare prin liste de adiacență ^ Reprezentare prin matrice de adiacență ^ | ||
| + | ^ **Timp** | ''O(|V| + |E|)'' | ''O(|V|²)''| | ||
| + | ^ **Spațiu** | ''O(|V|)'' | ''O(|V|)'' | | ||
| === Pseudocod === | === Pseudocod === | ||
| Line 241: | Line 276: | ||
| cât timp încă sunt noduri nevizitate | cât timp încă sunt noduri nevizitate | ||
| { | { | ||
| - | n = primul nod nevizitat. | + | n = primul nod nevizitat |
| - | + | ||
| - | dacă n este izolat | + | |
| - | { | + | |
| - | return false | + | |
| - | } | + | |
| - | | + | |
| nivel[n] = par | nivel[n] = par | ||
| - | enqueue(Q, n) // Punem nodul sursă în coada Q | + | enqueue(Q, n) // Punem nodul sursă în coada Q |
| // BFS | // BFS | ||
| 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ă nivel[u] nedefinit | ||
| { | { | ||
| - | nivel[u] = (nivel[v] == par) ? impar : par | + | dacă nivel[u] nedefinit |
| - | enqueue(Q, u) // Adăugăm nodul u în coadă | + | { |
| - | } | + | nivel[u] = (nivel[v] == par) ? impar : par |
| - | altfel dacă nivel[u] == nivel[v] | + | enqueue(Q, u) // Adăugăm nodul u în coadă |
| - | { | + | } |
| - | // Două noduri consecutive au acelaşi nivel | + | altfel dacă nivel[u] == nivel[v] |
| - | // Graful nu este bipartit | + | { |
| - | return false | + | // Două noduri adiacente au același nivel |
| + | // Graful nu este bipartit | ||
| + | return false | ||
| + | } | ||
| } | } | ||
| } | } | ||
| } | } | ||
| - | + | // Parcurgerea BFS s-a finalizat fără noduri adiacente pe același nivel | |
| - | // S-a terminat parcurgerea BFS fără să apară două noduri consecutive pe acelaşi nivel | + | |
| // Graful este bipartit | // Graful este bipartit | ||
| return true | return true | ||
| Line 277: | Line 308: | ||
| === Exemplu === | === Exemplu === | ||
| - | {{sd-ca:laboratoare:bipartit.jpg}} | + | {{sd-ca:laboratoare:bipartite_realization.svg.png?400}} |
| ==== Ciclu hamiltonian ==== | ==== 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 **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, lanțul formează un **ciclu hamiltonian**. |
| - | Un graf ce conţine un ciclu hamiltonian se numeşte graf hamiltonian. | + | Un graf ce conține un ciclu hamiltonian se numește **graf hamiltonian**. |
| + | |||
| + | <note important> | ||
| + | Problema găsirii unui ciclu hamiltonian este **NP-completă**, ceea ce înseamnă că nu se cunoaște niciun algoritm eficient (polinomial) pentru cazul general. | ||
| + | </note> | ||
| === Algoritm === | === 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: | + | În cadrul acestui laborator, vom folosi metoda **backtracking** pentru găsirea unui ciclu hamiltonian. 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ă | + | * La fiecare pas, se adaugă unul dintre nodurile care nu se află deja în listă. |
| - | * Se construieşte recursiv lanţul de lungime_lanţ + 1 | + | * Se construiește recursiv lanțul de ''lungime_lant + 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. | + | * Dacă dimensiunea listei este ''n'', se verifică dacă există o muchie de la ultimul nod din listă către primul. Dacă nu există o astfel de muchie, lanțul curent nu poate fi închis într-un ciclu hamiltonian și se continuă cu backtracking. |
| - | * 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. | + | * Pentru a afla **toate** ciclurile hamiltoniene, la revenirea din apelul recursiv nu se iese din funcție la prima potrivire, ci se încearcă în continuare alte posibilități. |
| + | |||
| + | === Complexitate === | ||
| + | |||
| + | ^ Complexitate ^ Valoare ^ | ||
| + | ^ **Timp** | ''O(|V|!)'' în cazul cel mai defavorabil | | ||
| + | ^ **Spațiu** | ''O(|V|)'' pentru stiva de recursivitate și lanț | | ||
| === Pseudocod === | === Pseudocod === | ||
| Line 298: | Line 338: | ||
| <code> | <code> | ||
| // Inițializări | // Inițializări | ||
| - | număr_noduri = număr de noduri din V | + | numar_noduri = număr de noduri din V |
| - | // Verifica dacă un nod este nou în lanţ | + | // Verifică dacă un nod este nou în lanț |
| - | nouÎnLanţ(nod, lanţ) | + | nouInLant(nod, lant) |
| { | { | ||
| - | return !lanţ.conţine(nod) | + | return !lant.contine(nod) |
| } | } | ||
| - | // Construieste lanţul hamiltonian | + | // Construiește lanțul hamiltonian |
| - | construireLanţ(lanţ, lungime_lanţ) | + | construireLant(lant, lungime_lant) |
| { | { | ||
| - | dacă lungime_lanţ == număr_noduri | + | dacă lungime_lant == numar_noduri |
| { | { | ||
| - | început = lanţ[0] | + | inceput = lant[0] |
| - | sfârşit = ultimul element din lanţ | + | sfarsit = ultimul element din lant |
| - | + | // Verifică dacă există muchie de la sfarsit spre inceput | |
| - | // Există muchie între cele 2 noduri | + | dacă muchie(sfarsit, inceput) |
| - | dacă muchie(început, sfârşit) | + | |
| { | { | ||
| - | // Lanţul este ciclu | + | afiseaza ciclul |
| - | afişează ciclul | + | |
| return true | return true | ||
| } | } | ||
| Line 326: | Line 364: | ||
| pentru orice nod u din V | pentru orice nod u din V | ||
| { | { | ||
| - | sfârşit = ultimul element din lanţ | + | sfarsit = ultimul element din lant |
| - | dacă muchie(u, sfârşit) şi nouÎnLanţ(u, lanţ) | + | // Verifică dacă există muchie de la sfarsit spre u |
| + | dacă muchie(sfarsit, u) si nouInLant(u, lant) | ||
| { | { | ||
| - | addLast(lanţ, u) // Adaugă u la lanţ | + | addLast(lant, u) // Adaugă u la lanț |
| - | + | ||
| - | construireLanţ(lanţ, lungime_lanţ + 1) | + | |
| - | // Pentru afişarea unui singur ciclu hamiltonian linia anterioară este inlocuită cu: | + | construireLant(lant, lungime_lant + 1) |
| - | // dacă construireLanţ(lanţ, lungime_lanţ + 1) == true | + | // Pentru afișarea unui singur ciclu hamiltonian, |
| - | // return true | + | // linia anterioară se înlocuiește cu: |
| - | + | // dacă construireLant(lant, lungime_lant + 1) == true | |
| - | removeLast(lanţ, u) // Backtrack | + | // return true |
| + | |||
| + | removeLast(lant, u) // Backtrack | ||
| } | } | ||
| } | } | ||
| Line 345: | Line 384: | ||
| // Apelează construirea ciclurilor hamiltoniene | // Apelează construirea ciclurilor hamiltoniene | ||
| - | cicluriHamiltoniene | + | cicluriHamiltoniene() |
| { | { | ||
| - | // Din moment ce ar trebui să formeze un ciclu, lanţul poate incepe cu orice nod | + | // Din moment ce formează un ciclu, lanțul poate începe cu orice nod |
| - | sursă = alegem un nod aleator din V | + | sursa = alegem un nod aleator din V |
| - | addLast(lanţ, sursă) | + | addLast(lant, sursa) |
| - | construireLanţ(lanţ, 1) | + | construireLant(lant, 1) |
| } | } | ||
| </code> | </code> | ||
| - | |||
| === Exemplu === | === Exemplu === | ||
| - | {{sd-ca:laboratoare:hamilton.png}} | + | {{sd-ca:laboratoare:hamilton.png?400}} |
| - | + | ||
| - | ===== Schelet ===== | + | |
| - | <note important> | + | |
| - | Daca folositi **Github Classroom**, va rugam sa va actualizati scheletul cu cel de mai jos. Cel din repo-ul clonat initial nu este la cea mai recenta versiune. | + | |
| - | </note> | + | |
| - | + | ||
| - | {{:sd-ca:laboratoare:lab07_2024.zip|Scheletul de laborator}} | + | |
| ===== Exerciții ===== | ===== Exerciții ===== | ||
| <note> | <note> | ||
| - | Trebuie să vă creați cont de [[https://code.devmind.ro/ | Devmind]], 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-11 Grafuri (Advanced) ]]** | + | Trebuie să vă creați cont de [[https://code.devmind.ro/|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 problema **Connected Components**. | 1) [**3.5p**] Rezolvați problema **Connected Components**. | ||
| + | 2) [**3.5p**] Rezolvați problema **Minimum Path**. | ||
| + | 3) [**3p**] Rezolvați problema **Check Bipartite**. | ||
| + | ===== Interviu ===== | ||
| - | 2) [**3.5p**] Rezolvați problema **Minimum Path**. | + | 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 din materia prezentată în cadrul laboratorului. |
| + | === Probleme recomandate === | ||
| - | 3) [**3p**] Rezolvati problema **Check Bipartite**. | + | **Sortare topologică:** |
| + | * [[https://leetcode.com/problems/course-schedule-ii/description|210. Course Schedule II]] (returnarea ordinii topologice efective) | ||
| + | * [[https://leetcode.com/problems/longest-increasing-path-in-a-matrix/description|329. Longest Increasing Path in a Matrix]] (DAG implicit pe matrice) | ||
| + | **Componente conexe și grafuri bipartite:** | ||
| + | * [[https://leetcode.com/problems/is-graph-bipartite/description|785. Is Graph Bipartite?]] (verificare bipartitivitate cu BFS/DFS) | ||
| + | * [[https://leetcode.com/problems/accounts-merge/description|721. Accounts Merge]] (componente conexe cu Union-Find) | ||
| + | * [[https://leetcode.com/problems/satisfiability-of-equality-equations/description|990. Satisfiability of Equality Equations]] (componente conexe pe graf implicit) | ||
| + | **Drumuri minime:** | ||
| + | * [[https://leetcode.com/problems/network-delay-time/description|743. Network Delay Time]] (Dijkstra clasic) | ||
| + | * [[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]] (Floyd-Warshall) | ||
| + | * [[https://leetcode.com/problems/cheapest-flights-within-k-stops/description|787. Cheapest Flights Within K Stops]] (Bellman-Ford cu constrângeri) | ||
| + | **Probleme avansate:** | ||
| + | * [[https://leetcode.com/problems/reconstruct-itinerary/description|332. Reconstruct Itinerary]] (circuit eulerian pe graf orientat) | ||
| + | * [[https://leetcode.com/problems/critical-connections-in-a-network/description|1192. Critical Connections in a Network]] (punți în graf cu algoritmul Tarjan) | ||
| + | * [[https://leetcode.com/problems/swim-in-rising-water/description|778. Swim in Rising Water]] (BFS/Dijkstra pe matrice cu cost variabil) | ||
| ===== Bibliografie ===== | ===== Bibliografie ===== | ||