2737. Find the Closest Marked Node 🔒
Problem Description
You are given a directed weighted graph with n
nodes (labeled from 0 to n-1) and a list of edges. Each edge is represented as [u, v, w]
, meaning there's a directed edge from node u
to node v
with weight w
.
You're also given:
- A starting node
s
- An array
marked
containing specific nodes of interest
Your task is to find the minimum distance from the starting node s
to any of the nodes in the marked
array.
The problem asks you to:
- Calculate the shortest path from node
s
to all reachable nodes in the graph - Among all the marked nodes, find which one has the smallest distance from
s
- Return this minimum distance
If none of the marked nodes can be reached from s
, return -1
.
Example scenario: If you start at node s
and there are multiple marked nodes, you need to find which marked node is closest to s
(considering the weighted paths) and return that distance. The graph is directed, so you can only travel along edges in the specified direction.
Intuition
When we need to find the shortest path from a single source node to multiple destination nodes, we can think about this problem in two ways:
- Run a shortest path algorithm from the source to each marked node individually
- Run a single shortest path algorithm from the source to all nodes, then check the distances to marked nodes
The second approach is more efficient. Since we're starting from a single source s
and need distances to multiple potential destinations, we can compute all shortest paths from s
in one go, then simply look up the distances to the marked nodes.
This naturally leads us to think about single-source shortest path algorithms. Dijkstra's algorithm is perfect for this scenario because:
- It finds the shortest paths from one source to all other nodes
- It works with weighted graphs (as long as weights are non-negative)
- We only need to run it once, regardless of how many marked nodes we have
The key insight is that instead of treating each marked node as a separate destination and solving multiple shortest path problems, we solve it once for all nodes. After getting the shortest distances from s
to every node in the graph, finding the answer becomes trivial - we just need to check which marked node has the minimum distance.
The algorithm builds an adjacency matrix g[i][j]
to store edge weights (using inf
for non-existent edges), then applies Dijkstra's algorithm to compute dist[i]
- the shortest distance from s
to node i
. Finally, we scan through all marked nodes and return the minimum distance found, or -1
if all marked nodes are unreachable (distance is inf
).
Learn more about Graph, Shortest Path and Heap (Priority Queue) patterns.
Solution Approach
The solution implements Dijkstra's algorithm to find the shortest paths from the starting node s
to all other nodes. Let's walk through the implementation step by step:
1. Build the Adjacency Matrix
g = [[inf] * n for _ in range(n)]
for u, v, w in edges:
g[u][v] = min(g[u][v], w)
We create an n × n
matrix g
where g[i][j]
represents the weight of the edge from node i
to node j
. Initially, all values are set to inf
(infinity). For each edge [u, v, w]
, we update g[u][v]
with the minimum weight (handling potential duplicate edges).
2. Initialize Dijkstra's Algorithm
dist = [inf] * n vis = [False] * n dist[s] = 0
dist[i]
stores the shortest distance from sources
to nodei
vis[i]
tracks whether nodei
has been processed- Set the distance to the source itself as 0
3. Main Dijkstra's Loop
for _ in range(n):
t = -1
for j in range(n):
if not vis[j] and (t == -1 or dist[t] > dist[j]):
t = j
vis[t] = True
for j in range(n):
dist[j] = min(dist[j], dist[t] + g[t][j])
The algorithm runs n
iterations:
- Find the unvisited node with minimum distance: We scan through all nodes to find the unvisited node
t
with the smallestdist[t]
value - Mark it as visited: Set
vis[t] = True
- Relax edges: For all neighbors
j
of nodet
, we updatedist[j]
if going throught
gives a shorter path:dist[j] = min(dist[j], dist[t] + g[t][j])
4. Find the Answer
ans = min(dist[i] for i in marked)
return -1 if ans >= inf else ans
After computing all shortest distances from s
, we check the distances to all marked nodes and return the minimum. If this minimum is still inf
(meaning no marked node is reachable), we return -1
.
Time Complexity: O(n²)
due to the nested loops in Dijkstra's implementation
Space Complexity: O(n²)
for the adjacency matrix
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Given:
n = 4
nodes (labeled 0, 1, 2, 3)edges = [[0,1,2], [0,2,5], [1,2,1], [1,3,3]]
- Starting node
s = 0
- Marked nodes
marked = [2, 3]
Step 1: Build the Adjacency Matrix
We create a 4×4 matrix initialized with inf
, then fill in the edge weights:
0 1 2 3 0 [ inf, 2, 5, inf] 1 [ inf, inf, 1, 3 ] 2 [ inf, inf, inf, inf] 3 [ inf, inf, inf, inf]
Step 2: Initialize Dijkstra's Algorithm
dist = [0, inf, inf, inf]
(distance from node 0 to each node)vis = [False, False, False, False]
(no nodes visited yet)
Step 3: Run Dijkstra's Algorithm
Iteration 1:
- Find unvisited node with minimum distance: node 0 (dist=0)
- Mark node 0 as visited:
vis = [True, False, False, False]
- Relax edges from node 0:
dist[1] = min(inf, 0 + 2) = 2
dist[2] = min(inf, 0 + 5) = 5
- Updated
dist = [0, 2, 5, inf]
Iteration 2:
- Find unvisited node with minimum distance: node 1 (dist=2)
- Mark node 1 as visited:
vis = [True, True, False, False]
- Relax edges from node 1:
dist[2] = min(5, 2 + 1) = 3
dist[3] = min(inf, 2 + 3) = 5
- Updated
dist = [0, 2, 3, 5]
Iteration 3:
- Find unvisited node with minimum distance: node 2 (dist=3)
- Mark node 2 as visited:
vis = [True, True, True, False]
- Relax edges from node 2: (no outgoing edges)
dist
remains[0, 2, 3, 5]
Iteration 4:
- Find unvisited node with minimum distance: node 3 (dist=5)
- Mark node 3 as visited:
vis = [True, True, True, True]
- Relax edges from node 3: (no outgoing edges)
- Final
dist = [0, 2, 3, 5]
Step 4: Find the Answer
- Check distances to marked nodes:
marked = [2, 3]
- Distance to node 2:
dist[2] = 3
- Distance to node 3:
dist[3] = 5
- Distance to node 2:
- Minimum distance =
min(3, 5) = 3
- Return
3
The shortest path from node 0 to any marked node is 3 (the path 0→1→2).
Solution Implementation
1class Solution:
2 def minimumDistance(
3 self, n: int, edges: List[List[int]], s: int, marked: List[int]
4 ) -> int:
5 """
6 Find the minimum distance from source node s to any marked node.
7
8 Args:
9 n: Number of nodes in the graph (0 to n-1)
10 edges: List of [from, to, weight] representing directed edges
11 s: Source node
12 marked: List of marked target nodes
13
14 Returns:
15 Minimum distance to reach any marked node, or -1 if unreachable
16 """
17 # Initialize adjacency matrix with infinity for all pairs
18 # graph[i][j] represents the minimum weight edge from node i to node j
19 graph = [[float('inf')] * n for _ in range(n)]
20
21 # Build the adjacency matrix from edges
22 # Keep only the minimum weight if there are multiple edges between same nodes
23 for from_node, to_node, weight in edges:
24 graph[from_node][to_node] = min(graph[from_node][to_node], weight)
25
26 # Initialize distance array for Dijkstra's algorithm
27 # distance[i] represents the shortest distance from source s to node i
28 distance = [float('inf')] * n
29
30 # Track visited nodes to avoid revisiting
31 visited = [False] * n
32
33 # Distance from source to itself is 0
34 distance[s] = 0
35
36 # Dijkstra's algorithm implementation
37 # Process all n nodes
38 for _ in range(n):
39 # Find the unvisited node with minimum distance
40 current_node = -1
41 for node in range(n):
42 if not visited[node] and (current_node == -1 or distance[current_node] > distance[node]):
43 current_node = node
44
45 # Mark current node as visited
46 visited[current_node] = True
47
48 # Update distances to all neighbors of current node
49 for neighbor in range(n):
50 # Relaxation step: check if path through current_node is shorter
51 distance[neighbor] = min(distance[neighbor], distance[current_node] + graph[current_node][neighbor])
52
53 # Find the minimum distance among all marked nodes
54 min_distance_to_marked = min(distance[node] for node in marked)
55
56 # Return -1 if no marked node is reachable, otherwise return the minimum distance
57 return -1 if min_distance_to_marked >= float('inf') else min_distance_to_marked
58
1class Solution {
2 public int minimumDistance(int n, List<List<Integer>> edges, int s, int[] marked) {
3 // Define infinity as a large value for unreachable nodes
4 final int INF = 1 << 29;
5
6 // Initialize adjacency matrix for the graph
7 // graph[i][j] represents the minimum weight of edge from node i to node j
8 int[][] graph = new int[n][n];
9 for (int[] row : graph) {
10 Arrays.fill(row, INF);
11 }
12
13 // Build the graph from the edge list
14 // Handle multiple edges between same nodes by keeping minimum weight
15 for (List<Integer> edge : edges) {
16 int from = edge.get(0);
17 int to = edge.get(1);
18 int weight = edge.get(2);
19 graph[from][to] = Math.min(graph[from][to], weight);
20 }
21
22 // Initialize distance array for Dijkstra's algorithm
23 // distance[i] represents the shortest distance from source s to node i
24 int[] distance = new int[n];
25 Arrays.fill(distance, INF);
26 distance[s] = 0;
27
28 // Track visited nodes during Dijkstra's algorithm
29 boolean[] visited = new boolean[n];
30
31 // Dijkstra's algorithm implementation
32 // Process all n nodes to find shortest paths
33 for (int i = 0; i < n; i++) {
34 // Find the unvisited node with minimum distance
35 int currentNode = -1;
36 for (int j = 0; j < n; j++) {
37 if (!visited[j] && (currentNode == -1 || distance[currentNode] > distance[j])) {
38 currentNode = j;
39 }
40 }
41
42 // Mark current node as visited
43 visited[currentNode] = true;
44
45 // Update distances to all neighbors of current node
46 for (int neighbor = 0; neighbor < n; neighbor++) {
47 distance[neighbor] = Math.min(distance[neighbor],
48 distance[currentNode] + graph[currentNode][neighbor]);
49 }
50 }
51
52 // Find the minimum distance among all marked nodes
53 int minDistance = INF;
54 for (int markedNode : marked) {
55 minDistance = Math.min(minDistance, distance[markedNode]);
56 }
57
58 // Return -1 if no marked node is reachable, otherwise return minimum distance
59 return minDistance >= INF ? -1 : minDistance;
60 }
61}
62
1class Solution {
2public:
3 int minimumDistance(int n, vector<vector<int>>& edges, int s, vector<int>& marked) {
4 // Initialize constants and data structures
5 const int INF = 1 << 29; // Large value representing infinity
6
7 // Create adjacency matrix for the graph (initialized with INF)
8 vector<vector<int>> graph(n, vector<int>(n, INF));
9
10 // Distance array to store shortest distances from source
11 vector<int> distance(n, INF);
12 distance[s] = 0; // Distance from source to itself is 0
13
14 // Visited array to track processed nodes
15 vector<bool> visited(n, false);
16
17 // Build the adjacency matrix from edges
18 // Keep only the minimum weight edge between any two nodes
19 for (auto& edge : edges) {
20 int from = edge[0];
21 int to = edge[1];
22 int weight = edge[2];
23 graph[from][to] = min(graph[from][to], weight);
24 }
25
26 // Dijkstra's algorithm implementation
27 for (int i = 0; i < n; ++i) {
28 // Find the unvisited node with minimum distance
29 int minNode = -1;
30 for (int j = 0; j < n; ++j) {
31 if (!visited[j] && (minNode == -1 || distance[minNode] > distance[j])) {
32 minNode = j;
33 }
34 }
35
36 // Mark the selected node as visited
37 visited[minNode] = true;
38
39 // Update distances to all neighbors of the selected node
40 for (int neighbor = 0; neighbor < n; ++neighbor) {
41 distance[neighbor] = min(distance[neighbor],
42 distance[minNode] + graph[minNode][neighbor]);
43 }
44 }
45
46 // Find the minimum distance among all marked nodes
47 int minDistance = INF;
48 for (int markedNode : marked) {
49 minDistance = min(minDistance, distance[markedNode]);
50 }
51
52 // Return -1 if no marked node is reachable, otherwise return the minimum distance
53 return minDistance >= INF ? -1 : minDistance;
54 }
55};
56
1/**
2 * Finds the minimum distance from source node to any marked node using Dijkstra's algorithm
3 * @param n - Number of nodes in the graph
4 * @param edges - Array of edges where each edge is [from, to, weight]
5 * @param s - Source node index
6 * @param marked - Array of marked node indices
7 * @returns Minimum distance to any marked node, or -1 if unreachable
8 */
9function minimumDistance(n: number, edges: number[][], s: number, marked: number[]): number {
10 // Initialize infinity value for unreachable nodes
11 const INFINITY: number = 1 << 29;
12
13 // Initialize adjacency matrix with infinity values
14 const adjacencyMatrix: number[][] = Array(n)
15 .fill(0)
16 .map(() => Array(n).fill(INFINITY));
17
18 // Distance array to store shortest distances from source
19 const distances: number[] = Array(n).fill(INFINITY);
20
21 // Visited array to track processed nodes
22 const visited: boolean[] = Array(n).fill(false);
23
24 // Build adjacency matrix from edges, keeping minimum weight for duplicate edges
25 for (const [fromNode, toNode, weight] of edges) {
26 adjacencyMatrix[fromNode][toNode] = Math.min(adjacencyMatrix[fromNode][toNode], weight);
27 }
28
29 // Set distance to source node as 0
30 distances[s] = 0;
31
32 // Dijkstra's algorithm main loop - process all nodes
33 for (let i = 0; i < n; ++i) {
34 // Find unvisited node with minimum distance
35 let minDistanceNode: number = -1;
36 for (let j = 0; j < n; ++j) {
37 if (!visited[j] && (minDistanceNode === -1 || distances[minDistanceNode] > distances[j])) {
38 minDistanceNode = j;
39 }
40 }
41
42 // Mark current node as visited
43 visited[minDistanceNode] = true;
44
45 // Update distances to all neighbors through current node
46 for (let neighbor = 0; neighbor < n; ++neighbor) {
47 distances[neighbor] = Math.min(
48 distances[neighbor],
49 distances[minDistanceNode] + adjacencyMatrix[minDistanceNode][neighbor]
50 );
51 }
52 }
53
54 // Find minimum distance among all marked nodes
55 let minimumMarkedDistance: number = INFINITY;
56 for (const markedNode of marked) {
57 minimumMarkedDistance = Math.min(minimumMarkedDistance, distances[markedNode]);
58 }
59
60 // Return result: -1 if no marked node is reachable, otherwise the minimum distance
61 return minimumMarkedDistance >= INFINITY ? -1 : minimumMarkedDistance;
62}
63
Time and Space Complexity
Time Complexity: O(n^2)
The algorithm implements Dijkstra's shortest path algorithm using an adjacency matrix representation. The time complexity breaks down as follows:
- Building the adjacency matrix from edges:
O(E)
where E is the number of edges - The main Dijkstra loop runs
n
iterations - In each iteration:
- Finding the unvisited node with minimum distance:
O(n)
- Marking the node as visited:
O(1)
- Updating distances to all neighbors:
O(n)
- Finding the unvisited node with minimum distance:
- Total for Dijkstra:
O(n) × (O(n) + O(n))
=O(n^2)
- Finding minimum distance among marked nodes:
O(|marked|)
which is at mostO(n)
Overall time complexity: O(E + n^2 + n)
= O(n^2)
(since E ≤ n^2
in the worst case)
Space Complexity: O(n^2)
The space usage consists of:
- Adjacency matrix
g
:O(n^2)
- a 2D array of sizen × n
- Distance array
dist
:O(n)
- Visited array
vis
:O(n)
Overall space complexity: O(n^2 + n + n)
= O(n^2)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Handling Multiple Edges Between Same Nodes
One common pitfall is forgetting to handle duplicate edges between the same pair of nodes. The graph might contain multiple edges from node u
to node v
with different weights.
Pitfall Example:
# Wrong approach - overwrites previous edge for u, v, w in edges: graph[u][v] = w # This overwrites any existing edge!
Solution: Always take the minimum weight when multiple edges exist:
for u, v, w in edges:
graph[u][v] = min(graph[u][v], w)
2. Self-Loops and Node Selection in Dijkstra
When the source node is also in the marked array, failing to properly handle the distance to itself (which should be 0) can cause issues.
Pitfall Example:
If s = 0
and marked = [0, 2, 3]
, forgetting to set distance[s] = 0
would result in all distances remaining at infinity.
Solution: Always initialize the source distance before running Dijkstra:
distance[s] = 0 # Critical initialization
3. Integer vs Float Infinity
Using integer maximum values instead of float('inf') can cause overflow issues during addition operations in the relaxation step.
Pitfall Example:
# Wrong - can cause overflow
INF = 10**9
dist[j] = min(dist[j], dist[t] + g[t][j]) # May overflow if both are large
Solution:
Use float('inf')
which handles arithmetic operations correctly:
graph = [[float('inf')] * n for _ in range(n)]
distance = [float('inf')] * n
4. Incorrect Node Selection Logic
The node selection step in Dijkstra must properly handle the initial case when no node has been selected yet (t = -1
).
Pitfall Example:
# Wrong - doesn't handle initial selection correctly
t = 0 # Starting with 0 might select a visited node
for j in range(n):
if not vis[j] and dist[t] > dist[j]:
t = j
Solution: Use a sentinel value and check for it:
t = -1
for j in range(n):
if not vis[j] and (t == -1 or dist[t] > dist[j]):
t = j
5. Returning Wrong Value for Unreachable Nodes
Forgetting to check if the minimum distance is still infinity before returning can lead to returning infinity instead of -1.
Pitfall Example:
# Wrong - returns infinity instead of -1
return min(distance[i] for i in marked)
Solution: Check for infinity explicitly:
ans = min(distance[i] for i in marked)
return -1 if ans >= float('inf') else ans
Which algorithm should you use to find a node that is close to the root of the tree?
Recommended Readings
https assets algo monster cover_photos graph svg Graph Fundamentals Tree with 0 cycle At this point you should be pretty familiar with trees A tree is a special kind of graph a connected acyclic cycle less graph A graph may contain cycle s and nodes could be disconnected A tree
Shortest Path Between A and B Prereq BFS on Graph problems graph_bfs Given an unweighted connected graph return the length of the shortest path between two nodes A and B in terms of the number of edges Assume there always exists a path between nodes A and B Input graph
https assets algo monster cover_photos heap svg Priority Queue and Heap What is the relationship between priority queue and heap Priority Queue is an Abstract Data Type and Heap is the concrete data structure we use to implement a priority queue Priority Queue A priority queue is a data structure
Want a Structured Path to Master System Design Too? Don’t Miss This!