Você está na página 1de 42

UFPA

Algoritmos de Busca em Grafos

January 5, 2022
Algoritmos de Busca

Problema fundamental em grafos:


Como explorar um grafo de forma sistemática ?
Algoritmos de Busca

Problema fundamental em grafos:


Como explorar um grafo de forma sistemática ?
Utilizando um algoritmo de busca :-)
Algoritmos de Busca

Problema fundamental em grafos:


Como explorar um grafo de forma sistemática ?
Utilizando um algoritmo de busca :-)
Como o seguinte grafo pode ser explorado ?
Algoritmos de Busca

Como percorrer o grafo, visitando todos os seus vértices e arestas, e


evitando repetições desnecessárias a um mesmo vértice ou aresta ?
Algoritmos de Busca

Como percorrer o grafo, visitando todos os seus vértices e arestas, e


evitando repetições desnecessárias a um mesmo vértice ou aresta ?

Ideia:
Evitar explorar vértice já explorados → marcar os vértices.
Alg. de Busca em Profundidade (DFS)
Como o seguinte labirinto poderia ser explorado ?
Alg. de Busca em Profundidade (DFS)
Como o seguinte labirinto poderia ser explorado ?

DFS
“Dentre todos os vértices marcados e incidentes a alguma aresta
ainda não explorada, escolher aquele mais recentemente alcançado
na busca”
Alg. de Busca em Profundidade (DFS)
Como o seguinte labirinto poderia ser explorado ?

DFS
“Dentre todos os vértices marcados e incidentes a alguma aresta
ainda não explorada, escolher aquele mais recentemente alcançado
na busca”
Ideia:
Explorar o vértice descoberto mais recentemente primeiro.
Alg. de Busca em Profundidade (DFS)
Como o seguinte labirinto poderia ser explorado ?

DFS
“Dentre todos os vértices marcados e incidentes a alguma aresta
ainda não explorada, escolher aquele mais recentemente alcançado
na busca”
Ideia:
Explorar o vértice descoberto mais recentemente primeiro.
Alg. de Busca em Profundidade (DFS)

Exemplos de Aplicações
• Verificar se existe um percurso de um vértice para outro;
• Verificar se o grafo é conectado ou não;
• ...
Obs.: Funciona em grafos direcionados e não direcionados.
Alg. de Busca em Profundidade (DFS) - Cormem et al.

Particularidades do Algoritimo de Cormem


• π[v ] é um vetor utilizado para salvar o antecessor de um
vértice. Ou seja, sempre que um vértice v for descoberto
durante a leitura da lista de adj. de um vértice já descoberto
u, π[v ] = u;
• Cada vértice é colorido conforme o seu estado:
Branco Estado inicial de um vértice;
Cinza O vértice foi descoberto;
Preto a lista de adjacências do vértice foi finalizada;
Alg. de Busca em Largura (DFS) - Cormem et al.

Particularidades do Algoritimo de Cormem (cont.)


• cor [u] - mantém a cor do vértice u ∈ V
• Selo de tempo (timestamp) - Cada vértice possui dois selos de
tempo:
d[v ] - salva o tempo de quando o vértice v foi
descoberto e colorido como cinza;
f [v ] - salva o tempo de quando de quando a busca
finaliza o exame da lista de adjacências do
vértice v e o colore como preto.
Alg. de Busca em Profundidade (DFS) - Cormem et al.

Seja G = (V , E ) um grafo:

DFS(G)
// Inicializacao - vertices coloridos de branco;
// π[v ] iniciado com nulo.
for cada vertice u ∈ V[G]
do cor[u] = BRANCO
π[u] = NULL
time = 0
for cada vertice u ∈ V[G]
do if cor[u] == BRANCO
then DFS-VISIT(u)
Alg. de Busca em Profundidade (DFS) - Cormem et al.

DFS-VISIT(u)
cor [u] = CINZA
tempo = tempo + 1
d[u] = tempo
for cada v ∈ Adj[u] // Explorar a aresta (u,v)
do if cor [v ] = BRANCO
then π[v ] = u // O antecessor de ’v’ = ’u’
DFS-VISIT(v)
cor [u] = PRETO // vertice ’u’ finalizado
f [u] = tempo = tempo + 1
Alg. de Busca em Profundidade (DFS) - Cormem et al.

DFS-VISIT(u)
cor [u] = CINZA
tempo = tempo + 1
d[u] = tempo
for cada v ∈ Adj[u] // Explorar a aresta (u,v)
do if cor [v ] = BRANCO
then π[v ] = u // O antecessor de ’v’ = ’u’
DFS-VISIT(v)
cor [u] = PRETO // vertice ’u’ finalizado
f [u] = tempo = tempo + 1

Obs. 1: Esse algoritmo foi originalmente proposto para um grafo


