2Brute2Best
🔥0

Dijkstra's Algorithm (template)

Medium·GeeksforGeeksBasic Anchor
GeeksforGeeks
30-Second Mental ModelShortest Paths (Dijkstra): min-heap with lazy deletion
Complexity:O(V · E)→O((V + E) log V)
Key Insight

Keep a min-heap of (distance, node). Pop the smallest entry. If it is stale (its distance is bigger than dist[u]), skip it. Otherwise u is settled: relax its edges and push every neighbour that improved. Never change an entry inside the heap; push a new one instead.

Core Validity Invariant

A popped entry is live when its distance equals dist[u]. A live node is final, because every unfinished route must leave through a heap entry at least as far away.

01

Problem

You are given V nodes numbered 0 to V − 1 and a list of undirected weighted edges. Each edge [u, v, w] joins u and v, and crossing it in either direction takes w time, with w ≥ 0. You are also given a source node src.

Return the shortest distance from src to every node as one array: entry i is the distance to node i. If a node cannot be reached, its entry is INT_MAX.

Write vector<int> dijkstra(int V, vector<vector<int>>& edges, int src) inside class Solution.

Input

V, the number of nodes. edges, a list of [u, v, w]. src, the start node.

Output

A list of V integers: the shortest distance from src to each node, or INT_MAX for a node that cannot be reached.

Example 1
Input: V = 6, src = 0, edges = [[3,4,3],[2,4,8],[1,3,1],[2,3,5],[2,1,2],[0,2,1],[0,1,4]]
Output: [0, 3, 1, 4, 7, INT_MAX]

Node 1 is 4 away by its direct edge, but 0 → 2 → 1 costs 1 + 2 = 3, so 3 wins. Node 5 has no edges, so it stays INT_MAX.

Example 2
Input: V = 3, src = 2, edges = [[0,1,5]]
Output: [INT_MAX, INT_MAX, 0]

The only edge does not touch the source, so only node 2 (the source itself) is at distance 0.

Constraints
  • • Nodes are numbered 0 to V − 1, and `src` is one of them.
  • • Every weight is 0 or more. Dijkstra needs non-negative weights.
  • • The graph is undirected: an edge can be used in both directions.
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.

  1. 1.

    The question says shortest. Does it mean the fewest edges, or the least total weight?

  2. 2.

    If the closest unfinished node is at distance 4 right now, can a longer route still reach it sooner?

  3. 3.

    BFS handles the fewest-steps question. What breaks when one edge costs 1 and another costs 8?

  4. 4.

    A node can be reached in two ways with two different distances. Which one do I keep, and what happens to the other?

  5. 5.

    How many heap pushes can one edge cause at most?

03

Brute force approach

04

Better approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceRelax every edge in both directions, V − 1 timesO(V · E)O(V)
BetterSettle the closest unsettled node by scanning all V nodesO(V² + E)O(V + E)
OptimalPop the closest tentative node from a min-heap; skip stale entriesO((V + E) log V)O(V + E)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “shortest distance from src to every node”One source and a distance for every node: Dijkstra, returning the whole array.
  • “weighted edges”Plain BFS counts edges, not cost. Use a priority queue ordered by cost.
  • “w ≥ 0”Non-negative weights are what make the closest unfinished node final.
one source+non-negative weights+min-heap of (distance, node)+stale entries skipped→Shortest Paths (Dijkstra): min-heap with lazy deletion

This is the template for the whole sub-pattern. The next three questions change one thing each: Network Delay Time takes the largest distance, Maximum Probability multiplies and pops the largest first, Minimum Effort uses max instead of +. The rule that never changes is: pop the closest node, and skip any entry that is stale.

10

Don't make this mistake

Pattern confusion: BFS gives the fewest edges, not the least cost
✗ queue<int> q; ... if (dist[v] == INT_MAX) { dist[v] = dist[u] + w; q.push(v); }
✓ priority_queue<pair<int,int>, vector<pair<int,int>>, greater<>> pq; ... if (d + w < dist[v]) { dist[v] = d + w; pq.push({dist[v], v}); }

On the dry run, BFS reaches node 1 straight from node 0 and fixes dist[1] = 4. But 0 → 2 → 1 costs 1 + 2 = 3. BFS also gives node 3 the value 6 (the true value is 4) and node 4 the value 9 (the true value is 7). BFS trusts the first arrival, and the first arrival is the route with the fewest edges, not the lowest cost.

Marking a node done when it is pushed, not when it is popped
✗ if (!done[v] && d + w < dist[v]) { dist[v] = d + w; done[v] = true; pq.push({dist[v], v}); }
✓ Mark a node done only when its live entry is popped, and relax from that node.

On the dry run, node 1 would be marked done at 4 as soon as node 0 pushes it. The route 0 → 2 → 1 (cost 3) is then never tried, because node 1 is already done, so node 1 stays at 4. A pushed distance can still drop.

Starting every distance at 0 instead of INT_MAX
✗ vector<int> dist(V, 0);
✓ vector<int> dist(V, INT_MAX); dist[src] = 0;

With every entry at 0, no edge ever looks shorter (d + w < 0 is never true), so nothing is relaxed. The whole output is 0 0 0 0 0 0, including node 5, which should be INT_MAX.

11

Want to implement this?

Write it yourself on GeeksforGeeks, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like Shortest Paths (Weighted) translate into production architectures and low-level memory mechanics.