Algoritmos para Grafos, via Sedgewick  |  Índice remissivo

Existe caminho?

Como se faz para decidir, algoritmicamente, se é possível ir de um certo vértice a um outro de um digrafo viajando pelos arcos?  Antes de responder essa pergunta, é preciso esclarecer o conceito de caminho. chick[A definição de caminho no livro de Sedgewick, páginas 10 a 11, é um tanto imprecisa.  Esta página dá uma definição melhor.]

Caminhos

Um  caminho  (= path)  num digrafo é uma sequência de vértices dotada da seguinte propriedade:  se v e w são vértices consecutivos na sequência então v-w é um arco.   Em geral, os vértices de um caminho não são todos distintos.

Dizemos que um arco v-w pertence a um caminho se o vértice w é o sucessor de v no caminho.   Todos os arcos de um caminho apontam no mesmo sentido, de um vértice para o seu sucessor.  Há quem goste de enfatizar esse fato dizendo caminho dirigido em vez de caminho.

A origem de um caminho é o primeiro vértice do caminho. O término é o último vértice.  Diz-se que um caminho com origem s e término t vai de s a t.

comprimento  (= length)  de um caminho é o número de termos da sequência menos um.  Em outras palavras, o comprimento é o número de arcos do caminho.

Em grafos, a existência de caminhos é uma propriedade simétrica:  para quaisquer dois vértices s e t, existe caminho de s a t se e somente se existe caminho de ts.

Exemplo A

Considere, por exemplo, o digrafo definido pelos arcos   0-1 0-5 1-0 1-5 2-4 3-1 5-3 .   Eis alguns caminhos nesse digrafo:

     3-1-0-1-0-5
     3-1-0-5
     2-4
     5

O primeiro desses caminhos tem comprimento 5 e o último tem comprimento 0.  Eis algumas sequências que não são caminhos:

     1-5-0
     4-2
     2-5

Exercícios 1

  1. Escreva uma função que verifique se uma dada sequência  seq[0..k]  de vértices de um digrafo é um caminho.  Faça duas versões da função: uma supõe que o digrafo é dado por sua matriz de adjacência e outra supõe que o digrafo é dado por listas de adjacência.
find path in digraph between two vertices
[Copied from 'Algorithms',
4th.ed., by Sedgewick and Wayne.]

Procurando um caminho num digrafo

Considere o problema básico:  Dados vértices s e t de um digrafo G, decidir se existe ou não existe um caminho de st em G.

A função DIGRAPHpath abaixo resolve o problema usando o método da busca em profundidade, um poderoso método de exploração de digrafos que discutiremos com mais cuidado em outra ocasião.

#define maxV 10000

/* Vamos supor que nossos digrafos têm no máximo maxV vértices. */

static int lbl[maxV];

/* A função abaixo recebe vértices s e t de um digrafo G representado por sua matriz de adjacência e devolve 1 se existe um caminho de st ou devolve 0 em caso contrário. */

int DIGRAPHpath( Digraph G, Vertex s, Vertex t) { 
   Vertex v;
   for (v = 0; v < G->V; v++)
      lbl[v] = 0;
   pathR( G, s);
   if (lbl[t] == 0) return 0;
   else return 1;
}

/* A função pathR visita todos os vértices que podem ser atingidos a partir de v sem passar por vértices cujo rótulo lbl é 0. */

void pathR( Digraph G, Vertex v) { 
   Vertex w;
   lbl[v] = 1;
   for (w = 0; w < G->V; w++)
      if (G->adj[v][w] == 1 && lbl[w] == 0)
         pathR( G, w);
}

A função DIGRAPHpath é apenas uma interface de comunicação com o usuário.  A busca propriamente dita é realizada pela função recursiva pathR.

Durante a execução da função, cada vértice recebe um rótulo (= label). Os rótulos são armazenados num vetor  lbl[0..V-1]  indexado pelos vértices.  O rótulo de um vértice é 0 se ele ainda não foi visitado e 1 em caso contrário.  Dizemos que um arco v-w é visitado quando a função pathR se depara com w ao examinar os vizinhos de v.

Eis uma maneira ligeiramente diferente de organizar o código de DIGRAPHpath. Essa versão para tão logo encontra um caminho de st.

/* (Esse código é uma adaptação do programa 17.11, p.52, de Sedgewick.) */

int DIGRAPHpath( Digraph G, Vertex s, Vertex t) { 
   Vertex v;
   for (v = 0; v < G->V; v++)
      lbl[v] = 0;
   return pathR( G, s, t);
}

int pathR( Digraph G, Vertex v, Vertex t) { 
   Vertex w;
   if (v == t) return 1;
   lbl[v] = 1;
   for (w = 0; w < G->V; w++)
      if (G->adj[v][w] == 1 && lbl[w] == 0)
         if (pathR( G, w, t) == 1)
            return 1;
   return 0;
}
find path between two vertices in undirected grid
[Copied from Sedgewick 2006 lecture slides.]  

Poderíamos escrever uma versão especial de DIGRAPHpath para grafos.  Mas o código da nova função seria idêntico ao de DIGRAPHpath.

 

