Coding Interview Prep · Aula

Floyd-Warshall: caminhos mínimos entre todos os pares

Preencha a matriz de distâncias entre todos os pares usando o algoritmo de Floyd-Warshall com três laços aninhados e aplique-o para encontrar o menor número de saltos entre todos os pares de nós.

Aula 3 de 413 etapas

Floyd-Warshall: caminhos mínimos entre todos os pares é uma aula grátis de Coding Interview Prep no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Coding Interview Prep, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Coding Interview Prep inclui 4 aulas no total.

Caminhos mais curtos entre todos os pares

Floyd-Warshall calcula os caminhos mais curtos entre cada par de nós em um grafo ponderado — inclusive em grafos com pesos negativos nas arestas (mas não com ciclos negativos). Executar Dijkstra a partir de cada origem leva O(V × (V+E) log V); Floyd-Warshall é executado em O(V³), independentemente da densidade das arestas. Para grafos densos com V ≤ 500, Floyd-Warshall costuma ser mais simples e ter velocidade comparável.

A ideia principal: nós intermediários

A ideia central de Floyd-Warshall: dp[i][j][k] = caminho mais curto de i até j usando apenas os nós {0, 1, ..., k} como intermediários. Ou o caminho mais curto usa o nó k como intermediário, ou não usa. Se usar: dp[i][j][k] = dp[i][k][k-1] + dp[k][j][k-1]. Se não usar: dp[i][j][k] = dp[i][j][k-1]. Como a terceira dimensão avança apenas para frente, ela pode ser eliminada — atualizamos no próprio local.

Inicialização da matriz de distâncias

Comece com uma matriz V×V: dist[i][i] = 0 (distância até si próprio igual a zero), dist[i][j] = weight para arestas diretas e dist[i][j] = inf para pares sem aresta. Em seguida, percorra todos os nós intermediários k, atualizando os pares (i, j). O laço externo sobre k deve vir primeiro para que os caminhos através de um conjunto crescente de intermediários permitidos sejam construídos corretamente.

def floyd_warshall(V, edges):
    INF = float('inf')
    dist = [[INF]*V for _ in range(V)]
    for i in range(V):
        dist[i][i] = 0
    for u, v, w in edges:
        dist[u][v] = w  # directed graph
    
    for k in range(V):       # intermediate node
        for i in range(V):
            for j in range(V):
                if dist[i][k] + dist[k][j] < dist[i][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]
    
    return dist

Implementação completa com exemplo

Vamos acompanhar o Floyd-Warshall em um grafo com 4 nós. Depois de processar cada nó intermediário k, a matriz é preenchida com caminhos mais curtos que passam por k. O algoritmo trata naturalmente vários saltos ao construir os caminhos mais curtos de forma incremental.

def floyd_warshall(V, edges):
    INF = float('inf')
    dist = [[INF]*V for _ in range(V)]
    for i in range(V):
        dist[i][i] = 0
    for u, v, w in edges:
        dist[u][v] = w
    for k in range(V):
        for i in range(V):
            for j in range(V):
                if dist[i][k] != INF and dist[k][j] != INF:
                    dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
    return dist

V = 4
edges = [(0,1,3),(0,2,7),(1,2,1),(1,3,5),(2,3,2)]
dist = floyd_warshall(V, edges)
for row in dist:
    print([x if x != float('inf') else 'INF' for x in row])

Detecção de ciclos negativos

Depois de executar Floyd-Warshall, verifique a diagonal principal: se algum dist[i][i] < 0, existe um ciclo negativo que passa pelo nó i. Isso ocorre porque um ciclo negativo permite chegar a i partindo de i com custo negativo. Se não existir nenhum ciclo negativo, todas as entradas da diagonal permanecerão iguais a 0.

def has_negative_cycle_fw(V, edges):
    dist = floyd_warshall(V, edges)
    for i in range(V):
        if dist[i][i] < 0:
            return True  # negative cycle through node i
    return False

# Negative cycle: 0->1->2->0 with weights 1,-3,1 (sum=-1)
edges_neg = [(0,1,1),(1,2,-3),(2,0,1)]
print(has_negative_cycle_fw(3, edges_neg))  # True

Reconstrução do caminho

Para reconstruir o caminho real de i até j, mantenha uma matriz next[i][j]: inicialmente, next[i][j] = j para arestas diretas. Ao atualizar através do intermediário k, defina next[i][j] = next[i][k]. Para recuperar o caminho: comece em i e siga os ponteiros de next até chegar a j. Isso acrescenta espaço O(V²) e O(V) por reconstrução de caminho.

def fw_with_path(V, edges):
    INF = float('inf')
    dist = [[INF]*V for _ in range(V)]
    nxt = [[None]*V for _ in range(V)]
    for i in range(V): dist[i][i] = 0
    for u, v, w in edges:
        dist[u][v] = w; nxt[u][v] = v
    for k in range(V):
        for i in range(V):
            for j in range(V):
                if dist[i][k] + dist[k][j] < dist[i][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]
                    nxt[i][j] = nxt[i][k]
    return dist, nxt

def get_path(nxt, i, j):
    if nxt[i][j] is None: return []
    path = [i]
    while i != j:
        i = nxt[i][j]; path.append(i)
    return path

Fecho transitivo

Uma variante mais simples: o fecho transitivo responde à pergunta “o nó j é alcançável a partir do nó i?” para todos os pares. Substitua as distâncias por valores booleanos: reach[i][j] = reach[i][j] or (reach[i][k] and reach[k][j]). Isso é Floyd-Warshall usando OR booleano em vez de adição e mínimo. Inicialize reach[i][i] = True e reach[i][j] = True para arestas diretas.

