Sunteți pe pagina 1din 9

Sortarea prin inserție

𝑻(𝒏) = 𝜣(𝒏𝟐 )
Pornim cu un sir gol de numere. Luăm câte un număr din
șirul inițial și il plasăm in sirul sortat la poziția
corespunzătoare. Plasarea numărului in șir la poziția
corespunzătoare se face prin comparare succesivă de la
stânga la dreapta sau invers.

Sortare prin interclasare (Merge Sort)


𝑻(𝒏) = 𝜣(𝒏𝒍𝒐𝒈𝟐 𝒏)
Sortarea prin interclasare se bazeaza pe urmatorul
principiu: pentru a sorta un vector cu n elemente, il
impartim in 2 vectori, care odata sortati, se interclaseaza.
Conform strategiei Divide and Conquer, descompunerea
unui vector in alti doi vector care umreaza a fi sortati are
loc pana cand avem de sortat vectori de un element.

Pasi:
1-Divide: sirul de n elemente se imparte in doua subsiruri
de n/2 elemente.
2-Conquer: se sorteaza recursiv cele 2 siruri.
3-Combine: se combina prin interclasare cele doua siruri
sortate obtinute la pasul Conquer.

Heapsort
𝑻(𝒏) = 𝜣(𝒏𝒍𝒐𝒈𝟐 𝒏)
Un heap este un sir de obiecte care pot fi văzute ca și un
arbore binar aproape complet – ultimul rand al arborelui
se completează de la stânga la dreapta.

Un sir A care reprezintă un heap are 2 atribute:


A.length: Lungimea heapului – ne furnizează numărul de
elemente din sir
A.heap-size: ne spune câte elemente din heap sunt
stocate in sirul A

Pentru un nod i avem:


PARENT(i) = i/2 - imparțire intreagă
LEFT(i) = 2i
RIGHT(i) = 2i+1
Quick Sort
𝑻(𝒏) = 𝜣(𝒏𝒍𝒐𝒈𝟐 𝒏)
Este un algoritm de sortare care pentru un sir n
foloseste o abordare de tip divide-and-conquer.

Divide: se partitionează șirul A[p...r] in 2 subsiruri


A[p…q-1] si A[q+1…r] astfel incat fiecare element
din subsirul A[p..q-1] este mai mic sau egal cu A[q]
si care la randul său este mai mic sau egal decât
fiecare element din subșirul A[q+1..r].

Conquer: se sortează subșirurile A[p..q-1] și


A[q+1..r] prin apeluri recursive la funcția quicksort.

Combine: sirul A[p,r] este deja sortat, deci combine


nu are obiect.

Recursivitate Abordare divide-and-conquer (divide et


Mecanism prin care un subprogram (procedură, impera):
funcţie) se autoapelează. Un algoritm recursiv poate Problema initială se sparge in subprobleme cu
avea un echivalent nerecursiv şi invers. structură similară cu problema originală, dar de
Implementarea recursivităţii are la bază structura dimeniune mai mică iar aceste subprobleme sunt
de date denumită stivă. rezolvate recursiv si solutiile recursive sunt
Stiva este acea formă de organizare a datelor combinate pentru a produce solutia problemei
(structură de date) cu proprietatea că operaţiile de initiale. Metoda se poate aplica în rezolvarea unei
introducere şi scoatere a datelor se fac în vârful ei. probleme care îndeplineşte următoarele condiţii:-se
O stivă funcţionează după principiul LIFO („Last in poate descompune în două sau mai multe
First Out”, în traducere „Ultimul intrat, primul subprobleme: -aceste suprobleme sunt
ieşit”). Din punct de vedere al modului în care se independente una faţă de alta (o subproblemă nu se
realizează autoapelul, există două tipuri de rezolvă pe baza alteia şi nu foloseşte rezultatele
recursivitate: celeilalte); -aceste subprobleme sunt similare cu
Directă: procedura (sau funcţia) se autoapelează (în problema iniţială; -la rândul lor subproblemele se
corpul său). Exemplul clasic este definirea funcţiei pot descompune (dacă este necesar) în alte
factorial. subprobleme mai simple; -aceste subprobleme
Indirectă: are loc atunci când o procedură (funcţie) simple se pot soluţiona imediat prin algoritmul
apelează o altă procedură (funcţie), care la rândul ei simplificat;
o apelează pe ea.
Programarea dinamică
Divide-and-conquer: se imparte problema in PD se aplică la probleme de optimizare:
subprobleme disjuncte care apoi sunt rezolvate -Au mai multe solutii posibile, fiecare caracterizate
recursiv. printr-o valoare;
Programare dinamică: se imparte problema in -Dorim să identificam o solutie cu valoarea cea mai
subprobleme care au părți comune: mica / cea mai mare;
-subproblemele partajează cel putin o sub- Pasi:- Se caracterizează structura unei soluții optime
subproblemă comună; - In mod recursiv, se definește valoarea unui
PD rezolvă fiecare sub-subproblemă o singură dată soluții optime
si salvează rezultatul intr-o tabelă cu rezultate: - Se calculează valoarea unei soluții optime
-Se evită astfel ca o sub-subproblemă să fie pornindu-se de jos în sus
rezolvată de mai multe ori; - Se calculează soluția optimă pe baza
informației obtinute până în acest pas
Abordarea top-down PD Cut Rod Abordarea bottom-up PD Matrix Chain
𝟐
𝑻(𝒏) = 𝜣(𝒏 ) 𝑻(𝒏) = 𝜣(𝒏𝟐 )

