Componentes conexas
Este capítulo trata das componentes conexas
de grafos não-dirigidos.
(Os conceitos correspondentes
para grafos dirigidos
são mais complexos e serão discutido em
outra ocasião.)
Grafos não-dirigidos conexos
Um grafo não-dirigido é conexo
se cada um de seus vértices está ao alcance
de cada um dos demais.
Em outras palavras,
um grafo não-dirigido é conexo se tem a seguinte propriedade:
para cada par s t
de seus vértices,
existe um
caminho com
origem
s e
término t.
Um grafo não-dirigido é desconexo se não for conexo.
Para caracterizar grafos não-dirigidos desconexos,
precisamos introduzir duas definições:
dizemos que um conjunto X de vértices é
-
isolado
se nenhuma aresta
tem uma ponta em X e outra fora de X e
-
trivial
se X for vazio ou contiver todos os vértices do grafo.
Agora podemos enunciar a caracterização:
um grafo não-dirigido é desconexo se e somente se
algum conjunto não-trivial de seus vértices é isolado.
Exercícios 1
-
[Sedgewick 17.2]
Faça uma lista de todos os subgrafos não-dirigidos
conexos do grafo não-dirigido
definido pelo conjunto de arestas
0-1 0-2 0-3 1-3 2-3.
Quais desses subgrafos são
induzidos?
-
Seja r um vértice de um grafo não-dirigido.
Suponha que todo vértice v do grafo
está ao alcance de r.
Mostre que o grafo é conexo.
-
Corte vazio.
Prove que um grafo não-dirigido é desconexo se e somente se
algum conjunto não-trivial de seus vértices é isolado.
-
Número de arestas.
Quantas arestas pode ter um grafo não-dirigido conexo
com V vértices?
-
Escreva uma função UGRAPHcon()
que decida se um grafo não-dirigido é ou não é conexo.
No caso de resposta negativa,
que informação adicional a função poderia devolver
como prova de que a resposta está correta?
-
★
O grafo do cavalo é
definido da seguinte maneira.
Os vértices são as casas do tabuleiro de xadrez
e dois vértices são adjacentes se um cavalo
(em inglês, o nome da peça é knight)
do jogo de xadrez pode
saltar de um deles para o outro em um só movimento.
O tabuleiro de xadrez tradicional tem 8 linhas e 8 colunas,
mas um grafo do cavalo generalizado pode ser definido sobre
um tabuleiro com t linhas e t colunas,
para qualquer t.
Escreva uma função UGRAPHchessKnight()
que construa o grafo do cavalo no tabuleiro t-por-t.
Escreva outra função para decidir se o grafo do cavalo t-por-t é conexo.
-
Caminhos.
Escreva um pequeno programa
que receba um grafo não-dirigido G e uma lista de pares de vértices
e imprima,
para cada par s t,
um caminho
de s a t.
-
Grafos não-dirigidos conexos aleatórios.
Escreva um algoritmo
para construir um grafo não-dirigido aleatórios conexo com V vértices e E arestas.
O seu algoritmo deve produzir, com aproximadamente a mesma probabilidade,
qualquer dos grafos não-dirigidos conexos
com V vértices e E arestas.
(Veja exercício Árvore aleatória no
capítulo Circuitos e florestas.)
Componentes conexas de grafos não-dirigidos
Num grafo não-dirigido,
a relação ao‑alcance‑de
entre vértices é uma
relação de equivalência,
ou seja, uma relação reflexiva, simétrica e transitiva.
A propriedade reflexiva
e a simétrica
são óbvias.
A propriedade transitiva
é tecnicamente um pouco mais delicada porque, por definição,
um caminho não deve ter arcos repetidos.
Portanto,
a relação ao‑alcance‑de
impõe uma partição do conjunto de vértices em
classes de equivalência:
dois vértices s e t
estão na mesma classe se e somente se
t está ao alcance de s.
Uma componente conexa
(= connected component)
de um grafo não-dirigido G é o subgrafo de G induzido
por alguma das classes de equivalência
da relação ao‑alcance‑de.
Podemos resumir essa definição dizendo que uma componente
de um grafo não-dirigido é um subgrafo conexo maximal do grafo.
(Um subgrafo conexo H de G
é maximal se não existe grafo conexo
H' tal que
H ⊂ H' ⊆ G,
ou seja,
não existe
subgrafo conexo H' de G
tal que H é subgrafo próprio
de H'.)
Problema das componentes conexas:
Encontrar as componentes conexas de um grafo não-dirigido.
Para resolver o problema,
poderíamos verificar, para cada par
s t
de vértices,
se t está ao alcance de s.
(Basta usar a função GRAPHreach(), por exemplo.)
Mas esse algoritmo é muito lento.
Trataremos a seguir de uma solução bem mais eficiente.
Exercícios 2
-
Transitividade.
Mostre que a relação ao‑alcance‑de é transitiva.
[Solução]
-
Subgrafo maximal.
Mostre que uma componente conexa
de um grafo não-dirigido G
é um subgrafo não-dirigido
conexo maximal de G.
O adjetivo maximal significa o seguinte:
se H é componente conexa de G então
não existe um grafo não-dirigido conexo H' tal que
H ⊂ H' ⊆ G,
ou seja,
não existe
subgrafo não-dirigido conexo H' de G tal que
H é subgrafo próprio de H'.
-
★
Ao alcance de um, ao alcance de todos.
Seja x um vértice de um grafo não-dirigido G
e seja X o conjunto de todos os vértices
ao alcance de x.
Mostre que G[X]
é uma componente conexa de G.
-
[Sedgewick 17.4]
Determine o número de componentes conexas do grafo não-dirigido
definido pelo conjunto de arestas
3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4.
-
Quantas componentes conexas tem o grafo não-dirigido da figura?
-
Mostre que toda aresta de um grafo não-dirigido
tem ambas as pontas na mesma componente conexa.
Mostre que todo vértice de um grafo não-dirigido pertence a uma e uma só
de suas componentes conexas.
Cálculo das componentes conexas
Há muitas maneiras eficientes de calcular
as componentes conexas de um grafo não-dirigido.
A função UGRAPHconComps() abaixo
usa uma busca em profundidade.
O código é análogo ao da função
GRAPHdfs().
Cada etapa
da busca descobre uma nova componente conexa.
Para identificar as componentes conexas,
vamos atribuir um rótulo
cc[v] a cada vértice v
de tal modo que dois vértices tenham o mesmo rótulo
se e somente se estiverem na mesma componente conexa.
(O vetor cc[] é alocado pelo usuário.)
#define UGraph Graph
/* O tipo UGraph é um sinônimo de Graph
que tem a missão de deixar claro, para o leitor humano,
que o grafo é não-dirigido.
(O "U" de "UGraph" é uma abreviatura de "undirected".) */
int cc[1000];
/* A função UGRAPHconComps() devolve o número de componentes conexas
do grafo não-dirigido G.
Além disso, armazena no vetor cc[]
uma numeração dos vértices tal que
dois vértices v e w pertencem à mesma componente
se e somente se cc[v] == cc[w].
(O código foi copiado do programa 18.4
de Sedgewick.) */
int UGRAPHconComps( UGraph G)
{
int id = 0;
for (vertex v = 0; v < G->V; ++v)
cc[v] = -1;
for (vertex v = 0; v < G->V; ++v)
if (cc[v] == -1)
dfsRconComps( G, v, id++);
return id;
}
/* A função auxiliar dfsRconComps() atribui o número id
a todos os vértices
que estão na mesma componente conexa que v.
A função supõe que o grafo G
é representado por listas de adjacência. */
static void dfsRconComps( UGraph G, vertex v, int id)
{
cc[v] = id;
for (link a = G->adj[v]; a != NULL; a = a->next)
if (cc[a->w] == -1)
dfsRconComps( G, a->w, id);
}
Depois de executar UGRAPHconComps() uma só vez,
é fácil verificar,
em tempo constante,
se dois vértices,
digamos s e t,
estão na mesma componente conexa:
int UGRAPHconnect( UGraph G, vertex s, vertex t) {
return cc[s] == cc[t];
}
Exercícios 3
-
Instâncias extremas.
A função UGRAPHconComps() está correta se G for conexo?
E se tiver um só vértice?
E se não tiver aresta alguma?
-
Que acontece se aplicarmos a função UGRAPHconComps()
a um grafo que não seja não-dirigido?
-
★
Componentes conexas via BFS.
Escreva uma função que faça busca em largura
para calcular as componentes conexas de um grafo não-dirigido.
-
[Sedgewick p.16]
Seja G um grafo não-dirigido com V vértices
e E arestas.
Quantas componentes conexas G pode ter no máximo?
Quantas componentes conexas G pode ter no mínimo?
-
Inserção de nova aresta.
Seja cc[] o vetor das componentes conexas
de um grafo não-dirigido G.
Dados vértices v e w de G,
como a inserção de uma aresta v-w
alteraria as componentes conexas de G?
Escreva uma função que modifique cc[]
para mostrar o efeito da inserção do arco v-w.
-
★
Componentes de subgrafos induzidos.
Seja X um conjunto de vértices
de um grafo não-dirigido G.
(Você pode supor que X é dado por seu
vetor característico.)
Escreva uma função que calcule as componentes conexas
do subgrafo induzido por X.
(O resultado desse exercício é útil, por exemplo,
em algumas heurísticas de coloração de vértices.)
-
[Sedgewick 18.30]
Prove que todo grafo não-dirigido conexo
com dois ou mais vértices
tem um vértice cuja remoção não desconecta o grafo.
Escreva uma função que encontre um tal vértice.
-
[Sedgewick 18.31]
Prove que todo grafo não-dirigido com dois ou mais vértices
tem pelo menos dois vértices
cuja remoção não aumenta o número de componentes conexas.
-
Escreva e teste um programa que gere um subgrafo aleatório de uma grade não-dirigida m-por-n com E arestas
e calcule o número de componentes conexas do grafo.
(Os valores de m, n e E
devem ser fornecidos pela linha de comando.)
Exercícios 4
-
[Sedgewick p.135]
Grafos não-dirigidos aleatórios conexos.
Faça experimentos para determinar quantas arestas um
grafo não-dirigido aleatório
com V vértices
precisa ter (em média) para ser conexo.
Faça experimentos com V valendo 100, 1000, 10000.
Use a função UGRAPHrandER()
para construir os grafos.
-
Componentes gigantes em grafos não-dirigidos aleatórios.
Quantas arestas um grafo não-dirigido aleatório com V vértices
precisa ter (em média)
para que uma de suas componentes conexas seja gigante?
Escreva um programa para fazer os experimentos
descritos a seguir.
Cada experimento consiste em construir um
grafo não-dirigido aleatório
com V vértices e E arestas em média
e determinar o número de componentes conexas de cada tamanho.
Para cada valor de V no conjunto {100, 1000, 10000},
faça os experimentos com E valendo
0.2V, 0.5V, 1V,
2V, 5V, 10V e 20V.
Repita cada experimento 100 vezes
(variando a semente do gerador de números aleatórios)
e imprima uma tabela mostrando o número médio de componentes
de cada tamanho.
(Quando o número de arestas
passa de
½ V ln V,
ou seja, pouco mais que V
multiplicado pelo número de dígitos decimais de V,
o grafo consiste, com grande probabilidade,
em uma componente conexa
gigante
e alguns vértices isolados.)
-
[Sedgewick-Wayne E.4.1.42]
Grafos não-dirigidos geométricos aleatórios conexos.
Escreva uma função que construa grafos não-dirigidos euclidianos
aleatórios
da seguinte maneira:
primeiro, escolha V pontos aleatórios
no quadrado [0,1)×[0,1);
depois, ligue dois pontos por uma aresta
se a distância entre eles for
≤ d.
Verifique experimentalmente que o grafo resultante
é quase certamente conexo se d² for maior que
(lg V)/(V)
e quase certamente desconexo se d² for menor
que esse número.