programmingconcepts

§ 3.9 · Algorithms

Dijkstra's algorithm

Find the shortest distance from one node to every other by always settling the closest node not yet settled.

Dijkstra's algorithm finds the shortest distance from one node to every other node in a graph whose edge weights are not negative. It repeatedly takes the unvisited node with the smallest known distance from a priority queue and relaxes its edges. With a binary heap it runs in O((V + E) log V). Negative weights need Bellman-Ford instead.

Time
O((V + E) log V)
Needs
Non-negative weights
Uses
A priority queue
Requires
Graph traversalHeaps
import heapq


def dijkstra(graph, start):
    dist = {start: 0}
    queue = [(0, start)]
    done = set()
    while queue:
        d, node = heapq.heappop(queue)
        if node in done:
            continue
        done.add(node)
        for nxt, weight in graph[node]:
            if nxt in done:
                continue
            nd = d + weight
            if nd >= dist.get(nxt, float("inf")):
                continue
            dist[nxt] = nd
            heapq.heappush(queue, (nd, nxt))
    return dist

Edit the graph

In the figure: click an empty spot to add a node, click one node and then another to connect or disconnect them, and select a node then press Delete to remove it. Up to 10 nodes.

step 0 / 0waitingcurrentdiscoveredvisited

Fig. 3.9. Numbers on edges are weights; numbers under nodes are the best distances found so far. Black nodes are settled and the dark edges form the shortest-path tree.

Intuition

Pour water into a network of pipes at the start node, where each pipe’s weight is how long water takes to flow through it. The water reaches nodes in order of their distance. The moment it first reaches a node, that’s the shortest time to get there, and nothing arriving later can beat it.

Dijkstra’s algorithm simulates that wave. Instead of BFS’s queue, it uses a priority queue that always hands back the closest waiting node.

How it works

  1. Set the start’s distance to 0 and every other node’s to ∞. Put the start in the priority queue.
  2. Take out the closest node. If it was already settled, skip it: that was an old, longer entry.
  3. Settle it: its distance is now final.
  4. For each neighbour, check whether going through this node is shorter than its best distance so far. If it is, update the distance and add the neighbour to the queue. This check is called relaxing the edge.
  5. Repeat until the queue is empty.

The dark edges in the figure record which node each distance came through. Following them back from any node traces its shortest path.

Why it works

When a node is the closest unsettled one, any other route to it would have to pass through some unsettled node that is already at least as far away, and with non-negative weights the rest of that route can only add more. So its distance can’t improve later, and settling it is safe.

Complexity

With a binary heap, each edge can push one entry, and each push or pop costs O(log V), so the total is O((V + E) log V). For dense graphs, a simple array scan for the minimum gives O(V²).

Common mistakes

  1. Negative weights. A settled distance might later shrink, and Dijkstra won’t notice. Use Bellman–Ford for graphs with negative edges.
  2. Not skipping stale entries. The queue can hold several entries for one node; only the first one popped is real.
  3. Stopping too early. To get every distance, run until the queue is empty. To find one target, you may stop when the target is settled.

Check yourself

Check yourself

In the figure's small-town graph, how far is G from A?

Check yourself

Why can't Dijkstra's algorithm handle negative edge weights?

Sources

  • Edsger W. Dijkstra, “A note on two problems in connexion with graphs,” Numerische Mathematik 1 (1959): 269–271.
  • Cormen, Leiserson, Rivest and Stein, Introduction to Algorithms, section 24.3 (3rd edition).

Newsletter

One new concept each week, by email.

Signup opens soon.