MEMOIZED-CUT-ROD(p,n)
1. Fie un sir auxiliar r[0..n]
2. for i=0 to n
3. 𝑟[𝑖] = −∞
4. return MEMOIZED-CUT-ROD-AUX(p,n,r)
MEMOIZED-CUT-ROD-AUX(p,n,r)
1. if 𝑟[𝑛] ≥ 0
2. return r[n]
3. if n==0
4. q=0
5. else 𝑞 = −∞
6. for i=1 to n
7. q=max(q, p[i]+MEMOIZED-CUT-
ROD-AUX(p,n-i,r))
8. r[n]=q PRINT-OPTIMAL-PARENS(s, i, j)
9. return q 1. If i == j
2. Print "𝐴"𝑖
3. Else print “(”
4. PRINT-OPTIMAL-PARENS(s, i, s[i, j])
5. PRINT-OPTIMAL-PARENS(s, s[i, j] + 1, j)
Greedy 𝑻(𝒏) = 𝜣(𝒏)
6. Print “)”
Metoda greedy este o metoda generala de proiectare
a algoritmilor care consta in construirea solutiei
global optimale printr-un sir de solutii cu character
de optim local atunci cand este posibila exprimarea
“optimului global”. Algoritmii greedy sunt in general
simpli si sunt folositi la problem de optimizare cum ar
fi: sa se gaseasca cea mai buna ordine de executare a
unor lucrari, sa se gaseasca cel mai scut drum intr-un
graf. Aceasta metoda se aplica problemelor in care se
da o multime A cu n elemente si se cere sa se
determine o submultime B care sa indeplineasca
anumite conditii pentru a fi acceptata. Este posibil sa
existe mai multe multimi acceptabile, de aceea se
cere sa se determine o solutie posibila care fie sa
maximizeze, fie sa minimieze o functie obiectiv dat.
Aceasta solutie posibila se numeste solutie-optima.
Metoda greedy intotdeauna face o alegere care sa
fie cea mai buna in acel moment. Folosind metoda Pasi :
greedy vom obtine cu siguranta o solutie posibila Determinam structura optimala a problemei,
maximal dar nu avem garantia ca elementul adaugat Dezvoltam o solutie recursive pe baza solutiei
este acel element maximal pentru care functia optimale,
obiectiv este maximizata/minimizata. Greedy Aratam ca daca se efectueaza o alegere greedy,
intotdeauna face alegerea care pare cea mai bună în ramane doar o subproblema,
fiecare moment, in speranța că această alegere va Aratam ca alegerea greedy este sigura,
conduce la soluția optimă. Greedy nu conduce la Dezvoltam un algoritm recursive pentru
alegerea optimală. Pentru o problemă de rezolvarea problemei greedy.
optimizare, in primul rând trebuie să cautăm soluția
prin programare dinamică si abia apoi soluția
greedy.
Backtracking O(exponentiala) 1. k = 1, si 𝑆 = ∅
2. While k>0
Conceptual, tehnica backtracking cauta 3. If k>n
sistematic solutia, intr-un spatiu al solutiilor 4. Print S // afisează soluția s
organizat sub forma unui arbore. Fiecare nod 5. k = k -1
din arbore defineste o stare a problemei. Toate 6. Else
drumurile de la radacina la Frunze formeaza 7. If (există 𝑣 ∈ 𝑆𝑘 neconsiderată până in acest moment)
spatiul starilor. O solutie a problemei 8. 𝑆 = {𝑣1 , … , 𝑣𝑘−1 , 𝑣}
reprezinta acele stari pentru care exista drum 9. If 𝛷(𝑆) adevărate // testarea conditiilor de continuare
de la radacina la un nod etichetat cu un n-uplu. 10. k = k +1
Pot exista arbori statici si arbori dinamici. 11. else k = k-1 // pasul de backtracking
Metoda de generare a tuturor solutiilor DAME(n)
posibile si apoi de determinare a solutiilor 1. k=1, 𝑆 = {𝑥1 , 𝑥2 , … 𝑥𝑛 } = {0, … ,0}
rezultate prin verificarea indeplinirii conditiilor 2. While k>0
interne necesita foarte mult timp. In cadrul 3. If (k > n) printeaza solutia S
tehnici backtracking nu se genereaza toate 4. k = k-1
solutiile posibile, ci numai acelea care 5. Else 𝑥 𝑖 = 𝑥𝑖 + 1

