Você está na página 1de 76

BCC402

Algoritmos e Programação
Avançada
Prof. Marco Antonio M. Carvalho
Prof. Túlio Ângelo M. Toffolo
2011/1
Na aula anterior

• Estruturas de Dados
– Pilhas;
– Listas;
– Filas
• Filas de Prioridades.
– Dicionários.

2
Na aula de hoje

• Definições e Estruturas de Grafos


• Busca em Largura;
• Busca em Profundidade.

3
Grafos

• Grafos são estruturas de dados muito


importantes e recorrentes
– Representam arbitrárias relações entre
elementos.
• O primeiro registro de uso data de 1736, por
Euler
– O problema era encontrar um caminho circular
por Königsberg (atual Kaliningrado) usando cada
uma das pontes sobre o rio Pregel (ou Pregolya,
Pregola) exatamente uma vez.
4
Grafos

• Infelizmente (?), este problema não tem solução;


• Por outro lado, a teoria de grafos foi desenvolvida.
5
Grafos- Aplicações

• Grafos podem ser utilizados para modelar:


– Malhas viárias;
– Dutos (oleodutos, gasodutos, etc.);
– Circuitos eletrônicos;
– Redes (elétricas, de computadores, etc.);
– Programas;
– Interações humanas;
– Enfim, inúmeras situações.

6
Grafos - Definições

• Um grafo G é um par G=(V, E) consistindo de um


conjunto não vazio V e um conjunto E de pares de
elementos contidos em V
– Os elementos de V são os vértices ou nós;
– Os elementos de E são as arestas entre vértices, e
podem ter pesos, ou valores associados.

1
Vértice

2
4

3 Aresta
7
Grafos - Definições

• Se existe uma aresta e entre os vértices u e v


– Usamos a notação e = (u, v);
– Os vértices são ditos vizinhos ou adjacentes;
– A aresta e é dita incidente a u e v;

• O grau de um vértice u, denotado por d(u), é o


número de arestas incidentes a u.

8
Arestas Especiais

• Se em e =(u, v), u e v são o mesmo vértice, a aresta e é


um laço ou loop;
• Em alguns casos, entre o mesmo par de vértices pode
haver mais de uma aresta. Neste caso, elas são
paralelas ou múltiplas.

2
9
[Grafos]
Simples vs. Não Simples
• Um grafo sem arestas múltiplas, ou loops é dito
simples;
• Um grafo com este tipo de arestas é dito não
simples
– Pode ser utilizado para representar situações em
que dois elementos são ligados por mais que um
meio
• Diferentes estradas entre duas cidades;
• Ou diferentes dutos entre dois pontos.
• Algoritmos diferenciados para percurso em cada
um dos tipos de grafos.
10
Grafos Orientados

• Em um grafo orientado, ou dígrafo, as arestas


possuem uma orientação definida
– O termo arco também é utilizado para se referir a este tipo
de aresta;
– Ao invés de denotarmos e=(u, v), denotamos e=uv
indicando os vértices inicial e final;

1 4
5

2 3
11
[Grafos]
Orientados vs. Não Orientados

• Grafos direcionados podem ser utilizados para


modelar ruas de cidades, pois (geralmente) são
de mão única;
• Em um grafo não orientado, as arestas podem
ser consideradas em qualquer direção
– Por exemplo, modelam estradas, que usualmente
são de mão dupla;

12
Grafos Ponderados

• Grafos nos quais as arestas possuem valores ou pesos


são chamados grafos ponderados
– Representam situações em que haja distância, tempo,
fluxo ou capacidade.

Belo
Horizonte 157
Mariana

115 68
92 18

Ouro
55 Preto
Congonhas

13
[Grafos]
Ponderados vs. Não Ponderados

• A noção de valores em arestas introduz o


conceito de menor caminho entre vértices
– Para grafos não ponderados, o menor caminho
é aquele com menos arestas, uma vez que todas
têm o mesmo peso (considerado unitário);
– Para grafos ponderados, algoritmos mais
elaborados são necessários, uma vez que há
diversas combinações de arestas com pesos
diferentes.

14
Caminhos

• Um caminho em um grafo é uma sequência de vértices conectados


