Dijkstra's algorithm computes single-source shortest paths in a weighted graph with non-negative edge weights. It's the go-to algorithm for shortest path problems when all weights are non-negative.
Intuition: Greedily expand outward from the source, always processing the closest unvisited vertex. With non-negative weights, once a vertex is processed, its shortest distance is finalized.
The algorithm maintains two sets:
- Finalized: Vertices whose shortest distance is known
- Unfinalized: Vertices still being explored
At each step, pick the unfinalized vertex with minimum distance and finalize it.
When we pick vertex u with minimum distance d:
- All other unfinalized vertices have distance >=
d - Any path to
uthrough them would be >=d(since edges are non-negative) - Therefore,
dis already the shortest possible distance tou
This greedy choice is safe only when all edge weights are >= 0.
0 --1--> 1
| |
5 -10
| |
v v
2 <------+
From source 0:
- Process 1 first (dist=1), mark as finalized
- Process 2 next (dist=5), mark as finalized
- But path 0→1→2 = 1 + (-10) = -9 is shorter!
- We already finalized 2, so we miss this shorter path.
Dijkstra(graph, source):
dist[] = infinity for all vertices
dist[source] = 0
parent[] = -1 for all vertices
pq = min-heap with (source, 0)
while pq not empty:
(u, d) = pq.poll() // Get minimum distance vertex
if d > dist[u]:
continue // Already processed with shorter distance
for each edge (u, v, weight):
if dist[u] + weight < dist[v]:
dist[v] = dist[u] + weight
parent[v] = u
pq.add((v, dist[v]))
return dist, parent
- Priority Queue: Use a min-heap keyed by distance
- Lazy Deletion: Instead of decreasing key (complex), add duplicates and skip stale entries
- Skip Check:
if d > dist[u]: continuehandles the lazy deletion
Textbooks often describe Dijkstra using a decrease-key operation: when a shorter path to vertex v is found, update its priority in the queue. However, most standard priority queue implementations (like Java's PriorityQueue) don't support efficient decrease-key.
Practical solution: Use lazy deletion instead:
- When finding a shorter path, add a new entry
(v, new_dist)to the queue - Skip stale entries when polling:
if (d > dist[u]) continue
Why complexity is unaffected: In the worst case (complete graph), E = V². Each edge relaxation may add one entry to the queue, so the queue can have up to O(E) entries. Each operation is O(log E) = O(log V²) = O(2 log V) = O(log V). Total: O(E log V) = O((V + E) log V) — same as the textbook version.
Graph:
0 --4--> 1 --1--> 3
| |
2 2
| |
v v
2 --3--> 4
Edges: (0,1,4), (0,2,2), (1,3,1), (1,4,2), (2,4,3)
Source: 0
Initial:
dist = [0, ∞, ∞, ∞, ∞]
pq = [(0, 0)]
Step 1: Poll (0, 0)
Relax 0→1: dist[1] = 4
Relax 0→2: dist[2] = 2
dist = [0, 4, 2, ∞, ∞]
pq = [(2, 2), (1, 4)]
Step 2: Poll (2, 2) - vertex 2 is closest
Relax 2→4: dist[4] = 2+3 = 5
dist = [0, 4, 2, ∞, 5]
pq = [(1, 4), (4, 5)]
Step 3: Poll (1, 4)
Relax 1→3: dist[3] = 4+1 = 5
Relax 1→4: dist[4] = min(5, 4+2) = 5 (no change)
dist = [0, 4, 2, 5, 5]
pq = [(4, 5), (3, 5)]
Step 4: Poll (4, 5) - no outgoing edges
Step 5: Poll (3, 5) - no outgoing edges
Final: dist = [0, 4, 2, 5, 5]
| Implementation | Time | Space |
|---|---|---|
| Binary Heap | O((V + E) log V) |
O(V) |
| Fibonacci Heap | O(V log V + E) |
O(V) |
| Array (no heap) | O(V²) |
O(V) |
| Graph Type | Best Implementation |
|---|---|
| Sparse (E ≈ V) | Binary heap: O(V log V) |
| Dense (E ≈ V²) | Array: O(V²) or Binary heap: O(V² log V) |
| Very large, sparse | Fibonacci heap (theory), Binary heap (practice) |
Interview tip: Binary heap is the standard choice. Fibonacci heap has better theoretical complexity but is rarely used in practice due to high constants.
| Aspect | Dijkstra | Bellman-Ford | Floyd-Warshall | BFS |
|---|---|---|---|---|
| Problem | Single-source | Single-source | All-pairs | Single-source |
| Weights | Non-negative only | Any (detects neg cycles) | Any (detects neg cycles) | Unweighted only |
| Negative cycles | Cannot detect | Detects them | Detects them | Cannot detect |
| Time | O((V+E) log V) |
O(V·E) |
O(V³) |
O(V+E) |
| Approach | Greedy | Relax V-1 times | DP on intermediates | Level-order |
Use Dijkstra when:
- Single-source shortest path needed
- All edge weights are non-negative
- You want the fastest algorithm for this case
Don't use Dijkstra when:
- Graph has negative edge weights → Use Bellman-Ford
- You need all-pairs shortest paths → Use Floyd-Warshall (small V) or V × Dijkstra (sparse)
- Graph is unweighted → Use BFS (simpler, same complexity)
Interview tip: If all edges have the same weight, BFS is simpler and faster than Dijkstra.
Interview tip: "When would you use Bellman-Ford over Dijkstra?" → When edges can be negative, or when you need to detect negative cycles.
If you only need the shortest path to one target:
while (!pq.isEmpty()) {
int[] curr = pq.poll();
int u = curr[0];
if (u == target) {
return dist[target]; // Found it!
}
// ... rest of algorithm
}Find not just the shortest, but the k shortest paths. Requires modification to allow revisiting vertices.
Run Dijkstra from both source and target simultaneously. Stop when searches meet. Can be up to 2× faster for point-to-point queries.
Dijkstra with a heuristic: prioritize vertices that seem closer to the target.
priority = dist[v] + heuristic(v, target)
Used in pathfinding (games, maps) when you have geometric information.
// WRONG: Dijkstra with negative weights
// Use Bellman-Ford instead// WRONG: Process every entry from priority queue
int[] curr = pq.poll();
// process...
// CORRECT: Skip if already processed with shorter distance
int[] curr = pq.poll();
if (curr[1] > dist[curr[0]]) continue; // Stale entry
// process...// WRONG: Can overflow
if (dist[u] + weight < dist[v])
// CORRECT: Check for infinity first
if (dist[u] != Integer.MAX_VALUE && dist[u] + weight < dist[v])| Application | Why Dijkstra? |
|---|---|
| GPS Navigation | Find shortest route between locations |
| Network Routing (OSPF) | Find shortest path in network topology |
| Social Networks | Degrees of separation |
| Game AI | Pathfinding for characters |
| Flight Booking | Cheapest flight path |
| Problem | Description |
|---|---|
| 743. Network Delay Time | Classic single-source shortest path |
| 787. Cheapest Flights Within K Stops | Dijkstra with constraint |
| 1514. Path with Maximum Probability | Max-heap variant (maximize instead of minimize) |
| 1631. Path With Minimum Effort | Dijkstra on grid |
| 778. Swim in Rising Water | Minimax path (modified Dijkstra) |
| 505. The Maze II | Dijkstra with special movement rules |
// Min-heap by distance
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[1] - b[1]);
// Add entries: {vertex, distance}
pq.offer(new int[]{source, 0});
// Poll minimum
int[] curr = pq.poll();
int vertex = curr[0];
int distance = curr[1];// List of edges from each vertex
List<List<int[]>> graph = new ArrayList<>();
// graph.get(u) = list of {neighbor, weight} pairs- Dijkstra is optimal for single-source shortest path with non-negative weights
- For dense graphs, the simple
O(V²)array-based version can outperform heap-based - The algorithm naturally handles disconnected graphs (unreachable vertices stay at infinity)
- Dijkstra was conceived by Edsger Dijkstra in 1956 and published in 1959