Dijkstra's Algorithm
Dijkstra's Algorithm
Dijkstra's algorithm finds the shortest path from a source vertex to all other vertices in a weighted graph with non-negative edge weights.
Core Idea
- Maintain distance to each vertex (initialize source as 0, others as ∞)
- Use a priority queue to always process the closest unvisited vertex
- Relax edges: if path through current vertex is shorter, update distance
Implementation
import java.util.*;
public class Dijkstra {
// Using priority queue (min-heap)
public int[] dijkstra(List<int[]>[] graph, int start, int n) {
int[] dist = new int[n];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[start] = 0;
// PriorityQueue stores {distance, vertex}
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[0] - b[0]);
pq.offer(new int[]{0, start});
while (!pq.isEmpty()) {
int[] current = pq.poll();
int d = current[0];
int u = current[1];
// Skip if we already found a shorter path
if (d > dist[u]) continue;
for (int[] edge : graph[u]) {
int v = edge[0];
int weight = edge[1];
// Relaxation
if (dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
pq.offer(new int[]{dist[v], v});
}
}
}
return dist;
}
}
Dijkstra with Path Reconstruction
public int[] dijkstraWithPath(List<int[]>[] graph, int start, int n) {
int[] dist = new int[n];
int[] prev = new int[n];
Arrays.fill(dist, Integer.MAX_VALUE);
Arrays.fill(prev, -1);
dist[start] = 0;
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[0] - b[0]);
pq.offer(new int[]{0, start});
while (!pq.isEmpty()) {
int[] current = pq.poll();
int d = current[0];
int u = current[1];
if (d > dist[u]) continue;
for (int[] edge : graph[u]) {
int v = edge[0];
int weight = edge[1];
if (dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
prev[v] = u;
pq.offer(new int[]{dist[v], v});
}
}
}
return dist;
}
// Reconstruct path from start to target
public List<Integer> getPath(int[] prev, int target) {
List<Integer> path = new ArrayList<>();
for (int v = target; v != -1; v = prev[v]) {
path.add(v);
}
Collections.reverse(path);
return path;
}
Complexity Analysis
| Operation | Time | Space |
|---|---|---|
| Dijkstra (binary heap) | O((V + E) log V) | O(V) |
| Dijkstra (fibonacci heap) | O(E + V log V) | O(V) |
Limitations
- No negative weights: Dijkstra doesn't work with negative edges
- No negative cycles: Algorithm assumes optimal substructure
When to Use
- Single-source shortest path in non-negative weighted graph
- Network routing protocols (OSPF)
- GPS navigation systems
Bellman-Ford and Floyd-Warshall
Bellman-Ford Algorithm
Bellman-Ford handles negative edge weights and can detect negative cycles.
public class BellmanFord {
public int[] bellmanFord(int n, int[][] edges, int start) {
int[] dist = new int[n];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[start] = 0;
// Relax edges V-1 times
for (int i = 0; i < n - 1; i++) {
for (int[] edge : edges) {
int u = edge[0];
int v = edge[1];
int weight = edge[2];
if (dist[u] != Integer.MAX_VALUE && dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
}
}
}
// Check for negative cycles
for (int[] edge : edges) {
int u = edge[0];
int v = edge[1];
int weight = edge[2];
if (dist[u] != Integer.MAX_VALUE && dist[u] + weight < dist[v]) {
throw new RuntimeException("Negative cycle detected!");
}
}
return dist;
}
}
Floyd-Warshall Algorithm
Finds shortest paths between all pairs of vertices.
public class FloydWarshall {
public int[][] floydWarshall(int n, int[][] edges) {
int[][] dist = new int[n][n];
// Initialize: ∞ for no edge, 0 for same vertex
for (int i = 0; i < n; i++) {
Arrays.fill(dist[i], Integer.MAX_VALUE / 2); // Avoid overflow
dist[i][i] = 0;
}
// Add edges
for (int[] edge : edges) {
dist[edge[0]][edge[1]] = edge[2];
}
// Floyd-Warshall: try all intermediate vertices
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
}
return dist;
}
}
Algorithm Comparison
| Algorithm | Time | Space | Negative Weights | Use Case |
|---|---|---|---|---|
| Dijkstra | O((V+E) log V) | O(V) | ❌ No | Single-source, non-negative |
| Bellman-Ford | O(V × E) | O(V) | ✅ Yes | Single-source, negative edges |
| Floyd-Warshall | O(V³) | O(V²) | ✅ Yes | All-pairs shortest path |
When to Use Each
| Scenario | Best Algorithm |
|---|---|
| Single source, non-negative weights | Dijkstra |
| Single source, negative weights | Bellman-Ford |
| All pairs shortest path | Floyd-Warshall |
| Detect negative cycle | Bellman-Ford |
| Dense graph, all pairs | Floyd-Warshall |
| Sparse graph, single source | Dijkstra |
Network Delay Time (Dijkstra Template)
public int networkDelayTime(int[][] times, int n, int k) {
List<int[]>[] graph = new ArrayList[n + 1];
for (int i = 0; i <= n; i++) graph[i] = new ArrayList<>();
for (int[] time : times) {
graph[time[0]].add(new int[]{time[1], time[2]});
}
int[] dist = new int[n + 1];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[k] = 0;
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[1] - b[1]);
pq.offer(new int[]{k, 0});
while (!pq.isEmpty()) {
int[] curr = pq.poll();
int node = curr[0], d = curr[1];
if (d > dist[node]) continue;
for (int[] edge : graph[node]) {
int next = edge[0], weight = edge[1];
if (dist[node] + weight < dist[next]) {
dist[next] = dist[node] + weight;
pq.offer(new int[]{next, dist[next]});
}
}
}
int max = 0;
for (int i = 1; i <= n; i++) {
if (dist[i] == Integer.MAX_VALUE) return -1;
max = Math.max(max, dist[i]);
}
return max;
}
Practice Problems
Given network of n nodes and times[i] = (ui, vi, wi) where ui is source, vi is target, wi is time for signal to travel. Return minimum time for signal to reach all nodes, or -1 if impossible.
Example:
Input: times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2
Output: 2
Signal from node 2 reaches node 1 in 1 unit, node 3 in 1 unit, and node 4 in 2 units.
Solution
```java
public int networkDelayTime(int[][] times, int n, int k) {
List<int[]>[] graph = new ArrayList[n + 1];
for (int i = 0; i <= n; i++) graph[i] = new ArrayList<>();
for (int[] time : times) {
graph[time[0]].add(new int[]{time[1], time[2]});
}
int[] dist = new int[n + 1];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[k] = 0;
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[1] - b[1]);
pq.offer(new int[]{k, 0});
while (!pq.isEmpty()) {
int[] curr = pq.poll();
int node = curr[0], d = curr[1];
if (d > dist[node]) continue;
for (int[] edge : graph[node]) {
int next = edge[0], weight = edge[1];
if (dist[node] + weight < dist[next]) {
dist[next] = dist[node] + weight;
pq.offer(new int[]{next, dist[next]});
}
}
}
int max = 0;
for (int i = 1; i <= n; i++) {
if (dist[i] == Integer.MAX_VALUE) return -1;
max = Math.max(max, dist[i]);
}
return max;
}
```Edge Cases:
- Some nodes unreachable (return -1)
- Single node network
- Negative edge weights (use Bellman-Ford)
- Multiple paths between nodes
Find the cheapest price from src to dst with at most k stops.
Example:
Input: n=4, flights=[[0,1,100],[1,2,100],[2,0,100],[1,3,600],[2,3,200]], src=0, dst=3, k=1
Output: 700
0->1->3 costs 700.
Solution
```java
public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
List<int[]>[] graph = new ArrayList[n];
for (int i = 0; i < n; i++) graph[i] = new ArrayList<>();
for (int[] f : flights) graph[f[0]].add(new int[]{f[1], f[2]});
int[] dist = new int[n];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[src] = 0;
for (int i = 0; i <= k; i++) {
int[] temp = dist.clone();
for (int u = 0; u < n; u++) {
if (dist[u] == Integer.MAX_VALUE) continue;
for (int[] edge : graph[u]) {
int v = edge[0], w = edge[1];
if (dist[u] + w < temp[v]) temp[v] = dist[u] + w;
}
}
dist = temp;
}
return dist[dst] == Integer.MAX_VALUE ? -1 : dist[dst];
}
```Edge Cases:
- No path exists
- K=0 (direct flights only)
- Destination unreachable
Find a path from start to end with maximum success probability.
Example:
Input: n=3, edges=[[0,1,0.5],[1,2,0.5],[0,2,0.2]], start=0, end=2
Output: 0.25
Path 0->1->2 has probability 0.5*0.5=0.25.
Solution
```java
public double maxProbability(int n, int[][] edges, double[] succProb, int start, int end) {
List<double[]>[] graph = new ArrayList[n];
for (int i = 0; i < n; i++) graph[i] = new ArrayList<>();
for (int i = 0; i < edges.length; i++) {
graph[edges[i][0]].add(new double[]{edges[i][1], succProb[i]});
graph[edges[i][1]].add(new double[]{edges[i][0], succProb[i]});
}
double[] prob = new double[n];
prob[start] = 1.0;
PriorityQueue<double[]> pq = new PriorityQueue<>((a, b) -> Double.compare(b[1], a[1]));
pq.offer(new double[]{start, 1.0});
while (!pq.isEmpty()) {
double[] curr = pq.poll();
int node = (int) curr[0];
double p = curr[1];
if (p < prob[node]) continue;
if (node == end) return p;
for (double[] edge : graph[node]) {
int next = (int) edge[0];
double newProb = p * edge[1];
if (newProb > prob[next]) {
prob[next] = newProb;
pq.offer(new double[]{next, newProb});
}
}
}
return 0.0;
}
```Edge Cases:
- No path exists
- Self-loop
- Multiple edges between same nodes
Quiz
1. Why can't Dijkstra's algorithm handle negative edge weights?
2. When should you use Floyd-Warshall over Dijkstra?
3. What is the primary purpose of Shortest Path Algorithms?
4. What is a common mistake when implementing Shortest Path Algorithms?
Flashcards
Question
What is the time complexity of Dijkstra's algorithm?
Click to reveal answer
Answer
O((V + E) log V) with a binary heap (priority queue). With a fibonacci heap, it's O(E + V log V). For dense graphs (E ≈ V²), it's O(V² log V).
Question
How does Bellman-Ford detect negative cycles?
Click to reveal answer
Answer
After V-1 relaxation passes (which should find all shortest paths), run one more pass. If any distance can still be reduced, a negative cycle exists.
Question
What is Shortest Path Algorithms?
Click to reveal answer
Answer
Shortest Path Algorithms is a key concept in software engineering.
Question
When to use Shortest Path Algorithms?
Click to reveal answer
Answer
Use Shortest Path Algorithms when building production systems that require reliability, scalability, and maintainability.
Question
Shortest Path Algorithms best practices
Click to reveal answer
Answer
Follow SOLID principles, write clean code, test thoroughly, document decisions, and monitor in production.
Revision Notes
Key Takeaways
- 1.Dijkstra: greedy, priority queue, non-negative weights only
- 2.Bellman-Ford: handle negative weights, detect negative cycles
- 3.Floyd-Warshall: all-pairs shortest path, O(V³)
- 4.Dijkstra's key check: if d > dist[u] continue (skip outdated entries)
- 5.Bellman-Ford: relax V-1 times, then check for negative cycles
Interview Tips
- •Dijkstra is the most common - memorize the priority queue template
- •Always check: are there negative weights? If yes, use Bellman-Ford
- •For 'shortest path' in unweighted graph, use BFS instead
- •Floyd-Warshall is great for dense graphs or when you need all pairs
- •Practice: Network Delay Time, Cheapest Flights, Path with Minimum Effort
Cheat Sheet
Shortest Path Algorithms Cheat Sheet
Dijkstra's Algorithm
int[] dist = new int[n];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[start] = 0;
PriorityQueue<int[]> pq = new PriorityQueue<>((a,b) -> a[0] - b[0]);
pq.offer(new int[]{0, start});
while (!pq.isEmpty()) {
int[] curr = pq.poll();
int d = curr[0], u = curr[1];
if (d > dist[u]) continue;
for (int[] edge : graph[u]) {
int v = edge[0], w = edge[1];
if (dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
pq.offer(new int[]{dist[v], v});
}
}
}
Bellman-Ford
int[] dist = new int[n];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[start] = 0;
for (int i = 0; i < n-1; i++) {
for (int[] edge : edges) {
int u = edge[0], v = edge[1], w = edge[2];
if (dist[u] != INF && dist[u] + w < dist[v])
dist[v] = dist[u] + w;
}
}
// Check negative cycle: one more pass, if any dist reduces → cycle
Floyd-Warshall
int[][] dist = new int[n][n];
// Init: dist[i][j] = edge weight or INF, dist[i][i] = 0
for (int k = 0; k < n; k++)
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]);
Algorithm Selection
| Scenario | Algorithm |
|---|---|
| Single source, non-negative | Dijkstra |
| Single source, negative edges | Bellman-Ford |
| All pairs | Floyd-Warshall |
| Detect negative cycle | Bellman-Ford |
Complexity
- Dijkstra: O((V+E) log V)
- Bellman-Ford: O(V × E)
- Floyd-Warshall: O(V³)