entre si, formando um percurso em um grafo.
– O caminho é delimitado por um vértice inicial e um vértice
final;
– Em um caminho simples, cada vértice aparece uma única vez;
– O comprimento de um caminho é a sua quantidade de vértices.

Vértice
Final
Vértice
Inicial

• Este exemplo de caminho é denotado por 1→2→4→3→5 15


Ciclos
• Um ciclo ou circuito é um caminho em que o vértice
inicial também é o vértice final
– A escolha do vértice inicial é arbitrária.
– Um grafo com ciclos é chamado cíclico;

• Este exemplo de ciclo pode ser denotado por


1→2→4→3→5→1. 16
[Grafos] Cíclicos vs. Acíclicos

• Um grafo acíclico não possui ciclos


– Árvores são grafos acíclicos não orientados;
– Grafos orientados acíclicos (DAGs) são
utilizados para modelar precedência entre
eventos ou elementos
• Ordenação topológica.

17
Densidade

• Um grafo G=(V, E) em que |E| é muito menor


que |V|2 é considerado esparso;
• Simetricamente, se |E| está próximo de |V|2, o
grafo é considerado denso;
• De acordo com a densidade do grafo, diferentes
estruturas de dados são utilizadas para
representá-lo.

18
Representação de Grafos

• Duas formas padrão


– Matriz de adjacências
• Indicada para grafos densos.
– Lista de adjacências
• Indicada para grafos esparsos.

19
Matriz de Adjacências

• Consiste em uma matriz |V|×|V| em que a posição i


representa o vértice i;
• Caso exista uma adjacência entre os vértices i e j a
posição i,j na matriz tem o valor 1, caso contrário tem o
valor 0
– Em grafos não orientados, a matriz de adjacências é
simétrica ao longo da diagonal principal;
– Em grafos orientados, apenas as arestas de saída são
representadas;
– No caso de grafos ponderados, o valor 1 pode ser
substituído pelo peso da aresta.

20
Matriz de Adjacências
1 2 3 4 5
1 0 1 0 1 0
2 1 0 1 1 1
3 0 1 0 0 1
4 1 1 0 0 1
5 0 1 1 1 0

1 2 3 4 5
1 0 1 0 1 0
2 0 0 1 0 1
3 0 0 0 0 1
4 0 1 0 0 0
5 0 0 0 1 0
21
Lista de Adjacências

• Consiste em um vetor de |V| listas. Em cada posição i,


há uma lista onde cada elemento é um vértice adjacente
ao vértice i;
– No caso de grafos não orientados, as adjacências são
armazenadas em ambos os vértices de incidência;
– Em grafos dirigidos, apenas as arestas de saída são
armazenadas.
– Em casos de grafos orientados, pode ser criado um
campo em cada elemento da lista para armazenar o valor.

22
Lista de Adjacências
1
2
3
4
5

1
2
3
4
5
23
Lista vs. Matriz de Adjacências

• Em uma matriz de adjacências determinar a existência


de uma aresta entre vértices u e v é imediato
– Em uma lista de adjacências este processo é mais
demorado, além disso, a remoção de uma aresta é
dificultada.
• Em contraposição, a matriz de adjacências requer
espaço suficiente para armazenar as arestas de um
grafo completo O(n2), mesmo que esse não seja o caso
– A lista de adjacências requer espaço apenas para
armazenar as arestas existentes O(n+m).
• Grafos pequenos geralmente são representadas por
matrizes, devido à simplicidade.

24
Lista de Adjacências
em Matrizes
• As listas de adjacências podem não usar listas
encadeadas
– Podem inclusive usar uma matriz para armazenar os
elementos.
• Aparentemente, esta estrutura combina o pior da matriz
de adjacências (muito espaço) com o pior da lista de
adjacências (determinar a existência de uma aresta);
• Contudo, faz sentido:
– É uma estrutura simples de implementar;
– O problema do espaço pode ser contornado limitando-se a
quantidade de colunas da matriz.

25
Lista de Adjacências
em Matrizes
1 2 3 4
1 2 4 ? ?
2 1 3 4 5
3 2 5 ? ?
4 1 2 5 ?
5 2 3 4 ?

