You are given a tree (i.e. a connected, undirected graph that has no cycles) consisting of n
nodes numbered from 0
to n - 1
and exactly n - 1
edges. The root of the tree is node 0
, and each node of the tree has a distinct value in the range [1, n]
.
You are also given a 2D integer array queries
of size k
, where queries[i] = [nodei, vali]
.
For each query i
, find the node that is closest to nodei
and has the value vali
. The answer to query i
is the distance between the closest node and nodei
. If multiple nodes have the same distance to nodei
, choose the node with the smallest number. If no node has the value vali
, the answer is -1
.
Return an integer array of size k
where answer[i]
is the answer to the ith
query.
Example 1:
Input: n = 7, edges = [[0,1],[0,2],[1,3],[1,4],[2,5],[2,6]], queries = [[3,4],[5,3],[2,4],[0,3]] Output: [0,-1,1,2] Explanation: The tree is shown above. For the 1st query: The node of value 4 is 1. The distance between node 3 and node 1 is |3-1| = 2. The node of value 4 is 4. The distance between node 3 and node 4 is |3-4| = 1. The closest node of value 4 to node 3 is 4, so the answer to the 1st query is 1. For the 2nd query: There is no node of value 3 in the tree, so the answer to the 2nd query is -1. For the 3rd query: The node of value 4 is 1. The distance between node 2 and node 1 is |2-1| = 1. The node of value 4 is 4. The distance between node 2 and node 4 is |2-4| = 2. The closest node of value 4 to node 2 is 1, so the answer to the 3rd query is 1. For the 4th query: There is no node of value 3 in the tree, so the answer to the 4th query is 2.
Example 2:
Input: n = 4, edges = [[0,1],[0,2],[2,3]], queries = [[0,3],[3,1],[2,3]] Output: [3,-1,0]
Constraints:
1 <= n <= 105
edges.length == n - 1
edges[i].length == 2
0 <= ai, bi < n
queries[i].length == 2
0 <= nodei < n
1 <= vali <= n
When 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 brute force method explores every possible node in the tree to find the closest one to the given path. We essentially look at each node individually and check how far away it is from the path. The node with the smallest distance is then selected.
Here's how the algorithm would work step-by-step:
def closest_node_to_path_brute_force(tree, path, start_node):
path_nodes = set(path)
min_distance = float('inf')
closest_node = None
# Iterate through each node in the tree
for current_node in tree:
# Calculate the distance to the closest node on the path
current_min_distance = float('inf')
for target_node in path_nodes:
distance = find_distance(tree, current_node, target_node)
current_min_distance = min(current_min_distance, distance)
# Update the minimum distance and closest node if needed
if current_min_distance < min_distance:
#If we find a closer node update values
min_distance = current_min_distance
closest_node = current_node
elif current_min_distance == min_distance:
#if the same distance pick one node
pass
return closest_node
def find_distance(tree, start_node, end_node):
if start_node == end_node:
return 0
visited = {start_node}
queue = [(start_node, 0)]
while queue:
node, distance = queue.pop(0)
for neighbor in get_neighbors(tree, node):
if neighbor == end_node:
return distance + 1
if neighbor not in visited:
visited.add(neighbor)
queue.append((neighbor, distance + 1))
return float('inf')
def get_neighbors(tree, node):
neighbors = []
for start, end in tree:
if start == node:
neighbors.append(end)
if end == node:
neighbors.append(start)
return neighbors
The most efficient approach involves first finding the path in the tree and then searching for the closest node. We can find the path using Depth-First Search (DFS). After finding the path, we use Breadth-First Search (BFS) starting from all nodes on the path to find the closest node to the path efficiently.
Here's how the algorithm would work step-by-step:
from collections import deque
class TreeNode:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def closest_node_to_path(root_node, start_node, end_node):
path_nodes = find_path(root_node, start_node, end_node)
if not path_nodes:
return None
queue = deque([(node, 0) for node in path_nodes])
visited_nodes = set(path_nodes)
# Begin BFS to find the closest node.
while queue:
current_node, distance = queue.popleft()
if current_node.left and current_node.left not in visited_nodes:
return current_node.left
if current_node.right and current_node.right not in visited_nodes:
return current_node.right
if current_node.left:
queue.append((current_node.left, distance + 1))
visited_nodes.add(current_node.left)
if current_node.right:
queue.append((current_node.right, distance + 1))
visited_nodes.add(current_node.right)
return None
def find_path(root_node, start_node, end_node):
path_nodes = []
path_found = find_path_recursive(root_node, start_node, end_node, path_nodes)
if path_found:
return path_nodes
else:
return []
def find_path_recursive(current_node, start_node, end_node, path_nodes):
if not current_node:
return False
path_nodes.append(current_node)
if current_node == end_node:
return True
# Prioritize left subtree exploration.
if current_node.left and find_path_recursive(current_node.left, start_node, end_node, path_nodes):
return True
# Then explore the right subtree.
if current_node.right and find_path_recursive(current_node.right, start_node, end_node, path_nodes):
return True
# Backtrack if neither subtree leads to the end node.
path_nodes.pop()
return False
Case | How to Handle |
---|---|
Null or empty tree | Return an empty list or null, depending on specification, as there is no path or node to compare against. |
Null or empty path | Return an empty list or null, depending on specification, as there's nothing to search for proximity to. |
Tree with only one node | Compare the single node to the path and return it if the path exists, otherwise return an empty list or null. |
Path with only one node | Find the closest node in the tree to this single node in the path. |
All nodes in the tree are identical | Return the first encountered node during tree traversal if it's closest to any node in the path, otherwise return null or empty list. |
No path exists between the specified start and end nodes | If no path exists, the path variable will be empty, and an empty list or null should be returned. |
Integer overflow during distance calculations | Use appropriate data types (e.g., long) or modular arithmetic to prevent overflows during distance calculations. |
Deeply unbalanced tree leading to stack overflow during recursion | Convert the recursive approach to an iterative approach using a stack or queue to avoid stack overflow errors. |