Skip to content

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.

python
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 dist

O((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:

python
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.

AlgorithmHandlesCostGives
BFSunweightedO(V + E)one source
Dijkstranon negativeO((V+E) log V)one source
Bellman-Fordnegative, detects cyclesO(VE)one source
Floyd-Warshallnegative, no negative cyclesO(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.