Você está na página 1de 26

Projeto de Algoritmos – Cap.

7 Algoritmos em Grafos 1

Motivação

• Muitas aplicações em computação


necessitam considerar conjunto de conexões
entre pares de objetos:
– Existe um caminho para ir de um objeto a
outro seguindo as conexões?

Algoritmos em Grafos∗ – Qual é a menor distância entre um objeto


e outro objeto?
– Quantos outros objetos podem ser
alcançados a partir de um determinado
objeto?

• Existe um tipo abstrato chamado grafo que é


usado para modelar tais situações.

Última alteração: 26 de Abril de 2004

∗ Transparências elaboradas por Charles Ornelas Almeida e Nivio Ziviani

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos 2 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.1 3

Aplicações Conceitos Básicos

• Alguns exemplos de problemas práticos que • Grafo: conjunto de vértices e arestas.


podem ser resolvidos através de uma
• Vértice: objeto simples que pode ter nome e
modelagem em grafos:
outros atributos.
– Ajudar máquinas de busca a localizar
informação relevante na Web. • Aresta: conexão entre dois vértices.

– Descobrir os melhores casamentos entre 3 aresta


posições disponíveis em empresas e
pessoas que aplicaram para as posições 0 1 4
de interesse.
– Descobrir qual é o roteiro mais curto para 2 vértice
visitar as principais cidades de uma região
turística. • Notação: G = (V, A)
– G: grafo
– V: conjunto de vértices
– A: conjunto de arestas
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.1 4 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.1 5

Grafos Direcionados Grafos Não Direcionados

• Um grafo direcionado G é um par (V, A), onde • Um grafo não direcionado G é um par (V, A),
V é um conjunto finito de vértices e A é uma onde o conjunto de arestas A é constituído de
relação binária em V . pares de vértices não ordenados.
– Uma aresta (u, v) sai do vértice u e entra – As arestas (u, v) e (v, u) são consideradas
no vértice v. O vértice v é adjacente ao como uma única aresta. A relação de
vértice u. adjacência é simétrica.
– Podem existir arestas de um vértice para – Self-loops não são permitidos.
ele mesmo, chamadas de self-loops.
0 1 4
0 1 4

3 2 5
3 2 5

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.1 6 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.1 7

Grau de um Vértice Caminho entre Vértices

• Em grafos não direcionados: • Um caminho de comprimento k de um


vértice x a um vértice y em um grafo
– O grau de um vértice é o número de
G = (V, A) é uma seqüência de vértices
arestas que incidem nele.
(v0 , v1 , v2 , . . . , vk ) tal que x = v0 e y = vk , e
– Um vérice de grau zero é dito isolado ou (vi−1 , vi ) ∈ A para i = 1, 2, . . . , k.
não conectado.
• O comprimento de um caminho é o número
Ex.: O vértice 1 tem 0 1 4 de arestas nele, isto é, o caminho contém os
grau 2 e o vértice 3 é
vértices v0 , v1 , v2 , . . . , vk e as arestas
isolado. (v0 , v1 ), (v1 , v2 ), . . . , (vk−1 , vk ).
3 2 5

• Se existir um caminho c de x a y então y é


• Em grafos direcionados alcançável a partir de x via c.
– O grau de um vértice é o número de • Um caminho é simples se todos os vértices
arestas que saem dele (out-degree) mais do caminho são distintos.
o número de arestas que chegam nele
Ex.: O caminho (0, 1, 2, 3) é simples e tem
(in-degree).
comprimento 3. O caminho (1, 3, 0, 3) não é
Ex.: O vértice 2 tem 0 1 4 simples.
in-degree 2, out-degree
0 1 4
2 e grau 4.
3 2 5

3 2 5
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.1 8 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.1 9

Ciclos Ciclos

• Em um grafo direcionado: • Em um grafo não direcionado:


– Um caminho (v0 , v1 , . . . , vk ) forma um ciclo – Um caminho (v0 , v1 , . . . , vk ) forma um ciclo
se v0 = vk e o caminho contém pelo se v0 = vk e o caminho contém pelo
menos uma aresta. menos três arestas.
– O ciclo é simples se os vértices – O ciclo é simples se os vértices
v1 , v2 , . . . , vk são distintos. v1 , v2 , . . . , vk são distintos.
– O self-loop é um ciclo de tamanho 1. Ex.: O caminho (0, 1, 2, 0) é um ciclo.
– Dois caminhos (v0 , v1 , . . . , vk ) e
0 1 4
(v00 , v10 , . . . , vk0 ) formam o mesmo ciclo se
existir um inteiro j tal que vi0 = v(i+j) mod k
para i = 0, 1, . . . , k − 1. 3 2 5

Ex.: O caminho (0, 1, 2, 3, 0) forma um ciclo. O


caminho(0, 1, 3, 0) forma o mesmo ciclo que os
caminhos (1, 3, 0, 1) e (3, 0, 1, 3).

0 1 4

3 2 5

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.1 10 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.1 11

Componentes Conectados Componentes Fortemente Conectados

• Um grafo não direcionado é conectado se • Um grafo direcionado G = (V, A) é


cada par de vértices está conectado por um fortemente conectado se cada dois vértices
caminho. quaisquer são alcançáveis a partir um do
outro.
• Os componentes conectados são as porções
conectadas de um grafo. • Os componentes fortemente conectados
de um grafo direcionado são conjuntos de
• Um grafo não direcionado é conectado se ele
vértices sob a relação “são mutuamente
tem exatamente um componente conectado.
alcançáveis”.
Ex.: Os componentes são: {0, 1, 2}, {4, 5} e {3}.
• Um grafo direcionado fortemente
0 1 4 conectado tem apenas um componente
fortemente conectado.
3 2 5 Ex.: {0, 1, 2, 3}, {4} e {5} são os componentes
fortemente conectados, {4, 5} não o é pois o
vértice 5 não é alcançável a partir do vértice 4.

0 1 4

3 2 5
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.1 12 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.1 13

Grafos Isomorfos Subgrafos

• G = (V, A) e G0 = (V 0 , A0 ) são isomorfos se • Um grafo G0 = (V 0 , A0 ) é um subgrafo de


existir uma bijeção f : V → V 0 tal que G = (V, A) se V 0 ⊆ V e A0 ⊆ A.
(u, v) ∈ A se e somente se (f (u), f (v)) ∈ A0 .
• Dado um conjunto V 0 ⊆ V , o subgrafo
0 1 induzido por V 0 é o grafo G0 = (V 0 , A0 ), onde
4 5
A0 = {(u, v) ∈ A|u, v ∈ V 0 }.

Ex.: Subgrafo induzido pelo conjunto de vértices


7 6 {1, 2, 4, 5}.

3 2 0 1 4

s w x t 3 2 5

v z y u 1 4

2 5

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.1 14 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.1 15

Versão Direcionada de um Grafo Não Versão Não Direcionada de um Grafo


Direcionado Direcionado

• A versão direcionada de um grafo não • A versão não direcionada de um grafo


direcionado G = (V, A) é um grafo direcionado G = (V, A) é um grafo não
direcionado G0 = (V 0 , A0 ) onde (u, v) ∈ A0 se e direcionado G0 = (V 0 , A0 ) onde (u, v) ∈ A0 se e
somente se (u, v) ∈ A. somente se u 6= v e (u, v) ∈ A.

• Cada aresta não direcionada (u, v) em G é • A versão não direcionada contém as arestas
substituída por duas arestas direcionadas de G sem a direção e sem os self-loops.
(u, v) e (v, u)
• Em um grafo não direcionado, u e v são
• Em um grafo direcionado, um vizinho de um vizinhos se eles são adjacentes.
vértice u é qualquer vértice adjacente a u na
0 1 4 0 1 4
versão não direcionada de G.
0 1 0 1
3 2 5 3 2 5

2 2
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.1 16 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.1 17

Outras Classificações de Grafos Grafos Completos

• Grafo ponderado: possui pesos associados • Um grafo completo é um grafo não


às arestas. direcionado no qual todos os pares de
vértices são adjacentes.
• Grafo bipartido: grafo não direcionado
G = (V, A) no qual V pode ser particionado • Possui (|V |2 − |V |)/2 = |V |(|V | − 1)/2 arestas,
em dois conjuntos V1 e V2 tal que (u, v) ∈ A pois do total de |V |2 pares possíveis de
implica que u ∈ V1 e v ∈ V2 ou u ∈ V2 e v ∈ V1 vértices devemos subtrair |V | self-loops e
(todas as arestas ligam os dois conjuntos V1 e dividir por 2 (cada aresta ligando dois vértices
V2 ). é contada duas vezes).

• Hipergrafo: grafo não direcionado em que • O número total de grafos diferentes com |V |
cada aresta conecta um número arbitrário de vértices é 2|V |(|V |−1)/2 (número de maneiras
vértices. diferentes de escolher um subconjunto a
partir de |V |(|V | − 1)/2 possíveis arestas).

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.1 18 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2 19

Árvores O Tipo Abstratos de Dados Grafo

• Árvore livre: grafo não direcionado acíclico e • Importante considerar os algoritmos em


conectado. É comum dizer apenas que o grafos como tipos abstratos de dados.
grafo é uma árvore omitindo o “livre”.
• Conjunto de operações associado a uma
• Floresta: grafo não direcionado acíclico, estrutura de dados.
podendo ou não ser conectado.
• Independência de implementação para as
• Árvore geradora de um grafo conectado operações.
G = (V, A): subgrafo que contém todos os
vértices de G e forma uma árvore.

