Você está na página 1de 81

Algoritmos e Estruturas de

Dados II - Grafos

Prof. Raimundo BARRETO


DCC/ICE/UFAM
Grafos
 Um grafo G = (V,E) é composto por:
• V: um conjunto de vértices ou nodos
• E  V V: conjunto de arcos ou arestas conectando os vertices
 Um arco e = (u,v) é um par de vértices
 Se (u,v) é ordenado, G é um grafo dirigido
Aplicações (1)
 Circuitos eletrônicos:
• Qual o caminho com menor resistência para CS16?
Aplicações (2)
 Transportes
• Qual a rota de POA até
MAO que maximiza
custo/beneficio?
Aplicações (2)
 Transportes
• Qual a rota de POA até
MAO que maximiza
custo/beneficio?
Aplicações (3)
 A Word-Wide Web
• Um robô deve coletar as páginas de interesse para os
usuários de uma máquina de busca no menor tempo
possível
Grafos – Terminologia
 Vértices adjacentes: conectados por um arco
 Grau de um vértice: número de vertices adjacentes a ele

 caminho: sequência de vértices v1 ,v2 ,. . .vk tais que todo


par de vértices consecutivos vi and vi+1 são adjacentes
Grafos – Terminologia (2)
 Caminho simples: sem vértices repetidos
Grafos - Terminologia (3)
 Ciclo: caminho simples, exceto pelo fato de que o
primeiro vértice se repete como adjacente ao
último vértice
Ciclo

CICLO HAMILTONIANO

É um ciclo que visita todos os vértices


uma única vez e retornar ao vértice inicial.
Ciclo

CICLO DE EULER

Percorre todas as arestas de


um grafo uma única vez até
voltar ao vértice inicial.
Ciclo

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

 Para cada vertice v do grafo, gerar uma lista


encadeada dos vértices adjacentes a v
Representação de Grafos
Matriz de Adjacência

 Matriz M representando V x V, G = (V,E)


 M[i,j] = V se (i,j)  E
 M[i,j] = F se (i,j)  E
 Espaço = O(n2)
Representação de Grafos
Orientado e não-Orientado
TEOREMA DAS
QUATRO CORES
Problemas
Teorema das Quatro Cores

Dado um mapa plano, dividido em regiões,


quatro cores são suficientes para o colorir,
de forma a que regiões vizinhas não
partilhem a mesma cor.

OBS: As regiões que só se tocam num


ponto não
são consideradas vizinhas.
Problemas
Teorema das Quatro Cores

Este foi colorido com 5 cores...

...mas é suficiente trocar quatro das dez


regiões para obter uma coloração com
apenas quatro cores.
PROBLEMA DO
CAIXEIRO VIAJANTE
Problemas
Caixeiro Viajante
Suponha que um caixeiro viajante deseja visitar N cidades (vértices)
e que, entre alguns pares de cidades existem rotas (arestas).
Cada rota tem uma distância ou o custo para percorrê-la.

O caixeiro viajante deseja encontrar um caminho que passe por cada


uma das N cidades apenas uma vez, e além disso que tenha um
custo menor que certo valor; onde o custo é a soma dos custos das
rotas percorridas.

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

31+44+63+6+51 = 195. Há algum caminho com custo menor?


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

for (i=0; i< MAXNODES; ++i)


for (j=0; j< MAXNODES; ++j)
pathk[i][j] = (pathk-1[i][j] ||
(pathk-1[i][k] && pathk-1[k][j]));
Algoritmo de Warshall
 Em termos lógicos, isso pode ser simplificado

for (i=0; i< MAXNODES; ++i)


for (j=0; j< MAXNODES; ++j)
pathk[i][j] = pathk-1[i][j];
for (k=0; k< MAXNODES; ++k)
for (i=0; i< MAXNODES; ++i)
if (pathk-1[i][k] == TRUE)
for (j=0; j< MAXNODES; ++j)
pathk[i][j]=pathk-1[i][j]||pathk-1[k][j];
Algoritmo de Warshall
 Obviamente que path0[i][j]= adj (por quê?)
 A seguinte rotina pode ser usada para calcular o
fechamento transitivo
Algoritmo de Warshall
BUSCA EM LARGURA
Caminhamento em Lagura
 Breadth-First Search (BFS)
 A partir de um vértice inicial s (ancôra), visita-se todos
os seus vértices adjacentes (componentes conexos).
 Para o vértice inicial s é assinado uma distância = 0
 No primeiro passo, todos os vértices conectados a s são
visitados e é assinalada uma distância=1 a estes vértices
 No segundo passo, todos os vértices alcançados a partir
dos vértices de distância=1 são visitados é assinalados a
estes a distância=2, e assim sucessivamente até que
todos tenham uma distância assinalada.
Caminhamento em Largura
Exemplo BFS

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

B = Aresta Reversa (cinza-cinza) – descendente para o ancestral


Exemplo DFS (2)

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

Qual seria a ordenação


topológica
(relações de precedência) ???
Ordenação Topológica
Observações

 A ordenação pode iniciar por qualquer vértice


 É possível encontrar diferentes ordenações
topológicas corretas para um mesmo grafo
orientado acíclico
Ordenação Topológica
Aplicações

 É utilizado sempre se necessita uma ordem para


execução de tarefas onde há pré-requisitos
(dependências) entre tarefas.
 Matérias para se cursar uma graduação: o algoritmo
garante que nenhum pré-requisito será quebrado.
 Construir um prédio, onde há várias tarefas: colocar
telhado, montar parede, montar porta, parede,
acabamento;
• Não se coloca o teto antes de subir as paredes.
Ordenação Topológica
Aplicações
 Uma outra aplicação são programas de planilha eletrônica.
• Há várias células e às vezes uma célula tem uma fórmula que depende de
outras células. Se existe uma célula A1, uma célula B1 com fórmula que
depende de A1 e uma célula C1 que depende de B1, não se pode atualizar A1,
depois C1 e depois B1.
Ordenação Topológica
Aplicações
 Para determinação de tarefas paralelizáveis, pode-se analisar a
saída do DFS em busca de paralelização.
• Executa um algoritmo que determina um conjunto de atividades máximo
que podem ser executadas em seqüência.
• Este conjunto (caminho) de atividades é atribuído a uma pessoa e retirado
do grafo.
• Aplica-se o algoritmo novamente (no grafo resultante ) e atribui o caminho
a uma segunda pessoa, e assim sucessivamente, tantas vezes quantas
forem necessárias.
• Este algoritmo gera o número mínimo de pessoas necessárias para
execução de todas as atividades.
• A pergunta respondida por este procedimento é: qual o mínimo de pessoas
necessárias para executar um dado conjunto de atividades?
Largura e Profundidade

Você também pode gostar