indeplinesc anumite conditii specifice 6. If 𝑥𝑖 ≤ 𝑛


7. If 𝛷(𝑆)
problemei, numite conditii interne.
8. K = k +1
1-Presupunem că o solutie este formată din 9. Else 𝑥𝑘 = 0 //se face backtracking daca s-a epuizat xi
vectorul S={𝑣_1,𝑣_2,…,𝑣_𝑚}. 10. k = k-1
Prin backtracking vom traversa în adâncime
domeniile vectorilor 𝑣_𝑖 până când identificăm realizăm următoarea alegere posibilă pentru 𝑣_𝑖 ∈
o solutie, 𝑆_𝑖
2-Pornim de la solutia vidă 𝑆=∅ 5-Dacă toate elementele din 𝑆_𝑖 s-au epuizat, atunci
3-La pasul pasul 𝑖 introducem în soluție o un se revine la 𝑆_(𝑖−1)
element din spațiul 𝑆_𝑖 de elemente posibile pentru Esentiale sunt conditiile de continuare Φ care ne
𝑣_𝑖 spun dacă un vector parțial {𝑣_1,𝑣_2,…,𝑣_𝑖} poate
4-Dacă un vector construit partial {𝑣_1,𝑣_2,…,𝑣_𝑖} conduce la o solutie.
nu poate conduce la o soluție, atunci revenim și
Liste înlănțuite
O structură de date în care obiectele sunt aranjate intr-o
ordine liniară. La implementarea prin siruri, ordinea este
determinată de indici. La implementarea prin pointeri,
ordinea este determinată de un pointer care arată către
obiectul următor.
Listă simplu înlănțuită: fiecare obiect are un atribut cheie și
un pointer next.
Listă dublu înlănțuită: fiecare obiect are un atribut cheie și
doi pointeri: next și prev.
La un element x, x.next arată către elementul succesor,
x.prev arată către elementul predecesor.
Dacă x.prev == NULL, atunci x este primul element, LIST-DELETE(L,x)
denumit head. 1. if x.prev ≠ NIL
Dacă x.next == NULL, atunci x este ultimul element, 2. x.prev.next = x.next
denumit tail. 3. Else L.head = x.next
Dacă lista este sortată, cheile apar în ordine sortată. 4. If x.next ≠ NIL
Lista circulară: ultimul element din listă pointează către 5. x.next.prev = x.prev
head, primul element din listă pointează către tail.
=> Necesitatea santinelelor: obiect dummy care un element L.nil , astfel încat orice referință la Nil să
marchează inceputul sau sfârșitul listei. Introducem se transforme in referinta la L.nil.
LIST-INSERT-SANTINELA(L, x) LIST-SEARCH-SANTINELA(L, k) LIST-DELETE-SANTINELA(L,x)
1. x.next = L.nil.next 1. x = L.nil.next 1. x.prev.next = x.next
2. L.nil.next.prev = x 2. While x ≠ L.nil and x.key ≠ k 2. x.next.prev = x.prev
3. L.nil.next = x 3. x = x.next
4. x.prev = L.nil 4. Return x