• Floresta geradora de um grafo G = (V, A):


subgrafo que contém todos os vértices de G e
forma uma floresta.

(a) (b)
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2 20 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2 21

Operadores do TAD Grafo Operação “Obter Lista de Adjacentes”

1. FGVazio(Grafo): Cria um grafo vazio. 1. ListaAdjVazia(v, Grafo): retorna true se a lista


de adjacentes de v está vazia.
2. InsereAresta(V1,V2,Peso, Grafo): Insere uma
aresta no grafo. 2. PrimeiroListaAdj(v, Grafo): retorna o
endereço do primeiro vértice na lista de
3. ExisteAresta(V1,V2,Grafo): Verifica se existe
adjacentes de v.
uma determinada aresta.
3. ProxAdj(v, Grafo, u, Peso, Aux, FimListaAdj):
4. Obtem a lista de vértices adjacentes a um
retorna o vértice u (apontado por Aux) da lista
determinado vértice (tratada a seguir).
de adjacentes de v, bem como o peso da
5. RetiraAresta(V1,V2,Peso, Grafo): Retira uma aresta (v, u). Ao retornar, Aux aponta para o
aresta do grafo. próximo vértice da lista de adjacentes de v, e
FimListaAdj retorna true se o final da lista de
6. LiberaGrafo(Grafo): Liberar o espaço
adjacentes foi encontrado.
ocupado por um grafo.

7. ImprimeGrafo(Grafo): Imprime um grafo.

8. GrafoTransposto(Grafo,GrafoT): Obtém o
transposto de um grafo direcionado.

9. RetiraMin(A): Obtém a aresta de menor peso


de um grafo com peso nas arestas.

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2 22 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2.1 23

Implementação da Operação “Obter Matriz de Adjacência


Lista de Adjacentes”
• A matriz de adjacência de um grafo
• É comum encontrar um pseudo comando do G = (V, A) contendo n vértices é uma matriz
tipo: n × n de bits, onde A[i, j] é 1 (ou verdadeiro)
for u ∈ ListaAdjacentes (v) do { faz algo com u } se e somente se existe um arco do vértice i
para o vértice j.
• O trecho de programa abaixo apresenta um
possível refinamento do pseudo comando • Para grafos ponderados A[i, j] contém o
acima. rótulo ou peso associado com a aresta e,
neste caso, a matriz não é de bits.
i f not ListaAdjVazia (v , Grafo)
then begin • Se não existir uma aresta de i para j então é
Aux : = PrimeiroListaAdj (v , Grafo ) ;
necessário utilizar um valor que não possa
FimListaAdj : = false ;
while not FimListaAdj ser usado como rótulo ou peso.
do ProxAdj(v , Grafo , u, Peso, Aux, FimListaAdj ) ;
end;
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2.1 24 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2.1 25

Matriz de Adjacência - Exemplo Matriz de Adjacência - Análise

0 1 4 0 1 4 • Deve ser utilizada para grafos densos, onde


|A| é próximo de |V |2 .
3 2 5 3 2 5 • O tempo necessário para acessar um
elemento é independente de |V | ou |A|.
0 1 2 3 4 5 0 1 2 3 4 5
0 1 1 0 1 1 • É muito útil para algoritmos em que
1 1 1 1 1 1 necessitamos saber com rapidez se existe
2 1 1 2 1 1
3 1 3 uma aresta ligando dois vértices.
4 4
5 5 • A maior desvantagem é que a matriz
(a) (b) necessita Ω(|V |2 ) de espaço. Ler ou examinar
a matriz tem complexidade de tempo O(|V |2 ).

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2.1 26 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2.1 27

Matriz de Adjacência - Implementação Matriz de Adjacência - Implementação

• A inserção de um novo vértice ou retirada de procedure InsereAresta ( var V1, V2: TipoValorVertice ;
var Peso : TipoPeso;
um vértice já existente pode ser realizada var Grafo : TipoGrafo ) ;
com custo constante. begin
Grafo.Mat[V1, V2] : = peso;
const MaxNumVertices = 100; end;
MaxNumArestas = 4500;
type function ExisteAresta ( Vertice1 , Vertice2 : TipoValorVertice ;
TipoValorVertice = 0..MaxNumVertices; var Grafo : TipoGrafo ) : boolean;
TipoPeso = integer ; begin
TipoGrafo = record ExisteAresta : = Grafo.Mat[ Vertice1 , Vertice2 ] > 0;
Mat: array [ TipoValorVertice , TipoValorVertice ] end; { ExisteAresta }
of TipoPeso;
NumVertices: 0 . .MaxNumVertices; {−−Operador para obter a l i s t a de adjacentes−−}
NumArestas : 0 . .MaxNumArestas; function ListaAdjVazia ( var Vertice : TipoValorVertice ;
end; var Grafo : TipoGrafo ) : boolean;
Apontador = TipoValorVertice ; var Aux: Apontador ; ListaVazia : boolean;
begin
procedure FGVazio(var Grafo : TipoGrafo ) ; ListaVazia : = true ; Aux := 0;
var i , j : integer ; while (Aux < Grafo.NumVertices) and ListaVazia do
begin i f Grafo.Mat[ Vertice , Aux] > 0
for i := 0 to Grafo.NumVertices do then ListaVazia : = false
for j := 0 to Grafo.NumVertices do Grafo.mat[ i , j ] : = 0 ; else Aux : = Aux + 1;
end; ListaAdjVazia : = ListaVazia = true ;
end; { ListaAdjVazia }
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2.1 28 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2.1 29

Matriz de Adjacência - Implementação Matriz de Adjacência - Implementação


{−−Operador para obter a l i s t a de adjacentes−−} procedure RetiraAresta ( var V1, V2: TipoValorVertice ;
function PrimeiroListaAdj ( var Vertice : TipoValorVertice ; var Peso : TipoPeso;
var Grafo : TipoGrafo ) : Apontador; var Grafo : TipoGrafo ) ;
var Aux: Apontador ; Listavazia : boolean; begin
begin i f Grafo.Mat[V1, V2] = 0
ListaVazia : = true ; Aux := 0; then writeln ( ’Aresta nao existe ’ )
while (Aux < Grafo.NumVertices) and ListaVazia do else begin Peso : = Grafo.Mat[V1, V2] ; Grafo.Mat[V1, V2] : = 0 ;
i f Grafo.Mat[ Vertice , Aux] > 0 end;
then begin PrimeiroListaAdj : = Aux; ListaVazia : = false ; end; { RetiraAresta }
end
else Aux : = Aux + 1; procedure LiberaGrafo ( var Grafo : TipoGrafo ) ;
i f Aux = Grafo.NumVertices begin { Nao faz nada no caso de matrizes de adjacencia }
then writeln ( ’ Erro : Lista adjacencia vazia ( PrimeiroListaAdj ) ’ ) ; end; { LiberaGrafo }
end; { PrimeiroListaAdj }
procedure ImprimeGrafo ( var Grafo : TipoGrafo ) ;
{−−Operador para obter a l i s t a de adjacentes−−} var i , j : integer ;
procedure ProxAdj ( var Vertice : TipoValorVertice ; var Grafo : TipoGrafo; begin
var Adj : TipoValorVertice ; var Peso: TipoPeso; write( ’ ’ );
var Prox : Apontador ; var FimListaAdj : boolean) ; for i := 0 to Grafo.NumVertices−1 do write( i : 3 ) ; writeln ;
{ −−Retorna Adj apontado por Prox−−} for i := 0 to Grafo.NumVertices−1 do
begin begin
Adj := Prox ; Peso : = Grafo.Mat[ Vertice , Prox ] ; Prox : = Prox + 1; write( i : 3 ) ;
while ( Prox < Grafo.NumVertices) and for j := 0 to Grafo.NumVertices−1 do write(Grafo.mat[ i , j ] : 3 ) ;
(Grafo.Mat[ Vertice , Prox] = 0 ) do Prox : = Prox + 1; writeln ;
i f Prox = Grafo.NumVertices then FimListaAdj : = true ; end;
end; { ProxAdj− } end; { ImprimeGrafo }

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2.2 30 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2.2 31

Listas de Adjacência usando Listas de adjacência - Análise


Apontadores
• Os vértices de uma lista de adjacência são
em geral armazenados em uma ordem
3 1 5
0 arbitrária.
5
0 1 1 3 2 7
1
• Possui uma complexidade de espaço
7 2 O(|V | + |A|)
3 2 3
• Indicada para grafos esparsos, onde |A| é
0 1 5 muito menor do que |V |2 .
5
0 1 0 5 2 7
1 • É compacta e usualmente utilizada na maioria
7 1 7 das aplicações.
2
3 2
3 • A principal desvantagem é que ela pode ter
tempo O(|V |) para determinar se existe uma
• Um arranjo Adj de |V | listas, uma para cada aresta entre o vértice i e o vértice j, pois
vértice em V . podem existir O(|V |) vértices na lista de
• Para cada u ∈ V , Adj[u] contém todos os adjacentes do vértice i.
vértices adjacentes a u em G.
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2.2 32 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2.2 33

Listas de Adjacência usando Listas de Adjacência usando


