25 of 32 · 6 min
When the edges have weights
Breadth first search finds the fewest hops. Once edges carry a distance, a cost or a time, fewest hops is the wrong answer: one long road can beat three short ones.
Dijkstra
Keep a tentative distance to every vertex. Repeatedly take the nearest unfinished vertex, and relax its edges.
import heapq
def dijkstra(graph, start, n):
dist = [float('inf')] * n
dist[start] = 0
heap = [(0, start)]
while heap:
d, node = heapq.heappop(heap)
if d > dist[node]:
continue # a stale entry, already improved
for nxt, weight in graph[node]:
if d + weight < dist[nxt]:
dist[nxt] = d + weight
heapq.heappush(heap, (dist[nxt], nxt))
return distO((V + E) log V) with a heap. It is breadth first search with a priority queue in place of the queue, which is why the heap page mattered.
It requires non negative weights. With a negative edge, a vertex finalised as nearest can still be improved later, and the algorithm has already moved on. This is a favourite interview question and the answer is not "it is slower", it is "it is wrong".
Bellman-Ford
Relax every edge, V minus 1 times.
O(VE), which is slower, and it handles negative weights. One more pass after finishing detects a negative cycle: if anything still improves, a loop exists that reduces the cost forever, and no shortest path is defined.
That detection is the real reason to use it. Currency arbitrage is exactly this question.
Floyd-Warshall
Shortest path between every pair, in three nested loops:
for k in range(n):
for i in range(n):
for j in range(n):
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])O(V cubed) and about five lines. The outer loop is the insight: after iteration k, dist[i][j] is the best path using only the first k vertices as intermediates. It is dynamic programming on a graph.
| Algorithm | Handles | Cost | Gives |
|---|---|---|---|
| BFS | unweighted | O(V + E) | one source |
| Dijkstra | non negative | O((V+E) log V) | one source |
| Bellman-Ford | negative, detects cycles | O(VE) | one source |
| Floyd-Warshall | negative, no negative cycles | O(V cubed) | all pairs |
Choosing
Unweighted, use BFS and do not reach for a heap. One source with ordinary distances, Dijkstra. Negative costs anywhere, Bellman-Ford. Small graph and you want every pair, Floyd-Warshall, because running Dijkstra V times is more code for a similar cost.