This is an old revision of the document!
Responsabili
În urma parcurgerii acestui laborator, studentul va fi capabil să:
Grafurile sunt utile pentru a modela diverse probleme şi se regăsesc implementaţi în multiple aplicaţii practice:
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.
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.
A, va determina componenta conexă din care face parte A.G = (V, E), se parcurg toate nodurile din V.| Complexitate | Reprezentare prin liste de adiacență | Reprezentare prin matrice de adiacență |
|---|---|---|
| Timp | O(|V| + |E|) | O(|V|²) |
| Spațiu | O(|V| + |E|) | O(|V|²) |
// Inițializări
pentru fiecare nod u din V
{
stare[u] = nevizitat
}
componente_conexe = 0
// Funcție de vizitare a nodului
vizitare(nod)
{
stare[nod] = vizitat
printeaza nod
}
// Parcurgerea în adâncime
DFS(nod)
{
stiva s
vizitare(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ă
{
vizitare(vecin)
s.introdu(vecin)
}
altfel
{
s.scoate(nodTop)
}
}
}
// Parcurgerea nodurilor din V
pentru fiecare nod u din V
{
dacă stare[u] == nevizitat
{
componente_conexe = componente_conexe + 1
DFS(u)
}
}
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.
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 xp[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 fac atribuirile:
d[y] = d[x] + 1 p[y] = x
Sursa având d[A] = 0 și p[A] = NULL.
B să fi fost descoperit, nu există drum între A și B, deci distanța este infinită.
| Complexitate | Reprezentare prin liste de adiacență | Reprezentare prin matrice de adiacență |
|---|---|---|
| Timp | O(|V| + |E|) | O(|V|²) |
| Spațiu | O(|V| + |E|) | O(|V|²) |
// Inițializări
pentru fiecare nod u din V
{
stare[u] = nevizitat
d[u] = infinit
p[u] = null
}
// Distanța între sursă și destinație
distanta(sursa, destinatie)
{
stare[sursa] = vizitat
d[sursa] = 0
enqueue(Q, sursa) // 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ă stare[u] == nevizitat
{
stare[u] = vizitat
p[u] = v
d[u] = d[v] + 1
enqueue(Q, u) // Adăugăm nodul u în coadă
}
}
return d[destinatie] // Dacă este infinit, nu există drum
}
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.
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.
Sortarea topologică se realizează printr-o parcurgere DFS, în care se rețin pentru fiecare nod:
tDesc[u] — momentul descoperirii nodului utFin[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 | Reprezentare prin liste de adiacență | Reprezentare prin matrice de adiacență |
|---|---|---|
| Timp | O(|V| + |E|) | O(|V|²) |
| Spațiu | O(|V| + |E|) | O(|V|²) |
// Inițializări
pentru fiecare nod u din V
{
stare[u] = nevizitat
p[u] = NULL
tDesc[u] = 0
tFin[u] = 0
}
contor_timp = 0
// Funcție de vizitare a nodului
vizitare(nod)
{
contor_timp = contor_timp + 1
tDesc[nod] = contor_timp
stare[nod] = vizitat
printeaza nod
}
// Parcurgere în adâncime (recursiv)
DFS(nod)
{
vizitare(nod)
pentru fiecare vecin al lui nod
{
dacă stare[vecin] == nevizitat
{
p[vecin] = nod
DFS(vecin)
}
}
contor_timp = contor_timp + 1
tFin[nod] = contor_timp
}
// Parcurgere noduri și calculare tDesc și tFin pentru fiecare nod
pentru fiecare nod u din V
{
dacă stare[u] == nevizitat
{
DFS(u)
}
}
// Sortare topologică
sortează nodurile din V descrescător în funcție de tFin[nod]
Profesorul Bumstead își sortează topologic hainele înainte de a se îmbrăca:
(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.tFin, astfel toate muchiile sunt orientate de la stânga la dreapta.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ă.
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.
A pentru nodurile de pe nivel par, B pentru nodurile de pe nivel impar.| Complexitate | Reprezentare prin liste de adiacență | Reprezentare prin matrice de adiacență |
|---|---|---|
| Timp | O(|V| + |E|) | O(|V|²) |
| Spațiu | O(|V|) | O(|V|) |
cât timp încă sunt noduri nevizitate
{
n = primul nod nevizitat
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 adiacente au același nivel
// Graful nu este bipartit
return false
}
}
}
}
// Parcurgerea BFS s-a finalizat fără noduri adiacente pe același nivel
// Graful este bipartit
return true
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.
Î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 (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.// 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)
}
1) [3.5p] Rezolvați problema Connected Components.
2) [3.5p] Rezolvați problema Minimum Path.
3) [3p] Rezolvati problema Check Bipartite.