Olá, caro Deviante! Tudo bem por aí?

No artigo anterior, utilizamos a Busca por Largura (BFS) para percorrer as partidas das quartas de final da Copa do Mundo de 2022. Como vimos, a BFS explora todos os vértices de um mesmo nível antes de avançar para o próximo.

Há outra estratégia popular de busca em grafos: a Busca por Profundidade (Depth-First Search – DFS). Em vez de explorar vários caminhos simultaneamente, ela prefere “mergulhar” em um único ramo da árvore, avançando o máximo possível antes de voltar para analisar outras possibilidades.

Para entender essa diferença, vamos imaginar um cenário alternativo:

E se o Brasil tivesse vencido a Croácia nas quartas de final da Copa de 2022?

Curioso?  Veja abaixo como essa relação Grafos/Copa do Mundo pode ser interessante de acompanhar.

Introdução

A DFS é uma excelente ferramenta para explorar cenários hipotéticos. Ela permite a navegação por diferentes possibilidades, voltando atrás quando encontra um caminho concluído.

Nosso grafo dessa vez será uma árvore de decisões:

Cenario hipotetico

Grafo exibindo os cenários onde o Brasil venceria a Croácia. Em um cenário o Brasil vence a Argentina e chega na Final com a França, no outro a Argentina vence e chega até a Final

Cada vértice representa um estado possível do torneio.

As Propriedades dos Vértices

Semelhante ao que vimos na implementação clássica da BFS, cada vértice possui algumas propriedades importantes:

  • Cor: indica o estado atual do vértice.
  • Tempo de descoberta (d): indica quando a DFS encontra o vértice pela primeira vez
  • Tempo de finalização (f): indica quando a DFS termina de explorar todos os caminhos descendentes desse vértice
  • Vértice-pai: identifica qual vértice levou à descoberta do atual

Inicialmente as propriedades se iniciam no modo padrão:

  • Cor: branca
  • d: indefinido
  • f: indefinido
  • Vértice-pai: nulo

As Cores da DFS

Durante a execução utilizaremos:

  • Branco: não visitado.
  • Cinza: descoberto. Ainda em processamento.
  • Azul: completamente finalizado (na literatura tradicional o valor é o preto)

A Principal Diferença para a BFS

Há uma sutil diferença na forma como BFS e a DFS armazenam o histórico das operações. A BFS utiliza a estrutura de Dados Fila, onde a lógica diz que o primeiro que entra é o primeiro que sai (FIFO: first in, first out). 

Já a DFS utiliza a estrutura de dados Pilha, na qual o último que entra é o primeiro que sai (LIFO: last in, last out).

Iniciando a Busca

Basicamente, a execução começa no vértice Brasil vence Croácia, que recebe a cor cinza e é colocado na pilha.

A partir daí, a DFS procura o primeiro caminho disponível.

Suponha que ela escolheu o vértice Brasil vence Argentina. Esse novo vértice também é empilhado, e em seguida o vértice Brasil vence França é inserido no topo da pilha.

Inicio de execução DFS

Início de execução DFS

Neste momento não existem mais caminhos para explorar.

O Primeiro Caminho Encontrado

Nesse cenário, o mundo seria abençoado com uma combinação épica (Brasil campeão, depois de eliminar Argentinos e Franceses).

Entretanto, a DFS não terminou sua execução. Ainda existe um outro caminho pra explorar.

O Backtracking

Ao perceber que chegou ao final de um ramo, a DFS começa a voltar. Esse processo é conhecido como backtracking.

Implementação completa DFS

Implementação completa DFS. O Algoritmo vai desempilhando os vertices até chegar no inicial e explorar o caminho da direita, no cenário onde a Argentina vence o Brasil

Primeiro ela retorna para o vértice Brasil vence Argentina e depois vai para Brasil vence Croácia.

A partir daí, procura caminhos ainda não explorados. No nosso caso, o cenário restante é o de:

  • Argentina vence Brasil
  • Argentina vence França

Agora temos um cenário completamente diferente, com a Argentina sendo a  campeã mundial.

Observe que o algoritmo não precisou reiniciar a busca. Ele simplesmente voltou para o último ponto de decisão e seguiu por outro caminho.

Tempos de Descoberta e Finalização

A DFS registrou dois momentos importantes para cada vértice:

  • Tempo de descoberta (d): o instante em que o vértice foi visitado pela primeira vez — quando recebe a cor cinza e entra na pilha.
  • Tempo de finalização (f): o instante em que todos os seus descendentes já foram completamente explorados — quando recebe a cor azul e sai da pilha.

No nosso exemplo, a execução completa produziu os seguintes registros:

Vértice d f
Brasil vence Croácia 1 6
Brasil vence Argentina 2 5
Brasil vence França 3 4
Argentina vence Brasil 7 10
Argentina vence França 8 9

Observe um padrão importante: o tempo de finalização de um vértice é sempre maior do que o de todos os seus descendentes. O vértice Brasil vence Croácia, só foi finalizado (f=6) quando a subárvore abaixo dele foi completamente explorada.

Há vários aspectos que podemos explorar futuramente baseando-nos nessas informações. Entre eles, a detecção de ciclos, ordenação topológica e análise de dependências.

Conclusão

Sabe aquela situação no atendimento eletrônico do banco em que você chega à opção “retornar ao menu inicial”?  Esse seria um ótimo exemplo para explicar a DFS, o algoritmo também volta para pontos anteriores quando precisa explorar novos caminhos.

Vamos combinar que imaginar cenários alternativos para uma Copa do Mundo é bem mais divertido. Daí resolvemos partir da hipótese de que o Brasil venceu a Croácia.

Para simplificar a análise, desconsiderei alguns resultados possíveis, como o cenário em que a França vence a final. Essa possibilidade geraria novos ramos e aumentaria significativamente o número de execuções da DFS.

Espero muito que os exemplos acima tenham ajudado a clarear o algoritmo de Busca por Profundidade pra você. Eu acredito que, em especial, a ideia de BackTracking ficou bem mais simples de entender, em comparação aos exemplos com os quais tive contato na primeira vez que estudei esse tema.

Muito obrigado pela leitura e um grande abraço.