0Pricing
Coding Interview Prep · Leçon

Floyd-Warshall : plus courts chemins entre toutes les paires

Remplissez la matrice des distances entre toutes les paires avec l’algorithme Floyd-Warshall à trois boucles imbriquées, puis utilisez-la pour trouver le plus petit nombre de sauts entre chaque paire de nœuds.

Floyd-Warshall : plus courts chemins entre toutes les paires est une leçon Coding Interview Prep gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Coding Interview Prep, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Coding Interview Prep comprend 4 leçons au total.

Plus courts chemins entre toutes les paires

Floyd-Warshall calcule les plus courts chemins entre chaque paire de nœuds d'un graphe pondéré — y compris les graphes avec des arêtes de poids négatif (mais sans cycles négatifs). Exécuter Dijkstra depuis chaque source prend O(V × (V+E) log V) ; Floyd-Warshall s'exécute en O(V³), quelle que soit la densité des arêtes. Pour les graphes denses avec V ≤ 500, Floyd-Warshall est souvent plus simple et offre une rapidité comparable.

L'idée essentielle : les nœuds intermédiaires

L'idée de Floyd-Warshall est la suivante : dp[i][j][k] = plus court chemin de i à j en utilisant uniquement les nœuds {0, 1, ..., k} comme intermédiaires. Soit le plus court chemin utilise le nœud k comme intermédiaire, soit il ne l'utilise pas. Dans le premier cas : dp[i][j][k] = dp[i][k][k-1] + dp[k][j][k-1]. Dans le second : dp[i][j][k] = dp[i][j][k-1]. Comme la troisième dimension ne progresse que vers l'avant, elle peut être supprimée : nous mettons à jour la matrice sur place.

Initialisation de la matrice des distances

Commencez avec une matrice V×V : dist[i][i] = 0 (distance de zéro vers soi-même), dist[i][j] = weight pour les arêtes directes et dist[i][j] = inf pour les paires sans arête. Parcourez ensuite tous les nœuds intermédiaires k en mettant à jour les paires (i, j). La boucle externe sur k doit être exécutée en premier afin de construire correctement les chemins à travers un ensemble croissant de nœuds intermédiaires autorisés.

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

Implémentation complète avec exemple

Suivons l'exécution de Floyd-Warshall sur un graphe à 4 nœuds. Après le traitement de chaque nœud intermédiaire k, la matrice se complète avec des chemins plus courts passant par le nœud k. L'algorithme gère naturellement les sauts multiples en construisant progressivement les plus courts chemins.

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])

Détection des cycles négatifs

Après l'exécution de Floyd-Warshall, vérifiez la diagonale principale : si un dist[i][i] < 0, un cycle négatif passant par le nœud i existe. En effet, un cycle négatif permet d'atteindre i depuis i avec un coût négatif. Si aucun cycle négatif n'existe, toutes les entrées de la diagonale restent égales à 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

Reconstitution du chemin

Pour reconstituer le chemin réel de i à j, maintenez une matrice next[i][j] : initialement, next[i][j] = j pour les arêtes directes. Lors d'une mise à jour via le nœud intermédiaire k, définissez next[i][j] = next[i][k]. Pour récupérer le chemin, partez de i et suivez les pointeurs next jusqu'à atteindre j. Cela ajoute un espace O(V²) et un coût O(V) pour chaque reconstitution de chemin.

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

Fermeture transitive

Une variante plus simple : la fermeture transitive répond à la question « le nœud j est-il accessible depuis le nœud i ? » pour toutes les paires. Remplacez les distances par des valeurs booléennes : reach[i][j] = reach[i][j] or (reach[i][k] and reach[k][j]). Il s'agit de Floyd-Warshall avec l'opération OR booléenne au lieu de l'addition et du minimum. Initialisez reach[i][i] = True et reach[i][j] = True pour les arêtes directes.

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)

Complexité et choix de l'algorithme

