Documente Academic
Documente Profesional
Documente Cultură
Elemente de complexitatea
algoritmilor si teoria grafurilor
1.1
Notatii asimptotice
Vom defini un tip de functii care reprezinta un bun model pentru descrierea
complexitatii temporale a unui algoritm, adica a dependentei timpului de
executie fata de dimensiunea datelor de intrare.
Definitia 1.1.1. O functie asimptotic pozitiv
a (prescurtat a.p.) este o
functie f : N \ A R a..
A N este o multime finita;
n0 N \ A astfel ncat f (n) > 0, n n0 .
Observatia 1.1.1. De cele mai multe ori, multimea A este de forma
A=
{0, 1, 2, . . . , k} , unde k N.
{z
}
|
(3n4 + n + 3) n 5
Exemplul 1.1.1. Functia f : D R, f (n) =
, unde
(5n + 1)(n 8)
D = {n N | n 5, n 6= 8}, este asimptotic pozitiva, deoarece D = N \ A
cu A = {0, 1, 2, 3, 4, 8} (multime finita) si f (n) > 0, n 9.
ln(n5 + 1) n
, nu este
(n 1)(n 6)
asimptotic pozitiva, deoarece (n 1)(n 6) > 0, n 7, dar lim [ln(n5 +
Exemplul 1.1.2. Functia g : N \ {1, 6} R, g(n) =
Figura 1.1.1:
Figura 1.1.2:
Figura 1.1.3:
10
f (n)
(3n4 + n + 3) n 5
lim
= lim
n n3
n n3 (5n + 1)(n 8)
q
1 n5
n4 n 3 + n13 + n34
= 0,
= lim
n
n5 (5 + n1 )(1 n8 )
rezulta ca f (n) = O (n3 ), dar f (n) 6= (n3 ) si f (n) 6= (n3 ).
Deoarece
f (n)
(3n4 + n + 3) n 5
lim
= lim
n n2
n n2 (5n + 1)(n 8)
q
3
1
4
1 n5
n n 3 + n3 + n4
= lim
= ,
n
n4 (5 + n1 )(1 n8 )
rezulta ca f (n) = (n2 ), dar f (n) 6= O (n2 ) si f (n) 6= (n2 ).
Deoarece
f (n)
(3n4 + n + 3) n 5
lim
= lim 2
n n2 n
n n
n(5n + 1)(n 8)
q
n4 n 3 + n13 + n34
1 n5
3
,
= lim
=
8
1
n
5
n4 n(5 + n )(1 n )
rezulta ca f (n) = (n2 n), deci f (n) = O (n2 n) si f (n) = (n2 n).
11
1.2
12
13
e3
1
e1
e2
4
e4
e7
e8
e9
e5
e6
Figura 1.2.1:
Figura 1.2.2:
14
Figura 1.2.3:
Pentru graful orientat din Exemplul 1.2.2, subgraful generat de submultimea de arce {(2, 3), (5, 4)} are reprezentarea din Figura 1.2.4, iar graful
partial generat de aceeasi submultime de arce are reprezentarea din Figura
1.2.5.
Definitia 1.2.12. Fie G = (V, E) un graf (neorientat sau orientat) si U V
o submultime de noduri a.. U 6= V . Subgraful obtinut din G prin
eliminarea nodurilor multimii U este subgraful G \ U = (V \ U, F ),
unde F este colectia tuturor muchiilor sau arcelor din E ce nu sunt incidente
cu niciun nod din U.
15
Figura 1.2.4:
Figura 1.2.5:
1.3
Reprezentarea grafurilor
In continuare descriem cateva forme de reprezentare (memorare) a grafurilor n informatica. Dintre aceste forme, cea mai utilizata este matricea de
adiacenta.
Definitia 1.3.1. Fie G = (V, E) un graf (neorientat sau orientat) unde
V = {v1 , . . . , vn } si E = {e1 , . . . , em }. Matricea de adiacenta
asociata
grafului G este matricea A = (aij )i,j=1,n definita prin
aij = numarul de muchii sau de arce ek E de la nodul vi la nodul vj (adica
muchii de forma ek = [vi , vj ], respectiv arce de forma ek = (vi , vj )),
i, j {1, . . . , n}.
Observatia 1.3.1. a) Daca graful neorientat G = (V, E) este simplu, atunci
1, daca vi si vj sunt adiacente (adica [vi , vj ] E),
aij =
0, n caz contrar.
16
0 1 0 1 0 0
1 1 0 0 1 0
0 0 0 0 0 2
A=
1 0 0 0 3 0 ,
0 1 0 3 0 0
0 0 2 0 0 0
iar matricea de adiacenta asociata
0
0
A=
1
0
0
1 0 0 0
0 1 1 1
1 0 0 0
.
0 0 0 0
0 0 1 0
17
L(1)
= {3}, L(2)
= {1, 3}, L(3)
= {2}, L(4)
= {2, 5}, L(5)
= {2}.
Evident, pentru grafurile neorientate notiunile de succesor direct si predecesor
direct coincid, fiind si sinonime cu notiunile de vecin sau adiacent.
Observatia 1.3.5. Alegerea uneia sau alteia dintre formele de reprezentare a
grafurilor descrise mai sus depinde de eficienta si de usurinta implementarii
operatiilor necesare de prelucrare a acestor forme, deci de tipul problemei
modelate prin grafuri si de algoritmul de rezolvare utilizat.
1.4
Grade
18
gradul de ie
sire (semigradul exterior) al lui x, notat d+
G (x) =
d+ (x), reprezinta numarul de arce e E incidente cu x spre exterior;
gradul de intrare (semigradul interior) al lui x, notat d
G (x) =
d (x), reprezinta numarul de arce e E incidente cu x spre interior;
gradul (total al) lui x, notat dG (x) = d(x), este
d(x) = d+ (x) + d (x).
Exemplul 1.4.1. Pentru graful neorientat din Exemplul 1.2.1, gradele nodurilor sunt: d(1) = 2, d(2) = 4, d(3) = 2, d(4) = 4, d(5) = 4, d(6) = 2.
Pentru graful orientat din Exemplul 1.2.2, gradele nodurilor sunt:
d+ (1) = 1,
d+ (2) = 3,
d+ (3) = 2,
d+ (4) = 0,
d+ (5) = 1,
d (1) = 1,
d (2) = 2,
d (3) = 1,
d (4) = 2,
d (5) = 1,
d(1) = 2,
d(2) = 5,
d(3) = 3,
d(4) = 2,
d(5) = 2.
n
P
j=1
n
P
aij , d (vi ) =
j=1
{1, . . . , n}.
n
P
j=1
aji , i
xV
1.5
19
Conexitate
20
21
Exemplul 1.5.2. Graful neorientat din Exemplul 1.2.1 nu este conex, deoarece
nu exista lanturi ntre nodurile 1 si 3. Graful orientat din Exemplul 1.2.2
este conex, dar nu este tare-conex, deoarece nu exista drum de la nodul 4 la
nodul 1 (desi exista drum de la nodul 1 la nodul 4!).
Definitia 1.5.3. a) O component
a conex
a a unui graf (orientat sau
neorientat) G = (V, E) este un subgraf G[U] generat de o submultime
U V de noduri cu proprietatea ca G[U] este conex si maximal cu
aceasta proprietate (n raport cu incluziunea), adica pentru orice nod
x V \ U subgraful G[U {x}] nu mai este conex.
b) O component
a tare-conex
a a unui graf orientat G = (V, E) este un
subgraf G[U] generat de o multime U V de noduri cu proprietatea
ca G[U] este tare-conex si maximal cu aceasta proprietate (n raport cu
incluziunea), adica pentru orice noduri x1 , x2 , . . . , xp V \ U subgraful
G[U {x1 , x2 , . . . , xp }] nu mai este tare-conex.
Observatia 1.5.6. Un graf este conex daca si numai daca are o singura componenta conexa. Un graf orientat este tare-conex daca si numai daca are o
singura componenta tare-conexa. Numarul de componente tare-conexe ale
unui graf orientat este mai mare sau egal decat numarul de componente
conexe ale acelui graf, deoarece orice componenta tare-conexa este inclusa
ntr-o componenta conexa (conform definitiei anterioare si Observatiei 1.5.4).
Observatia 1.5.7. Orice nod izolat x genereaza o componenta conexa si o
componenta tare-conexa, ambele avand forma G[{x}] = ({x}, ).
Propozitia 1.5.1. a) Fie G[V1 ], . . . , G[Vk ] componentele conexe (diferite)
ale unui graf G = (V, E). Atunci {V1 , . . . , Vk } este o partitie a multimii
V (adica Vi 6= i, Vi Vj = i 6= j, V1 Vk = V ).
b) Fie G[V1 ], . . . , G[Vr ] componentele tare-conexe (diferite) ale unui graf
orientat G = (V, E). Atunci {V1 , . . . , Vr } este de asemenea o partitie
a multimii V .
Exemplul 1.5.3. Componentele conexe ale grafului neorientat din Exemplul
1.2.1 sunt generate de submultimile de noduri V1 = {1, 2, 4, 5} si V2 = {3, 6},
deci acel graf are 2 componente conexe. Componentele tare-conexe ale grafului orientat din Exemplul 1.2.2 sunt generate de submultimile de noduri
V1 = {1, 2, 3}, V2 = {4} si V3 = {5}, deci acel graf are 3 componente tareconexe.
Observatia 1.5.8. Submultimile de muchii sau arce ale componentelor conexe
ale unui graf formeaza de asemenea o partitie a multimii de muchii sau arce
22
a grafului (deoarece pentru orice muchie e = [x, y] sau arc e = (x, y) nodurile
x si y se afla ntr-o aceeasi componenta conexa si aceasta componenta va
contine si pe e). Afirmatia nu mai este valabila pentru componentele tareconexe. De exemplu, pentru graful din Exemplul 1.2.2 arcul (5, 4) nu apartine
nici-unei componente tare-conexe.
Definitia 1.5.4.
b) O p
adure este un graf fara cicluri.
c) Un arbore partial al unui graf G = (V, E) este un graf partial al lui
G ce este arbore (adica un arbore T = (V, F ) cu F E).
Observatia 1.5.9. Arborii si padurile sunt grafuri simple (deoarece orice bucla
este un ciclu si orice doua muchii sau arce multiple formeaza un ciclu).
Observatia 1.5.10. Componentele conexe ale unei paduri sunt arbori.
Exemplul 1.5.4. Graful neorientat din Exemplul 1.2.1 nu este padure (deoarece are cicluri), deci nici arbore. Graful sau partial reprezentat n Figura
1.5.1 este o padure (avand doua componente conexe arbori). Graful orientat
din Exemplul 1.2.2 nu este arbore (deoarece are cicluri). Doi arbori partiali
ai sai sunt reprezentati n Figura 1.5.2.
1
e1
e2
e4
e5
6
Figura 1.5.1:
1
5
Figura 1.5.2:
2
5
1.6
23
Parcurgerea grafurilor
Prin parcurgerea unui graf se ntelege o metoda sistematica de vizitare succesiva a nodurilor sale (n vederea prelucrarii informatiilor atasate n structura
de date modelata prin graful dat).
Definitia 1.6.1. Fie G = (V, E) un graf si x V un nod arbitrar fixat.
Parcurgerea n ad
ancime (DF, depth first) a grafului G pornind din
nodul x, numit si r
ad
acin
a a acestei parcurgeri, consta n:
se vizitez
a nodul x, acesta devine nod curent;
daca nodul curent vi are succesori directi (adica noduri vj pentru care
exista muchie sau arc de la vi la vj ) nevizitati, atunci se viziteaza primul
astfel de nod vj ; nodul vj devine nod curent si se continua procedeul de
parcurgere pornind din acest nod;
daca nodul curent vj nu mai are succesori directi nevizitati, atunci se
revine la nodul predecesor direct vi (cel din care a fost vizitat); nodul vi
redevine nod curent si se continua procedeul de parcurgere pornind din
acest nod;
daca nodul curent nu mai are nici succesori directi nevizitati, nici predecesor direct (deci este chiar radacina x), atunci parcurgerea se ncheie.
Observatia 1.6.1. Pentru parcurgerea DF, considerand cate o muchie sau un
arc de la fiecare nod curent vi la primul sau succesor direct nevizitat vj (care
va deveni urmatorul nod curent) se obtine un arbore, numit arbore DF.
Exemplul 1.6.1. Pentru graful din Exemplul 1.2.2, parcurgerea n adancime
pornind din nodul 2 este
DF (2) : 2, 3, 1, 4, 5
(considerand ca ordinea dintre succesorii directi ai fiecarui nod este ordinea
crescatoare). Arborele DF corespunzator acestei parcurgeri este reprezentat
n Figura 1.6.1.
Pentru acelasi graf, parcurgerea DF pornind din nodul 3 este
DF (3) : 3, 1, 2, 4, 5,
iar arborele DF corespunzator este reprezentat n Figura 1.6.2.
Prezentam n continuare doi algoritmi, unul recursiv si altul nerecursiv,
pentru implementarea parcurgerii n adancime.
24
Figura 1.6.1:
3
Figura 1.6.2:
25
pentru orice nod i din parcurgerea DF . Descrierea n pseudocod a algoritmului recursiv de parcurgere n adancime pornind din nodul x are urmatoarea
forma.
DF RECURSIV(x) :
VIZITEAZA(x);
// se viziteaz
a x, de exemplu se afi
seaz
a x
V IZ[x] 1;
// x a fost vizitat
for y = 1, n do
if (axy 1) and (V IZ[y] = 0) then
// y este primul succesor direct nevizitat al lui x
T AT A[y] x;
DF RECURSIV(y);
// se continu
a parcurgerea DF
// din nodul y
26
VIZ[x]=1;
for(y=1;y<=n;y++)
if ((A[x][y]>=1)&&(VIZ[y]==0))
{ TATA[y]=x;
DF_recursiv(y);
}
}
void main()
{ int x,i;
clrscr();
citire_graf();
for(i=1;i<=n;i++)
{ VIZ[i]=0; TATA[i]=0;
}
cout<<"Nodul de pornire: x=";cin>>x;
cout<<"Parcurgerea DF: ";
DF_recursiv(x);
cout<<"\nArborele DF este dat de vectorul TATA: ";
for (i=1;i<=n;i++) cout<<TATA[i]<<" ";
getch();
}
Exemplul 1.6.2. Pentru graful din Exemplul 1.2.2, fisierul de intrare graf2.dat
folosit la citirea grafului n programul anterior contine datele:
5 7
//5 noduri si 7 arce
1 2
//arcul (1, 2)
2 3
//arcul (2, 3)
2 4
//arcul (2, 4)
2 5
//arcul (2, 5)
3 1
//arcul (3, 1)
3 2
//arcul (3, 2)
5 4
//arcul (5, 4).
Algoritmul 1.6.2 (parcurgerea DF, nerecursiv). Fie din nou G = (V, E)
un graf avand multimea de noduri V = {1, . . . , n} si matricea de adiacenta
A = (aij )i,j=1,n . Fie x V un nod arbitrar fixat. Pentru implementarea nerecursiva a parcurgerii DF (x) vom utiliza vectorii V IZ si T AT A cu aceleasi
semnificatii ca n algoritmul anterior, un vector URM cu semnificatia
URM[i] = urmatorul succesor direct al nodului i,
si o structura de tip stiv
a S, memorata ca un vector, ce contine nodurile
vizitate si n curs de prelucrare, adica de vizitare a tuturor succesorilor.
Descrierea n pseudocod a algoritmului are urmatoarea forma.
27
DF(x) :
VIZITEAZA(x);
// se viziteaz
a x, de exemplu se afi
seaz
a x
V IZ[x] 1;
// x a fost vizitat
T AT A[x] 0;
varf 1; S[varf ] x;
// x se introduce ^
n v^
arful stivei
while (varf > 0) do
// stiva este nevid
a
i S[varf ];
// i este nodul din v^
arful stivei
j URM[i] + 1;
// j va fi urm
atorul succesor direct
// nevizitat al lui i, dac
a exist
a
while (aij = 0) and (j n) do j j + 1;
if (j > n) then
// nodul i nu mai are succesori direct
i nevizitat
i
varf varf 1;
// s-a ^
ncheiat prelucrarea lui i
//
si ^
l elimin
am din stiv
a
else
URM[i] j;
// j este urm
atorul succesor direct
// al lui i
if (V IZ[j] = 0) then
// j nu a fost vizitat
VIZITEAZA(j);
// se viziteaz
a j
V IZ[j] 1;
// j a fost vizitat
T AT A[j] i;
varf varf + 1;
S[varf ] j;
// se introduce j ^
n v^
arful stivei
// stiva este vid
a, nu mai exist
a noduri neprelucrate,
// parcurgerea este
^ncheiat
a.
Observatia 1.6.2. Pentru un graf cu n noduri si m muchii sau arce, implementarea anterioara a parcurgerii DF are complexitatea O (n2 ), deoarece
oricare din cele n noduri este vizitat (deci introdus si extras din stiva) cel
mult cate o data, iar cautarea succesorilor directi j nevizitati ai fiecarui nod
i extras din stiva se efectueaza n cel mult n pasi, prin parcurgerea liniei i
din matricea de adiacenta.
Daca graful este memorat prin intermediul listelor de adiacenta, atunci
cautarea succesorilor directi j nevizitati ai fiecarui nod i extras din stiva se
efectueaza, prin parcurgerea lor succesiva, n exact d(i) (pentru graf neorientat) sau d+ (i) (pentru graf orientat) pasi. Cum, conform Propozitiei 1.4.2,
P
P +
d(x) = 2m si
d (x) = m, obtinem ca n acest caz parcurgerea DF
xV
xV
28
unde functia DF(i) este cea din Algoritmul 1.6.1 sau cea din Algoritmul
29
30
Figura 1.6.3:
3
2
1
Figura 1.6.4:
31
BF(x) :
VIZITEAZA(x);
V IZ[x] 1;
T AT A[x] 0;
coada 1;
// nodurile se adaug
a la S pe pozit
ia "coada "
varf 1;
//
si se elimin
a de pe pozit
ia "varf "
S[coada] x;
while (varf coada) do
// coada este nevid
a
i S[varf ];
j URM[i] + 1;
while (aij = 0) and (j n) do j j + 1;
if (j > n) then
varf varf + 1;
else
URM[i] j;
if (V IZ[j] = 0) then
VIZITEAZA(j);
V IZ[j] 1;
T AT A[j] i;
coada coada + 1;
S[coada] j;
Observatia 1.6.7. Analog parcurgerii DF, implementarea anterioara a parcurgerii BF are complexitatea O (n2 ), iar daca graful este memorat prin
intermediul listelor de adiacenta, atunci complexitatea este O (n + m).
Observatia 1.6.8. Observatiile 1.6.3, 1.6.4 si 1.6.5 si algoritmii corespunzatori
raman valabile daca nlocuim parcurgerea DF cu parcurgerea BF.