Skip to content
advancedPhase 5 · Graphs

Shortest Path

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

1h 30m
6 problems
Topic Progress0%

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/3solved
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 the primary purpose of Shortest Path Algorithms?

Question 3 options

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

Question 4 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

What is Shortest Path Algorithms?

Answer

Shortest Path Algorithms is a key concept in software engineering.

Question

When to use Shortest Path Algorithms?

Answer

Use Shortest Path Algorithms when building production systems that require reliability, scalability, and maintainability.

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