🧠
Dijkstra's Algorithm with Min-Heap (Priority Queue)
💡 Dijkstra's algorithm efficiently finds shortest paths from a source to all nodes using a priority queue to always expand the closest node next.
Intuition
Use a min-heap to greedily pick the next node with the smallest known distance, updating neighbors' distances only if a shorter path is found.
Algorithm
- Build adjacency list from times.
- Initialize distance array with infinity, set source distance to 0.
- Use a min-heap to store nodes prioritized by current shortest distance.
- Pop the closest node from the heap, update distances of neighbors if shorter paths are found, and push updated neighbors into the heap.
- After processing all reachable nodes, return the maximum distance or -1 if any node is unreachable.
💡 The priority queue ensures we always process the next closest node, avoiding unnecessary work and infinite loops.
import heapq
from collections import defaultdict
def network_delay_time(times, n, k):
graph = defaultdict(list)
for u, v, w in times:
graph[u].append((v, w))
dist = [float('inf')] * (n + 1)
dist[k] = 0
heap = [(0, k)]
while heap:
time, node = heapq.heappop(heap)
if time > dist[node]:
continue
for nei, w in graph[node]:
if dist[node] + w < dist[nei]:
dist[nei] = dist[node] + w
heapq.heappush(heap, (dist[nei], nei))
ans = max(dist[1:])
return ans if ans < float('inf') else -1
# Example usage
if __name__ == '__main__':
times = [[2,1,1],[2,3,1],[3,4,1]]
n = 4
k = 2
print(network_delay_time(times, n, k))
Line Notes
graph = defaultdict(list)Build adjacency list for quick neighbor access to efficiently find edges.
dist = [float('inf')] * (n + 1)Initialize distances as infinity to represent unknown shortest paths.
heap = [(0, k)]Min-heap initialized with source node and distance 0 to start processing.
if time > dist[node]Skip outdated heap entries to avoid redundant processing of stale distances.
heapq.heappush(heap, (dist[nei], nei))Push updated neighbor distances into heap to process next.
import java.util.*;
public class NetworkDelayTime {
public static int networkDelayTime(int[][] times, int N, int K) {
Map<Integer, List<int[]>> graph = new HashMap<>();
for (int[] time : times) {
graph.computeIfAbsent(time[0], x -> new ArrayList<>()).add(new int[]{time[1], time[2]});
}
int[] dist = new int[N + 1];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[K] = 0;
PriorityQueue<int[]> heap = new PriorityQueue<>(Comparator.comparingInt(a -> a[0]));
heap.offer(new int[]{0, K});
while (!heap.isEmpty()) {
int[] cur = heap.poll();
int time = cur[0], node = cur[1];
if (time > dist[node]) continue;
for (int[] edge : graph.getOrDefault(node, new ArrayList<>())) {
int nei = edge[0], w = edge[1];
if (dist[node] + w < dist[nei]) {
dist[nei] = dist[node] + w;
heap.offer(new int[]{dist[nei], nei});
}
}
}
int ans = 0;
for (int i = 1; i <= N; i++) {
if (dist[i] == Integer.MAX_VALUE) return -1;
ans = Math.max(ans, dist[i]);
}
return ans;
}
public static void main(String[] args) {
int[][] times = {{2,1,1},{2,3,1},{3,4,1}};
int n = 4, k = 2;
System.out.println(networkDelayTime(times, n, k));
}
}
Line Notes
Map<Integer, List<int[]>> graph = new HashMap<>()Adjacency list for graph edges to quickly access neighbors.
Arrays.fill(dist, Integer.MAX_VALUE)Initialize distances to max value to represent unknown paths.
PriorityQueue<int[]> heap = new PriorityQueue<>Min-heap to pick the closest node next for processing.
if (time > dist[node]) continueSkip stale entries in heap to avoid redundant work.
heap.offer(new int[]{dist[nei], nei})Add updated neighbor distances to heap for future processing.
#include <bits/stdc++.h>
using namespace std;
int networkDelayTime(vector<vector<int>>& times, int N, int K) {
unordered_map<int, vector<pair<int,int>>> graph;
for (auto &time : times) {
graph[time[0]].push_back({time[1], time[2]});
}
vector<int> dist(N + 1, INT_MAX);
dist[K] = 0;
priority_queue<pair<int,int>, vector<pair<int,int>>, greater<>> pq;
pq.push({0, K});
while (!pq.empty()) {
auto [time, node] = pq.top(); pq.pop();
if (time > dist[node]) continue;
for (auto &[nei, w] : graph[node]) {
if (dist[node] + w < dist[nei]) {
dist[nei] = dist[node] + w;
pq.push({dist[nei], nei});
}
}
}
int ans = 0;
for (int i = 1; i <= N; i++) {
if (dist[i] == INT_MAX) return -1;
ans = max(ans, dist[i]);
}
return ans;
}
int main() {
vector<vector<int>> times = {{2,1,1},{2,3,1},{3,4,1}};
int n = 4, k = 2;
cout << networkDelayTime(times, n, k) << "\n";
return 0;
}
Line Notes
unordered_map<int, vector<pair<int,int>>> graphAdjacency list for graph edges to efficiently find neighbors.
vector<int> dist(N + 1, INT_MAX)Initialize distances to max int to represent unknown shortest paths.
priority_queue<pair<int,int>, vector<pair<int,int>>, greater<>> pqMin-heap for selecting the closest node next.
if (time > dist[node]) continueSkip outdated heap entries to avoid redundant processing.
pq.push({dist[nei], nei})Push updated neighbor distances into heap for processing.
function networkDelayTime(times, n, k) {
const graph = new Map();
for (const [u, v, w] of times) {
if (!graph.has(u)) graph.set(u, []);
graph.get(u).push([v, w]);
}
const dist = new Array(n + 1).fill(Infinity);
dist[k] = 0;
const heap = new MinPriorityQueue({priority: x => x[0]});
heap.enqueue([0, k]);
while (!heap.isEmpty()) {
const [time, node] = heap.dequeue().element;
if (time > dist[node]) continue;
if (!graph.has(node)) continue;
for (const [nei, w] of graph.get(node)) {
if (dist[node] + w < dist[nei]) {
dist[nei] = dist[node] + w;
heap.enqueue([dist[nei], nei]);
}
}
}
const ans = Math.max(...dist.slice(1));
return ans === Infinity ? -1 : ans;
}
// Example usage
// Note: MinPriorityQueue is from '@datastructures-js/priority-queue' or similar library
// For testing here, a simple priority queue implementation is needed or use a polyfill
// console.log(networkDelayTime([[2,1,1],[2,3,1],[3,4,1]], 4, 2));
Line Notes
const graph = new Map()Build adjacency list for graph to quickly find neighbors.
const dist = new Array(n + 1).fill(Infinity)Initialize distances to infinity to represent unknown paths.
const heap = new MinPriorityQueue({priority: x => x[0]})Min-heap to pick the closest node next for processing.
if (time > dist[node]) continueSkip stale heap entries to avoid redundant work.
heap.enqueue([dist[nei], nei])Add updated neighbor distances to heap for processing.
TimeO(E log N) where E is edges and N is nodes due to priority queue operations
SpaceO(N + E) for graph and distance arrays
Each edge is processed once and each node is pushed into the heap multiple times but bounded by O(E log N).
💡 For n=10^5 and E=10^5, this approach is efficient enough to run within time limits.
Interview Verdict: Accepted / Optimal for large inputs
This is the standard solution expected in interviews for weighted shortest path problems.