Skip to content
advanced Phase 5 · Graphs

Shortest Path

Master Dijkstra's, Bellman-Ford, and Floyd-Warshall algorithms.

1h 30m
6 problems
Topic Progress 0%

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

  1. Maintain distance to each vertex (initialize source as 0, others as ∞)
  2. Use a priority queue to always process the closest unvisited vertex
  3. 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

0 / 3 solved
Network Delay Time
Dijkstra's Algorithm

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
Cheapest Flights Within K Stops
Modified Dijkstra / BFS with Pruning

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
Path with Maximum Probability
Modified Dijkstra (Max)

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?

Question 1 options

2. When should you use Floyd-Warshall over Dijkstra?

Question 2 options

3. What is a common mistake when implementing Shortest Path Algorithms?

Question 3 options

Flashcards

Question

What is the time complexity of Dijkstra's algorithm?

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?

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

Shortest Path Algorithms best practices

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³)