Busca em grafos: BFS e DFS
Em 1736, Leonhard Euler mostrou que nenhum passeio atravessa cada uma das sete pontes de Königsberg exatamente uma vez, e a teoria dos grafos começou com essa resposta.
Pontos e linhas
Um grafo é um conjunto de pontos, chamados vértices ou nós, ligados por linhas, chamadas arestas. A ideia é simples e serve para uma variedade enorme de coisas: cidades ligadas por estradas, páginas web ligadas por links, pessoas ligadas por amizades, quadrados de um labirinto ligados onde não há parede. Depois que um problema é desenhado como grafo, o mesmo punhado de algoritmos responde a perguntas como "dá para ir daqui até lá?" e "qual é o caminho mais curto?"
Guardando um grafo no código
Um jeito comum de guardar um grafo é a lista de adjacência: para cada vértice, uma lista dos seus vizinhos. As notas do curso 6.006 do MIT usam exatamente isso, um array ou dicionário Adj em que Adj[u] lista os vértices ligados a u. Para o grafo acima, fica assim:
const graph = { // lista de adjacência
A: ["B", "C"],
B: ["A", "D"],
C: ["A", "D", "E"],
D: ["B", "C", "F"],
E: ["C", "F"],
F: ["D", "E"],
};Busca em largura: anel por anel
A busca em largura (BFS) explora um grafo em anéis em volta do início. Primeiro o próprio início, à distância 0. Depois todos os vizinhos dele, à distância 1. Depois todo vértice novo ao lado desses, à distância 2, e assim por diante. Para isso ela mantém uma fila, em que o primeiro a entrar é o primeiro a sair. Pegue o vértice da frente da fila, visite-o e acrescente os vizinhos ainda não vistos no fim. A partir de A, no exemplo, a BFS visita A, depois B e C, depois D e E, depois F. Como ela avança para fora um anel por vez, a primeira vez que a BFS chega a um vértice ela encontrou um caminho com o menor número de arestas. F está a 3 arestas de A, por exemplo A, B, D, F.
function bfs(graph, start) {
const seen = new Set([start]);
const queue = [start];
const order = [];
while (queue.length) {
const u = queue.shift(); // primeiro a entrar, primeiro a sair
order.push(u);
for (const v of graph[u]) {
if (!seen.has(v)) { seen.add(v); queue.push(v); }
}
}
return order; // A B C D E F
}Busca em profundidade: até onde der, depois volte
A busca em profundidade (DFS) se comporta como alguém num labirinto que mantém uma mão na parede. Ela segue um caminho o mais fundo possível. Quando chega a um beco sem saída ou a um lugar já visto, ela volta até o último ponto de escolha e tenta a próxima opção. Ela usa uma pilha em vez de uma fila, último a entrar, primeiro a sair, e é muito natural de escrever com recursão, em que a pilha de chamadas faz o trabalho de lembrar. A partir de A, uma DFS recursiva visita A, B, D, C, E, F. A DFS chega a todo vértice a que a BFS chega, mas o caminho que ela segue não é necessariamente o mais curto.
function dfs(graph, u, seen = new Set(), order = []) {
seen.add(u);
order.push(u);
for (const v of graph[u]) if (!seen.has(v)) dfs(graph, v, seen, order);
return order; // ordem: A B D C E F
}Arraste para girar. Role ou faça pinça para dar zoom.
Qual usar
No primeiro labirinto do laboratório, a BFS visita 105 quadrados e acha um caminho de 22 movimentos, o mais curto possível. A DFS por acaso mergulha na direção do objetivo e visita só 31 quadrados, mas o caminho dela leva 28 movimentos. Nenhuma é sempre mais barata. As duas tratam cada vértice e cada aresta um número fixo de vezes, então as duas rodam em tempo O(V + E) para V vértices e E arestas. Escolha a BFS quando precisar do menor número de passos: o menor número de movimentos num quebra-cabeça, o menor número de ligações entre duas pessoas. Escolha a DFS quando precisar explorar tudo ou seguir escolhas até o fim: conferir se todas as salas de um labirinto se ligam, ou testar as jogadas de um quebra-cabeça uma linha de cada vez e voltar quando travar. Quando as arestas têm comprimentos diferentes, como as estradas, nenhuma das duas basta sozinha; métodos como o algoritmo de Dijkstra estendem a ideia da BFS com distâncias.
| grafo | Vértices ligados por arestas |
| vértice (nó) | Um ponto de um grafo |
| aresta | Uma linha que liga dois vértices |
| lista de adjacência | Para cada vértice, a lista dos seus vizinhos |
| fila | Primeiro a entrar, primeiro a sair; usada pela BFS |
| BFS | Busca em largura: anel por anel; acha caminhos com menos arestas |
| DFS | Busca em profundidade: fundo primeiro, depois volta; usa uma pilha |
| O(V + E) | O tempo de execução da BFS e da DFS |
- No grafo de exemplo, quantas arestas tem o caminho mais curto de A até F?
- Quantas arestas tem o grafo de exemplo?
- Qual estrutura de dados a busca em largura usa?
- Uma fila (primeiro a entrar, primeiro a sair)
- Uma pilha (último a entrar, primeiro a sair)
- Uma lista ordenada
- Nenhuma estrutura
- Qual busca garante achar um caminho com o menor número de arestas?
- BFS
- DFS
- As duas, sempre
- Nenhuma
- Ponha os vértices na ordem em que a BFS a partir de A os visita (vizinhos em ordem alfabética).
- Ponha os vértices na ordem em que a DFS recursiva a partir de A os visita (vizinhos em ordem alfabética).
- Na grade de 15 por 9 do laboratório, sem nenhuma parede, quantos movimentos tem o caminho mais curto de S (canto superior esquerdo) até G (canto inferior direito)?
- Por que a BFS e a DFS precisam lembrar quais vértices já viram?
- Senão um ciclo no grafo faria a busca dar voltas para sempre
- Para mostrá-los no fim
- Para ordená-los
- Só é necessário na DFS
- Em que ano Euler resolveu o problema das pontes de Königsberg?
- 1683
- 1736
- 1843
- 1945
- Qual é o tempo de execução da BFS num grafo com V vértices e E arestas?
- O(V + E)
- O(V × E)
- O(log V)
- O(2ⱽ)
- Você quer saber o menor número de ligações do tipo amigo de amigo entre duas pessoas numa rede social. Qual busca?
- BFS
- DFS
- Busca binária
- Merge sort
- Um grafo é um conjunto de vértices ligados por arestas; listas de adjacência o guardam.
- A BFS usa uma fila, vai anel por anel e acha caminhos com menos arestas.
- A DFS usa uma pilha (ou recursão), vai fundo primeiro e depois volta.
- As duas rodam em O(V + E) e precisam lembrar os vértices vistos.
- A solução de Euler de 1736 para as pontes de Königsberg deu início à teoria dos grafos.
Fontes deste curso (11)
- MIT OpenCourseWare 6.006 Introduction to Algorithms, Spring 2020 (Demaine, Ku, Solomon)
- lecture notes 1 Introduction, 3 Sorting, 9 Breadth-First Search, 10 Depth-First Search (ocw.mit.edu/courses/6-006-introduction-to-algorithms-spring-2020/pages/lecture-notes) and Fall 2011 lecture 13 BFS notes (ocw.mit.edu/courses/6-006-introduction-to-algorithms-fall-2011)
- Cormen, Leiserson, Rivest, Stein, Introduction to Algorithms (MIT Press)
- with the CLRS-based Khan Academy algorithms unit by Thomas Cormen and Devin Balkcom of Dartmouth (khanacademy.org/computing/computer-science/algorithms: binary search, selection sort analysis, merge sort, quicksort overview and analysis)
- MacTutor History of Mathematics, Al-Khwarizmi (mathshistory.st-andrews.ac.uk/Biographies/Al-Khwarizmi: word algorithm from the Latin title Algoritmi de numero Indorum)
- D. E. Joyce, Euclid's Elements Book VII Propositions 1 and 2 (mathcs.clarku.edu/~djoyce/elements/bookVII)
- Wolfram MathWorld, Merge Sort (mathworld.wolfram.com/MergeSort.html: proposed by John von Neumann in 1945, after Knuth 1998 p. 159)
- Stanford CS "Tony Hoare: Quicksort" (cs.stanford.edu/people/eroberts/courses/soco/projects/2008-09/tony-hoare/quicksort.html: devised while a student at Moscow State University for Russian-English machine translation with a tape dictionary, programmed in Algol 60 and published 1961; the year 1959 from the Wikipedia Quicksort article and its cited sources)
- Bodleian Libraries, Ada Lovelace (blogs.bodleian.ox.ac.uk/adalovelace: 1843 paper, Note G Bernoulli numbers)
- Fourmilab text of Menabrea's Sketch with Lovelace's notes (fourmilab.ch/babbage/sketch.html)
- Seven Bridges of Konigsberg (Euler 1736) and Tower of Hanoi (Edouard Lucas, 1883) checked against the Wikipedia articles and their cited sources.
Data de verificação: 2026-10-17