Stiva
Last-in, first-out
INSERT -> PUSH, DELETE -> POP
STACK-EMPTY(S) PUSH(S,x) POP(S)
1. If S.top == 0 1. S.top = S.top + 1 1. If STACK-EMPTY(S)
2. Return true 2. S[S.top] = x 2. eroare
3. Else return false 3. Else S.top = S.top – 1
4. Return S[S.top + 1]

Coada ENQUEUE(Q,x) DEQUEUE(Q)


First-in, first-out 1. Q[Q.tail] = x 1. x = Q[Q.head]
INSERT -> ENQUEUE, DELETE -> DEQUEUE 2. If Q.tail == Q.length 2. If Q.head == Q.length
Coada are head si tail; ENQUEUE se face la tail, 3. Q.tail = 1 3. Q.head = 1
DEQUEUE se face la head 4. Else Q.tail = Q.tail + 1 4. Else Q.head = Q.head + 1
5. Return x
Conceptul de dictionar
Fiecare element din dictionar se caracterizează prin Tabela de dispersie: o generalizare a notiunii de
(cheie, valoare). “array” (sir cu index): se permite examinarea
Operatii esentiale pentru dictionare: INSERT, oricărei poziții in timp O(1).
SEARCH, DELETE. Tabele de dispersie sunt bazate pe conceptul de
hashing.

Tabele direct adresabile


DIRECT-ADDRESS-SEARCH(T, k) DIRECT-ADDRESS-INSERT(T, x) DIRECT-ADDRESS-DELETE(T, x)
1. return T[k] 1. T[x.key] = x 1. T[x.key] = NIL

Tabele de dispersie Funcția de dispersie (hash function)


ℎ:𝑈→{0,1,…,𝑚−1}. Fiecare element cu cheia k este
Dacă universul de chei U este mare (adică valori salvat în locația T[h(k)]. Pt functia de dispersie are
mari ale lui m), se pune problema dimensiunii loc condiția 𝑚≪|𝑈|. Trebuie să fie o funcție care să
alocării unui sir T[0..m-1]. In general, numărul de minimizeze șansa apariției coliziunilor. Deoarece
chei K care se stochează este mic în comparație cu 𝑚≪|𝑈|, in cel mai rău caz coliziunile nu pot fi
mărimea lui U. Dacă numărul de chei K din dicționar evitate =>metodă de rezolvare a coliziunilor.
este mult mai mic decât 𝑚=|𝑈|, tabelele de
dispersie reduc necesarul de memorie la Θ(|𝐾|) iar Rezolvarea coliziunilor
operațiile de căutare se fac in timp O(1). La tabele Plasăm elementele care au valori de hash egale in
de dispersie, O(1) pentru căutare este valabil pentru același slot, într-o listă inlănțuită . Slotul j conține un
cazul mediu de utilizare. La tabele direct adresabile pointer către lista care salvează elementele care au
O(1) are loc si pentru cazul cel mai rău. j ca si valoare de hash. Daca nici un element nu are
valoare de hash pe j, atunci la T[j] = NIL.

Functia de dispersie
CHAINED-HASH-INSERT(T, x) CHAINED-HASH-SEARCH(T, k) CHAINED-HASH-DELETE(T, x)
1. Insert x la varful listei T[h(x.key)] 1. Se caută un element cu cheia k 1. Se sterge x din lista T[h(x.key)]
în lista T[h(k)]
HASH-INSERT(T, k) HASH-SEARCH(T, k)
1. i = 0 1. i = 0
2. Repeat 2. Repeat
3. j = h(k,i) 3. j = h(k,i)
4. If T[j] == NIL 4. If T[j] == k
5. T[j] = k 5. Return j
6. Return j 6. i =i+1
7. Else i = i + 1 7. Until T[j] == NIL or i == m
8. Until i == m 9. Return NIL
9. Return “hash table overflow”

