Algoritmos para Grafos  |  Índice

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.)

Sumário:

figs/Sedgewick-Wayne/tinyGtwo-x.png

Grafos não-dirigidos conexos

figs/Sedgewick-Wayne/tinyGtwo-xx.png

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 é

  1. isolado se nenhuma aresta tem uma ponta em X e outra fora de X e
  2. 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

  1. [Sedgewick 17.2]  Faça uma lista de todos os sub­grafos 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 sub­grafos são induzidos?
  2. 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.
  3. Corte vazio.  Prove que um grafo não-dirigido é desconexo se e somente se algum conjunto não-trivial de seus vértices é isolado.
  4. Número de arestas.  Quantas arestas pode ter um grafo não-dirigido conexo com V vértices?
  5. 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?
  6. 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.
  7. 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 st.
  8. 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.

figs/Sedgewick-Wayne/tinyGtwo-x.png

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 st 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 sub­grafo 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 sub­grafo conexo maximal do grafo. (Um sub­grafo conexo H de G é maximal se não existe grafo conexo H' tal que  HH'G, ou seja, não existe sub­grafo conexo H' de G tal que H é sub­grafo 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

  1. Transitividade.  Mostre que a relação ao‑alcance‑de é transitiva. [Solução]
  2. Subgrafo maximal.  Mostre que uma componente conexa de um grafo não-dirigido G é um sub­grafo 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  HH'G, ou seja, não existe sub­grafo não-dirigido conexo H' de G tal que H é sub­grafo próprio de H'.
  3. 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.
  4. [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.
  5. Quantas componentes conexas tem o grafo não-dirigido da figura?
  6. 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 st, estão na mesma componente conexa:

int UGRAPHconnect( UGraph G, vertex s, vertex t) { 
   return cc[s] == cc[t]; 
}

Exercícios 3

  1. 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?
  2. Que acontece se aplicarmos a função UGRAPHconComps() a um grafo que não seja não-dirigido?
  3. 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.
  4. [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? 
  5. 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.
  6. 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 sub­grafo induzido por X.  (O resultado desse exercício é útil, por exemplo, em algumas heurísticas de coloração de vértices.)
  7. [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. 
  8. [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.
  9. Escreva e teste um programa que gere um sub­grafo 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

  1. [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.
  2. 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.)
  3. [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)/(piV) e quase certamente desconexo se d² for menor que esse número.