Apontadores - Implementação Apontadores - Implementação
• No uso de apontadores a lista é constituída {−− Entram aqui os operadores FLVazia, Vazia, Insere, Retira e Imprime do
TAD Lista de Apontadores−−}
de células, onde cada célula contém um item procedure FGVazio(var Grafo : TipoGrafo ) ;
da lista e um apontador para a célula var i : integer ;
begin
seguinte.
for i := 0 to Grafo.NumVertices−1 do FLVazia(Grafo. Adj [ i ] ) ;
end; { FGVazio }
const MaxNumVertices = 100;
MaxNumArestas = 4500;
procedure InsereAresta(var V1, V2: TipoValorVertice ; var Peso: TipoPeso;
type
var Grafo : TipoGrafo ) ;
TipoValorVertice = 0..MaxNumVertices;
var x : TipoItem ;
TipoPeso = integer ;
begin
TipoItem = record
x . Vertice : = V2; x .Peso : = Peso;
Vertice : TipoValorVertice ;
Insere (x , Grafo. Adj [V1] ) ;
Peso : TipoPeso;
end; { InsereAresta }
end;
Apontador = ^Celula ;
function ExisteAresta ( Vertice1 , Vertice2 : TipoValorVertice ;
Celula = record
var Grafo : TipoGrafo ) : boolean;
Item : TipoItem ;
var Aux: Apontador;
Prox : Apontador;
EncontrouAresta : boolean;
end;
begin
TipoLista = record
Aux : = Grafo. Adj [ Vertice1 ] . Primeiro^.Prox;
Primeiro : Apontador;
EncontrouAresta : = false ;
Ultimo : Apontador;
while (Aux <> n i l ) and ( EncontrouAresta = false ) do
end;
begin
TipoGrafo = record
i f Vertice2 = Aux^.Item . Vertice then EncontrouAresta : = true ;
Adj : array [ TipoValorVertice ] of TipoLista ;
Aux : = Aux^.Prox;
NumVertices : TipoValorVertice ;
end;
NumArestas: 0 . .MaxNumArestas;
ExisteAresta : = EncontrouAresta;
end;
end; { ExisteAresta }

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2.2 34 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2.2 35

Listas de Adjacência usando Listas de Adjacência usando


Apontadores - Implementação Apontadores - Implementação
{−−Operador para obter a l i s t a de adjacentes−−} procedure RetiraAresta ( var V1, V2: TipoValorVertice ;
function ListaAdjVazia ( var Vertice : TipoValorVertice ; var Peso : TipoPeso;
var Grafo : TipoGrafo ) : boolean; var Grafo : TipoGrafo ) ;
begin var AuxAnterior , Aux: Apontador;
ListaAdjVazia : = Grafo. Adj [ Vertice ] . Primeiro = EncontrouAresta : boolean;
Grafo. Adj [ Vertice ] . Ultimo ; x : TipoItem ;
end; { ListaAdjVazia }
begin
AuxAnterior : = Grafo. Adj [V1] . Primeiro ;
{−−Operador para obter a l i s t a de adjacentes−−}
Aux : = Grafo. Adj [V1] . Primeiro^.Prox;
function PrimeiroListaAdj ( var Vertice : TipoValorVertice ;
EncontrouAresta : = false ;
var Grafo : TipoGrafo ) : Apontador;
while (Aux <> n i l ) and ( EncontrouAresta = false ) do
begin
begin
PrimeiroListaAdj : = Grafo. Adj [ Vertice ] . Primeiro^.Prox;
i f V2 = Aux^.Item . Vertice
end; { PrimeiroListaAdj }
then begin
Retira (AuxAnterior , Grafo. Adj [V1] , x ) ;
{−−Operador para obter a l i s t a de adjacentes−−}
Grafo.NumArestas : = Grafo.NumArestas − 1;
procedure ProxAdj ( var Vertice : TipoValorVertice ;
EncontrouAresta : = true ;
var Grafo : TipoGrafo;
end;
var Adj : TipoValorVertice ;
AuxAnterior : = Aux; Aux : = Aux^.Prox;
var Peso : TipoPeso;
var Prox : Apontador; end;
var FimListaAdj : boolean) ; end; { RetiraAresta }
{ −−Retorna Adj e Peso do Item apontado por Prox−−}
begin
Adj := Prox^.Item . Vertice ;
Peso := Prox^.Item .Peso;
Prox := Prox^.Prox;
i f Prox = n i l then FimListaAdj : = true ;
end; { ProxAdj− }
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2.2 36 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2.3 37

Listas de Adjacência usando Listas de Adjacência usando Arranjos


Apontadores - Implementação Cab Prox Peso
3
0 4 4
procedure LiberaGrafo ( var Grafo : TipoGrafo ) ; 5
0 1 1 6 5
var AuxAnterior , Aux: Apontador; V
2 2 0
begin 7 3 3 0
for i := 0 to Grafo.NumVertices−1 do 4 1 0 5
begin 3 2 A 5 1 6 3
Aux : = Grafo. Adj [ i ] . Primeiro^.Prox; 6 2 0 7
dispose(Grafo. Adj [ i ] . Primeiro ) ; { Libera celula cabeca}
while Aux <> n i l do Cab Prox Peso
begin AuxAnterior : = Aux; Aux : = Aux^.Prox ; dispose(AuxAnterior ) ; 0 4 4
end; 5 1 6 5
0 1 V
end; 2 7 7
end; { LiberaGrafo } 3 3 0
7
4 1 0 5
procedure ImprimeGrafo ( var Grafo : TipoGrafo ) ; 3 2 5 0 6 5
A
var i : integer ; Aux: Apontador; 6 2 0 7
begin 7 1 0 7
for i := 0 to Grafo.NumVertices−1 do
begin • Cab: endereços do último item da lista de
write( ’ Vertice ’ , i :2 , ’ : ’ ) ; adjacentes de cada vértice (nas |V | primeiras
i f not Vazia(Grafo. Adj [ i ] )
then begin posições) e os vértices propriamente ditos
Aux : = Grafo. Adj [ i ] . Primeiro^.Prox; (nas |A| últimas posições)
while Aux <> n i l do
begin • Prox: endereço do próximo item da lista de
write(Aux^.Item . Vertice :3 , ’ ( ’ ,Aux^.Item .Peso, ’ ) ’ ) ;
adjacentes.
Aux : = Aux^.Prox;
end; • Peso: valor do peso de cada aresta do grafo
end;
writeln ;
(nas últimas |A| posições).
end;
end; { ImprimeGrafo }

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2.3 38 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2.3 39

Listas de Adjacência usando Arranjos Listas de Adjacência usando Arranjos


- Implementação - Implementação

const MaxNumVertices = 100; procedure InsereAresta ( var V1, V2: TipoValorVertice ;


MaxNumArestas = 4500; var Peso : TipoPeso;
MaxTam = MaxNumVertices+2∗MaxNumArestas; var Grafo : TipoGrafo ) ;
type var Pos: integer ;
TipoValorVertice = 0..MaxNumVertices; begin
TipoPeso = integer ; Pos:= Grafo. ProxDisponivel ;
i f Grafo. ProxDisponivel = MaxTam
TipoTam = 0..MaxTam;
then writeln ( ’nao ha espaco disponivel para a aresta ’ )
TipoGrafo = record
else begin
Cab : array [TipoTam] of TipoTam;
Grafo. ProxDisponivel : = Grafo. ProxDisponivel + 1;
Prox : array [TipoTam] of TipoTam;
Grafo.Prox[Grafo.Cab[V1] ] : = Pos;
Peso : array [TipoTam] of TipoTam;
Grafo.Cab[Pos] : = V2; Grafo.Cab[V1] : = Pos;
ProxDisponivel : TipoTam;
Grafo.Prox[Pos] : = 0 ; Grafo.Peso[Pos] : = Peso;
NumVertices : 0 . .MaxNumVertices;
end;
NumArestas : 0 . .MaxNumArestas;
end; { InsereAresta}
end;
Apontador = TipoTam;
function ExisteAresta ( Vertice1 , Vertice2 : TipoValorVertice ;
var Grafo : TipoGrafo ) : boolean;
procedure FGVazio(var Grafo : TipoGrafo ) ;
var Aux: Apontador ; EncontrouAresta : boolean;
var i : integer ;
begin
begin
Aux : = Grafo.Prox[ Vertice1 ] ; EncontrouAresta : = false ;
for i := 0 to Grafo.NumVertices do while (Aux < > 0) and ( EncontrouAresta = false ) do
begin begin
Grafo.Prox[ i ] : = 0 ; Grafo.Cab[ i ] : = i ; i f Vertice2 = Grafo.Cab[Aux] then EncontrouAresta : = true ;
Grafo. ProxDisponivel : = Grafo.NumVertices; Aux : = Grafo.Prox[Aux] ;
end; end;
end; ExisteAresta : = EncontrouAresta;
end; { ExisteAresta }
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2.3 40 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.2.3 41

Listas de Adjacência usando Arranjos Listas de Adjacência usando Arranjos


- Implementação - Implementação