Floyd-Warshall : O(V³) en temps et O(V²) en espace. Pour les graphes denses (E ≈ V²) avec V ≤ 300, il est plus rapide que l'exécution de Dijkstra V fois (également en O(V³) dans ce cas). Pour les graphes creux avec V = 1000 et E = 3000, V exécutions de Dijkstra coûtent O(V×E×log V) ≈ 33M, tandis que Floyd-Warshall coûte O(V³) = 10⁹ : Dijkstra est plus efficace. Sachez choisir l'algorithme approprié dans chaque situation.

Nombre minimal de sauts entre toutes les paires

Attribuez à toutes les arêtes un poids égal à 1 (ou utilisez une matrice d’adjacence booléenne avec Floyd-Warshall en utilisant une addition plutôt que min) : dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]). Cela calcule le nombre minimal de sauts entre toutes les paires — le résultat d’un BFS entre toutes les paires, mais calculé en un seul parcours de Floyd-Warshall en 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]

Contexte des entretiens : quand les recruteurs vous interrogent sur Floyd-Warshall

Floyd-Warshall est souvent abordé lors d’entretiens pour des questions portant sur : (1) les distances entre toutes les paires dans un petit graphe, (2) la recherche d’un cycle dont le poids total est négatif, (3) le calcul de plus courts chemins dans des problèmes de propagation de contraintes et (4) les problèmes demandant explicitement des solutions en O(V³), où V ≤ 200. Mentionnez toujours la structure à trois boucles et la condition d’absence de cycles négatifs pour garantir la correction.

Graphes non orientés avec Floyd-Warshall

Pour les graphes non orientés, ajoutez les deux directions pour chaque arête : dist[u][v] = dist[v][u] = weight. Le reste de l’algorithme est identique. La matrice obtenue est symétrique : dist[i][j] == dist[j][i] pour toutes les paires. Lors de l’initialisation, veillez à ne pas attribuer accidentellement des arêtes orientées : les arêtes non orientées doivent être ajoutées dans les deux directions à la matrice initiale avant l’exécution des trois boucles.

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

Vérification rapide

Testez votre compréhension des concepts de structures de données et d’algorithmes — préparation aux entretiens de programmation de cette leçon.

Récapitulatif de la leçon

Dans cette leçon, vous avez appris : Floyd-Warshall calcule les plus courts chemins entre toutes les paires grâce à trois boucles imbriquées et à la récurrence dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]), les cycles de poids négatif sont détectables en vérifiant si une valeur dist[i][i] < 0 existe après l’exécution, et l’algorithme s’exécute en O(V³) et utilise un espace en O(V²). Ensuite, nous reviendrons sur les applications des plus courts chemins avec le problème du délai réseau et les techniques de reconstruction de chemins.

Questions Fréquemment Posées

La leçon « Floyd-Warshall : plus courts chemins entre toutes les paires » est-elle gratuite ?

Oui — le texte complet de « Floyd-Warshall : plus courts chemins entre toutes les paires » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Coding Interview Prep, passe à CoddyKit PRO. Le cours Coding Interview Prep comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Floyd-Warshall : plus courts chemins entre toutes les paires » ?

Remplissez la matrice des distances entre toutes les paires avec l’algorithme Floyd-Warshall à trois boucles imbriquées, puis utilisez-la pour trouver le plus petit nombre de sauts entre chaque paire… Tu pratiques Coding Interview Prep avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Coding Interview Prep ?

Aucune expérience préalable n'est requise. Coding Interview Prep sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.

Combien de temps prend la leçon « Floyd-Warshall : plus courts chemins entre toutes les paires » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Coding Interview Prep ?

Oui. Chaque leçon Coding Interview Prep inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Algorithme de Dijkstra avec file de priorité
  2. Bellman-Ford et cycles négatifs
  3. Floyd-Warshall : plus courts chemins entre toutes les paires
  4. Délai du réseau et reconstruction du chemin
← Retour à Coding Interview Prep