direcionado, mas pode ser utilizado em grafos não direcionados
(Verifique).
Obs. 2: A ordem em que as arestas/vértices são visitados depende
da representação do grafo.
Exemplo

Vamos empregar o alg. DFS no seguinte grafo, iniciando pelo


vértice 2
Uma implementação do Alg. DFS (Sedgewick)
public class BuscaEmProfundidade {
private boolean[] marcado;
private int cont;
public BuscaEmProfundidade(Grafo G, int s) {
marcado = new boolean[G.V()];
dfs(G,s);
}
private void dfs(Grafo G, int v) {
marcado[v] = true;
cont++;
for (int w: G.adj())
if (!marcado[w]) dfs(G,w);
}
public boolean marcado(int w) {
return marcado[w];
}
public int cont() {
return cont;
Trabalho

Adapte o alg. DFS de Sedgewick para funcionar de acordo com o


algoritmo de Cormem et al.
Alg. de Busca em Largura (BFS)
BFS
“Dentre todos os vértices marcados e incidentes a alguma aresta
não explorada, escolher aquele menos recentemente alcançado na
busca.”
Alg. de Busca em Largura (BFS)
BFS
“Dentre todos os vértices marcados e incidentes a alguma aresta
não explorada, escolher aquele menos recentemente alcançado na
busca.”
Ideia:
A partir de um vértice s, explorar todos os vértices a partir de s.
Alg. de Busca em Largura (BFS)
BFS
“Dentre todos os vértices marcados e incidentes a alguma aresta
não explorada, escolher aquele menos recentemente alcançado na
busca.”
Ideia:
A partir de um vértice s, explorar todos os vértices a partir de s.
Como o seguinte grafo pode ser explorado ?
Alg. de Busca em Largura (BFS)
BFS
“Dentre todos os vértices marcados e incidentes a alguma aresta
não explorada, escolher aquele menos recentemente alcançado na
busca.”
Ideia:
A partir de um vértice s, explorar todos os vértices a partir de s.
Como o seguinte grafo pode ser explorado ?

A expansão assim é realizada em “camadas”.


Obs.: Funciona em grafos direcionados e não direcionados.
Alg. de Busca em Largura (BFS) - Cormem et al.

Particularidades do Algoritimo de Cormem


• Seja um grafo G = (V , E ) e um vértice (fonte) s;
• π[u] é um vetor utilizado para salvar o antecessor de um
vértice u. Se o vértice u não tem antecessor, π[u] = nulo;
• Cada vértice é colorido conforme o seu estados:
Branco Estado inicial de um vértice;
Cinza O vértice foi descoberto;
Preto As adjacências do vértice foram finalizada;
• Se (u, v ) ∈ E e o vértice u = preto, então o vértice
v = cinza or preto
• Vértices cinza podem ter vértices brancos adjacentes; eles
representam uma fronteira entre vértices descobertos e não
descobertos.
Alg. de Busca em Largura (BFS) - Cormem et al.

Particularidades do Algoritimo de Cormem (cont.)


• cor [u] - mantém a cor do vértice u ∈ V ;
• d[v ] - mantém a distância entre o vértice fonte s e o vértice u;
• Fila Q para gerenciar o conjunto dos vértices cinzas.
Alg. de Busca em Largura (BFS) - Cormem et al.

BFS(G,s)
for cada u ∈ V[G] - {s} //Todos exceto s=fonte
do cor [u] = BRANCO
d[u] = ∞
π[u] = NULO
cor [s] = CINZA
d[s] = 0
π[s] = NULO // vertice s nao possui antecessor
Q = ∅
ENFILEIRAR(Q,s) // inicializa a fila Q com o vert. s
Alg. de Busca em Largura (BFS) (cont.)

while (Q 6= 0 )
do u = DESENFILEIRAR(Q)
for cada v ∈ Adj[u] // Explorar (u,v)
do If cor [v ] = BRANCO
then cor [v ] = CINZA
d[v ] = d[u] + 1
π[v ] = u
ENFILEIRAR(Q,v)
cor [u] = PRETO
Alg. de Busca em Largura (BFS)

Exemplo:
Uma implementação do Alg. BFS (Sedgewick)
public class BuscaEmLargura {
private boolean[] marcado;
public BuscaEmLargura(Grafo G, int s) {
marcado = new boolean[G.V()];
bfs(G,s);
}
private void bfs(Grafo G, int s) {
Queue<Integer> queue = new Queue<Integer>();
marcado[s] = true;
queue.enqueue(s);
while( !q.vazio() ) {
int v = queue.dequeue();
for (int w: G.adj(v))
if ( !marcado[w] ) {
marcado[w] = true;
queue.enqueue(w);
} } } }
public boolean marcado(int w) {
Trabalho

Adapte o alg. BFS de Sedgewick para funcionar de acordo com o


algoritmo de Cormem et al.
Componentes Conectados

Um grafo não direcionado está conectado se todo par de vértices é


conectado por algum percurso.
Componentes Conectados

Um grafo não direcionado está conectado se todo par de vértices é


conectado por algum percurso.

Relação de Equivalência (Relembrando)


Uma relação de equivalência é uma relação que possui as
propriedades: Reflexiva, Simétrica e Transitiva.

Obs.: Uma relação simétrica e transitiva também é uma relação de


equivalência. Se s ≡ t, então t ≡ s por simetria e s ≡ s por
transitividade.
Componentes Conectados

Um grafo não direcionado está conectado se todo par de vértices é


conectado por algum percurso.

Relação de Equivalência (Relembrando)


Uma relação de equivalência é uma relação que possui as
propriedades: Reflexiva, Simétrica e Transitiva.

Obs.: Uma relação simétrica e transitiva também é uma relação de


equivalência. Se s ≡ t, então t ≡ s por simetria e s ≡ s por
transitividade.
Componentes Conectados
Os componentes conectados de um grafo são classes de
equivalência de vértices sobre a relação “está conectado a”. Assim,
dois vértices estão na mesma classe sse eles estão conectados.
Componentes Conectados

Exemplo
Quais são os componentes conectados do seguinte grafo ?
Seja o grafo G (V , E ) onde V = {1, 2, 3, 4, 5, 6} e
E = {(1, 2), (1, 5), (2, 5), (3, 6), (2, 1), (5, 1), (5, 2), (6, 3)}
Um algoritmo para encontrar os Componentes
Conectados (Sedgewick)
O algoritmo a seguir permite encontrar as classes de equivalência.
Ou seja, ele determina quais vértices estão conectados. Para isso,
vamos utilizar o DFS.
Um algoritmo para encontrar os Componentes
Conectados (Sedgewick)
O algoritmo a seguir permite encontrar as classes de equivalência.
Ou seja, ele determina quais vértices estão conectados. Para isso,
vamos utilizar o DFS.
public class ComponentesConectados {
private boolean[] marcado;
private int[] id;
private int cont;
public ComponentesConectados(Grafo G) {
marcado = new boolean[G.V()];
id = new int[G.V()]
for (int s = 0; s < G.V(); s++)
if ( !marcado[s]) {
dfs(G,s);
cont++;
} }
Um algoritmo para encontrar os Componentes
Conectados (cont.)

id[v ] = i se o vertice ’v ’ está no i’th componente conectado


processado. Ele Associa o mesmo valor para todos os vértices no
mesmo componente.
private void dfs(Grafo G, int v) {
marcado[v] = true;
id[v] = cont;
for (int w: G.adj())
if (!marcado[w])
dfs(G,w);
}

Para cada vértice v no 1o. componente id[v ] = 0; para cada


vértice v no 2o. componente id[v ] = 1; etc.
Um algoritmo para encontrar os Componentes
Conectados (cont.)

// Verifica se dois vertices estao conectados


public boolean conectado(int v, int w)
{ return id[v] == id[w]; }
//Identificador do componente para v
public int id(int v) {
return id[v];
}
// Numero de componentes conectados
public int cont() { return cont; }
}
Componentes Conectados - Exemplos
Utilize o algoritmo para encontrar os componentes fortemente
conectados nos seguintes grafos:
Exemplo 1:
G (V , E ) onde V = {1, 2, 3, 4, 5, 6} e
E = {(1, 2), (1, 5), (2, 5), (3, 6), (2, 1), (5, 1), (5, 2), (6, 3)}
Componentes Conectados - Exemplos
Utilize o algoritmo para encontrar os componentes fortemente
conectados nos seguintes grafos:
Exemplo 1:
G (V , E ) onde V = {1, 2, 3, 4, 5, 6} e
E = {(1, 2), (1, 5), (2, 5), (3, 6), (2, 1), (5, 1), (5, 2), (6, 3)}
Exemplo 2:
Exercı́cios

Ordenação Topológica
1. (Feofiloff) Calcule uma ordenação topológica do dı́grafo
definido pelos arcos: 0-1 0-2 0-4 1-2 2-3 4-2 4-5 5-3 6-4 6-7
7-5
2. Calcule uma ordenação topológica para o seguinte DAG: 1-4,
1-5, 1-6, 2-4, 2-5, 3-5, 4-6, 4-7, 4-10, 5-9, 8-10,
Componentes Conectados - Exercı́cios

1. Altere o algoritmo DFS (Cormem et al.) para encontrar os


componentes conectados;
2. Altere o algoritmo DFS (Cormem et al.) para retornar o
tamanho (número de vértices) de cada componente
conectado.

Você também pode gostar