← Daily Logs
LeetCode 743

Network Delay Time

MediumJul 26, 2026graphdijkstrashortest-pathweighted-graph

`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

js
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)

All entries