{−−Operador para obter a l i s t a de adjacentes−−} procedure RetiraAresta ( var V1, V2: TipoValorVertice ;
function ListaAdjVazia (var Vertice : TipoValorVertice ; var Peso: TipoPeso ; var Grafo : TipoGrafo ) ;
var Grafo : TipoGrafo ) : boolean; var Aux, AuxAnterior : Apontador ; EncontrouAresta : boolean;
begin begin
ListaAdjVazia : = Grafo.Prox[ Vertice ] = 0; AuxAnterior : = V1; Aux : = Grafo.Prox[V1] ;
end; { ListaAdjVazia } EncontrouAresta : = false ;
while (Aux < > 0) and ( EncontrouAresta = false ) do
begin
{−−Operador para obter a l i s t a de adjacentes−−}
i f V2 = Grafo.Cab[Aux]
function PrimeiroListaAdj (var Vertice : TipoValorVertice ;
then EncontrouAresta : = true
var Grafo : TipoGrafo ) : Apontador;
else begin AuxAnterior : = Aux; Aux : = Grafo.Prox[Aux] ; end;
begin
end;
PrimeiroListaAdj : = Grafo.Prox[ Vertice ] ;
i f EncontrouAresta
end; { PrimeiroListaAdj }
then Grafo.Cab[Aux] : = MaxNumVertices+2∗MaxNumArestas
{−−Apenas marca como retirado −−}
{−−Operador para obter a l i s t a de adjacentes−−}
else writeln ( ’Aresta nao existe ’ ) ;
procedure ProxAdj ( var Vertice : TipoValorVertice ;
end; { RetiraAresta }
var Grafo : TipoGrafo;
var Adj : TipoValorVertice ;
procedure LiberaGrafo ( var Grafo : TipoGrafo ) ;
var Peso : TipoPeso;
begin {Nada no caso de posicoes contiguas} end; { LiberaGrafo }
var Prox : Apontador;
var FimListaAdj : boolean) ; procedure ImprimeGrafo ( var Grafo : TipoGrafo ) ;
{ −−Retorna Adj apontado por Prox−−} var i : integer ;
begin begin
Adj := Grafo.Cab[Prox ] ; Peso : = Grafo.Peso[Prox ] ; writeln ( ’ Cab Prox Peso ’ ) ;
Prox : = Grafo.Prox[Prox ] ; for i := 0 to Grafo.NumVertices+2∗Grafo.NumArestas−1 do
i f Prox = 0 then FimListaAdj : = true ; writeln ( i :2 ,Grafo.Cab[ i ]:4 ,Grafo.Prox[ i ] : 4 , Grafo.Peso[ i ] : 4 ) ;
end; { ProxAdj− } end; { ImprimeGrafo }

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.3 42 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.3 43

Busca em Profundidade Busca em Profundidade

• A busca em profundidade, do inglês • Para acompanhar o progresso do algoritmo


depth-first search), é um algoritmo para cada vértice é colorido de branco, cinza ou
caminhar no grafo. preto.

• A estratégia é buscar o mais profundo no • Todos os vértices são inicializados branco.


grafo sempre que possível.
• Quando um vértice é descoberto pela
• As arestas são exploradas a partir do vértice primeira vez ele torna-se cinza, e é tornado
v mais recentemente descoberto que ainda preto quando sua lista de adjacentes tenha
possui arestas não exploradas saindo dele. sido completamente examinada.

• Quando todas as arestas adjacentes a v • d[v]: tempo de descoberta


tiverem sido exploradas a busca anda para
• t[v]: tempo de término do exame da lista de
trás para explorar vértices que saem do
adjacentes de v.
vértice do qual v foi descoberto.
• Estes registros são inteiros entre 1 e 2|V | pois
• O algoritmo é a base para muitos outros
existe um evento de descoberta e um evento
algoritmos importantes, tais como verificação
de término para cada um dos |V | vértices.
de grafos acíclicos, ordenação topológica e
componentes fortemente conectados.
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.3 44 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.3 45

Busca em Profundidade - Busca em Profundidade -


Implementação Implementação
procedure BuscaEmProfundidade ( var Grafo : TipoGrafo ) ; procedure VisitaDfs (u: TipoValorVertice ) ;
var Tempo : TipoValorTempo; var FimListaAdj : boolean;
x : TipoValorVertice ; Peso : TipoPeso;
d, t : array [ TipoValorVertice ] of TipoValorTempo; Aux : Apontador;
Cor : array [ TipoValorVertice ] of TipoCor; v : TipoValorVertice ;
Antecessor : array [ TipoValorVertice ] of integer ; begin
Cor[u] : = cinza ; Tempo : = Tempo + 1; d[u] : = Tempo;
{−−−Entra aqui o procedimento VisitaDFS (a seguir)−−−} writeln ( ’ Visita ’ ,u:2 , ’ Tempo descoberta : ’ ,d[u]:2 , ’ cinza ’ ) ; readln;
i f not ListaAdjVazia (u, Grafo)
begin then begin
Tempo := 0; Aux : = PrimeiroListaAdj (u, Grafo ) ; FimListaAdj : = false ;
for x := 0 to Grafo.NumVertices−1 do while not FimListaAdj do
begin Cor[ x ] : = branco ; Antecessor[ x] := −1; end; begin
for x := 0 to Grafo.NumVertices−1 do ProxAdj(u, Grafo , v , Peso, Aux, FimListaAdj ) ;
i f Cor[ x ] = branco then VisitaDfs (x ) ; i f Cor[ v ] = branco
end; { BuscaEmProfundidade } then begin Antecessor[ v ] : = u ; VisitaDfs (v ) ; end;
end;
end;
Cor[u] : = preto ; Tempo : = Tempo + 1; t [u] : = Tempo;
writeln ( ’ Visita ’ ,u:2 , ’ Tempo termino : ’ , t [u]:2 , ’ preto ’ ) ; readln;
end; { VisitaDfs }

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.3 46 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.3 47

Busca em Profundidade - Exemplo Busca em Profundidade - Exemplo


b( / ) b( / ) c(1/ ) b( / ) c(1/ ) p(2/5) p(1/6) p(2/5)
0 1 0 1 0 1 0 1

b( / ) b( / ) b( / ) b( / )

b( / ) 2 3 b( / ) 2 3
p(3/4) 2 3 p(3/4) 2 3

(a) (b) (f) (g)

c(1/ ) c(2/ ) c(1/ ) c(2/ ) p(1/6) p(2/5) p(1/6) p(2/5)


0 1 0 1 0 1 0 1

b( / ) b( / ) c(7/ ) p(7/8)
b( / ) 2 3 c(3/ ) 2 3
p(3/4) 2 3 p(3/4) 2 3

(c) (d) (h) (i)

c(1/ ) c(2/ )
0 1

b( / )

p(3/4) 2 3

(e)
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.3 48 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.3.1 49

Busca em Profundidade - Análise Classificação de Arestas

• Os dois anéis da BuscaEmProfundidade têm • Existem:


custo O(|V |) cada um, a menos da chamada
1. Arestas de árvore: são arestas de uma
do procedimento VisitaDfs(u) no segundo
árvore de busca em profundidade. A aresta
anel.
(u, v) é uma aresta de árvore se v foi
• O procedimento VisitaDfs é chamado descoberto pela primeira vez ao percorrer a
exatamente uma vez para cada vértice u ∈ V , aresta (u, v).
desde que VisitaDfs é chamado apenas para
2. Arestas de retorno: conectam um vértice u
vértices brancos e a primeira ação é pintar o
com um antecessor v em uma árvore de
vértice de cinza.
busca em profundidade (inclui self-loops).
• Durante a execução de VisitaDfs(u) o anel
3. Arestas de avanço: não pertencem à árvore
principal é executado |Adj[u]| vezes.
de busca em profundidade mas conectam um
• Desde que u∈V |Adj[u]| = O(|A|), o tempo vértice a um descendente que pertence à
P

total de execução de VisitaDfs é O(|A|). árvore de busca em profundidade.

• Logo, a complexidade total da 4. Arestas de cruzamento: podem conectar


BuscaEmProfundidade é O(|V | + |A|). vértices na mesma árvore de busca em
profundidade, ou em duas árvores diferentes.

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.3.1 50 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.3.2 51

Classificação de Arestas Teste para Verificar se Grafo é Acíclico

• Classificação de arestas pode ser útil para • A busca em profundidade pode ser usada
derivar outros algoritmos. para verificar se um grafo é acíclico ou
contém um ou mais ciclos.
• Na busca em profundidade cada aresta pode
ser classificada pela cor do vértice que é • Se uma aresta de retorno é encontrada
alcançado pela primeira vez: durante a busca em profundidade em G,
– Branco indica uma aresta de árvore. então o grafo tem ciclo.

– Cinza indica uma aresta de retorno. • Um grafo direcionado G é acíclico se e


– Preto indica uma aresta de avanço quando somente se a busca em profundidade em G
u é descoberto antes de v ou uma aresta não apresentar arestas de retorno.
de cruzamento caso contrário.
3/6 2/9 1/10
arv arv
2 1 0

arv ret arv


avan cruz

3 cruz 4 cruz 5
4/5 7/8 11/12
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.4 52 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.4 53

Busca em Largura Busca em Largura

• Expande a fronteira entre vértices • Cada vértice é colorido de branco, cinza ou


descobertos e não descobertos preto.
uniformemente através da largura da
• Todos os vértices são inicializados branco.
fronteira.
• Quando um vértice é descoberto pela
• O algoritmo descobre todos os vértices a uma
primeira vez ele torna-se cinza.
distância k do vértice origem antes de
descobrir qualquer vértice a uma distância • Vértices cinza e preto já foram descobertos,
k + 1. mas são distinguidos para assegurar que a
busca ocorra em largura.
• O grafo G(V, A) pode ser direcionado ou não
direcionado. • Se (u, v) ∈ A e o vértice u é preto, então o
vértice v tem que ser cinza ou preto.

