Dijkstra & Bellman-Ford Algorithms¶
Dijkstra's algorithm and Bellman-Ford algorithm are fundamental shortest path algorithms for weighted graphs. They differ in their assumptions about edge weights and their time complexity, making them suitable for different types of problems.
Dijkstra's Algorithm¶
Dijkstra's algorithm finds the shortest path from a source vertex to all other vertices in a graph with non-negative edge weights. It uses a greedy approach with a priority queue.
How It Works¶
- Initialize: Set distance to source as 0, all others as infinity
- Priority Queue: Use a min-heap to always process the vertex with minimum distance
- Relaxation: For each vertex, update distances to its neighbors if a shorter path is found
- Greedy Choice: Always process the unvisited vertex with minimum distance
Basic Implementation¶
Problem: Find shortest distances from a source vertex to all other vertices using Dijkstra's algorithm.
Sample Input:
- Graph: {0: [(1,4), (2,1)], 1: [(3,1)], 2: [(1,2), (3,5)], 3: []}
- Source: 0
Sample Output: [0, 3, 1, 4] (Shortest distances from 0 to all vertices)
#include <vector>
#include <queue>
#include <climits>
using namespace std;
class Dijkstra {
public:
vector<int> shortestPath(vector<vector<pair<int, int>>>& graph, int source) {
int n = graph.size();
vector<int> dist(n, INT_MAX);
vector<bool> visited(n, false);
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
dist[source] = 0;
pq.push({0, source});
while (!pq.empty()) {
int u = pq.top().second;
pq.pop();
if (visited[u]) continue;
visited[u] = true;
for (auto& edge : graph[u]) {
int v = edge.first;
int weight = edge.second;
if (dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
pq.push({dist[v], v});
}
}
}
return dist;
}
};
Dijkstra with Path Reconstruction¶
Problem: Find shortest path and reconstruct the actual path from source to destination.
Sample Input:
- Graph: {0: [(1,4), (2,1)], 1: [(3,1)], 2: [(1,2), (3,5)], 3: []}
- Source: 0, Destination: 3
Sample Output:
- Distance: 4
- Path: [0, 2, 1, 3]
#include <vector>
#include <queue>
#include <climits>
using namespace std;
class DijkstraWithPath {
public:
pair<int, vector<int>> shortestPath(vector<vector<pair<int, int>>>& graph,
int source, int destination) {
int n = graph.size();
vector<int> dist(n, INT_MAX);
vector<int> parent(n, -1);
vector<bool> visited(n, false);
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
dist[source] = 0;
pq.push({0, source});
while (!pq.empty()) {
int u = pq.top().second;
pq.pop();
if (visited[u]) continue;
visited[u] = true;
if (u == destination) break;
for (auto& edge : graph[u]) {
int v = edge.first;
int weight = edge.second;
if (dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
parent[v] = u;
pq.push({dist[v], v});
}
}
}
// Reconstruct path
vector<int> path;
if (dist[destination] != INT_MAX) {
int current = destination;
while (current != -1) {
path.push_back(current);
current = parent[current];
}
reverse(path.begin(), path.end());
}
return {dist[destination], path};
}
};
Bellman-Ford Algorithm¶
Bellman-Ford algorithm finds shortest paths from a source vertex to all other vertices, even with negative edge weights. It can also detect negative cycles.
How It Works¶
- Initialize: Set distance to source as 0, all others as infinity
- Relaxation: For each edge, try to improve the distance to the destination
- Repeat: Perform relaxation V-1 times (where V is number of vertices)
- Negative Cycle Check: If one more relaxation improves any distance, negative cycle exists
Basic Implementation¶
Problem: Find shortest distances using Bellman-Ford algorithm, handling negative weights.
Sample Input:
- Graph: {0: [(1,4), (2,1)], 1: [(3,1)], 2: [(1,2), (3,5)], 3: []}
- Source: 0
Sample Output: [0, 3, 1, 4] (Shortest distances from 0 to all vertices)
#include <vector>
#include <climits>
using namespace std;
class BellmanFord {
public:
vector<int> shortestPath(vector<vector<pair<int, int>>>& graph, int source) {
int n = graph.size();
vector<int> dist(n, INT_MAX);
dist[source] = 0;
// Relax all edges V-1 times
for (int i = 0; i < n - 1; i++) {
for (int u = 0; u < n; u++) {
if (dist[u] != INT_MAX) {
for (auto& edge : graph[u]) {
int v = edge.first;
int weight = edge.second;
if (dist[u] + weight < dist[v]) {
dist[v] = dist[u] + weight;
}
}
}
}
}
return dist;
}
bool hasNegativeCycle(vector<vector<pair<int, int>>>& graph) {
int n = graph.size();
vector<int> dist(n, 0); // Start with all distances as 0
// Relax all edges V times
for (int i = 0; i < n; i++) {
for (int u = 0; u < n; u++) {
for (auto& edge : graph[u]) {
int v = edge.first;
int weight = edge.second;
if (dist[u] + weight < dist[v]) {
if (i == n - 1) {
return true; // Negative cycle detected
}
dist[v] = dist[u] + weight;
}
}
}
}
return false;
}
};
Advanced Applications¶
All-Pairs Shortest Path (Floyd-Warshall)¶
Problem: Find shortest distances between all pairs of vertices.
Sample Input:
Sample Output:
#include <vector>
#include <algorithm>
using namespace std;
class FloydWarshall {
public:
vector<vector<int>> allPairsShortestPath(vector<vector<int>>& graph) {
int n = graph.size();
vector<vector<int>> dist = graph;
// Floyd-Warshall algorithm
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (dist[i][k] != INT_MAX && dist[k][j] != INT_MAX) {
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
}
}
return dist;
}
};
Shortest Path with K Stops¶
Problem: Find cheapest flight route with at most K stops.
Sample Input:
- Flights: [[0,1,100],[1,2,100],[0,2,500]]
- Source: 0, Destination: 2, Stops: 1
Sample Output: 200 (Route: 0 → 1 → 2 with 1 stop)
#include <vector>
#include <queue>
#include <climits>
using namespace std;
class ShortestPathKStops {
public:
int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int k) {
vector<vector<pair<int, int>>> graph(n);
// Build adjacency list
for (auto& flight : flights) {
graph[flight[0]].push_back({flight[1], flight[2]});
}
// {cost, city, stops_remaining}
priority_queue<vector<int>, vector<vector<int>>, greater<vector<int>>> pq;
pq.push({0, src, k + 1});
while (!pq.empty()) {
auto current = pq.top();
pq.pop();
int cost = current[0];
int city = current[1];
int stops = current[2];
if (city == dst) {
return cost;
}
if (stops > 0) {
for (auto& edge : graph[city]) {
int nextCity = edge.first;
int nextCost = edge.second;
pq.push({cost + nextCost, nextCity, stops - 1});
}
}
}
return -1;
}
};
Comparison and Use Cases¶
Dijkstra vs Bellman-Ford¶
| Aspect | Dijkstra | Bellman-Ford |
|---|---|---|
| Edge Weights | Non-negative only | Negative weights allowed |
| Time Complexity | O((V + E) log V) | O(VE) |
| Space Complexity | O(V) | O(V) |
| Negative Cycles | Cannot detect | Can detect |
| Best For | Dense graphs, non-negative weights | Sparse graphs, negative weights |
When to Use Each Algorithm¶
Use Dijkstra when: - All edge weights are non-negative - You need the shortest path from one source - Graph is dense (many edges) - You need optimal time complexity
Use Bellman-Ford when: - Graph may have negative edge weights - You need to detect negative cycles - Graph is sparse (few edges) - You need to find shortest paths from one source
Use Floyd-Warshall when: - You need shortest paths between all pairs - Graph is small (V ≤ 400) - You need to detect negative cycles in the entire graph
Use Cases¶
Real-World Applications¶
- GPS Navigation: Finding shortest routes
- Network Routing: Optimizing data packet paths
- Social Networks: Finding shortest connection paths
- Game AI: Pathfinding in games
- Resource Allocation: Optimizing resource distribution
Competitive Programming¶
- Shortest Path Problems: Standard shortest path queries
- Negative Cycle Detection: Finding problematic cycles
- All-Pairs Shortest Path: When you need all distances
- Constrained Shortest Path: With additional constraints
- Graph Optimization: Various graph optimization problems
Complexity Analysis¶
Time Complexity¶
- Dijkstra: O((V + E) log V) with binary heap
- Bellman-Ford: O(VE)
- Floyd-Warshall: O(V³)
Space Complexity¶
- Dijkstra: O(V) for distance array and priority queue
- Bellman-Ford: O(V) for distance array
- Floyd-Warshall: O(V²) for distance matrix
Optimization Tips¶
- Choose Right Algorithm: Use Dijkstra for non-negative weights, Bellman-Ford for negative weights
- Data Structures: Use appropriate priority queue implementations
- Early Termination: Stop when destination is reached (for single-source)
- Path Reconstruction: Store parent pointers for path recovery
- Negative Cycle Detection: Always check for negative cycles with Bellman-Ford
- Memory Management: Use adjacency lists for sparse graphs, matrices for dense graphs