Network Delay Time
`shortestPath`'s graph-then-queue skeleton carries over almost untouched, but edges now cost `wi` instead of always `1`, which breaks BFS's free guarantee that the first visit to a node is the shortest one — so `q.shift()` becomes 'pop the cheapest queued entry' and `visited` moves from 'seen once' to 'shortest distance settled'.
The problem
Implement `networkDelayTime(times, n, k)` where `times[i] = [ui, vi, wi]` is a directed edge from `ui` to `vi` costing `wi` time, and `k` is the node a signal starts from. Return the time for the signal to reach every node from `1` to `n`, or `-1` if some node is never reached.
`times = [[1,2,1],[2,3,1],[1,4,4],[3,4,1]], n = 4, k = 1` returns `3` — node `4` is reached fastest via `1 -> 2 -> 3 -> 4` (cost `3`), not the direct edge `1 -> 4` (cost `4`), so the direct edge never ends up on anyone's shortest path.
The approach
`shortestPath`'s shape is unchanged at the top level: build an adjacency list from the edge list, then traverse from the source with a `[node, distanceFromSource]` queue and a `visited` Set. Both differences below fall out of the same fact — edges carry a weight now, not an implicit `1`.
Building the adjacency list only does `graph[u].push([v, w])`, not the reciprocal push `createGraphAdjacencyObjectFromEdgeList` does for `undirectedPath`/`shortestPath` — `times[i]` is one-directional, so arriving at `v` says nothing about a way back to `u`.
BFS got shortest-path-in-edges for free because every edge cost the same `1`, so the first visit to a node was necessarily via the fewest edges. That stops being true once edges have different weights — a queue entry pushed later can still represent a cheaper path — so `q.shift()` is replaced with `q.sort((a, b) => a[1] - b[1])` before every pop, always taking the currently-cheapest known path instead of the oldest one. A node is only added to `visited` when it's actually popped this way, not when it's first pushed, so stale, more-expensive duplicates of an already-visited node just get skipped on pop.
There's no early return on reaching one target — every node needs its distance — so the loop drains the queue completely and the answer is the max distance across all `visited` nodes: the network only finishes 'receiving' once its slowest node does, and `visited.size < n` means some node was never reached at all.
The solution
class Solution {
/**
* @param {number[][]} times
* @param {number} n
* @param {number} k
* @return {number}
*/
networkDelayTime(times, n, k) {
const graph = {};
for (const [u, v, w] of times) {
if (!(u in graph)) graph[u] = [];
graph[u].push([v, w]);
}
const visited = new Set();
const q = [[k, 0]];
let maxDistance = 0;
while (q.length) {
q.sort((a, b) => a[1] - b[1]);
const [curr, distanceFromSource] = q.shift();
if (visited.has(curr)) continue;
visited.add(curr);
maxDistance = Math.max(maxDistance, distanceFromSource);
for (const [node, weight] of graph[curr] || []) {
if (!visited.has(node)) {
q.push([node, distanceFromSource + weight]);
}
}
}
return visited.size === n ? maxDistance : -1;
}
}Time O(E^2)Space O(V + E)