Arbori binari de cautare INORDER-TREE-WALK(x)


Un arbore este binar de căutare dacă este un arbore 1. If x != Nil
binar (adică fiecare nod are cel mult 2 copii) si daca 2. INORDER-TREE-WALK(x.left)
x este un nod in arbore, si y este un nod in 3. Print x.key
subarborele stang atunci 𝑦.𝑘𝑒𝑦 ≤𝑥.𝑘𝑒𝑦. Dacă y este 4. INORDER-TREE-WALK(x.right)
un nod in subarborele drept, atunci 𝑥.𝑘𝑒𝑦 ≤𝑦.𝑘𝑒𝑦.
Fiecare nod de arbore contine o cheie, date auxiliare TREE-INSERT(T, z)
si 3 referinte: left, right si p care pointează către 1. y = Nil
nodurile copii stanga respectiv dreapta și către 2. x = T.root
nodul părinte. Dacă un copil sau părintele lipseste, 3. While x != Nil
4. y=x
referinta respectivă va fi Nil.
5. If z.key < x.key
TRANSPLANT(t, u, v) 6. x = x.left
Operatii arbore de cautare 1. If u.p == Nil 7. Else x = x.right
2. T.root = v 8. z.p = y
TREE-DELETE(T, z) 3. Elseif u == u.p.left 9. If y == Nil
1. If z.left == Nil 4. u.p.left = v 10. T.root = z
TREE-SEARCH(x,
2. TRANSPLANT(T,k) z, z.right) 5. Else u.p.right = v 11. Else if z.key < y.key
1. Elseif
3. If x ==z.right
Nil or==k ==
Nilx.key 6. If v != Nil 12. y.left = z
2.
4. Return x
TRANSPLANT(T, z, z.left) 7. v.p = u.p 13. Else y.right = z
3. Else
5. If k <y x.key
= TREE-MINIMUM(z.right)
4.
6. Return
If y.p !=TREE-SEARCH(x.left,
z k) TREE-SUCCESSOR(x)
5. Else ReturnTRANSPLANT(T,
7. TREE-SEARCH(x.right, k)
y, y.right) 1. If x.right != Nil
8. y.right = z.right 2. Return TREE-MINIMUM(x.right)
9. y.right.p = y 3. y = x.p
10. TRANSPLANT(T, z, y) TREE-MINIMUM(x) 4. While y != Nil and x == y.right
11. y.left = z.left 1. While x.left != Nil 5. x=y
12. y.left.p = y 2. x = x.left 6. y = y.p
3. Return x 7. Return y

Arborele rosu si negru numar de noduri negre. Punand constangeri asupra


