Loading problems...
TL;DR: To solve LeetCode 743, implement Dijkstra's Algorithm to find the shortest path from the source node to all other nodes, then return the maximum of these shortest path values.
The Network Delay Time problem asks us to simulate a signal spreading through a network of nodes. Given a list of directed edges with associated travel times (weights) and a starting node k, we must determine how long it takes for the signal to reach every node in the graph. If the signal cannot reach every node, we must return -1.
This is a classic variation of the Single-Source Shortest Path problem. We are essentially looking for the "longest shortest path" from the source k. The time it takes for the last node to receive the signal is determined by the node that is "furthest" away from k in terms of travel time.
A naive approach to solving this problem involves exploring all possible paths from the source node k to every other node to find the minimum travel time. This can be attempted using a standard Depth-First Search (DFS).
In this brute force method, we would recursively traverse the graph. For every node, we would track the current accumulated time. If we reach a node with a smaller time than previously recorded, we update it and continue traversing its neighbors.
textfunction dfs(node, currentTime): if currentTime >= recordedTime[node]: return recordedTime[node] = currentTime for neighbor, weight in graph[node]: dfs(neighbor, currentTime + weight)
The intuition for moving from a brute force approach to Dijkstra's Algorithm lies in the greedy nature of signal propagation. A signal always arrives at a node via the fastest available route.
If we are standing at the source node k, the very first node the signal will reach is the immediate neighbor connected by the edge with the smallest weight. Once the signal reaches that neighbor, we can consider that node "visited" or "finalized" because no other path could possibly reach it faster (assuming non-negative weights).
Key Invariant: Dijkstra's algorithm maintains a priority queue of nodes to visit, ordered by their current known distance from the source. The invariant is that when we extract a node from the priority queue, the distance associated with it is the globally optimal shortest path to that node.
Visual Description:
Imagine the algorithm as a wavefront expanding from node k.
k is reachable (time 0).
To implement the optimal solution for LeetCode 743, we strictly follow the Dijkstra subpattern:
times list into an adjacency list where graph[u] contains pairs of (v, w).dist (or a hash map) initialized to infinity (∞) for all nodes, except the source node k, which is initialized to 0. This array tracks the shortest time found so far to reach each node.(time, node). Initialize it with (0, k). The heap ensures we always process the node with the smallest accumulated time next.(curr_time, u).curr_time is greater than dist[u], it means we found a shorter path to u previously, and this is a "stale" entry. Discard it.v of u with weight w.curr_time + w < dist[v], we have found a faster way to reach v. Update dist[v] = curr_time + w and push (dist[v], v) into the heap.dist array.
-1.dist array (ignoring the 0th index if using 1-based indexing).Let's trace the algorithm with Example 1: times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2.
Initialization:
{2: [(1, 1), (3, 1)], 3: [(4, 1)]}dist array: [INF, INF, 0, INF, INF] (Indices 0 to 4, 0 is unused)[(0, 2)] (Format: time, node)Iteration 1:
(0, 2). Node 2 is finalized.0 + 1 < INF. Update dist[1] = 1. Push (1, 1).0 + 1 < INF. Update dist[3] = 1. Push (1, 3).[(1, 1), (1, 3)]Iteration 2:
(1, 1). Node 1 is finalized.[(1, 3)]Iteration 3:
(1, 3). Node 3 is finalized.1 + 1 < INF. Update dist[4] = 2. Push (2, 4).[(2, 4)]Iteration 4:
(2, 4). Node 4 is finalized.[] (Empty)Final Calculation:
dist array: [INF, 1, 0, 1, 2]2.2.Dijkstra's algorithm is proven correct for graphs with non-negative edge weights. The correctness relies on the greedy choice property. When we extract a node u with distance d from the priority queue, we assert that there is no other path to u shorter than d.
Suppose there existed a path shorter than d. This path would have to pass through some other node v currently in the priority queue (or the frontier). However, since u was picked before v, the distance to v must be greater than or equal to d (since the queue is a Min-Heap). Because edge weights are non-negative, extending the path from v can only increase the total distance. Thus, no path through v can result in a shorter distance to u.
This guarantees that dist contains the true shortest path for all reachable nodes.
cpp#include <vector> #include <queue> #include <algorithm> using namespace std; class Solution { public: int networkDelayTime(vector<vector<int>>& times, int n, int k) { // Adjacency list: node -> vector of {neighbor, weight} vector<vector<pair<int, int>>> adj(n + 1); for (const auto& t : times) { adj[t[0]].push_back({t[1], t[2]}); } // Min-heap priority queue: {time, node} // Orders by time ascending priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq; // Distance array initialized to infinity const int INF = 1e9; vector<int> dist(n + 1, INF); // Start from node k dist[k] = 0; pq.push({0, k}); while (!pq.empty()) { int currTime = pq.top().first; int u = pq.top().second; pq.pop(); // If we found a shorter path to u already, skip this stale entry if (currTime > dist[u]) { continue; } for (auto& edge : adj[u]) { int v = edge.first; int weight = edge.second; // Relaxation step if (dist[u] + weight < dist[v]) { dist[v] = dist[u] + weight; pq.push({dist[v], v}); } } } // Find the maximum time needed int maxTime = 0; for (int i = 1; i <= n; ++i) { if (dist[i] == INF) return -1; // Unreachable node maxTime = max(maxTime, dist[i]); } return maxTime; } };
javaimport java.util.*; class Solution { public int networkDelayTime(int[][] times, int n, int k) { // Adjacency list Map<Integer, List<int[]>> graph = new HashMap<>(); for (int[] time : times) { graph.putIfAbsent(time[0], new ArrayList<>()); graph.get(time[0]).add(new int[]{time[1], time[2]}); } // Min-heap storing {time, node} PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[0] - b[0]); // Distance array int[] dist = new int[n + 1]; Arrays.fill(dist, Integer.MAX_VALUE); dist[k] = 0; pq.offer(new int[]{0, k}); while (!pq.isEmpty()) { int[] curr = pq.poll(); int currTime = curr[0]; int u = curr[1]; // Skip stale entries if (currTime > dist[u]) { continue; } if (graph.containsKey(u)) { for (int[] edge : graph.get(u)) { int v = edge[0]; int weight = edge[1]; if (dist[u] + weight < dist[v]) { dist[v] = dist[u] + weight; pq.offer(new int[]{dist[v], v}); } } } } int maxTime = 0; for (int i = 1; i <= n; i++) { if (dist[i] == Integer.MAX_VALUE) return -1; maxTime = Math.max(maxTime, dist[i]); } return maxTime; } }
pythonimport heapq from collections import defaultdict class Solution: def networkDelayTime(self, times: list[list[int]], n: int, k: int) -> int: # Build adjacency list graph = defaultdict(list) for u, v, w in times: graph[u].append((v, w)) # Min-heap stores (time, node) pq = [(0, k)] # Track minimum time to reach each node # Initialize with infinity dist = {i: float('inf') for i in range(1, n + 1)} dist[k] = 0 while pq: curr_time, u = heapq.heappop(pq) # Optimization: If we pulled a stale path, ignore it if curr_time > dist[u]: continue for v, weight in graph[u]: if dist[u] + weight < dist[v]: dist[v] = dist[u] + weight heapq.heappush(pq, (dist[v], v)) # Find the max time among all shortest paths max_time = max(dist.values()) return max_time if max_time != float('inf') else -1
Let be the number of nodes (n) and be the number of edges (times.length).
Time Complexity: or
The Dijkstra pattern used here is directly applicable to several other popular interview questions:
if current_time > dist[node] after popping from the heap.Space Complexity:
dist array takes .dist without checking if it is still infinity.k, its distance remains infinity. The problem requires returning -1 in this case, but max() would return infinity (or a very large integer).1 to n, not 0 to n-1.n + 1 or adjust indices by subtracting 1 to map to 0-indexed arrays.