AlgoVerse

Dijkstra's algorithm

Shortest distances from one source node to every node of a graph with non-negative weights, using a priority queue.

Dijkstra's algorithm starts at the source node and makes nodes "final" in order of their distance from the source. In each round it picks, among the nodes that are not final yet, the one with the smallest current distance. Because every weight is non-negative, no other path can later make that node's distance smaller, so its distance is settled.

Read the full explanation ↓
Input:
V=6 · E=8 · src=APDF export

A six-node directed graph where the shortest path to B goes through C, not along the direct edge A → B.

  1. from heapq import heappop, heappush
  2. INF = float("inf")
  3. def dijkstra(graph, src):
  4. dist = {v: INF for v in graph}
  5. prev = {v: None for v in graph}
  6. dist[src] = 0
  7. pq = [(0, src)]
  8. done = set()
  9. while pq:
  10. d, u = heappop(pq)
  11. if u in done:
  12. continue
  13. done.add(u)
  14. for v, w in graph[u]:
  15. if dist[u] + w < dist[v]:
  16. dist[v] = dist[u] + w
  17. prev[v] = u
  18. heappush(pq, (dist[v], v))
  19. return dist, prev

Invariant: For every node in done, dist already holds the true shortest distance from the source.

Start

421510362ABCDEF
  • Final
  • Current node (u)
  • Shortest-path tree so far
  • Edge being checked

Priority queue pq

empty

Local variables

src'A'

Orange = changed in this step

graph dict
  • 'A': [('B', 4), ('C', 2)]
  • 'B': [('D', 5)]
  • 'C': [('B', 1), ('E', 10)]
  • 'D': [('E', 3), ('F', 6)]
  • 'E': [('F', 2)]
  • 'F': []

Call stack

  1. dijkstra()line 6
Step 1 of 90
Black = pop · Orange = key stepcompare: 0/8 · push: 0/9 · pop: 0/9 · visit: 0/6 · relax: 0/8 · skip: 0/3

    How the work grows with input size

    Average of 5 random graphs per n (fixed seeds). Hollow marker = the input on screen.

    • comparisons
    • relax
    • current input (n=6)
    0204060481216202428n = number of nodescomparisonsrelax
    Show as a table
    ncomparisonsrelax
    45.23.2
    813.88
    1220.812
    162816.6
    2035.421.6
    2437.223.8
    2847.228.8
    Explanation

    The idea

    Dijkstra's algorithm starts at the source node and makes nodes "final" in order of their distance from the source. In each round it picks, among the nodes that are not final yet, the one with the smallest current distance. Because every weight is non-negative, no other path can later make that node's distance smaller, so its distance is settled.

    The steps

    1. Every node's distance is ∞ and the source's distance is 0. The source goes into the priority queue.
    2. The node with the smallest distance is taken out of the queue (pop). If it was already final, it is skipped.
    3. The node becomes final and is added to done.
    4. For each outgoing edge u → v with weight w, check whether going through u reaches v sooner. If it does, dist[v] goes down (this is called relaxing the edge), prev[v] = u is recorded, and v goes into the queue with its new distance.
    5. Repeat from step 2 until the queue is empty.

    At the end, dist holds the shortest distance to every node, and following prev backwards from any node gives the path itself.

    Why negative weights are not allowed

    With a negative edge, a shorter path through that edge could appear after a node has already been made final, so the distance treated as settled would be wrong. For negative weights, use Bellman–Ford instead.

    Notes on this implementation

    • The priority queue is Python's heapq, holding (dist[v], v) pairs: a distance and a node name. When two distances are equal, the node name decides which comes out first, so every run is the same.
    • Instead of decreasing a key inside the queue, each improvement pushes a new entry, and stale entries are recognised through done and skipped when they come out. This is simpler and keeps the cost at O((V + E) log V).

    What to watch in the trace

    • The number next to each node is its dist. When an edge is relaxed, the change is highlighted.
    • In the "textbook-style example", watch dist[B] first become 4, then drop to 3 once C is final.
    • Compare the number of relaxations in the "worst case" input with the "best case".