culorilor nodurilor oricarei cai simple de la radacina
Un arbore rosu si negru este un arbore binar de la frunza, arborii rosu si negru asigura ca nici una din
cautare la care fiecare nod are un bit suplimentar, aceste chei nu este de doua ori mai lunga decat
reprezentand culoarea: rosu sau negru. Fiecare nod oricare alta, in consecinta arborele este aproximativ
de arbore contine urmatoare campuri: cheia, balansat. Intr-un arbore rosu si negru vom utiliza o
culoarea, nodul stang, nodul drept si parintele. singura santinela pentru a reprezenta Nil. Santinela
Proprietati: 1-fiecare nod este rosu sau negru, 2- T.nill este un obiect cu aceleasi attribute ca un nod
radacina este negru, 3-fiecare frunza este neagra, 4- oarecare. Culoarea santinelei este negru iar
daca un nod este rosu atunci ambii copii sunt negrii, celelalte attribute, p,left,right si key pot avea valori
5-pentru fiecare nod, toate caile simple de la nodul arbitrare.
respective la frunzele descendente contin acelasi
Rotatie la dreapta a unui abore binar LEFT-ROTATE (T,x) RIGHT-ROTATE (T,x)
1. y = x.right 1. y=x.left
Cand facem o rotatie la dreapta a unui nod y,
2. x.right = y.left 2. y.left=x.right
presupunem ca copilul stanga x nu este T.nil; y
3. If y.left != T.nil 3. If x.right != T.nil
poate fi orice nod in arbore a carui copil stanga
4. y.left.p = x 4. x.right.p=y
nu este T.nil. Operatia de rotatie la dreapta il 5. y.p = x.p 5. x.p=y.p
face pe x noua radacina a subarborelui, cu y ca 6. If x.p ==T.nil 6. If y.p==T.nil
si copilul dreapta a lui x iar copilul dreapta a lui 7. T.root = y 7. T.root=x
x devine copilul stanga a lui y. 8. Elseif x == x.p.left 8. Elseif y==y.p.right
9. x.p.left = y 9. y.p.right=x
10. Else x.p.right = y 10. Else y.p.left=y
11. y.left = x 11. x.left=y
12. x.p = y 12. y.p=x
Grafuri
Graf: 𝐺=(𝑉,𝐸) V – multimea nodurilor, E – multimea salvează cu arcul v in lista de adiacență a lui u.
arcelor Dezavantajul listelor de adiacență: nu permit o cale
Pentru grafuri cu densitate mică: liste de adiacență rapidă de a determina dacă un anume arc (u,v) se
Pentru grafuri dense: matrice de adiacență alfă in graf.

Liste de adiacență: Matrice de adiacenta

Reprezintă un sir Adj de |V| liste, cate o listă pentru Presupunem că nodurile sunt numerotate cu 1, 2, ...,
fiecare nod din graf. Pentru fiecare 𝑢∈𝑉, 𝐴𝑑𝑗[𝑢] |V|
conține toate nodurile v din graf astfel incat există Folosim o matrice de dimensiune |𝑉|×|𝑉|, 𝐴=(𝑎_𝑖𝑗)
un arc (𝑢,𝑣)∈𝐸. Notatia 𝐺.𝐴𝑑𝑗[𝑢] pentru a ne referi astfel încât 𝑎_𝑖𝑗={1, 𝑑𝑎𝑐ă (𝑖,𝑗)∈𝐸 sau 0, î𝑛 𝑐𝑎𝑧
la lista de adiacență a nodului u. Dacă G graf 𝑐𝑜𝑛𝑡𝑟𝑎𝑟)}. Matricea de adiacență necesită spațiu de
orientat, atunci un arc (u,v) este reprezentat prin memorie Θ(𝑉^2), indiferent de numărul de noduri
𝑣∈𝐴𝑑𝑗[𝑢]. Dacă G graf neorientat, atunci u apare in Pentru un graf neorientat, matricea este simetrică:
Adj[v] și v apare in Adj[u]. Spațiul de memorie 𝐴=𝐴t. Dacă graful este ponderat, atunci in matrice
necesar: Θ(𝑉+𝐸). Dacă graful este ponderat cu se salvează ponderile w(u,v).
ponderile 𝑤:𝐸→𝑅, atunci ponderea w(u,v) se
BFS(G,s)
Parcurgerea in latime (breadth-first) O(V+E) 1. For fiecare nod 𝑢 ∈ 𝐺. 𝑉 − {𝑠}
Dacă se dă ca și start un nod s din graf, parcurgerea 2. u.color = WHITE
in latime explorează in mod sistematic arcele din G 3. u.d = ∞
4. u.π = Nil // predecesorul lui u
pentru a descoperi fiecare nod care poate fi
5. s.color = GRAY
identificat pornind de la s.
6. s.d = 0
Calculează distanța de la s la fiecare nod din graf
7. s. π = Nil
care este conectat la s.
8. Q = ∅ //se initializează coada Q la multimea vidă
Algoritmul explorează frontiera dintre nodurile 9. ENQUEUE(Q, s)
descoperite si cele nedescoperite în lațime, adică 10. While 𝑄 ≠ ∅
descoperă toate nodurile la distanță k, apoi cele la 11. u = DEQUEUE(Q)
distanță k+1, etc. 12. For fiecare nod 𝑣 ∈ 𝐺. 𝐴𝑑𝑗[𝑢]
Se presupune ca initial toate nodurile din graf sunt 13. If v.color == WHITE
colorate alb (nedescoperite). 14. v.color = GRAY
Dacă (𝑢,𝑣)∈𝐸 și u este nod negru, atunci v va fi sau 15. v.d = u.d + 1
negru sau gri: toate nodurile adiacente unui nod 16. v. π = u
negru au fost descoperite. 17. ENQUEUE(Q,v)
Nodurile gri pot avea noduri albe adiacente: acestea 18 u.color = BLACK
reprezintă frontiera dintre nodurile descoperite. PRINT-PATH(G, s, v)
Parcurgerea in latime calculează un arbore de 1. If v == s Print s
parcurgere in latime cu radacina in s. 2. Elseif v.π == Nil
De fiecare data cand se descoperă un nod v alb la 3. Print „nu exista cale de la s la v”
scanarea listei de adiacență a nodului u, arcul (u,v) 4. Else PRINT-PATH(G, s, v.π)
se salvează in arborele de parcurgere. 5. Print v
Parcurgerea in adancime O(V+E) DFS-VISIT(G, u) DFS(G)
1. Time = time + 1 1. For fiecare nod 𝑢 ∈ 𝐺. 𝑉
Presupune o căutare in graf cat de adânc
2. u.d = time 2. u.color = WHITE
posibil. Se parcurge pe arcele nodului cel mai
3. u.color = GRAY 3. u.π = Nil
recent descoperit. 4. Time = 0
4. For fiecare 𝑣 ∈ 𝐺. 𝐴𝑑𝑗[𝑢]
Dacă toate arcele unui nod v au fost explorate, 5. If v.color == WHITE 5. For fiecare nod 𝑢 ∈ 𝐺. 𝑉
atunci algoritmul face back-tracking la 6. v.π = u 6. If u.color == WHITE
următorul nod care trebuie explorat după v. 7. DFS-VISIT(G, v) 7. DFS-VISIT(G, u)
Folosim aceasi convenție de colorare: WHITE, 8. u.color = BLACK
GRAY si BLACK. Pentru fiecare nod folosim 2 9. Time = time+1
stampile de timp: -v.d momentul in care 10. u.f = time
nodul a fost descoperit (cand este marcat
GRAY).
-v.f momentul in care parcurgerea termină lista de
adiacență a lui v (cand nodul este marcat BLACK).