1 2 3 4
1 2 4 ? ?
2 3 5 ? ?
3 5 ? ? ?
4 2 ? ? ?
5 4 ? ? ?
26
Códigos

• Está disponibilizado um conjunto de códigos originais do


Skiena
– Nome de funções, variáveis e comentários traduzidos;
– Utilizam a estrutura de lista de adjacências em matrizes;
– Os algoritmos a seguir também estão implementados.
• No entanto, é necessário saber o funcionamento dos
algoritmos e também adaptar os códigos às
necessidades
– O uso dos códigos é opcional.

27
Percurso em Grafos

• A operação básica em grafos é o percurso


– Ou seja, visitar todos os vértices e arestas uma
única vez em alguma ordem;
• Uma possível dificuldade seria não terminar a
busca nunca, por causa de repetição
– Por isso marcamos os vértices já visitados.
• Existem dois algoritmos básicos
– Busca em Largura;
– Busca em Profundidade.

28
Busca em Largura (BFS)

• A busca em largura (Breadth-First Search - BFS)


expande a exploração de um grafo em níveis
– A partir do vértice inicial, o nível explorado é o dos vértices
adjacentes;
– Após a exploração deste nível, passa-se à exploração do
vértices adjacentes aos do nível anterior;
– Caso o grafo seja desconectado, ao fim da exploração de
um componente, passa-se ao próximo;
– O procedimento se repete até que todos os vértices
tenham sido explorados.

29
Busca em Largura (BFS)

• Uma estrutura de fila é utilizada para guiar os passos da


busca;
• Durante a exploração, um vértice é descoberto na
primeira vez em que é encontrado, quando é então
enfileirado
– Representado pela cor cinza.
• Quando o vértice é retirado da fila, ele é considerado
visitado
– Todas as arestas incidentes a ele foram exploradas;
– Representado pela cor preta.

30
Busca em Largura (BFS)

• F: null
31
Busca em Largura (BFS)

• F: 1→null
32
Busca em Largura (BFS)

• F: 2→3→4→5→null
33
Busca em Largura (BFS)

• F: 3→4→5→null
34
Busca em Largura (BFS)

• F: 3→4→5→6→null
35
Busca em Largura (BFS)

• F: 4→5→6→7→null
36
Busca em Largura (BFS)

• F: 5→6→7→8→null
37
Busca em Largura (BFS)

• F: 6→7→8→9→null
38
Busca em Largura (BFS)

• F: 7→8→9→null
39
Busca em Largura (BFS)

• F: 8→9→null
40
Busca em Largura (BFS)

• F: 9→null
41
Busca em Largura (BFS)

• F: null
42
Busca em Largura (BFS)

• Existe uma relação entre o penúltimo e último


vértice descobertos
– O último foi descoberto a partir do penúltimo;
– Dizemos então que o penúltimo é pai do último;
• Se seguirmos a genealogia dos vértices,
obtemos o caminho de menor comprimento
entre o vértice inicial da busca e todos os outros
– Em grafos não ponderados.

43
Busca em Largura - Código

• O arquivo bfs-dfs.c possui uma implementação


da BFS
– Basta adaptar as funções processa_vertice() e
processa_aresta(), de acordo com a
necessidade.
• A função encontra_caminho() analisa a ordem
de exploração dos vértices para encontrar o
menor caminho entre dois vértices.

44
Busca em Profundidade (DFS)

• A busca em profundidade (Depth-First Search - DFS)


explora todos os níveis de cada adjacência, uma por vez
– A partir do vértice inicial, explora-se todos os níveis
possíveis de uma adjacência;
– Quando não for mais possível expandir a busca, retorna-
se ao último nó com adjacências ainda não exploradas e
retoma-se o processo;
– Em caso de grafos desconectados, uma vez que um
componente for totalmente explorado, passa-se ao
próximo;
– A exploração é repetida até que todos os nós tenham sido
visitados.

45
Busca em Profundidade (DFS)

• Uma estrutura de pilha é utilizada para guiar os passos


da busca;
• Durante a exploração, um vértice é descoberto na
primeira vez em que é encontrado, quando é então
empilhado
– Representado pela cor cinza.
• Quando o vértice não possui mais adjacências a serem
exploradas, ele é desempilhado, sendo considerado
visitado ou terminado
– Representado pela cor preta.