• Vértices cinza podem ter alguns vértices


adjacentes brancos, e eles representam a
fronteira entre vértices descobertos e não
descobertos.

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.4 54 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.4 55

Busca em Largura - Implementação Busca em Largura - Implementação

{−− Entram aqui os operadores FFVazia, Vazia, Enfileira e Desenfileira do−−} procedure VisitaBfs (u: TipoValorVertice ) ;
{−− TAD Filas com arranjos ou apontadores, dependendo da implementação−−} var v : TipoValorVertice ; Aux: Apontador ; FimListaAdj : boolean;
{−− da busca em largura usar arranjos ou apontadores, respectivamente−−} Peso: TipoPeso ; Item : TipoItem ; Fila : TipoFila ;
procedure BuscaEmLargura ( var Grafo : TipoGrafo ) ; begin
var x : TipoValorVertice ; Cor[u] : = cinza ; Dist [u] : = 0 ;
Dist : array [ TipoValorVertice ] of integer ; FFVazia ( Fila ) ; Item . Vertice : = u;
Cor : array [ TipoValorVertice ] of TipoCor; Enfileira ( Item , Fila ) ;
write( ’ Visita origem ’ ,u:2 , ’ cor : cinza F: ’ ) ;
Antecessor : array [ TipoValorVertice ] of integer ;
ImprimeFila ( Fila ) ; readln;
while not FilaVazia ( Fila ) do
{−−−Entra aqui o procedimento VisitaBfs (a seguir)−−−}
begin
Desenfileira ( Fila , Item ) ; u : = Item . vertice ;
begin
i f not ListaAdjVazia (u, Grafo)
for x := 0 to Grafo.NumVertices−1 do
then begin
begin
Aux : = PrimeiroListaAdj (u,Grafo ) ; FimListaAdj : = false ;
Cor[ x ] : = branco ; Dist [ x ] : = i n f i n i t o ; Antecessor[ x] := −1;
while FimListaAdj = false do
end;
begin
for x := 0 to Grafo.NumVertices−1 do
ProxAdj(u, v , Peso, Aux, FimListaAdj ) ;
i f Cor[ x ] = branco then VisitaBfs (x ) ;
i f Cor[ v ] = branco
end; { BuscaEmLargura }
then begin
Cor[ v ] : = cinza ; Dist [ v ] : = Dist [u] + 1;
Antecessor[ v ] : = u;
Item . Vertice : = v ; Item .Peso : = Peso;
Enfileira ( Item , Fila ) ;
end;
end;
end;
Cor[u] : = preto ;
write( ’ Visita ’ , u:2 , ’ Dist ’ , Dist [u]:2 , ’ cor : preto F: ’ ) ;
ImprimeFila ( Fila ) ; readln;
end;
end; { VisitaBfs }
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.4 56 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.4 57

Busca em Largura - Exemplo Busca em Largura - Exemplo


(a) (b) (e) (f)
c(0) b( ) b( ) p(0) c(1) b( ) p(0) p(1) b( ) p(0) p(1) c(0)
0 1 4 0 1 4 0 1 4 0 1 4

3 2 5 3 2 5 3 2 5 3 2 5
b( ) b( ) b( ) c(1) b( ) b( ) p(1) p(2) b( ) p(1) p(2) b( )
F 0 F 1 3
F F 4
0 1 1
0

(c) (d) (g) (h)


p(0) p(1) b( ) p(0) p(1) b( ) p(0) p(1) p(0) p(0) p(1) p(0)
0 1 4 0 1 4 0 1 4 0 1 4

3 2 5 3 2 5 3 2 5 3 2 5
c(1) c(2) b( ) p(1) c(2) b( ) p(1) p(2) c(1) p(1) p(2) p(1)
F 3 2 F 2 F
F 5
1 2 2
1

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.4 58 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.4 59

Busca em Largura - Análise (para Caminhos Mais Curtos


listas de adjacência)
• A busca em largura obtém o caminho mais
• O custo de inicialização do primeiro anel em curto de u até v.
BuscaEmLargura é O(|V |) cada um.
• O procedimento VisitaBfs contrói uma árvore
• O custo do segundo anel é também O(|V |). de busca em largura que é armazenada na
variável Antecessor.
• VisitaBfs: enfileirar e desenfileirar têm custo
O(1), logo, o custo total com a fila é O(|V |). • O programa abaixo imprime os vértices do
caminho mais curto entre o vértice origem e
• Cada lista de adjacentes é percorrida no
outro vértice qualquer do grafo, a partir do
máximo uma vez, quando o vértice é
vetor Antecessor obtido na busca em largura.
desenfileirado.
procedure ImprimeCaminho (Origem, v : TipovalorVertice ) ;
• Desde que a soma de todas as listas de begin
adjacentes é O(|A|), o tempo total gasto com i f Origem = v
then write(Origem:3)
as listas de adjacentes é O(|A|).
else i f Antecessor[ v] = −1
then write( ’Nao existe caminho de ’ ,Origem:3 , ’ ate ’ ,v:3)
• Complexidade total: é O(|V | + |A|).
else begin
Imprimecaminho(Origem, Antecessor[ v ] ) ;
write(v: 3 ) ;
end;
end; { ImprimeCaminho }
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.5 60 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.5 61

Ordenação Topológica Ordenação Topológica

• Ordenação linear de todos os vértices, tal que • Os grafos direcionados acíclicos são usados
se G contém uma aresta (u, v) então u para indicar precedências entre eventos.
aparece antes de v.
• Uma aresta direcionada (u, v) indica que a
• Pode ser vista como uma ordenação de seus atividade u tem que ser realizada antes da
vértices ao longo de uma linha horizontal de atividade v.
tal forma que todas as arestas estão
direcionadas da esquerda para a direita. 1/18 16/17 19/20
0 5 9
• Pode ser feita usando a busca em 2/15 7/12
profundidade. 4/5 3 1 6 8 9/10

2 4 7
3/14 6/13 8/11

9 0 5 1 2 4 6 7 8 3

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.5 62 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.5 63

Ordenação Topológica Ordenação Topológica -