Sortare topologică O(V+E)


TOPOLOGICAL-SORT(G)
O sortare topologică a unui “direct acyclic graph”
1. Apelează DFS(G) pentru a calcula v.f pentru
G=(V,E) este o ordonare a nodurilor din G astfel
fiecare nod v
încât dacă G conține un arc (u,v) atunci u apare
2. Pe măsură ce fiecare nod este terminat de
inaintea lui v in sirul sortat. parcurs, inserăm acest nod in varful unei liste
Dacă graful contine cicluri, atunci sortarea inlănțuite
topologică nu este posibilă 3. Return lista inlănțuită

Componente tare conexe STRONGLY-CONNECTED-COMPONENTS(G)


Problemă: descompunerea unui graf orientat in 1. Apelează DFS(G) pentru a calcula u.f pentru
componentele tare conexe. Pentru un graf orientat fiecare nod u
G=(V,E), o componentă tare conexă este un set 2. Calculează 𝐺 𝑇 = (𝑉, 𝐸 𝑇 )
maximal de noduri 𝐶 astfel incat pentru orice 3. Apelează DFS(𝐺 𝑇 ) dar in bucla principală a
pereche de noduri u si v din C există o cale de la u la lui DFS considerăm ordinea nodurilor ca fiind
v si de la v la u. ordinea descrescătoate a lui u.f
4. Returnăm nodurile fiecărui arbore din
Problema de acoperire minima. pădurea formată la linia 3 ca si o componentă
La proiectarea circuitelor electrice de multe ori este tare conexă separată
necesara legarea pinilor mai multor component, GENERIC-MST(G, w)
conectandu-le cu fire, fiecare fir conectand doi pini.
Pentru interconectarea unei multimi de n pini vor fi
1. A = ∅
necesare n-1 fire. Avand in vedere ca pot fi mai multe 2. While A nu este un arbore minimal de
aranjamente de interconectare, se cere sa se acoperire
determine cablarea care foloseste cea mai mica 3. Găseste un arc (u,v) care este “safe” pentru A
cantitate de cabluri. Vom folosit un graf neorientat 4. 𝐴 = 𝐴 ∪ {(𝑢, 𝑣)}
conex, G=(V,E) unde V este multimea pinilor si E este
5. Return A
multimea interconectarilor posibila dintre perechile
de pini. Pentru fiecare pereche de pini (u,v) apartin lui MST-KRUSKAL(G, w)
E avem un cost w(u,v) care reprezinta costul cablului 1. A = ∅
de la pinul u la pinul u la pinul v. Deoarece multimea 2. For fiecare 𝑣 ∈ G. 𝑉
T este aciclica si conecteaza toate nodurile, ea trebuie 3. MAKE-SET(v)
sa formeze un arbore care va fi arborele de acoperire. 4. Sortează arcele din E in ordine crescătoare a costului w
5. For fiecare arc (u,v) luat in ordine crescătoare a
costului w
Algoritmul lui Kruskal (Greedy) O(E lg V) 6. If FIND-SET(u) ≠ FIND-SET(v)
Algoritmul găseste arcul safe (u,v) dintre toate 7. 𝐴 = 𝐴 ∪ {(𝑢, 𝑣)}
8. UNION(u,v)
arcele care conectează 2 arbori in pădurea de arbori
9. Return A
– arc cu costul cel mai mic
Algoritmul lui Prim (Greedy) O(E lg V) Algoritmul Belman-Ford O(VE)
La fiecare pas se adaugă lui A un arc usor care Algoritmul indica FALSE in cazul in care gaseste un
conectează pe A cu un nod izolat. ciclu cu cost negativ.

