You are given an integer n representing the number of nodes in a graph, labeled from 0 to n - 1.
You are also given an integer array nums of length n sorted in non-decreasing order, and an integer maxDiff.
An undirected edge exists between nodes i and j if the absolute difference between nums[i] and nums[j] is at most maxDiff (i.e., |nums[i] - nums[j]| <= maxDiff).
You are also given a 2D integer array queries. For each queries[i] = [ui, vi], determine whether there exists a path between nodes ui and vi.
Return a boolean array answer, where answer[i] is true if there exists a path between ui and vi in the ith query and false otherwise.
Example 1:
Input: n = 2, nums = [1,3], maxDiff = 1, queries = [[0,0],[0,1]]
Output: [true,false]
Explanation:
[0,0]: Node 0 has a trivial path to itself.[0,1]: There is no edge between Node 0 and Node 1 because |nums[0] - nums[1]| = |1 - 3| = 2, which is greater than maxDiff.[true, false].Example 2:
Input: n = 4, nums = [2,5,6,8], maxDiff = 2, queries = [[0,1],[0,2],[1,3],[2,3]]
Output: [false,false,true,true]
Explanation:
The resulting graph is:

[0,1]: There is no edge between Node 0 and Node 1 because |nums[0] - nums[1]| = |2 - 5| = 3, which is greater than maxDiff.[0,2]: There is no edge between Node 0 and Node 2 because |nums[0] - nums[2]| = |2 - 6| = 4, which is greater than maxDiff.[1,3]: There is a path between Node 1 and Node 3 through Node 2 since |nums[1] - nums[2]| = |5 - 6| = 1 and |nums[2] - nums[3]| = |6 - 8| = 2, both of which are within maxDiff.[2,3]: There is an edge between Node 2 and Node 3 because |nums[2] - nums[3]| = |6 - 8| = 2, which is equal to maxDiff.[false, false, true, true].Constraints:
1 <= n == nums.length <= 1050 <= nums[i] <= 105nums is sorted in non-decreasing order.0 <= maxDiff <= 1051 <= queries.length <= 105queries[i] == [ui, vi]0 <= ui, vi < nWhen you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The problem asks us to determine if a path exists between two specific points in a network. The brute force approach explores every single possible route to check for a connection. We'll try out every combination of steps from the start point to the destination.
Here's how the algorithm would work step-by-step:
def path_exists_brute_force(number_of_nodes, edges, source_node, destination_node):
graph = [[] for _ in range(number_of_nodes)]
for start_node, end_node in edges:
graph[start_node].append(end_node)
visited_nodes = [False] * number_of_nodes
queue = [source_node]
visited_nodes[source_node] = True
# Use a queue for breadth-first search to explore all possible paths
while queue:
current_node = queue.pop(0)
if current_node == destination_node:
return True
# Explore all neighbors of the current node
for neighbor_node in graph[current_node]:
# Avoid revisiting nodes to prevent infinite loops
if not visited_nodes[neighbor_node]:
queue.append(neighbor_node)
visited_nodes[neighbor_node] = True
# If the queue is empty and the destination isn't found, no path exists
return FalseTo efficiently determine if a path exists between nodes in a graph for multiple queries, we preprocess the graph to store connectivity information. This allows us to answer each query in constant time by simply checking a precomputed result, avoiding repeated traversals of the graph for each query.
Here's how the algorithm would work step-by-step:
def path_exists(number_of_nodes: int, edges: list[list[int]], queries: list[list[int]]) -> list[bool]:
parent_node = list(range(number_of_nodes))
def find_connected_component(node_a: int) -> int:
if parent_node[node_a] != node_a:
parent_node[node_a] = find_connected_component(parent_node[node_a])
return parent_node[node_a]
def union_connected_components(node_a: int, node_b: int) -> None:
root_a = find_connected_component(node_a)
root_b = find_connected_component(node_b)
parent_node[root_a] = root_b
# Connect nodes based on the given edges.
for node_a, node_b in edges:
union_connected_components(node_a, node_b)
# Ensure the parent array reflects the ultimate parents.
for node in range(number_of_nodes):
find_connected_component(node)
result_list = []
# Evaluate path existence for each query.
for start_node, end_node in queries:
# Check if nodes are in the same connected component.
result_list.append(parent_node[start_node] == parent_node[end_node])
return result_list| Case | How to Handle |
|---|---|
| Null or empty graph (n=0, edge list empty) | Return an empty list of boolean results since no paths can exist in an empty graph. |
| Graph with a single node (n=1) | Return a list of 'false' values corresponding to queries that do not originate from that single node. |
| Queries with identical start and end nodes | Return 'true' if the node exists, 'false' otherwise, as a node is trivially reachable from itself. |
| Disconnected graph - no path exists between certain nodes. | The pathfinding algorithm (BFS/DFS) should correctly identify and return 'false' for these unreachable queries. |
| Graph contains cycles | The chosen pathfinding algorithm (BFS or DFS with visited set) handles cycles correctly by avoiding infinite loops and revisiting nodes. |
| Large graph and large number of queries (scalability) | Consider using a more efficient pathfinding algorithm like BFS and avoid repeatedly constructing the graph adjacency list for each query; consider pre-computing path information if memory allows. |
| All queries ask for the same path | The algorithm should correctly handle repeated queries without performance degradation, potentially benefiting from caching results if implemented. |
| Graph represented with self-loops (edge from a node to itself) | The pathfinding algorithm should not get stuck in a self-loop, but recognize it as a valid (trivial) path from a node to itself if the query asks for it. |