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:

  1. Calculate the shortest path from node s to all reachable nodes in the graph
  2. Among all the marked nodes, find which one has the smallest distance from s
  3. 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. Run a shortest path algorithm from the source to each marked node individually
  2. 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 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

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 source s to node i
  • vis[i] tracks whether node i 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 smallest dist[t] value
  • Mark it as visited: Set vis[t] = True
  • Relax edges: For all neighbors j of node t, we update dist[j] if going through t 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 Evaluator

Example 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
  • 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).

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)
  • Total for Dijkstra: O(n) × (O(n) + O(n)) = O(n^2)
  • Finding minimum distance among marked nodes: O(|marked|) which is at most O(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 size n × 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
Loading...
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

How does merge sort divide the problem into subproblems?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More