devly
Voltar para Algoritmos

Desvendando Conexões: Grafos

Aprenda a representar grafos e domine os algoritmos de travessia fundamentais: Busca em Largura (BFS) e Busca em Profundidade (DFS).

Atualizado em: 16 de Junho, 2025
Notas do Autor

Árvores são elegantes, mas o mundo real é mais bagunçado. Redes sociais, rotas de voos, o mapa de uma cidade, a própria internet... tudo isso são Grafos. Meu primeiro problema de grafo em uma entrevista pareceu um salto gigantesco de complexidade em relação a árvores.

Mas a verdade é que, uma vez que você entende como representar um grafo e domina as duas formas principais de "caminhar" por ele, a maioria dos problemas se torna muito mais gerenciável.

Tudo se resume a duas ideias: explorar "fundo" primeiro (DFS) ou explorar "largo" primeiro (BFS). Esta aula é sobre te dar o mapa e a bússola para navegar em qualquer problema de grafo que aparecer na sua frente.


1. O que são Grafos? (Árvores sem Regras)

Um Grafo é uma estrutura de dados que consiste em um conjunto de vértices (ou nós) e um conjunto de arestas que conectam pares de vértices. Pense neles como uma generalização de árvores: uma árvore é um tipo de grafo, mas um grafo pode ter ciclos, múltiplos caminhos entre nós e até ser desconectado.

Vertex (Vértice/Nó): Representa uma entidade (ex: uma pessoa, uma cidade).
Edge (Aresta): Representa a conexão entre dois vértices (ex: uma amizade, uma estrada).
Undirected (Não-direcionado): A aresta é uma via de mão dupla (A -- B).
Directed (Direcionado): A aresta é uma via de mão única (A -> B).
Unweighted (Não-ponderado): Todas as arestas têm o mesmo "custo".
Weighted (Ponderado): Cada aresta tem um peso ou custo associado (ex: distância entre cidades).

2. Representando Grafos no Código

Antes de atravessar um grafo, precisamos construí-lo. As duas formas mais comuns são a Lista de Adjacência e a Matriz de Adjacência.

Lista de Adjacência (A Mais Comum)

É a forma mais usada em entrevistas. Basicamente, é um mapa (ou objeto) onde cada chave é um vértice, e o valor é um array de seus vizinhos.

javascript
// Grafo: A -- B -- C
//         |    |
//         D -- E

const graph = {
  'A': ['B', 'D'],
  'B': ['A', 'C', 'E'],
  'C': ['B'],
  'D': ['A', 'E'],
  'E': ['B', 'D']
};

// Usando um Map em JS (mais flexível para chaves não-string)
const graphWithMap = new Map();
graphWithMap.set('A', ['B', 'D']);
// etc.

Matriz de Adjacência

É uma matriz NxN (onde N é o número de vértices). Se matrix[i][j] for 1, existe uma aresta entre os vértices i e j. É boa para grafos densos, mas gasta muita memória para grafos com poucas conexões (esparsos).

Qual Usar?

Na maioria dos cenários de entrevista, a Lista de Adjacência é a melhor escolha. É mais eficiente em termos de espaço para a maioria dos grafos e torna mais fácil iterar sobre os vizinhos de um nó.

3. Atravessando o Grafo: DFS e BFS

"Atravessar" (ou percorrer) um grafo significa visitar todos os seus vértices de forma sistemática. Os dois algoritmos clássicos para isso são a Busca em Profundidade (DFS) e a Busca em Largura (BFS).

DFS (Busca em Profundidade): O Explorador de Labirintos 🧭

O DFS explora o mais fundo possível por um caminho antes de voltar (backtrack). É como entrar em um labirinto e seguir sempre a primeira parede à sua direita até bater em um beco sem saída, para então voltar e tentar o próximo caminho.

Pode ser implementado elegantemente com recursão (que usa a call stack implicitamente) ou iterativamente com uma Pilha (Stack).

javascript
function dfsRecursive(graph, startNode, visited = new Set()) {
  // 1. Marca o nó atual como visitado para não entrar em loop infinito.
  visited.add(startNode);
  console.log(startNode); // Processa o nó

  // 2. Para cada vizinho do nó atual...
  const neighbors = graph[startNode] || [];
  for (const neighbor of neighbors) {
    // 3. ...se o vizinho ainda não foi visitado, chama a recursão para ele.
    if (!visited.has(neighbor)) {
      dfsRecursive(graph, neighbor, visited);
    }
  }
}

// Chamada inicial:
// dfsRecursive(graph, 'A');

DFS Iterativo (com Pilha)

A versão iterativa faz a mesma coisa, mas troca a "mágica" da call stack da recursão por uma Pilha (Stack) explícita. O resultado é o mesmo.

javascript
function dfsIterative(graph, startNode) {
  const stack = [startNode]; // 1. Começa com uma Pilha contendo o nó inicial.
  const visited = new Set();
  visited.add(startNode);

  while (stack.length > 0) {
    // 2. Pega o último nó da pilha (LIFO).
    const currentNode = stack.pop();
    console.log(currentNode); // Processa o nó

    // 3. Adiciona os vizinhos não visitados à pilha.
    const neighbors = graph[currentNode] || [];
    for (const neighbor of neighbors) {
      if (!visited.has(neighbor)) {
        visited.add(neighbor);
        stack.push(neighbor);
      }
    }
  }
}

BFS (Busca em Largura): A Onda de Propagação 🌊

O BFS explora todos os vizinhos de um nó antes de passar para os vizinhos dos vizinhos. É como uma onda se espalhando a partir de um ponto, visitando tudo em um nível antes de ir para o próximo.

É implementado com uma Fila (Queue).

javascript
function bfs(graph, startNode) {
  const queue = [startNode]; // 1. Começa com uma Fila contendo o nó inicial.
  const visited = new Set();   // 2. Um conjunto para rastrear nós já visitados/enfileirados.
  visited.add(startNode);

  while (queue.length > 0) {
    // 3. Pega o primeiro nó da fila.
    const currentNode = queue.shift();
    console.log(currentNode); // Processa o nó

    // 4. Adiciona todos os vizinhos não visitados ao final da fila.
    const neighbors = graph[currentNode] || [];
    for (const neighbor of neighbors) {
      if (!visited.has(neighbor)) {
        visited.add(neighbor);
        queue.push(neighbor);
      }
    }
  }
}

// Chamada inicial:
// bfs(graph, 'A');

DFS vs. BFS: O Guia Definitivo para Entrevistas

  • Use DFS (Pilha/Recursão) quando:
    • A pergunta for simplesmente "existe um caminho entre A e B?".
    • Você precisar detectar ciclos em um grafo.
    • O problema envolver topologia de ordenação (topological sort).
    • Você precisar visitar todos os nós e a ordem não importar tanto.
  • Use BFS (Fila) quando:
    • A pergunta for "qual o caminho MAIS CURTO entre A e B?" em um grafo não-ponderado. Esta é a aplicação mais famosa e cobrada!
    • Você precisar atravessar o grafo por níveis.

💊 Pílula Devly

Árvores são grafos com regras. Grafos são o mundo real. Para navegar, você tem duas lanternas: a DFS, para explorar um caminho até o fim (com uma pilha/recursão), e a BFS, para explorar por camadas (com uma fila). E lembre-se: se a pergunta for "caminho mais curto", a resposta quase sempre é BFS.

Anterior

Navegando em Hierarquias: Árvores

Voltar

Próximo

Padrões para Quebrar a Banca

Continuar