Algorithme de Dijkstra avec file de priorité
Implémentez Dijkstra avec heapq, suivez les étapes de relaxation sur un graphe pondéré et résolvez cheapest-flights-within-k-stops.
Algorithme de Dijkstra avec file de priorité est une leçon Coding Interview Prep gratuite sur CoddyKit. Ceci est la leçon 1 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 dans des graphes pondérés
L’algorithme de Dijkstra trouve le plus court chemin depuis un nœud source unique vers tous les autres nœuds d’un graphe pondéré dont les poids d’arêtes sont non négatifs. Il fonctionne en traitant gloutonnement les nœuds dans l’ordre de leur meilleure distance connue — en développant toujours le nœud non visité le plus proche. La structure de données clé est un tas min (file de priorité), qui récupère efficacement le nœud dont la distance est la plus petite.
Vue d’ensemble des étapes de l’algorithme
Algorithme de Dijkstra : (1) initialisez dist[source] = 0 et dist[all others] = inf. (2) Ajoutez (0, source) à un tas min. (3) Retirez le nœud u dont la distance est la plus petite. S’il a déjà été visité avec une distance inférieure, ignorez-le. (4) Pour chaque voisin v de u : si dist[u] + weight(u,v) < dist[v], mettez à jour dist[v] et ajoutez (dist[v], v) au tas. (5) Répétez jusqu’à ce que le tas soit vide.
Implémentation Python avec heapq
Le module Python heapq implémente un tas min. Nous représentons le graphe sous forme de liste d’adjacence : graph[u] = [(v, weight), ...]. Le tas stocke des tuples (distance, node). Nous utilisons un ensemble visited pour ignorer les entrées obsolètes du tas — celles qui ont été ajoutées avant la découverte d’un meilleur chemin.
import heapq
def dijkstra(graph, source):
n = len(graph)
dist = [float('inf')] * n
dist[source] = 0
heap = [(0, source)] # (distance, node)
visited = set()
while heap:
d, u = heapq.heappop(heap)
if u in visited:
continue
visited.add(u)
for v, weight in graph[u]:
if dist[u] + weight < dist[v]:
dist[v] = dist[u] + weight
heapq.heappush(heap, (dist[v], v))
return distExemple détaillé
Considérez un graphe de 5 nœuds et les arêtes suivantes : 0→1 (4), 0→2 (1), 2→1 (2), 1→3 (1), 2→3 (5), 3→4 (3). Les plus courts chemins depuis le nœud 0 sont les suivants : vers 1 en passant par 0→2→1, avec un coût de 3 ; vers 2, avec un coût de 1 ; vers 3 en passant par 0→2→1→3, avec un coût de 4 ; vers 4 en passant par 0→2→1→3→4, avec un coût de 7. Dijkstra trouve tous ces chemins en un seul parcours, et pas uniquement le chemin vers une cible donnée.
import heapq
def dijkstra(graph, source):
dist = [float('inf')] * len(graph)
dist[source] = 0
heap = [(0, source)]
visited = set()
while heap:
d, u = heapq.heappop(heap)
if u in visited:
continue
visited.add(u)
for v, w in graph[u]:
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w
heapq.heappush(heap, (dist[v], v))
return dist
graph = [
[(1,4),(2,1)], # 0
[(3,1)], # 1
[(1,2),(3,5)], # 2
[(4,3)], # 3
[] # 4
]
print(dijkstra(graph, 0)) # [0, 3, 1, 4, 7]Pourquoi Dijkstra échoue avec des poids négatifs
La validité de Dijkstra repose sur le fait qu'une fois qu'un nœud est retiré du tas min, sa distance est définitive. Cela n'est vrai que si les poids des arêtes sont non négatifs. Avec une arête négative u→v de poids -5, après avoir visité v, nous pourrions trouver un chemin passant par u qui est plus court — mais v est déjà marqué comme visité. Une seule arête négative peut invalider tous les calculs de distance ultérieurs.
Vols les moins chers avec au plus K escales (LeetCode 787)
Ce problème ajoute une contrainte : au plus k escales. La version standard de Dijkstra ne gère pas nativement le nombre d'étapes. Solution : étendre l'état à (cost, node, stops_remaining). Utilisez Dijkstra avec ce triplet, ou utilisez Bellman-Ford avec k+1 passes de relaxation. La version modifiée de Dijkstra s'arrête lorsque stops_remaining atteint 0, ce qui empêche les sauts supplémentaires.
import heapq
from collections import defaultdict
def findCheapestPrice(n, flights, src, dst, k):
graph = defaultdict(list)
for u, v, w in flights:
graph[u].append((v, w))
heap = [(0, src, k + 1)] # (cost, node, hops_left)
visited = {} # node -> min hops_left seen at this cost level
while heap:
cost, node, hops = heapq.heappop(heap)
if node == dst:
return cost
if hops == 0:
continue
if visited.get(node, 0) >= hops:
continue
visited[node] = hops
for nxt, w in graph[node]:
heapq.heappush(heap, (cost + w, nxt, hops - 1))
return -1
print(findCheapestPrice(4,[[0,1,100],[1,2,100],[0,2,500]],0,2,1)) # 200Analyse de la complexité temporelle
Avec un tas binaire, Dijkstra s'exécute en O((V + E) log V) : chaque sommet est retiré une fois (V retraits), chaque arête peut déclencher une insertion (E insertions), et chaque opération sur le tas coûte O(log V). Avec un tas de Fibonacci, la borne s'améliore pour atteindre O(E + V log V), mais le module de tas binaire de Python est utilisé. Pour les graphes creux (E ≈ V), la version avec tas binaire est en O(V log V) ; pour les graphes denses (E ≈ V²), elle est en O(V² log V).
Reconstituer le plus court chemin
Pour récupérer le chemin réel (et pas seulement les distances), maintenez un tableau prev : lors de la mise à jour de dist[v], définissez prev[v] = u. Une fois l'algorithme terminé, reconstituez le chemin de la source à la destination en remontant les pointeurs : commencez à dst, suivez les pointeurs prev jusqu'à source, puis inversez le résultat.
import heapq
def dijkstra_path(graph, source, target):
n = len(graph)
dist = [float('inf')] * n
prev = [-1] * n
dist[source] = 0
heap = [(0, source)]
visited = set()
while heap:
d, u = heapq.heappop(heap)
if u in visited: continue
visited.add(u)
for v, w in graph[u]:
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w
prev[v] = u
heapq.heappush(heap, (dist[v], v))
# Reconstruct
path, node = [], target
while node != -1:
path.append(node)
node = prev[node]
return dist[target], path[::-1]Utiliser un dictionnaire pour les graphes creux
Lorsque les nœuds sont des chaînes ou des entiers non contigus, utilisez un defaultdict(list) pour la liste d'adjacence et un dict classique pour les distances. C'est fréquent dans les problèmes de LeetCode, comme celui du délai de propagation du réseau, où les nœuds sont numérotés de 1 à n. Pensez à utiliser dist = {node: inf for node in all_nodes} et à vérifier les nœuds inaccessibles après l'algorithme.
import heapq
from collections import defaultdict
def networkDelayTime(times, n, k):
graph = defaultdict(list)
for u, v, w in times:
graph[u].append((v, w))
dist = {i: float('inf') for i in range(1, n+1)}
dist[k] = 0
heap = [(0, k)]
while heap:
d, u = heapq.heappop(heap)
if d > dist[u]: continue
for v, w in graph[u]:
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w
heapq.heappush(heap, (dist[v], v))
ans = max(dist.values())
return ans if ans < float('inf') else -1
print(networkDelayTime([[2,1,1],[2,3,1],[3,4,1]], 4, 2)) # 2Comparaison avec BFS pour les graphes non pondérés
Pour les graphes non pondérés, BFS trouve les plus courts chemins en O(V + E) — plus rapidement que Dijkstra, en O((V+E) log V). Dijkstra généralise BFS aux graphes pondérés en utilisant une file de priorité plutôt qu'une file FIFO classique. Lorsque tous les poids des arêtes sont égaux, Dijkstra se réduit à BFS. Choisissez BFS pour les graphes non pondérés, Dijkstra pour les poids non négatifs et Bellman-Ford pour les poids négatifs.
Dijkstra avec optimisation par décrémentation de clé
La version théorique de Dijkstra utilise une file de priorité avec une opération de décrémentation de clé : lorsqu'une distance de nœud s'améliore, mettez sa priorité à jour directement. Cela nécessite un tas de Fibonacci pour obtenir O(E + V log V), mais cette structure est difficile à implémenter. L'approche par suppression différée utilisée lors des entretiens consiste plutôt à insérer une nouvelle entrée et à ignorer les retraits obsolètes — elle est plus simple, avec seulement un surcoût constant. En Python, la suppression différée avec un tas binaire est l'implémentation standard pour les entretiens.
Vérification rapide
Vérifiez votre compréhension des concepts de Structures de données et algorithmes — préparation aux entretiens de programmation présentés dans cette leçon.
Récapitulatif de la leçon
Dans cette leçon, vous avez appris que Dijkstra utilise un tas min pour traiter gloutonnement les nœuds dans l'ordre de leur meilleure distance actuelle, qu'il s'exécute en O((V+E) log V) et échoue avec les arêtes de poids négatif, et que les entrées obsolètes du tas sont gérées en vérifiant un ensemble de nœuds visités lors du retrait. Ensuite, nous étudierons Bellman-Ford, qui gère les poids négatifs grâce à n-1 passes de relaxation.
Questions Fréquemment Posées
La leçon « Algorithme de Dijkstra avec file de priorité » est-elle gratuite ?
Oui — le texte complet de « Algorithme de Dijkstra avec file de priorité » 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 « Algorithme de Dijkstra avec file de priorité » ?
Implémentez Dijkstra avec heapq, suivez les étapes de relaxation sur un graphe pondéré et résolvez cheapest-flights-within-k-stops. 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 1 sur 4.
Combien de temps prend la leçon « Algorithme de Dijkstra avec file de priorité » ?
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
- Algorithme de Dijkstra avec file de priorité
- Bellman-Ford et cycles négatifs
- Floyd-Warshall : plus courts chemins entre toutes les paires
- Délai du réseau et reconstruction du chemin