46
Busca em Profundidade (DFS)

7 3 1 5 9

8
• P: null
47
Busca em Profundidade (DFS)

7 3 1 5 9

8
• P: 1→null
48
Busca em Profundidade (DFS)

7 3 1 5 9

8
• P: 2→1→null
49
Busca em Profundidade (DFS)

7 3 1 5 9

8
• P: 6→2→1→null
50
Busca em Profundidade (DFS)

7 3 1 5 9

8
• P: 2→1→null
51
Busca em Profundidade (DFS)

7 3 1 5 9

8
• P: 1→null
52
Busca em Profundidade (DFS)

7 3 1 5 9

8
• P: 9→5→1→null
53
Busca em Profundidade (DFS)

7 3 1 5 9

8
• P: 8→4→1→null
54
Busca em Profundidade (DFS)

7 3 1 5 9

8
• P: 7→3→1→null
55
Busca em Profundidade (DFS)

7 3 1 5 9

8
• P: 1→null
56
Busca em Profundidade (DFS)

7 3 1 5 9

8
• P: null
57
Classificação de Arestas

• Podemos utilizar DFS para classificar as arestas em:


– Arestas de Árvore
• São aquelas que incidem em vértices ainda não
descobertos;
• Formam uma árvore durante a exploração do grafo.
– Arestas de Retorno
• Incidem a vértices já descobertos pela busca, formando
ciclos assim.
• Se não há aresta de retorno, trata-se de uma
árvore;
• Isto funciona para grafos não direcionados.
58
Classificação de Arestas

59
Classificação de Arestas

Árvore Retorno
60
Classificação de Arestas

4 5

Árvore Retorno
61
Classificação de Arestas

4 5

Árvore Retorno
62
Classificação de Arestas

6 4 5

Árvore Retorno
63
Classificação de Arestas

6 4 5

Árvore Retorno
64
Classificação de Arestas

6 4 5

Árvore Retorno
65
Conectividade

• Dois vértices u e v de um grafo G são ditos


conexos se houver um caminho em G com
início em u e final em v
– Consideramos que todo vértice é conectado a si
mesmo (mesmo sem a existência de uma
aresta).
• Um Componente Conexo é formado por um
conjunto de vértices conexos.

66
Conectividade

Um grafo conexo (ou conectado). Um grafo desconexo


(ou desconectado).
O vértice 4 é isolado.

67
Conectividade

• Podemos usar tanto BFS quando DFS para


detectar componentes conectados
– Durante a exploração:
• Se todos os vértices foram visitados sem
interrupções, o grafo é composto de um único
componente conectado;
• Caso contrário, recomeçamos a exploração no
primeiro vértice ainda não descoberto, detectando
assim um novo componente conectado.

68
Conectividade

6
5

4 8

2
3 7

69
Conectividade

6
5

4 8

2
3 7

70
Conectividade

6
5

4 8

2
3 7

71
Busca em Profundidade
Código

• O arquivo bfs-dfs.c possui uma implementação


da DFS
– Novamente, basta adaptar as funções
processa_vertice() e processa_aresta(), de
acordo com a necessidade.
• O arquivo findcycle.c analisa as arestas para
detectar ciclos;
• O arquivo connected.c enumera os
componentes conexos de um grafo.

72
Busca em Largura vs.
Busca em Profundidade

• Apesar de terem a mesma complexidade, e explorarem


o grafo completamente, as diferentes ordens de
visitação dos nós conferem diferentes propriedades:
– A busca em largura é usada para detecção de
componentes conexos e obtenção da menor distância em
grafos não orientados;
– A busca em profundidade é utilizada para ordenação
topológica, para verificar se um grafo é acíclico, bipartido,
quais são seus vértices de articulação, etc.;
– Em outros problemas, ambos podem ser utilizados sem
distinção.

73
Perguntas?

74
Na próxima aula

• Prática de Estruturas de Dados


– Pilhas;
– Listas;
– Filas
• Filas de Prioridades.
– Dicionários.

75
FIM

76

Você também pode gostar