Depth First Search (DFS)
Aula relacionada recomendada
A busca em profundidade (DFS) é uma técnica para percorrer grafos . Ela começa em um vértice inicial e segue para um novo vértice adjacente que ainda não foi visitado. Quando não há mais vértices adjacentes a serem visitados, o algoritmo volta para o vértice anterior e tenta explorar outros caminhos.
Implementação
O algoritmo pode ser implementado utilizando uma estrutura de pilha, em que o topo da pilha representa o vértice atual. Assim, iteramos sobre a vizinhança dele e adicionamos à pilha um vértice adjacente ainda não visitado.
Também podemos implementar o DFS de forma recursiva, simplificando o código
dfs_recursivo.cpp | |
---|---|
Aplicações
Contar componentes
Quantidade de componentes
A busca em profundidade é capaz de contar o número de componentes conexas de um grafo. Basta iterar sobre todos os vértices do grafo e, para cada vértice ainda não visitado, executa-se o algoritmo para marcar os vértices em sua componente como visitados, somando um à quantidade de componentes.
components.cpp | |
---|---|
Tamanho de uma componente
Pode-se adaptar o algoritmo para retornar quantos vértices fazem parte de uma componente do grafo.
components_size.cpp | |
---|---|
Bipartição
Para descobrir se um grafo é bipartido e, caso for, achar sua bipartição, podemos rodar um DFS que carrega além do vértice atual, a cor desse vértice. Assim, para cada chamada da função nós podemos inverter a cor. Caso um vértice tenha mesma cor de um vértice adjacente, o grafo não é bipartido.
A bipartição do grafo ficará guardada no vetor cor
, em que cor[u]
guarda se o vértice \(u\) pertence à cor 0
ou 1
.
Para facilitar a implementação, pode-se utilizar o operador \(xor\) ^
para inverter a cor entre 0
e 1
, e o operador \(and\) &
para garantir que, caso apenas uma tentativa retorne false
, todas as demais também serão falsas.
biparticao.cpp | |
---|---|
Caminho entre dois vértices
Podemos adicionar ao algoritmo um vetor pai
, em que pai[u]
guarda o vértice que chamou o DFS para o vértice u
. Assim, executa-se o DFS a partir do vértice v
. Depois basta começar no vértice u
e ir voltando utilizando o vetor de pais até chegar no vértice v
, guardando todo vértice intermediário na resposta.
path.cpp | |
---|---|
Ciclos
Grafos Não direcionados
Para identificar se um grafo não direcionado possui um ciclo, algum vértice é adjacente à um vértice que já foi visitado anteriormente, que não seja o seu pai.
Para facilitar a implementação, utilizaremos o operador \(or\) |
, para que caso alguma instância do DFS retorne true
, as demais também irão.
undirected_cycle.cpp | |
---|---|
Grafos Direcionados
Achar um ciclo em um grafo direcionado é mais complexo, pois podemos chegar em um vértice que já foi visitado mas não faz parte de um ciclo.
Por exemplo, no grafo abaixo, uma possível ordem de visita do DFS é \(A, B, C\). Quando estamos no vértice \(C\), o vértice \(B\) já teria sido visitado, porém ele não faz parte de um ciclo.

Para resolver esse problema, iremos utilizar a seguinte coloração:
Desse modo, um ciclo existe se há uma aresta ligando dois vértices de cor \(1\), ou seja, o DFS de dois vértices adjacentes ainda não terminou.
directed_cycle.cpp | |
---|---|
Recuperando um ciclo
Para recuperar um ciclo, basta verificar a existência de ciclos no grafo e, caso existir, armazenar dois vértices \(u\) e \(v\) que fazem parte de um ciclo e recuperar o caminho de \(u\) até \(v\). O ciclo será o mesmo caminho, porém com uma aresta adicional, voltando para \(u\).