Exemplo B

Considere o digrafo G com vértices  0 1 2 3 4 5  descrito a seguir.  À esquerda temos a lista de arcos do digrafo e à direita a matriz de adjacência do digrafo (com "-" no lugar de "0").

                                                     - - 1 1 1 -
                                                     - - - - - -
     0-2 0-3 0-4 2-1 2-4 3-4 3-5 4-1 4-5 5-1         - 1 - - 1 -
                                                     - - - - 1 1
                                                     - 1 - - - 1
                                                     - 1 - - - -

Para verificar a existência de um caminho do vértice 0 ao vértice 1, invocamos a função DIGRAPHpath com argumentos (G,0,1).  Segue o rastreamento da execução da função.  Cada linha da figura abaixo registra o momento em que um arco é visitado; registra também cada invocação de pathR. A execução de cada nova instância de pathR é indicada por uma indentação apropriada da linha.

                                    0 1 2 3 4 5
                                               
     pathR(G,0)                     1 - - - - -
        0-2 pathR(G,2)              1 - 1 - - -
          2-1 pathR(G,1)            1 1 1 - - -
          2-4 pathR(G,4)            1 1 1 - 1 -
            4-1                                
            4-5 pathR(G,5)          1 1 1 - 1 1
              5-1                              
        0-3 pathR(G,3)              1 1 1 1 1 1
          3-4                                  
          3-5                                  
        0-4                                    

Na coluna direita da figura, cada linha exibe o estado do vetor lbl (com "-" no lugar de "0") imediatamente depois da execução de "lbl[v] = 1" na correspondente invocação de pathR.

O estado final de lbl permite concluir que existe caminho de 01 no digrafo G.  (Na verdade, existe caminho de 0 a qualquer um dos demais vértices de G.)

Exemplo C

Considere novamente o digrafo G do exemplo anterior.  Desta vez, queremos decidir a existência de um caminho de 23.  Segue o rastreamento da execução de DIGRAPHpath com argumentos (G,2,3).

                                  0 1 2 3 4 5
                                             
     pathR(G,2)                   - - 1 - - -
        2-1 pathR(G,1)            - 1 1 - - -
        2-4 pathR(G,4)            - 1 1 - 1 -
          4-1                                
          4-5 pathR(G,5)          - 1 1 - 1 1
            5-1                              

O estado final de lbl permite concluir que não existe caminho de 23.  (Também não existe caminho de 20.)

DFS maze exploration
[Copied from Sedgewick 2006 lecture slides.]  

Exemplo D

Procurando a saída de um labirinto.  [Copiado do slide 22 de um conjunto de slides que Sedgewick usou na Universidade de Princeton em 2006.]

Exercícios 2

  1. As duas versões de DIGRAPHpath funcionam corretamente quando s é igual a t?  E quando G->A vale 0?  E quando G->V vale 1?
  2. Seja G o grafo definido pelo conjunto de arestas a seguir:
         0-6 0-1 0-2 0-5 4-3 5-3 5-4 6-4 7-8 9-12 9-10 9-11 11-12
    

    Faça o rastreamento de DIGRAPHpath(G,2,6).

  3. Escreva uma boa documentação para a segunda versão da função recursiva pathR (ou seja, diga o que a função faz).  [Solução]
  4. [Sedgewick 17.89, p.62]  Re-escreva DIGRAPHpath e pathR supondo que o digrafo está representado por um vetor de listas de adjacência.

Caminhos simples

Um caminho é  simples  (= simple path)  se não tem vértices repetidos.   Não é difícil verificar que a existência de um caminho implica na existência de um caminho simples com mesma origem e mesmo término.

No grafo do exemplo acima, o caminho  1-0-1-5  não é simples.  Já os três caminhos a seguir são simples:

     3-1-0-5
     1
     2-4

Um caminho é  fechado  (= closedcyclic path)  se tem dois ou mais vértices e o primeiro vértice coincide com o último.  É claro que caminhos fechados não são simples.

Exercícios 3

  1. Mostre que existe um caminho simples com origem s e término t se e somente se existe um caminho (não necessariamente simples) com origem s e término t.
  2. [Sedgewick 17.4, p.15]  Faça uma lista de todos os caminhos simples com exatamente 4 vértices no grafo definido pelas arestas abaixo.
         3-7 1-4 7-8 0-5 5-2 3-8 2-9 0-6 4-9 2-6 6-4
    
  3. [Sedgewick 17.88, p.62]  Modifique DIGRAPHpath de modo que DIGRAPHpath(G,s,t,d) verifique a existência de um caminho simples de s a t que tenha comprimento pelo menos d.
  4. Escreva uma função que verifique se uma sequência seq[0..k] de vértices de um digrafo é um caminho simples. A função deve devolver  -1  se a sequência não é um caminho,  0  se a sequência é um caminho simples,  1  se a sequência é um caminho não simples.  Faça duas versões da função: uma supõe que o digrafo é dado por sua matriz de adjacência e outra supõe que o digrafo é dado por listas de adjacência.

Valid HTML 4.01 Strict Valid CSS!