Escolar Documentos
Profissional Documentos
Cultura Documentos
Dados II - Grafos
CICLO HAMILTONIANO
CICLO DE EULER
Ciclo de Euler
Pontes de Konigsberg
Ciclo
Ciclo de Euler
Pontes de Konigsberg
Ciclo
Ciclo de Euler
Pontes de Konigsberg
Há uma solução?
Grafos - Terminologia (3)
Grafo conexo: existe um caminho entre quaisquer
dois vértices no grafo
Conexo Não-conexo
Grafos –Terminologia (4)
Subgrafo: subconjunto dos vértices e arcos que
formam um grafo
Componente conexo: um subgrafo conexo
maximal de um grafo.
Grafos Terminologia (5)
Árvore – grafo conexo sem ciclos
Floresta – coleção de árvores
Árvore
Árvore
Floresta
Árvore
Árvore
Representação de Grafos
Idéia Básica
• Dois tipos de objetos: vértices e arcos
• Aos arcos associam-se referências aos vértices iniciais
Representação de Grafos
Lista de Arcos
Fácil de implementar
Encontrar os arcos que incidem em um
vértice exige percorrer toda a lista
Representação de Grafos
Lista de adjacência
Aplicação Prática:
Considere um braço de robô cuja função é soldar todas as conexões de
uma placa de circuito impresso. O menor caminho que visita cada ponto
de solda exatamente uma única vez define o caminho mais eficiente
para o robô
Problemas
Caixeiro Viajante
Problemas
Caixeiro Viajante
51+6+44+44+50 = 195
CAMINHO ENTRE
CIDADES
Caminho entre Cidades
Problema: queremos sair de uma cidade a e chegar
a outra cidade b usando exatamente nr estradas
Uma estratégia é criar um grafo com as cidades
como nós e as estradas como arcos
Procure um nó c de modo que (a,c) E, e exista
um caminho de comprimento nr-1 de c até b.
• Se essas condições forem atingidas o caminho existirá
Algoritmo
Suponha que a entrada do algoritmo seja:
• Uma linha contento 4 inteiros:
• n = representa a quantidade de cidades
• a = cidade origem
• b = cidade destino
• nr = quantidade de estradas
• Várias linhas contendo 2 inteiros representando duas cidades que
contem estradas ligando-as:
• c1 = cidade origem
• c2 = cidade destino
O algoritmo usa a função findpath(k,a,b) que retorna
TRUE se existir um caminho de comprimento k de a até b
Algoritmo
citypath {
scanf (%d”, &n);
scanf (%d %d”, &a, &b);
scanf (%d”, &nr);
while(scanf(%d %d”, &c1, &c2) != EOF)
join(c1,c2);
if (findpath(nr,a,b))
printf(“existe”)
else printf(“não existe”);
}
Algoritmo - findpath
findpath(k, a, b)
{
if (k==1) Complexidade?
return adj(a,b);
for (c=0; c<n; ++c)
if (adj(a,c) && findpath(k-1,c,b)
return(TRUE);
return (FALSE);
}
Algoritmo - Deficiências
Vários caminhos são investigados diversas vezes
durante o processo recursivo
Ele só diz se existe ou não o caminho, ele não diz
qual o caminho
Ele só verifica a presença de um caminho de
comprimento específico
Fechamento Transitivo
Considere que o vetor adj2 tal que adj2[i][j]
indique se existe ou não um caminho de
comprimento igual a 2 entre i e j
adj2 é chamado de matriz de caminhos de
comprimento 2
adj2 é o produto de adj por ele mesmo, com a
multiplicação substituída pela conjunção (&&) e a
adição substituída pela disjunção (||)
Diz-se que adj2 é o produto booleano de adj por
si mesma
Fechamento Transitivo
Fechamento Transitivo
Fechamento Transitivo
Suponha que queiramos saber se existe um
caminho de comprimento 3 ou menos entre dois
nós de um grafo
Se tal caminho existir entre os nós i e j, ele deverá
ter comprimento 1, 2, ou 3
Nesse caso, adj[i][j] || adj2[i][j] || adj3[i][j],
deverá ser TRUE
Fechamento Transitivo
Fechamento Transitivo
Suponha que queiramos formar uma matriz path
tal que path[i][j] seja TRUE sse existir um
caminho do nó i até o nó j de qualquer
comprimento
Se o grafo contiver n nós: path[i][j] = adj[i][j] ||
adj2[i][j] || ... || adjn[i][j],
Fechamento Transitivo
Fechamento Transitivo
Podemos escrever uma rotina C que aceite uma
matriz de adjacência, adj, e calcule seu
fechamento transitivo, path.
Essa rotina usa uma rotina auxiliar, prod(a,b,c),
que define o vetor c como produto booleano de a
por b.
Fechamento Transitivo
Fechamento Transitivo
Algoritmo de Warshall
O método apresentado anteriormente é muito ineficiente
Vamos definir a matriz pathk de modo que pathk[i][j] seja
true sse existir um caminho do nó i ao j que não passe por
nenhum nó com numeração acima de k (exceto i e j)
Se pathk[i][j]==true, implica que pathk+1[i][j]==true
A única situação em que pathk+1[i][j]==true e pathk[i][j]
== false é se existir algum caminho de i até j passando
por k+1, mas não existe outro caminho de i até j passando
somente pelos nós de 1 até k
Algoritmo de Warshall
Sendo assim, pathk+1[i][j] será true, sse uma das
duas condições abaixo se verificar:
1. pathk[i][j]== TRUE
2. pathk[i][k+1] == TRUE e pathk[k+1][j]== TRUE
Algoritmo de Warshall
r s t u r s t u
0 1 0
Q s Qw r
0 1 1 1
v w x y v w x y
r s t u r s t u
1 0 2 1 0 2
Q r t x Q t x v
1 2 2 1 2 2 2 2
1 2 2
v w x y v w x y
Exemplo BFS (2)
r s t u r s t u
1 0 2 3 1 0 2 3
Q x v u Q v u y
2 1 2 2 2 3 2 1 2 3 2 3 3
v w x y v w x y
r s t u r s t u
1 0 2 3 1 0 2 3
Q u y Q y
2 1 2 3 3 3 2 1 2 3 3
v w x y v w x y
Exemplo BFS (3)
r s t u s
1 0 2 3
Q - r w
2 1 2 3
v w x y x
v t
u y
Algoritmo BFS
BFS(G,s)
01 for each vertex u V[G]-{s}
02 cor[u] branco // cor de u
03 d[u] // distância de s
04 p[u] NIL // predecessor de u
05 cor[s] cinza
06 d[s] 0
07 p[s] NIL
08 Enfila(Q,s)
09 while Q do
10 u Topo(Q)
11 for each v Adj[u] do
12 if cor[v] = branco then
13 cor[v] cinza
14 d[v] d[u] + 1
15 p[v] u
16 Enfila(Q,v)
17 Desenfila(Q)
18 cor[u] preto
Propriedades do BFS
Dado um grafo G = (V,E), o BFS descobre todos os
vértices alcançaveis a partir da fonte s
Computa a menor distância para todos os vértices
alcançaveis
O sub-grafo contendo os caminhos percorridos é chamado
de breadth-first tree
Para cada vértice v alcançável de s, o caminho na
breadth-first tree de s a v, corresponde ao caminho
mínimo ente s e v em G
Tempo de Execução do BFS
Seja um grafo G = (V,E)
• Os vértices são enfilados quando sua cor é branca
• Assumindo que o enfilamento e desefilamento são O(1), esta
operação custa O(|V|)
• As listas de adjacência são percorridas somente quando um
vértice é desenfilado
• A soma de de todos os tamanhos de lista é Q(|E|). Portanto, tempo
O(|E|) é gasto no percorrimento
Portanto o algoritmo é O(|V|+|E|)
• ou seja, linear com a lista de adjacência que representa G
BUSCA EM
PROFUNDIDADE
Busca em Profundidade
Depth-first search (DFS)
Iniciando em um vertice s, escolhe-se um adjacente t
não visitado
Visita-se t e escolhe-se um adjacente u não visitado, e
assim por diante
Quando todos os adjacentes a t tiverem sido visitados,
toma-se um próximo adjacente a s não visitado e
prossegue-se o caminhamento
Se restarem quaisquer vértices não descobertos, então
um deles será selecionado como uma nova origem
Algoritmo DFS (2)
Inicializa todos os vértices de branco
Visita cada um dos vértives brancos
Um vértice é branco se ainda não foi visitado
Um vértice é cinza se foi visitado mas se os seus
adjancentes ainda não foram todos visitados
Um vértice é preto se ele e todos os seus adjcentes foram
visitados
Exemplo DFS
u v w u v w u v w
1/ 1/ 2/ 1/ 2/
3/
x y z x y z x y z
u v w u v w u v w
1/ 2/ 1/ 2/ 1/ 2/
B B
4/ 3/ 4/ 3/ 4/5 3/
x y z x y z x y z
u v w u v w u v w
1/ 2/ 1/ 2/7 1/ 2/7
B B F B
4/5 3/6 4/5 3/6 4/5 3/6
x y z x y z x y z
u v w u v w u v w
1/8 2/7 1/8 2/7 9/ 1/8 2/7 9/
B B B C
F F F
4/5 3/6 4/5 3/6 4/5 3/6
x y z x y z x y z
F = Aresta Direta (cinza-preto) – ancestral para o descendente
C = Aresta Cruzamento (cinza-preto) – entre sub-árvores
Exemplo DFS (3)
u v w u v w
1/8 2/7 9/ 1/8 2/7 9/
B C B C
F F
4/5 3/6 10/ 4/5 3/6 10/ B
x y z x y z
u v w u v w
1/8 2/7 9/ 1/8 2/7 9/12
B C C
F F B
4/5 3/6 10/11 B 4/5 3/6 10/11 B
x y z x y z
Busca em Profundidade
Algoritmo DFS
Propriedades
O resultado da DFS é um grafo de precedência
Pode-se identificar ciclos no grafos (usando as
arestas de retorno (B))
Tempos de descoberta e término podem ser
usados para diversas finalidades.
Tempo de Execução do DFS
Os loops do DFS tomam tempo Q(|V|) cada,
excluindo o tempo de execução de DFS-Visit
DFS-Visit é chamado uma vez para cada vértice
branco e pinta o vértice de cinza imediatamente
Para cada chamada DFS-visit o loop interage
sobre os adjacentes do vértice
Assim o custo somado de todas as chamadas de
DFS-Visit é Q(|E|)
Portanto, o DFS é Q(|V|+|E|)
DFS Timestamping
O DFS gera uma ordenação de tempo monotonicamente
crescente, ou seja um “relógio global” entre os vértices
Tempo de descoberta/tempo de término
• d[u]/f[u]
Propriedades
Teorema dos Parêntesis (1)
Tempos de descoberta e fim podem ser associados a
uma estrutura de parentesis
• A descoberta de u é representada com "(u"
• O fim do processmento de u é representado com "u)"
• A história das descobertas e fins gera uma expressão bem
formado (parentesis corretamente aninhados)
Teorema dos Parêntesis (2)
Ordenação Topológica
Ordenação de um DAG (tem que ser acíclico)
Ordenação linear de todos os vértices tal que, para
toda aresta (u,v) no DAG, u aparece antes de v na
ordenação
TopSort(G)
1) Execute DFS(G) para computar os tempos de
término f[v] de cada vértice v
2) Gere uma lista ordenada de acordo com f[v]
Ordenação Topológica
Exemplo