Dijkstra's Algorithm: Detailed Explanation
Algorithm Overview
Dijkstra's algorithm is a classic algorithm used to find the shortest path from a single source node to all other nodes in a weighted graph with non-negative edge weights.
Steps
- Initialize distances from the source to all nodes as infinity, except the source itself (0).
- Use a min-priority queue (min-heap) to select the node with the smallest known distance.
- For each neighbor of the current node, update their distances if a shorter path is found.
- Repeat until all nodes have been processed.
Time and Memory Complexity
- Time Complexity: O((V + E) log V) using a binary heap
- Space Complexity: O(V + E) for adjacency list and distance tracking
Easy Problems
1. Find shortest path in a small road network
// C++ Code
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int INF = 1e9;
void dijkstra(int src, vector<pair<int,int>> adj[], vector<int> &dist) {
priority_queue<pair<int,int>, vector<pair<int,int>>, greater<>> pq;
pq.push({0, src});
dist[src] = 0;
while (!pq.empty()) {
int u = pq.top().second, d = pq.top().first;
pq.pop();
if (d > dist[u]) continue;
for (auto [v, w] : adj[u]) {
if (dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
pq.push({dist[v], v});
}
}
}
}
2. Minimum time to deliver a message
Given nodes as communication towers, calculate the fastest time to deliver a message from one tower to another.
Intermediate Problems
1. Network Delay Time (Leetcode 743)
// Assume n nodes, times is edge list
int networkDelayTime(vector<vector<int>>& times, int n, int k) {
vector<pair<int,int>> adj[n+1];
for (auto &e : times)
adj[e[0]].push_back({e[1], e[2]});
vector<int> dist(n+1, 1e9);
dijkstra(k, adj, dist);
int res = *max_element(dist.begin()+1, dist.end());
return res == 1e9 ? -1 : res;
}
2. Cheapest Flights Within K Stops
Use Dijkstra variant with state tracking for number of stops.
int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int k) {
vector<pair<int,int>> adj[n];
for (auto &f : flights) adj[f[0]].emplace_back(f[1], f[2]);
queue<tuple<int,int,int>> q;
vector<int> cost(n, INT_MAX);
q.push({src, 0, 0});
while (!q.empty()) {
auto [u, c, stops] = q.front(); q.pop();
if (stops > k) continue;
for (auto [v, w] : adj[u]) {
if (c + w < cost[v]) {
cost[v] = c + w;
q.push({v, cost[v], stops+1});
}
}
}
return cost[dst] == INT_MAX ? -1 : cost[dst];
}
Hard Problem
1. Path with Minimum Effort (Leetcode 1631)
Apply Dijkstra with edge weights based on maximum effort.
int minimumEffortPath(vector<vector<int>>& heights) {
int m = heights.size(), n = heights[0].size();
vector<vector<int>> dist(m, vector<int>(n, INT_MAX));
priority_queue<tuple<int,int,int>, vector<tuple<int,int,int>>, greater<>> pq;
pq.push({0, 0, 0});
dist[0][0] = 0;
int dir[5] = {-1, 0, 1, 0, -1};
while (!pq.empty()) {
auto [d, x, y] = pq.top(); pq.pop();
if (x == m-1 && y == n-1) return d;
for (int i = 0; i < 4; ++i) {
int nx = x + dir[i], ny = y + dir[i+1];
if (nx >= 0 && ny >= 0 && nx < m && ny < n) {
int nd = max(d, abs(heights[nx][ny] - heights[x][y]));
if (nd < dist[nx][ny]) {
dist[nx][ny] = nd;
pq.push({nd, nx, ny});
}
}
}
}
return 0;
}
All these problems demonstrate the power and flexibility of Dijkstra’s algorithm in handling various pathfinding challenges in graphs.