MST-PRIM(G, w, r) BELLMAN-FORD(G, w, s)
1. For fiecare 𝑢 ∈ G. 𝑉 1. INITIALIZE-SINGLE-SOURCE(G, s)
2. u.key = ∞ 2. For i = 1 to |G.V| - 1
3. u.π = Nil 3. For fiecare arc (u,v)
4. r.key = 0 4. RELAX(u, v, w)
5. Q = V 5. For fiecare arc (u,v)
6. While Q != ∅ 6. If v.d > u.d + w(u,v)
7. u = EXTRACT-MIN(Q) 7. Return FALSE
8. For fiecare 𝑣 ∈ 𝐺. 𝐴𝑑𝑗[𝑢] 8. Return TRUE
9. If 𝑣 ∈ 𝑄 and w(u,v) < v.key INITIALIZE-SINGLE-SOURCE(G, s)
10 v.π = u 1. For fiecare nod v din graful G
11. v.key = w(u,v) 2. v.d = ∞
3. 𝑣. 𝜋 = Nil
4. s.d = 0
Cai minime in grafuri aciclice (DAG) O(V+E) RELAX(u, v, w)
1. If v.d > u.d + w(u,v)
DAG-SHORTEST-PATH(G, w, s) 2. v.d = u.d + w(u,v)
1. TOPOLOGICAL-SORT(G) 3. 𝑣. 𝜋 = u
2. INITIALIZE-SINGLE-SOURCE(G, s)
3. For fiecare 𝑢 ∈ G. 𝑉 //in ordinea topologica
4. For fiecare nod v ∈ G.Adj[u] Algoritmul lui Dijkstra
5. RELAX(u, v, w) Rezolvă problema căii minime dacă 𝑤(𝑢,𝑣)≥0

DIJKSTRA(G, w, s)
1. INITIALIZE-SINGLE-SOURCE(G, s)
2. 𝑆=∅
3. Q = G.V // coadă min-priority
4. While Q=∅
5. u = EXTRACT-MIN(Q)
6. 𝑆=𝑆∪{𝑢}
7. For fiecare nod 𝑣∈𝐺.𝐴𝑑𝑗[𝑢]
8. RELAX(u, v, w)

S-ar putea să vă placă și