Implementação
• Algoritmo para ordenar topologicamente um
grafo direcionado acíclico G = (V, A): • Basta inserir uma chamada para o
1. Chama BuscaEmProfundidade(G) para procedimento InsLista no procedimento
obter os tempos de término t[u] para cada BuscaDfs, logo após o momento em que o
vértice u. tempo de término t[u] é obtido e o vértice é
pintado de preto.
2. Ao término de cada vértice insira-o na
frente de uma lista linear encadeada. • Ao final, basta retornar a lista obtida (ou
3. Retorna a lista encadeada de vértices. imprimí-la.

procedure InsLista ( var Item : TipoItem ; var Lista : TipoLista ) ;


• A Custo O(|V | + |A|), uma vez que a busca
{−− Insere antes do primeiro item da l i s t a −−}
em profundidade tem complexidade de tempo var Aux: Apontador;
O(|V | + |A|) e o custo para inserir cada um begin
Aux : = Lista . Primeiro^.Prox;
dos |V | vértices na frente da lista linear
new( Lista . Primeiro^.Prox ) ;
encadeada custa O(1). Lista . Primeiro^.Prox^.Item : = Item ;
Lista . Primeiro^.Prox^.Prox : = Aux;
end; { Insere }
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.6 64 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.6 65

Componentes Fortemente Conectados Componentes Fortemente Conectados


- Algoritmo
• Um componente fortemente conectado de
G = (V, A) é um conjunto maximal de vértices • Usa o transposto de G, definido
C ⊆ V tal que para todo par de vértices u e v GT = (V, AT ), onde AT = {(u, v) : (v, u) ∈ A},
em C, u e v são mutuamente alcançáveis isto é, AT consiste das arestas de G com
suas direções invertidas.
• Podemos particionar V em conjuntos Vi ,
1 ≤ i ≤ r, tal que vértices u e v são • G e GT possuem os mesmos componentes
equivalentes se e somente se existe um fortemente conectados, isto é, u e v são
caminho de u a v e um caminho de v a u. mutuamente alcançáveis a partir de cada um
em G se e somente se u e v são mutuamente
0 1 0 1 0,1,2
alcançáveis a partir de cada um em GT .

3 2 3 2 3

(a) (b) (c)

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.6 66 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.6 67

Componentes Fortemente Conectados Componentes Fortemente Conectados


- Algoritmo - Exemplo

1. Chama BuscaEmProfundidade(G) para obter • A parte (b) apresenta o resultado da busca


os tempos de término t[u] para cada vértice u. em profundidade sobre o grafo transposto
obtido, mostrando os tempos de término e a
2. Obtem GT .
classificação das arestas.
3. Chama BuscaEmProfundidade(GT ),
• A busca em profundidade em GT resulta na
realizando a busca a partir do vértice de
floresta de árvores mostrada na parte (c).
maior t[u] obtido na linha 1. Inicie uma nova
cruz
busca em profundidade a partir do vértice de 1/8 2/7 1/6 3/4
0 3
maior t[u] dentre os vértices restantes se ret arv cruz
0 1 0 1
houver. cruz arv arv ret 2

4. Retorne os vértices de cada árvore da 3 2 3 2 arv


cruz
floresta obtida como um componente 4/5 3/6 7/8 2/5
1
fortemente conectado separado.
(a) (b) (c)
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.6 68 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.6 69

Componentes Fortemente Conectados Componentes Fortemente Conectados


- Implementação - Implementação
procedure GrafoTransposto ( var Grafo : TipoGrafo ; var GrafoT : TipoGrafo ) ; • O Programa BuscaEmProfundidadeCfc utiliza
var v , u : TipoValorVertice ;
Peso: TipoPeso;
a função MaxTT para obter o vértice de maior
Aux : Apontador; t[u] dentre os vértices restantes u ainda não
begin
visitados por VisitaDFS.
FGVazio(GrafoT) ;
GrafoT.NumVertices : = Grafo.NumVertices; type
GrafoT.NumArestas := Grafo.NumArestas; TipoTempoTermino = record
for v := 0 to Grafo.NumVertices−1 do t : array [ TipoValorVertice ] of TipoValorTempo;
i f not ListaAdjVazia (v , Grafo) Restantes : array [ TipoValorVertice ] of boolean;
then begin NumRestantes: TipoValorVertice ;
Aux : = PrimeiroListaAdj (v , Grafo ) ; end;
FimListaAdj : = false ; Function MaxTT ( var TT: TipoTempoTermino) : TipoValorVertice ;
while not FimListaAdj do var i , Temp: integer ;
begin begin
ProxAdj(v , Grafo , u, Peso, Aux, FimListaAdj ) ; i :=0;
InsereAresta(u, v , Peso, GrafoT) ; while not TT.Restantes[ i ] do i : = i + 1;
end; Temp : = TT. t [ i ] ; MaxTT : = i ;
end; for i := 0 to Grafo.NumVertices−1 do
end; { GrafoTransposto } i f TT.Restantes[ i ]
then i f Temp < TT. t [ i ]
then begin Temp : = TT. t [ i ] ; MaxTT : = i ; end;
end; { MaxTT }

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.6 70 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.6 71

Componentes Fortemente Conectados Componentes Fortemente Conectados


- Implementação - Implementação
procedure BuscaEmProfundidadeCfc ( var Grafo : TipoGrafo; procedure VisitaDfs (u: TipoValorVertice ) ;
var TT: TipoTempoTermino) ; var FimListaAdj : boolean;
var Peso : TipoPeso;
Tempo : TipoValorTempo; Aux : Apontador;
x , VRaiz : TipoValorVertice ; v : TipoValorVertice ;
d, t : array [ TipoValorVertice ] of TipoValorTempo; begin
Cor : array [ TipoValorVertice ] of TipoCor; Cor[u] : = cinza ; Tempo : = Tempo + 1; d[u] : = Tempo;
Antecessor : array [ TipoValorVertice ] of integer ; TT.Restantes[u] : = false ; TT.NumRestantes : = TT.NumRestantes−1;
{−−−Entra aqui o procedimento VisitaDFS (a seguir)−−−} writeln ( ’ Visita ’ ,u:2 , ’ Tempo descoberta : ’ ,d[u]:2 , ’ cinza ’ ) ; readln;
begin i f not ListaAdjVazia (u, Grafo)
Tempo := 0; then begin
for x := 0 to Grafo.NumVertices−1 do Aux : = PrimeiroListaAdj (u, Grafo ) ;
begin Cor[ x ] : = branco ; Antecessor[ x] := −1; end; FimListaAdj : = false ;
TT.NumRestantes : = Grafo.NumVertices; while not FimListaAdj do
for x := 0 to Grafo.NumVertices−1 do begin
TT.Restantes[ x ] : = true ; ProxAdj(u, Grafo , v , Peso, Aux, FimListaAdj ) ;
while TT.NumRestantes > 0 do i f Cor[ v ] = branco
begin then begin
VRaiz : = MaxTT (TT) ; Antecessor[ v ] : = u;
writeln ( ’Raiz da proxima arvore : ’ ,VRaiz: 2 ) ; VisitaDfs (v ) ;
VisitaDfs (VRaiz) ; end;
end; end;
end; { BuscaEmProfundidadeCfc } end;
Cor[u] : = preto ; Tempo : = Tempo + 1; t [u] : = Tempo;
writeln ( ’ Visita ’ ,u:2 , ’ Tempo termino : ’ , t [u]:2 , ’ preto ’ ) ; readln;
end; { VisitaDfs }
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.6 72 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.7 73

Componentes Fortemente Conectados Árvore Geradora Mínima - Aplicação


- Análise
• Projeto de redes de comunicações
• Utiliza o algoritmo para busca em conectando n localidades.
profundidade duas vezes, uma em G e outra
• Arranjo de n − 1 conexões, conectando duas
em GT . Logo, a complexidade total é
localidades cada.
O(|V | + |A|).
• Objetivo: dentre as possibilidades de
conexões, achar a que usa menor quantidade
de cabos.

• Modelagem:
– G = (V, A): grafo conectado, não
direcionado.
– V : conjunto de cidades.
– A: conjunto de possíveis conexões
– p(u, v): peso da aresta (u, v) ∈ A, custo
total de cabo para conectar u a v.

• Solução: encontrar um subconjunto T ⊆ A,


acíclico, que conecta todos os vértices de G e
cujo peso total p(T ) = (u,v)∈T p(u, v) é
P

minimizado.

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.7 74 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.7.1 75

Árvore Geradora Mínima (AGM) AGM - Algoritmo Genérico

• Como G0 = (V, T ) é acíclico e conecta todos • Uma estratégia gulosa permite obter a AGM
os vértices, T forma uma árvore chamada adicionando uma aresta de cada vez.
árvore geradora de G.
• Invariante: Antes de cada iteração, S é um
• O problema de obter a árvore T é conhecido subconjunto de uma árvore geradora mínima.
como árvore geradora mínima (AGM).
• A cada passo adicionamos a S uma aresta
Ex.: Árvore geradora mínima T cujo peso total é (u, v) que não viola o invariante. (u, v) é
12. T não é única, pode-se substituir a aresta chamada de uma aresta segura.
(3, 5) pela aresta (2, 5) obtendo outra árvore
procedure GenericoAGM;
geradora de custo 12. 1 S := ∅ ;
2 while S não constitui uma árvore geradora mínima do
0 0 3 Encontre uma aresta (u, v) que é segura para S ;
6 5
1 1 4 S := S + {(u, v)}
1 2 2 3 1 2 2 3 5 return S ;
2 2
5 6 4 4 4
4 5 4 5 • Dentro do while, S tem que ser um
3 3
subconjunto próprio da AGM T , e assim tem
(a) (b)
que existir uma aresta (u, v) ∈ T tal que
(u, v) 6∈ S e (u, v) é seguro para S.
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.7.1 76 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.7.1 77

AGM - Definição de Corte AGM - Teorema para reconhecer


arestas seguras
• Um corte (V 0 , V − V 0 ) de um grafo não
direcionado G = (V, A) é uma partição de V . • Seja G = (V, A) um grafo conectado, não
direcionado, com pesos p sobre as arestas V .
• Uma aresta (u, v) ∈ A cruza o corte
(V 0 , V − V 0 ) se um de seus vértices pertence • seja S um subconjunto de V que está incluído
a V 0 e o outro vértice pertence a V − V 0 . em alguma AGM para G.

• Um corte respeita um conjunto S de arestas • Seja (V 0 , V − V 0 ) um corte qualquer que


se não existirem arestas em S que o cruzem. respeita S.

• Uma aresta cruzando o corte que tenha custo • Seja (u, v) uma aresta leve cruzando
mínimo sobre todas as arestas cruzando o (V 0 , V − V 0 ).
corte é uma aresta leve.
• Satisfeitas essas condições, a aresta (u, v) é
p
0 uma aresta segura para S.
p 6 5 p
1
V’ 1 2 2 3 V’
2
V − V’ 5 4 V − V’
6 4
4 5
3
b b

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.7.2 78 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.7.2 79

AGM - Algoritmo de Prim Algoritmo de Prim - Exemplo

• O algoritmo de Prim para obter uma AGM (a) (b) 0


0 0
pode ser derivado do algoritmo genérico. 6 5 6 5
1
1 2 2 3 1 3
• O subconjunto S forma uma única árvore, e a 2 2
5 6 4 4
aresta segura adicionada a S é sempre uma 1
4 5 4 5
aresta de peso mínimo conectando a árvore a 3
um vértice que não esteja na árvore.
(c) 0 (d) 0
• A árvore começa por um vértice qualquer (no 0 0
caso 0) e cresce até que “gere” todos os 2 2 2 2
1 3 1 3
vértices em V . 2 2
1 1
• A cada passo, uma aresta leve é adicionada à
4 5 4 4
árvore S, conectando S a um vértice de 6 4 6 4

GS = (V, S).
(e) 0 (f) 0
• De acordo com o teorema anterior, quando o 0 0
2 2 2 2
algoritmo termina, as arestas em S formam 1 3 1 3
uma árvore geradora mínima. 2 2
1 1
4 5 4 5
5 4 3 4
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.7.2 80 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.7.2 81

Algoritmo de Prim - Implementação Algoritmo de Prim - Implementação


{−− Entram aqui os operadores de uma das implementações para grafos−−} function RetiraMin ( var A: Vetor ) : Item ;
{−− bem como o operador Constroi do TAD HEAP −−} begin
procedure AgmPrim ( var Grafo : TipoGrafo ; var Raiz : TipoValorVertice ) ; if n < 1
var Antecessor : array [ TipoValorVertice ] of integer ; then writeln ( ’ Erro : heap vazio ’ )
p : array [ TipoValorVertice ] of TipoPeso; else begin
Itensheap : array [ TipoValorVertice ] of boolean; RetiraMin : = A[ 1 ] ;
Pos : array [ TipoValorVertice ] of TipoValorVertice ; A[ 1 ] : = A[n ] ; Pos[A[n ] . chave] : = 1 ;
A : Vetor ; n : = n − 1;
u, v : TipovalorVertice ; Refaz ( 1 , n, A) ;
end;
procedure Refaz (Esq, Dir : Indice ; var A : Vetor ) ; end; { Retira }
label 999;
var i : Indice ; j : integer ; x : Item ; procedure DiminuiChave ( i : Indice ; ChaveNova: TipoPeso ; var A: Vetor ) ;
begin var x : Item ;
i : = Esq; j := 2 ∗ i ; x : = A[ i ] ; begin
while j <= Dir do i f ChaveNova > p[A[ i ] .Chave]
begin then writeln ( ’ Erro : ChaveNova maior que a chave atual ’ )
i f j < Dir else begin
then i f p[A[ j ] .Chave] > p[A[ j + 1].Chave] then j : = j + 1; p[A[ i ] .Chave] : = ChaveNova;
i f p[ x .Chave] <= p[A[ j ] .Chave] then goto 999; while ( i >1) and (p[A[ i div 2 ] .Chave] > p[A[ i ] .Chave] )
A[ i ] : = A[ j ] ; Pos[A[ j ] .Chave] : = i ; do begin
i := j ; j := 2 ∗ i ; x : = A[ i div 2 ] ;
end; A[ i div 2 ] : = A[ i ] ; Pos[A[ i ] .Chave] : = i div 2;
999 : A[ i ] : = x ; Pos[ x .Chave] : = i ; A[ i ] : = x ; Pos[ x .Chave] : = i ;
end; { Refaz } i : = i div 2;
end;
end;
end; { DiminuiChave }

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.7.2 82 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.7.2 83

Algoritmo de Prim - Implementação Algoritmo de Prim - Implementação


begin { AgmPrim }
• Para realizar de forma eficiente a seleção de
for u := 0 to Grafo.NumVertices do
begin {Constroi o heap com todos os valores igual a I n f i n i t o } uma nova aresta, todos os vértices que não
Antecessor[u] := −1; p[u] : = I n f i n i t o ; estão na AGM residem no heap A.
A[u+1].Chave : = u ; {Heap a ser construido }
ItensHeap[u] : = true ; Pos[u] : = u+1; • O heap contém os vértices, mas a condição
end;
n : = Grafo.NumVertices;
do heap é mantida pelo peso da aresta
p[Raiz ] : = 0 ; através do arranjo p[v] (heap indireto).
Constroi(A) ;
while n >= 1 do {enquanto heap nao vazio} • Pos[v] fornece a posição do vértice v dentro
begin
u : = RetiraMin(A) .Chave;
do heap A, para que o vértice v possa ser
i f (u <> Raiz) acessado a um custo O(1), necessário para a
then write( ’Aresta de arvore : v [ ’ ,u, ’ ] v [ ’ ,Antecessor[u] , ’ ] ’ ) ; readln;
operação DiminuiChave.
ItensHeap[u] : = false ;
i f not ListaAdjVazia (u,Grafo)
• Antecessor[v] armazena o antecessor de v na
then begin
Aux : = PrimeiroListaAdj (u,Grafo ) ; FimListaAdj : = false ; árvore.
while not FimListaAdj do
begin • Quando o algoritmo termina, A está vazia e a
ProxAdj(u, Grafo , v , Peso, Aux, FimListaAdj ) ; AGM está de forma implícita como
i f ItensHeap[ v ] and (Peso < p[ v ] )
then begin S = {(v, Antecessor [v]) : v ∈ V − {Raiz }}
Antecessor[ v ] : = u ; DiminuiChave(Pos[ v ] ,Peso,A) ;
end
end;
end;
end;
end; { AgmPrim }
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.7.2 84 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.7.3 85

Algoritmo de Prim - Análise AGM - Algoritmo de Kruskal

• O corpo do anel while é executado |V | vezes. • Pode ser derivado do algoritmo genérico.
• S é uma floresta e a aresta segura adicionada
• O procedimento Refaz tem custo O(log |V |).
a S é sempre uma aresta de menor peso que
• Logo, o tempo total para executar a operação conecta dois componentes distintos.
retira o item com menor peso é O(|V | log |V |).
• Considera as arestas ordenadas pelo peso.
• O while mais interno para percorrer a lista de (a) (b)
adjacentes é O(|A|) (soma dos comprimentos 6
0
5
0
1
de todas as listas de adjacência é 2|A|). 1 2 2 3 1 3
2 2
5 4
• O teste para verificar se o vértice v pertence 6 4
4 5 4 5
ao heap A tem custo O(1). 3

• Após testar se v pertence ao heap A e o peso (c) (d)


da aresta (u, v) é menor do que p[v], o 0 0

antecessor de v é armazenado em 1 3 1 3
2 2
Antecessor e uma operação DiminuiChave é
realizada sobre o heap A na posição Pos[v], a 4 5 4 5
qual tem custo O(log |V |).
(e) (f)
• Logo, o tempo total para executar o algoritmo
0 0
de Prim é
1 3 1 3
O(|V log |V | + |A| log |V |) = O(|A| log |V |). 2 2

4 5 4 5

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.7.3 86 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.7.3 87

AGM - Algoritmo de Kruskal Algoritmo de Kruskal - Implementação

• Sejam C1 e C2 duas árvores conectadas por • Usa fila de prioridades para obter arestas em
(u, v): ordem crescente de pesos.

– Como (u, v) tem de ser uma aresta leve • Testa se uma dada aresta adicionada ao
conectando C1 com alguma outra árvore, conjunto solução S forma um ciclo.
(u, v) é uma aresta segura para C1 . • Tratar conjuntos disjuntos: maneira
• É guloso porque, a cada passo, ele adiciona à eficiente de verificar se uma dada aresta
forma um ciclo. Utiliza estruturas dinâmicas.
floresta uma aresta de menor peso.
• Os elementos de um conjunto são
• Obtém uma AGM adicionando uma aresta de
representados por um objeto. Operações:
cada vez à floresta e, a cada passo, usa a
– CriaConjunto(x): cria novo conjunto cujo
aresta de menor peso que não forma ciclo.
único membro, x, é seu representante.
• Inicia com uma floresta de |V | árvores de um Para que os conjuntos sejam disjuntos, x
vértice: em |V | passos, une duas árvores até não pode pertencer a outro conjunto.
que exista apenas uma árvore na floresta. – União(x, y): une conjuntos dinâmicos
contendo x (Cx ) e y (Cy ) em novo conjunto,
cujo representante pode ser x ou y. Como
os conjuntos na coleção devem ser
disjuntos, Cx e Cy são destruídos.
– EncontreConjunto(x): retorna apontador
para o representante do conjunto (único)
contendo x.
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.7.3 88 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.7.3 89

Algoritmo de Kruskal - Implementação AGM - Análise do Algoritmo de Kruskal

• Primeiro refinamento: • A inicialização do conjunto S tem custo O(1).

procedure Kruskal ; • Ordenar arestas (linha 3) custa O(|A| log |A|).


1. S := ∅ ;
2. for v := 0 to Grafo.NumVertices−1 do CriaConjunto ( v ) ;
• A linha 2 realiza |V | operações CriaConjunto.
3. Ordena as arestas de A pelo peso;
• O anel (linhas 4-7) realiza O(|A|) operações
4. for cada ( u, v ) de A tomadas em ordem ascendente de peso do
5. i f EncontreConjunto (u) <> EncontreConjunto ( v) EncontreConjunto e Uniao, a um custo
then begin O((|V | + |A|)α(|V |)) onde α(|V |) é uma
6. S := S + {(u, v)} ;
função que cresce lentamente (α(|V |) < 4).
7. Uniao ( u, v ) ;
end; • O limite inferior para construir uma estrutura
end;
dinâmica envolvendo m operações
EncontreConjunto e Uniao e n operações
• A implementação das operações União e
CriaConjunto é mα(n).
EncontraConjunto deve ser realizada de
forma eficiente. • Como G é conectado temos que
|A| ≥ |V | − 1, e assim as operações sobre
• Esse problema é conhecido na literatura conjuntos disjuntos custam O(|A|α(|V |).
como União-EncontraConjunto.
• Como α(|V |) = O(log |A|) = O(log |V |), o
tempo total do algoritmo de Kruskal é
O(|A| log |A|).
• Como |A| < |V |2 , então log |A| = O(log |V |), e
o custo do algoritmo de Kruskal é também
O(|A| log |V |).

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.8 90 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.8 91

Caminhos Mais Curtos - Aplicação Caminhos Mais Curtos


• Um motorista procura o caminho mais curto • Caminhos mais curtos a partir de uma
entre Diamantina e Ouro Preto. Possui mapa origem: dado um grafo ponderado
com as distâncias entre cada par de G = (V, A), desejamos obter o caminho mais
interseções adjacentes. curto a partir de um dado vértice origem
• Modelagem: s ∈ V até cada v ∈ V .
– G = (V, A): grafo direcionado ponderado, • Muitos problemas podem ser resolvidos pelo
mapa rodoviário. algoritmo para o problema origem única:
– V : interseções.
– Caminhos mais curtos com destino
– A: segmentos de estrada entre único: reduzido ao problema origem única
interseções invertendo a direção de cada aresta do
– p(u, v): peso de cada aresta, distância grafo.
entre interseções.
– Caminhos mais curtos entre um par de
Pk
• Peso de um caminho: p(c) = i=1 p(vi−1 , vi ) vértices: o algoritmo para origem única é
• Caminho mais curto: a melhor opção conhecida.
 n
c
o – Caminhos mais curtos entre todos os

 min p(c) : u ; v se existir caminho de u a v
δ(u, v) = pares de vértices: resolvido aplicando o

 ∞ caso contrário algoritmo origem única |V | vezes, uma vez
para cada vértice origem.
• Caminho mais curto do vértice u ao vértice
v: qualquer caminho c com peso
p(c) = δ(u, v).
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.8 92 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.8 93

Caminhos Mais Curtos Árvore de caminhos mais curtos


• A representação de caminhos mais curtos • Uma árvore de caminhos mais curtos com
pode ser realizada pela variável Antecessor. raiz em u ∈ V é um subgrafo direcionado
• Para cada vértice v ∈ V o Antecessor [v] é um G0 = (V 0 , A0 ), onde V 0 ⊆ V e A0 ⊆ A, tal que:
outro vértice u ∈ V ou nil (-1). 1. V 0 é o conjunto de vértices alcançáveis a
• O algoritmo atribui a Antecessor os rótulos de partir de s ∈ G,
vértices de uma cadeia de antecessores com 2. G0 forma uma árvore de raiz s,
origem em v e que anda para trás ao longo de
3. para todos os vértices v ∈ V 0 , o caminho
um caminho mais curto até o vértice origem s.
simples de s até v é um caminho mais
• Dado um vértice v no qual Antecessor [v] 6= nil , curto de s até v em G.
o procedimento ImprimeCaminho pode
imprimir o caminho mais curto de s até v.
• Os valores em Antecessor [v], em um passo
intermediário, não indicam necessariamente
caminhos mais curtos.
• Entretanto, ao final do processamento,
Antecessor contém uma árvore de caminhos
mais curtos definidos em termos dos pesos
de cada aresta de G, ao invés do número de
arestas.
• Caminhos mais curtos não são
necessariamente únicos.

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.8 94 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.8 95

Algoritmo de Dijkstra Relaxamento

• Mantém um conjunto S de vértices cujos • O relaxamento de uma aresta (u, v) consiste


caminhos mais curtos até um vértice origem em verificar se é possível melhorar o melhor
já são conhecidos. caminho até v obtido até o momento se
passarmos por u.
• Produz uma árvore de caminhos mais curtos
de um vértice origem s para todos os vértices • Se isto acontecer, p[v] e Antecessor[v] devem
que são alcançáveis a partir de s. ser atualizados.
• Utiliza a técnica de relaxamento: i f p[ v] > p[u] + peso da aresta ( u, v)
then p[ v ] = p[u] + peso da aresta ( u, v)
– Para cada vértice v ∈ V o atributo p[v] é Antecessor[ v ] : = u
um limite superior do peso de um caminho
mais curto do vértice origem s até v.
– O vetor p[v] contém uma estimativa de um
caminho mais curto.

• O primeiro passo do algoritmo é inicializar os


antecessores e as estimativas de caminhos
mais curtos:
– Antecessor[v] = nil para todo vértice
v ∈V,
– p[u] = 0, para o vértice origem s, e
– p[v] = ∞ para v ∈ V − {s}.
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.8 96 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.8 97

Algoritmo de Dijkstra - 1o Refinamento Algoritmo de Dijkstra - Exemplo


procedure Dijkstra ( Grafo , Raiz ) ; (a) (b) 0
1. for v := 0 to Grafo.NumVertices−1 do 0 0
2. p[ v ] : = I n f i n i t o ;
1 10 1 10
1 10
3. Antecessor[ v] := −1; 1 3 4 1 3 4
4. p[Raiz ] : = 0 ;
5. Constroi heap no vetor A; 5 1 6 5 1 6
6 S := ∅ ; 2 3 2 3
7. While heap > 1 do 2 2 3
8. u : = RetiraMin(A) ;
9 S := S + u (c) 0
10. for v ∈ ListaAdjacentes [u ] do
0
11. i f p[ v] > p[u] + peso da aresta ( u, v) 1 10
1 10
12. then p[ v ] = p[u] + peso da aresta ( u, v) 1 4
3
13. Antecessor[ v ] : = u
5 1 6
• Invariante: o número de elementos do heap é 2 3
6 2 3
igual a V − S no início do anel while.
Iteração S d[0] d[1] d[2] d[3] d[4]
• A cada iteração do while, um vértice u é
extraído do heap e adicionado ao conjunto S, (a) ∅ ∞ ∞ ∞ ∞ ∞
mantendo assim o invariante. (b) {0} 0 1 ∞ 3 10
• RetiraMin obtém o vértice u com o caminho (c) {0, 1} 0 1 6 3 10
mais curto estimado até o momento e
adiciona ao conjunto S.
• No anel da linha 10, a operação de
relaxamento é realizada sobre cada aresta
(u, v) adjacente ao vértice u.

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.8 98 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.8 99

Algoritmo de Dijkstra - Exemplo Algoritmo de Dijkstra


(d) 0 (e) 0
• Para realizar de forma eficiente a seleção de
0 0
1
1 10
9 1
1 10
6
uma nova aresta, todos os vértices que não
1 3 4 1 3 4 estão na árvore de caminhos mais curtos
5 1 6 5 1 6 residem no heap A baseada no campo p.
2 3 2 3 • Para cada vértice v, p[v] é o caminho mais
5 2 3 5 2 3
curto obtido até o momento, de v até o vértice
(f) 0 raiz.
0
1 10
1 6 • O heap mantém os vértices, mas a condição
1 3 4
do heap é mantida pelo caminho mais curto
5 1 6 estimado até o momento através do arranjo
2
2
3 p[v], o heap é indireto.
5 3

Iteração S d[0] d[1] d[2] d[3] d[4]


• O arranjo Pos[v] fornece a posição do vértice
v dentro do heap A, permitindo assim que o
(d) {0, 1, 3} 0 1 5 3 9
vértice v possa ser acessado a um custo O(1)
(e) {0, 1, 3, 2} 0 1 5 3 6 para a operação DiminuiChaveInd.
(f) {0, 1, 3, 2, 4} 0 1 5 3 6
Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.8 100 Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.8 101

Algoritmo de Dijkstra - Implementação Algoritmo de Dijkstra - Implementação


procedure Dijkstra ( var Grafo : TipoGrafo ; var Raiz : TipoValorVertice ) ; ..
.
var Antecessor : array [ TipoValorVertice ] of integer ;
P : array [ TipoValorVertice ] of TipoPeso;
while n >= 1 do {enquanto heap nao vazio}
Itensheap : array [ TipoValorVertice ] of boolean;
begin
Pos : array [ TipoValorVertice ] of TipoValorVertice ;
u : = RetiraMinInd(A) .Chave;
A : Vetor ;
ItensHeap[u] : = false ;
u, v : TipovalorVertice ;
i f not ListaAdjVazia (u,Grafo)
then begin
{−− Entram aqui os operadores de uma das implementações de grafos, bem
Aux : = PrimeiroListaAdj (u,Grafo ) ; FimListaAdj : = false ;
como o operador Constroi da implementação de filas de prioridades, assim
while not FimListaAdj do
como os operadores RefazInd, RetiraMinInd e DiminuiChaveInd do Progra-
ma Constroi−−} begin
ProxAdj(u, Grafo , v , Peso, Aux, FimListaAdj ) ;
begin { Dijkstra } i f p[ v] > p[u] + Peso
for u := 0 to Grafo.NumVertices do then begin
begin {Constroi o heap com todos os valores igual a I n f i n i t o } p[ v ] : = p[u] + Peso; Antecessor[ v ] : = u;
Antecessor[u] := −1; p[u] : = I n f i n i t o ; DiminuiChaveInd(Pos[ v ] ,p[ v ] ,A) ;
A[u+1].Chave : = u ; {Heap a ser construido } write( ’Caminho: v [ ’ ,v, ’ ] v [ ’ ,Antecessor[ v ] , ’ ] ’ ,
ItensHeap[u] : = true ; Pos[u] : = u+1; ’ d[ ’ ,p[ v ] , ’ ] ’ ) ; readln;
end; end;
n : = Grafo.NumVertices ; {Tamanho do heap} end;
p[Raiz ] : = 0 ; end;
Constroi(A) ; end;
end; { Dijkstra }
..
.

Projeto de Algoritmos – Cap.7 Algoritmos em Grafos – Seção 7.8 102

Porque o Algoritmo de Dijkstra


Funciona

• O algoritmo usa uma estratégia gulosa:


sempre escolher o vértice mais leve (ou o
mais perto) em V − S para adicionar ao
conjunto solução S,

• O algorimo de Dijkstra sempre obtém os


caminhos mais curtos, pois cada vez que um
vértice é adicionado ao conjunto S temos que
p[u] = δ(Raiz, u).

Você também pode gostar