Algoritmos para Grafos, via Sedgewick  |  Índice remissivo

Componentes de grafos

Esta página trata exclusivamente de grafos.  Os conceitos correspondentes aos desta página para digrafos não simétricos são mais complexos e serão discutido em outra ocasião.

[Esta página corresponde a uma parte da seção 18.5 (DFS Algorithms), p.99-103, do capítulo 18 (Graph Search) do livro de Sedgewick.]

Grafos conexos e caminhos

Já dissemos que um grafo é conexo se não tem cortes vazios.  Eis uma fato básico importante, que estabelece a relação entre essa definição e a existência de caminhos que ligam pares de vértices: 

Fato:  Um grafo é conexo se e somente se, para cada par (s,t) de seus vértices, existe um caminho com origem  s  e término  t.

Em virtude da simetria dos grafos, a existência de um caminho de s a t equivale à existência de um caminho de ts.  Portanto, um grafo é conexo se e somente se quaisquer dois de seus vértices são ligados por um caminho.

Exercícios 1

  1. Seja (s,t) um par de vértices de um grafo G sem cortes vazios. Prove que existe um caminho de s a t em G.
  2. Suponha que todo par de vértices de um grafo G está ligado por um caminho.  Prove que G não tem cortes vazios.
  3. Seja s um vértice de um grafo. Suponha que todo vértice v do grafo é término de um caminho com origem s.  Mostre que o grafo é conexo.
  4. Seja G um grafo conexo com V vértices.  Quantas são as arestas de G?
  5. Escreva uma função GRAPHconnected que decida se um grafo é ou não conexo. (A função deve devolver 1 para indicar resposta afirmativa e 0 para indicar resposta negativa.)  O que mais a função poderia devolver, além de 10, para provar que sua resposta está correta?
  6. A função GRAPHconnected do exercício anterior é capaz de determinar se um digrafo é fracamente conexo?  Por que?

Componentes de grafos

Uma componente de um grafo G é um tipo especial de subgrafo de G.  Mais especificamente, é um tipo especial de subgrafo induzido de G.   Nossa definição de componente começa com o conceito de conjunto fechado:  um conjunto X de vértices de um grafo G é fechado se

  1. X não é vazio,
  2. o subgrafo induzido por X é conexo,  e
  3. o leque de X é vazio.

[A expressão conjunto fechado não é padrão. É apenas meu recurso didático para definir componentes.]   Uma componente (= component) de um grafo é o subgrafo induzido por qualquer subconjunto fechado do seu conjunto de vértices.   É claro que qualquer componente de um grafo é um grafo conexo.  (A expressão "componente conexa" é às vezes usada no lugar de "componente".)

O conjunto de vértices de todo grafo admite uma única partição  X1, X2, …, Xk  em que cada Xi é um conjunto fechado.  O subgrafo induzido por cada Xi é uma componente.  Assim, todo grafo tem uma coleção bem definida de componentes.   É claro que um grafo é conexo se e somente se tem uma só componente.

components of undirected graph
[Copiado das transparências de Sedgewick de 2006.]

Exercícios 2

  1. Mostre que duas componentes diferentes de um grafo não têm vértices em comum.
  2. [Sedgewick 17.4, p.15]  Determine o número de componentes do grafo definido pelo conjunto de arestas a seguir.
         3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4
    

Cálculo das componentes de grafos

A função abaixo calcula o número de componentes de um grafo. Ela usa uma busca em profundidade (com cc no papel de pre) para fazer o serviço.

Além de contar o número de componentes, a função atribui 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.

#define maxV 1000
static int cc[maxV];

/* A função abaixo devolve o número de componentes do grafo G. Além disso, armazena no vetor cc o número da componente a que o vértice pertence: se o vértice v pertence à k-ésima componente então cc[v] == k-1. (O código foi copiado do programa 18.4, p.100, de Sedgewick.) */

int GRAPHcc( Graph G) { 
   Vertex v; int id = 0;
   for (v = 0; v < G->V; v++) 
      cc[v] = -1;
   for (v = 0; v < G->V; v++)
      if (cc[v] == -1) 
         dfsRcc( G, v, id++);
   return id;
}

/* A função dfsRcc marca com id todos os vértices que estão na mesma componente conexa que v. (Ou seja, faz cc[w] = id para todo w que esteja na mesma componente que v.)  A função supõe que o grafo é representado por listas de adjacência. */

void dfsRcc( Graph G, Vertex v, int id) { 
   link p; 
   cc[v] = id;
   for (p = G->adj[v]; p != NULL; p = p->next)
      if (cc[p->w] == -1) 
         dfsRcc( G, p->w, id); 
}

Depois de executar GRAPHcc uma só vez, é possível saber, em tempo constante, se dois vértices, digamos s e t, estão na mesma componente:

    int GRAPHconnect( Graph G, Vertex s, Vertex t) { 
       return (cc[s] == cc[t]); 
    }

Exercícios 3

  1. [Sedgewick 18.30, p.106]  Prove que todo grafo 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. 
  2. [Sedgewick 18.31, p.106]  Prove que todo grafo com dois ou mais vértices tem pelo menos dois vértices cuja remoção não aumenta o número de componentes.
  3. [Sedgewick p.16]  Seja G um grafo com V vértices e E arestas.  Quantas componentes tem G no máximo?  Quantas componentes tem G no mínimo? 
  4. [Sedgewick p.135]  [Grafos aleatórios conexos]  Faça experimentos para determinar quantas arestas um grafo aleatório com V vértices precisa ter para ser conexo.  (Faça experimentos com V valendo 100, 1000, 10000.)  Use a função GRAPHrand1 para gerar os grafos.   Faça experimentos para determinar quantas arestas um grafo aleatório com V vértices precisa ter para que uma de suas componentes seja "gigante" (contenha, digamos, mais que 95% dos vértices).  [Demonstra-se que quando o número de arestas passa de ½ V ln V (ou seja, pouco mais que V multiplicado pelo número de dígitos de V), o grafo consiste, com grande probabilidade, em uma componente "gigante" e alguns vértices isolados.]
  5. [Evolução das componentes de grafos aleatórios]  Escreva um programa para fazer uma série de experimentos.  Cada experimento consiste em gerar um grafo aleatório com V vértices e E arestas e determinar o número de componentes, nc,  e o tamanho tm de uma componente máxima. Para cada valor de V no conjunto {100, 1000, 10000}, faça os experimentos com E valendo 0.1V, 0.2V, 0.5V, 1, 2V, 5V e 10V.  Repita cada experimento 10 vezes (variando a semente do gerador de números aleatórios) e tire a média dos valores de nc e a média dos valores de tm. [Demonstra-se que quando o número de arestas passa de ½ V ln V (ou seja, pouco mais que V multiplicado pelo número de dígitos de V), o grafo consiste, com grande probabilidade, em uma componente "gigante" e alguns vértices isolados.]
  6. [Sedgewick-Wayne E.4.1.42, p.564]  [Grafos euclidianos aleatórios conexos]  Escreva uma função que gere grafos 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 a raiz quadrada de (lg V)/(πV) e quase certamente desconexo se d for menor que esse número.
  7. A função GRAPHcc é capaz de determinar se um digrafo é fracamente conexo?

Validated by HTML Validator (based on Tidy) Valid HTML 4.01 Strict Valid CSS!