Escolar Documentos
Profissional Documentos
Cultura Documentos
G.P. Telles
IC–UNICAMP
5 de janeiro de 2021
1 / 96
Parte I
Algoritmos em grafos
2 / 96
I Um grafo não-orientado é um par G = (V, E) onde
I Por exemplo,
V = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
E = {(1, 5), (1, 2), (5, 3), (2, 5), (2, 3),
(1, 6), (2, 6), (3, 4), (10, 4), (9, 8)}
3 / 96
I Grafos normalmente são representados por um diagrama com
cı́rculos conectados por segmentos de reta.
3 4 10
9
5 2
1 6 7
4 / 96
3 4 10
9
5 2
1 6 7
5 / 96
3 4 10
9
5 2
1 6 7
5 / 96
1 3 4
0 2
6 / 96
1 3 4
0 2
6 / 96
3 4 10
9
5 2
1 6 7
5 2
4 10
1 6
7 / 96
3 4 10
9
5 2
1 6 7
5 2
4 10
1 6
8 / 96
I Um passeio P em um grafo G é uma seqüência não-vazia e finita
v0 , e1 , v1 , e2 , v2 , . . . , ek , v` tal que para todo i, 1 ≤ i ≤ `, vi−1 e vi
são extremos de ei .
v` 3 4 10
9
v0 5 2
1 6 7
9 / 96
v` 3 4 10
9
v0 5 2
1 6 7
10 / 96
v0 v` 3 4 10
9
5 2
1 6 7
11 / 96
v0 v` 3 4 10
9
5 2
1 6 7
I Um ciclo é um passeio fechado tal que: (i) tem pelo menos uma
aresta, (ii) os vértices v2 , . . . , v` são distintos e (iii) as arestas são
distintas.
12 / 96
3 4 10
9
5 2
1 6 7
13 / 96
1 5
2 3 6 7
0 4
14 / 96
1 5
2 3 6 7
0 4
0 4
16 / 96
3 4 10
9
5 2
1 6 7
17 / 96
I Vamos supor desse ponto em diante que os grafos são sempre
simples.
I Grafos que não são simples podem ser representados e tratados
pelos algoritmos com modificações diretas.
18 / 96
Representação de grafos em memória
19 / 96
Notação e rótulos
20 / 96
Matriz de adjacências
1 se (i, j) ∈ E
A[i, j] =
0 caso contrário
21 / 96
Operações
22 / 96
Memória
23 / 96
Lista de adjacências
24 / 96
Operações
25 / 96
Memória
26 / 96
Vetor de adjacências
I Para grafos que mudam pouco ou não mudam podemos usar dois
vetores de inteiros, o vetor V para os vértices e o vetor E para as
arestas.
I Para cada vértice u, V [u] registra a posição em E do primeiro
vizinho de u. A vizinhança de u que está no intervalo
E[V [u] . . V [u + 1] − 1].
I São necessários n + 2m inteiros para grafos não-orientados e n + m
inteiros para grafos orientados.
27 / 96
Busca em grafos
28 / 96
Buscas em grafos
29 / 96
Busca em largura
30 / 96
Seções
31 / 96
Busca em largura
32 / 96
Algoritmo
33 / 96
BFS(G, s)
1 for each vertex u ∈ G. V \ {s}
2 u.color = white
3 u. d = ∞
4 u. π = nil
5 s. color = gray
6 s. d = 0
7 s. π = nil
8 queue Q = ∅
9 Enqueue(Q, s)
10 while Q 6= ∅
11 u = Dequeue(Q)
12 for each v ∈ G.Adj [u]
13 if v.color == white
14 v.color = gray
15 v. d = u. d + 1
16 v. π = u
17 Enqueue(Q, v)
18 u.color = black
34 / 96
Custo
35 / 96
I A inicialização (1-8) custa O(n).
I Cada vértice é colocado na fila e retirado dela no máximo uma vez
(zero vezes para os não-alcançáveis). Então o laço da linha 9 é
executado no máximo n vezes e todas as operações na fila custam
O(n) no total.
I Quando um vértice é retirado da fila sua vizinhança é visitada. A
soma dos tamanhos das vizinhanças é 2m para grafos
não-orientados e m para grafos orientados. De qualquer forma, é
O(m). O trabalho por vizinho é O(1).
I O custo total é O(n + m).
36 / 96
Árvore de busca em largura
37 / 96
Subgrafo de predecessores
38 / 96
Impressão de caminhos em uma árvore de busca em largura
PRINT-PATH(G, s, v)
1 if v == s
2 print s
3 elseif v.π == NIL
4 print “unreachable”
5 else
6 PRINT-PATH(G, s, v.π)
7 print v
39 / 96
Correção da BFS
40 / 96
Busca em profundidade
41 / 96
Seções
42 / 96
Busca em profundidade
43 / 96
I O algoritmo usa cores para manter o estado de cada vértice
durante a busca:
I branco: não alcançado pela busca.
I cinza: alcançado pela busca e com alguma aresta incidente
não-visitada.
I preto: finalizado.
I O algoritmo mantém um contador de tempo e registra quando cada
vértice u foi descoberto pela busca (u.d) e quando foi finalizado
(u.f ) pela busca.
I O campo u.π registra o vértice inicial da aresta que foi usada para
descobrir u.
44 / 96
Pseudo-código
DFS-Visit(G, u)
DFS(G) 1 time = time + 1
2 u. d = time
1 for each vertex u ∈ G. V
3 u.color = gray
2 u.color = white
4 for each v ∈ G. Adj [u]
3 u. π = NIL
5 if v.color == white
4 time = 0
6 v. π = u
5 for each vertex u ∈ G. V
7 DFS-Visit(G, v)
6 if u.color == white
8 u.color = black
7 DFS-Visit(G, u)
9 time = time + 1
10 u. f = time
45 / 96
Análise
46 / 96
Subgrafo de predecessores
I Os campos π em cada vértice definem um subgrafo de
predecessores Gπ = (V, Eπ ) tal que
47 / 96
Timestamps
48 / 96
Teoremas
49 / 96
Classificação de arestas
50 / 96
Classificação de arestas em grafos orientados
51 / 96
Classificação de arestas em grafos não-orientados
52 / 96
Ordenação topológica
53 / 96
I Grafos orientados acı́clicos aparecem em várias situações que
modelam dependência.
I Por exemplo,
I Podemos representar unidades de compilação de um programa
como vértices e adicionar uma aresta entre as unidades u e v se
u define nomes que v vai usar.
I Podemos representar atividades como vértices e adicionar uma
aresta entre i e j se i tem que ser realizada antes de j.
6
2 1
9
4
5 3
7
0
8
54 / 96
Ordenação topológica
55 / 96
6
2 1
9
4
5 3
7
0
8
1 2 7 3 5 9 6 4 0 8
56 / 96
Algoritmo
Topological-Sort(G)
1 Let L be an empty list
2 Run a modified DFS(G) that whenever a vertex v is finalized,
it adds v to the head of L
3 return L
57 / 96
Complexidade
58 / 96
Correção
59 / 96
Componentes fortemente conexos
60 / 96
Componentes conexas
61 / 96
Componentes conexas
62 / 96
Componentes fortemente conexas
63 / 96
6
4
2 1 9 8
0
5 3 7
64 / 96
Grafo transposto
65 / 96
6
4
2 1 9 8
0
5 3 7
4
2 1 9 8
0
5 3 7
66 / 96
Algoritmo
Strongly-Connected-Components(G)
1 Call DFS(G) modified to output a list L of vertices
in order of decreasing finishing times
2 Compute GT
3 Call DFS(GT ) modified such that its main loop process vertices
in the order given by L
4 Output the vertices of each tree in the depth-first forest built by
line 3 as a separate strongly connected component
67 / 96
Complexidade
68 / 96
Idéia da correção
69 / 96
C 6
C 6
4 D
4 D
2 1 9 8
2 1 9 8 B
B A
A 0
0 7
7 5 3
5 3
A B
I GCFC é acı́clico.
I A ordem gerada na DFS em G garante que a DFS em GT não
consegue avançar de um componente C para outro C 0 porque
todas as arestas que saem de C vão para componentes que já
foram finalizados.
70 / 96
Section 6
71 / 96
Seções
72 / 96
Árvore geradora de custo mı́nimo
mı́nimo.
I Uma árvore geradora de custo mı́nimo também é chamada de
árvore geradora mı́nima e abreviada MST.
I Pode haver mais de uma MST para um grafo.
73 / 96
4
5
3 2
2 2
2 2
2 3
4
3 1 4 1
3
2 2
4
5
3 2
2 2
2 2
2 3
4
3 1 4 1
3
2 2
74 / 96
I Vários problemas práticos estão relacionados com encontrar uma
MST.
I Um conjunto deles são problemas de encontrar uma forma de
conectar elementos usando a menor quantidade de recursos, por
exemplo, conectar prédios por uma fibra óptica minimizando a
quantidade de fibra.
75 / 96
Método guloso genérico
76 / 96
Pseudo-código
Generic-MST(G, w)
1 A=∅
2 while A does not form a spanning tree
3 find an edge (u, v) that is safe for A
4 A = A ∪ {(u, v)}
5 return A
77 / 96
Arestas seguras
78 / 96
4
5
3 2
2 2
2 2
2 3
4
3 1 4 1
3
2 2
79 / 96
I Seja A um conjunto de arestas. Um corte respeita A se nenhuma
aresta em A cruza o corte.
4
5
3 2
2 2
2 2
2 3
4
3 1 4 1
3
2 2
80 / 96
I Teorema (CLRS 23.1) Sejam G = (V, E) um grafo não-orientado e
conexo com pesos reais nas arestas,
A um subconjunto de E em uma MST para G e
(S, V − S) um corte que respeita A. Se (u, v) é uma aresta leve
que cruza (S, V − S) então (u, v) é segura para A.
I Corolário (CLRS 23.2) Sejam G = (V, E) um grafo não-orientado e
conexo com pesos reais nas arestas,
A um subconjunto de E em uma MST para G e
C um componente conexo na floresta GA = (V, A).
Se (u, v) é uma aresta leve que conecta C a outro componente em
GA então (u, v) é segura para A.
81 / 96
I Os algoritmos de Prim e Kruskal estão fundamentados nesses dois
resultados.
I O conjunto A é implı́cito nos algoritmos.
82 / 96
Subsection 1
Algoritmo de Prim
83 / 96
Algoritmo de Prim
84 / 96
Algoritmo
MST-Prim(G, w)
1 for each u ∈ G.V
2 u.cost = ∞
3 u.π = NIL
4 V1 .cost = 0
5 Add the vertices of G to a priority queue Q on cost
6 while Q 6= ∅
7 u = Extract-Min(Q)
8 for each v ∈ G.Adj [u]
9 if Exists(Q, v) and w(u, v) < v.cost
10 v.π = u
11 v.cost = w(u, v)
12 Decrease-Cost(Q, v, w(u, v))
85 / 96
Complexidade
86 / 96
Vetor
87 / 96
Complexidade
88 / 96
heap
89 / 96
Complexidade
90 / 96
Complexidade, heap de Fibonacci
91 / 96
Subsection 2
Algoritmo de Kruskal
92 / 96
Algoritmo de Kruskal
93 / 96
Pseudo-código
MST-Kruskal(G, w)
1 A=∅
2 for each vertex v ∈ G.V
3 Make-Set(v)
4 sort the edges of G.E into nondecreasing order by weight w
5 for each edge (u, v) ∈ G.E in nondecreasing order of weight
6 if Find-Set(u) 6= Find-Set(v)
7 A = A ∪ {(u, v)}
8 Union(u, v)
9 return A
94 / 96
Complexidade
95 / 96
I A inicialização de A (linha 1) custa O(1).
I A ordenação (linha 4) custa O(m log m).
I O loop nas linhas 2–3 faz n chamadas a Make-Set.
I O loop nas linhas 5–8 faz O(m) chamadas a Find-Set e Union.
I As operações nos conjuntos disjuntos custam então
O((n + m)α(n)). Como o grafo é conexo, m ≥ n − 1 e então as
operações nos conjuntos disjuntos custam O(mα(n)).
I O total é
O(m log m + mα(n)) = O(m log m) = O(m log n2 ) = O(m log n).
96 / 96