def transitive_closure(V, edges):
    reach = [[False]*V for _ in range(V)]
    for i in range(V):
        reach[i][i] = True
    for u, v, _ in edges:
        reach[u][v] = True
    for k in range(V):
        for i in range(V):
            for j in range(V):
                reach[i][j] = reach[i][j] or (reach[i][k] and reach[k][j])
    return reach

edges = [(0,1,1),(1,2,1)]
R = transitive_closure(3, edges)
print(R[0][2])  # True (0 can reach 2 via 0->1->2)

Complexidade e quando usar

Floyd-Warshall: O(V³) de tempo, O(V²) de espaço. Para grafos densos (E ≈ V²) com V ≤ 300, ele é mais rápido que executar Dijkstra V vezes (também O(V³ nesse caso). Para grafos esparsos com V = 1000 e E = 3000, V execuções de Dijkstra custam O(V×E×log V) ≈ 33M, enquanto Floyd-Warshall custa O(V³) = 10⁹ — Dijkstra vence. Saiba quando cada um é apropriado.

Número mínimo de saltos entre todos os pares

Defina todos os pesos das arestas como 1 (ou use uma matriz de adjacência booleana com Floyd-Warshall, usando soma em vez de mínimo): dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]). Isso calcula o número mínimo de saltos entre todos os pares — um resultado de BFS entre todos os pares, mas calculado com uma única passagem de Floyd-Warshall de O(V³).

def min_hops_all_pairs(V, adj_list):
    INF = float('inf')
    dist = [[INF]*V for _ in range(V)]
    for i in range(V):
        dist[i][i] = 0
        for j in adj_list[i]:
            dist[i][j] = 1
    for k in range(V):
        for i in range(V):
            for j in range(V):
                dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])
    return dist

adj = [[1,2],[2],[3],[],[]]
print(min_hops_all_pairs(5, adj)[0])  # [0, 1, 1, 2, INF]

Contexto de entrevista: quando os entrevistadores perguntam sobre Floyd-Warshall

Floyd-Warshall aparece em entrevistas em questões que envolvem: (1) distâncias entre todos os pares em um grafo pequeno, (2) verificar se existe algum ciclo com peso total negativo, (3) calcular caminhos mais curtos em problemas de propagação de restrições e (4) problemas que pedem explicitamente soluções O(V³), em que V ≤ 200. Mencione sempre a estrutura de três laços e o requisito de não haver ciclos negativos para garantir a correção.

Grafos não direcionados com Floyd-Warshall

Para grafos não direcionados, adicione as duas direções para cada aresta: dist[u][v] = dist[v][u] = weight. O restante do algoritmo é idêntico. A matriz resultante é simétrica: dist[i][j] == dist[j][i] para todos os pares. Ao inicializar, tome cuidado para não atribuir acidentalmente arestas direcionais — as arestas não direcionadas devem ser adicionadas nas duas direções à matriz inicial antes da execução dos três laços.

def fw_undirected(V, edges):
    INF = float('inf')
    dist = [[INF]*V for _ in range(V)]
    for i in range(V): dist[i][i] = 0
    for u, v, w in edges:
        dist[u][v] = w
        dist[v][u] = w  # both directions for undirected
    for k in range(V):
        for i in range(V):
            for j in range(V):
                if dist[i][k] + dist[k][j] < dist[i][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]
    return dist

Verificação rápida

Teste sua compreensão dos conceitos de Estruturas de Dados e Algoritmos — Preparação para Entrevistas de Programação desta lição.

Recapitulação da lição

Nesta lição, você aprendeu que: Floyd-Warshall calcula os caminhos mais curtos entre todos os pares com três laços aninhados e a recorrência dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]), os ciclos negativos podem ser detectados verificando se algum dist[i][i] < 0 após a conclusão e o algoritmo é executado em tempo O(V³) e espaço O(V²). Em seguida, retomaremos as aplicações de caminhos mais curtos com Network Delay Time e técnicas de reconstrução de caminhos.

Grátis para começar

Aprenda Coding Interview Prep com um tutor de IA — grátis

Escreva e execute código real no seu navegador, obtenha ajuda instantânea de um tutor de IA 24/7 e continue de onde parou na web ou no app.

Cursos
90
Aulas
360

Perguntas Frequentes

A aula “Floyd-Warshall: caminhos mínimos entre todos os pares” é grátis?

Sim — o texto completo de “Floyd-Warshall: caminhos mínimos entre todos os pares” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Coding Interview Prep, atualize para CoddyKit PRO. O curso de Coding Interview Prep inclui 4 aulas no total.

O que vou aprender em “Floyd-Warshall: caminhos mínimos entre todos os pares”?

Preencha a matriz de distâncias entre todos os pares usando o algoritmo de Floyd-Warshall com três laços aninhados e aplique-o para encontrar o menor número de saltos entre todos os pares de nós. Você pratica Coding Interview Prep com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Coding Interview Prep?

Nenhuma experiência prévia é necessária. Coding Interview Prep no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “Floyd-Warshall: caminhos mínimos entre todos os pares”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Coding Interview Prep?

Sim. Cada aula de Coding Interview Prep inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Algoritmo de Dijkstra com fila de prioridades
  2. Bellman-Ford e ciclos negativos
  3. Floyd-Warshall: caminhos mínimos entre todos os pares
  4. Tempo de atraso da rede e reconstrução de caminhos
← Voltar para Coding Interview Prep