Expert: advanced patterns and interviews · Graphs · topic 45 of 61 · 40 min
Shortest paths: Dijkstra and friends
Explanation
When edges have costs
In a weighted graph, each edge carries a number: kilometres, minutes, or a ticket price. The length of a path is the SUM of its edge weights, not the number of edges. A direct flight might cost ₹9,000 while a route with one stop costs ₹3,500 + ₹2,500 = ₹6,000 (made-up fares). The cheapest path can have more edges.
Why BFS fails
BFS settles a vertex the first time it reaches it, by the fewest edges. With edges 0 → 2 costing 10, and 0 → 1 → 2 costing 1 + 1, BFS reaches 2 directly in one step and reports a cost of 10, but the true cheapest cost is 2. We need an algorithm that settles vertices in order of their total distance.
Dijkstra's idea
Dijkstra's algorithm keeps a best-known distance for every vertex: 0 for the source and infinity for the rest. It repeatedly picks the unfinished vertex with the smallest known distance and relaxes its edges: for an edge u → v with weight w, if dist[u] + w is smaller than dist[v], it updates dist[v]. With non-negative weights, the closest unfinished vertex can never be improved later, because any other route to it would pass through a vertex that is at least as far away and then add more non-negative weight.
A min-heap (heapq, Module 8) finds the closest vertex fast. Python's heapq has no "decrease key" operation, so we use lazy deletion: when a distance improves, push a new (distance, vertex) pair and leave the old one in the heap. When a pair is popped whose distance is larger than the best known, it is stale, and we skip it.
import heapq
def dijkstra(adj, src):
dist = {u: float("inf") for u in adj}
dist[src] = 0
heap = [(0, src)]
while heap:
d, u = heapq.heappop(heap)
if d > dist[u]:
continue # stale entry: a shorter path was found later
for v, w in adj[u]:
nd = d + w
if nd < dist[v]:
dist[v] = nd
heapq.heappush(heap, (nd, v))
return dist
adj = {"A": [("B", 4), ("C", 1)], "B": [("D", 1)], "C": [("B", 2), ("D", 5)], "D": []}
print(dijkstra(adj, "A")) # Output: {'A': 0, 'B': 3, 'C': 1, 'D': 4}Cost: each edge can push one entry, so the heap holds O(E) entries, and each push or pop is O(log E) = O(log V). Total time O((V + E) log V), space O(V + E).
No negative edges
Dijkstra can give wrong answers when an edge weight is negative. With A → B costing 2, A → C costing 3 and C → B costing −2, it settles B at 2, but the true cheapest cost is 3 − 2 = 1. Negative edges appear in problems about gains and losses, such as currency exchange.
Friends of Dijkstra
- Bellman-Ford: relax EVERY edge, V − 1 times in total. It allows negative edges and runs in O(V × E). If a V-th round can still improve some distance, the graph has a negative cycle (a loop whose total is below zero) reachable from the source, and "shortest" is not defined.
- 0-1 BFS: when every weight is 0 or 1, use a deque; push to the front for a 0-edge and to the back for a 1-edge. O(V + E).
- Floyd–Warshall: shortest paths between ALL pairs with three nested loops over k, i and j, updating dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]). O(V³) time and O(V²) space, so only for small graphs (a few hundred vertices). It allows negative edges but not negative cycles.
- Plain BFS: still the best choice when every edge has the same weight.
How map apps fit in
A navigation app models roads as a weighted directed graph, where the weight is the expected travel time, which changes with traffic. Dijkstra's algorithm is the textbook starting point. Real routing engines for a whole country use more advanced methods on top of it, such as A* search (which adds a smart estimate of the remaining distance) and heavy preprocessing of the road network, so that a route across thousands of kilometres is found in milliseconds. In an interview, it is good to say this.
Common mistakes
- Marking a vertex as done when it is pushed (as in BFS). Fix: a vertex is final only when it is popped with its current best distance.
- Pushing (vertex, distance) instead of (distance, vertex). heapq orders tuples by their first item. Fix: put the distance first.
- Leaving out the stale check. The answer stays right, but stale pops redo work, which can be much slower. Fix: if d > dist[u]: continue.
- Using Dijkstra with negative weights. Fix: Bellman-Ford.
- Forgetting both directions for two-way roads, or returning infinity where the prompt asks for -1. Fix: read the input format and the output rule twice.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Shortest paths: Dijkstra and friends · 1/8
This lesson finds the cheapest path when edges have weights, such as minutes or rupees.
Prefer a video by a teacher? Find free videos on “dijkstra algorithm priority queue python explained shortest path” on YouTube ↗
In real life: Fastest route across a city
A ride-hailing or maps app estimates how long each road segment takes right now and runs a shortest-path search to suggest a route and an arrival time. The tiny made-up network below has travel times in minutes. The direct road from Home to Office takes 30 minutes, but going through the Market and the Station is faster. Real apps use far bigger graphs, live traffic and more advanced algorithms, but the core question is the same.
import heapq
roads = [("Home", "Market", 7), ("Market", "Station", 6), ("Station", "Office", 9),
("Home", "Office", 30), ("Market", "Office", 20)]
adj = {}
for a, b, minutes in roads:
adj.setdefault(a, []).append((b, minutes))
adj.setdefault(b, []).append((a, minutes))
dist = {place: float("inf") for place in adj}
dist["Home"] = 0
heap = [(0, "Home")]
while heap:
d, u = heapq.heappop(heap)
if d > dist[u]:
continue
for v, w in adj[u]:
if d + w < dist[v]:
dist[v] = d + w
heapq.heappush(heap, (d + w, v))
print(dist["Office"])Practice (0/4 done)
Write dijkstra(n, edges, src) for a DIRECTED graph with vertices 0 to n − 1. edges is a list of triples (u, v, w) meaning u → v with weight w ≥ 0. Return a list of length n where position v is the shortest distance from src to v, and -1 if v cannot be reached. Use heapq. Example: dijkstra(3, [(0, 1, 5)], 0) returns [0, 5, -1].
A network has n machines labelled 1 to n. times is a list of triples (u, v, w): a signal sent from u reaches v after w milliseconds (directed, w ≥ 0). A signal starts at machine k. Write network_delay_time(times, n, k) that returns how long it takes until ALL n machines have the signal, or -1 if some machine never gets it. Example: network_delay_time([(2, 1, 1), (2, 3, 1), (3, 4, 1)], 4, 2) returns 2.
Cities are labelled 0 to n − 1. roads is a list of triples (u, v, cost) for TWO-WAY roads (cost ≥ 0). Write cheapest_path(n, roads, a, b) that returns the minimum total cost to travel from city a to city b, 0 if a == b, and -1 if b cannot be reached. Example: cheapest_path(4, [(0, 1, 5), (1, 2, 5), (0, 2, 15), (2, 3, 2)], 0, 3) returns 12.
Write shortest_route(n, edges, s, t) for a DIRECTED graph with vertices 0 to n − 1 and edges (u, v, w), w ≥ 0. Return the cheapest path itself as a list of vertices from s to t (both included), [s] if s == t, and [] if t cannot be reached. The tests only use graphs where the cheapest path is unique. Hint: keep prev[v], the vertex you came from when dist[v] last improved, then walk back from t.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Graph problem patternsGraphs · same module
- Depth-first search (DFS)Graphs · same module
- Minimum spanning treesTries, union-find and bit manipulation
- Heap patterns: top-k and streamsHeaps and greedy algorithms
- Heaps and priority queuesHeaps and greedy algorithms
- Sliding window: variable sizeHashing, two pointers and sliding window
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
Why can BFS give a wrong answer on a weighted graph?
Answer: It counts edges, not the total weight
BFS settles a vertex by the fewest edges, but a path with more, cheaper edges can cost less.
In lazy-deletion Dijkstra you pop (7, u) while dist[u] is already 5. What should you do?
Answer: Skip it: the entry is stale
A cheaper path to u was found after this entry was pushed, so it is out of date.
What is the time of Dijkstra with a binary heap?
Answer: O((V + E) log V)
Up to E pushes and pops, each costing O(log V).
A graph has a few negative edges but no negative cycle. Which algorithm should you use for one source?
Answer: Bellman-Ford
Bellman-Ford allows negative edges; Dijkstra may settle a vertex too early.
Every edge weight is either 0 or 1. What is a simple O(V + E) method?
Answer: 0-1 BFS with a deque
Push to the front for a 0-edge and to the back for a 1-edge; the deque stays in distance order.
Why push (dist, vertex) and not (vertex, dist) into heapq?
Answer: heapq orders tuples by their first item
The heap must hand out the smallest distance, so the distance goes first.
Why should BFS mark a vertex as visited when it is added to the queue?Related
Answer: So the same vertex is not added to the queue many times
If you wait until it is popped, several neighbours can each add it before then.
In Kruskal's algorithm, the next cheapest edge joins two vertices already in the same group. What do you do?Related
Answer: Skip it, because it would close a cycle
Both ends are already connected, so the edge adds nothing but a cycle.
An undirected graph has 5 edges. What do all the degrees add up to?Related
Answer: 10
Every edge touches two vertices, so it adds 2 to the total: 2 × 5 = 10.
Kahn's algorithm outputs 4 vertices of a 6-vertex graph. What does that tell you?Related
Answer: The graph has a cycle
Vertices on a cycle never reach in-degree 0, so they are left out.
From Topological sort
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 1 more topic in “Graphs” before its exam: Graph problem patterns.
- Module 9 exam: Graphs · 14 questions · 25 min, without help. 6 topics of this module still to finish first.
- Then the next module, Tries, union-find and bit manipulation. 61 topics left in the course before the final exam.