You are given a 2D integer array descriptions
where descriptions[i] = [parenti, childi, isLefti]
indicates that parenti
is the parent of childi
in a binary tree of unique values. Furthermore,
isLefti == 1
, then childi
is the left child of parenti
.isLefti == 0
, then childi
is the right child of parenti
.Construct the binary tree described by descriptions
and return its root.
The test cases will be generated such that the binary tree is valid.
Example 1:
Input: descriptions = [[20,15,1],[20,17,0],[50,20,1],[50,80,0],[80,19,1]] Output: [50,20,80,15,17,19] Explanation: The root node is the node with value 50 since it has no parent. The resulting binary tree is shown in the diagram.
Example 2:
Input: descriptions = [[1,2,1],[2,3,0],[3,4,1]] Output: [1,2,null,null,3,4] Explanation: The root node is the node with value 1 since it has no parent. The resulting binary tree is shown in the diagram.
Constraints:
1 <= descriptions.length <= 104
descriptions[i].length == 3
1 <= parenti, childi <= 105
0 <= isLefti <= 1
descriptions
is valid.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:
We're given a list of parent-child relationships that describe a binary tree. The brute force method involves checking every single description and slowly building the tree by trying out every possible connection between the parent and child nodes until we get it right.
Here's how the algorithm would work step-by-step:
class TreeNode:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def create_binary_tree_from_descriptions(descriptions):
all_nodes = set()
for parent_value, child_value, is_left in descriptions:
all_nodes.add(parent_value)
all_nodes.add(child_value)
possible_roots = list(all_nodes)
for potential_root_value in possible_roots:
root = TreeNode(potential_root_value)
node_map = {potential_root_value: root}
is_valid_tree = True
for parent_value, child_value, is_left in descriptions:
if parent_value not in node_map:
node_map[parent_value] = TreeNode(parent_value)
if child_value not in node_map:
node_map[child_value] = TreeNode(child_value)
parent_node = node_map[parent_value]
child_node = node_map[child_value]
# Check if the description is already satisfied.
if is_left:
if parent_node.left is not None and parent_node.left != child_node:
is_valid_tree = False
break
parent_node.left = child_node
else:
if parent_node.right is not None and parent_node.right != child_node:
is_valid_tree = False
break
parent_node.right = child_node
# Check for multiple roots.
parents = set()
for parent_value, child_value, _ in descriptions:
parents.add(parent_value)
children = set()
for _, child_value, _ in descriptions:
children.add(child_value)
root_candidates = parents.difference(children)
if len(root_candidates) != 1 or root.value not in root_candidates:
is_valid_tree = False
if is_valid_tree:
return root
return None
We're given descriptions of parent-child relationships and need to construct a tree. Instead of recursively searching or trying to build the tree from the root down, the clever approach is to first identify the root and then build the tree by linking children to their parents using a helper lookup structure.
Here's how the algorithm would work step-by-step:
class TreeNode:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def create_binary_tree_from_descriptions(descriptions):
child_values = set()
node_map = {}
for parent_value, child_value, is_left in descriptions:
child_values.add(child_value)
if parent_value not in node_map:
node_map[parent_value] = TreeNode(parent_value)
if child_value not in node_map:
node_map[child_value] = TreeNode(child_value)
root_value = None
for parent_value, _, _ in descriptions:
if parent_value not in child_values:
root_value = parent_value
break
#Ensure root exists
if root_value is None:
return None
# Use description to build the tree
for parent_value, child_value, is_left in descriptions:
parent_node = node_map[parent_value]
child_node = node_map[child_value]
# Link children to parents
if is_left:
parent_node.left = child_node
else:
parent_node.right = child_node
return node_map[root_value]
Case | How to Handle |
---|---|
Empty descriptions array | Return null or an empty tree as no tree structure can be created from empty descriptions. |
Descriptions array with only one element. | Create a single node tree; the root will be the parent of that one description element if it's not present in child values, otherwise return null (invalid input). |
Descriptions form a cycle, leading to an infinite recursion during tree construction. | Maintain a 'visited' set to detect cycles and return null if a cycle is detected during tree construction. |
Multiple nodes claim the same parent (invalid tree structure). | If a parent already has a left or right child as specified in a new description, handle the conflict, perhaps by choosing the first child encountered and ignoring subsequent ones, or throwing an error, depending on requirements. |
Parent node not found in descriptions, resulting in an orphaned subtree. | If a parent is not already present when processing a child, create the parent node immediately; otherwise, track or return null because a root node should appear somewhere. |
Input describes a forest (multiple disconnected trees) instead of a single tree. | Identify the root nodes (those without parents) and construct the separate trees, then return them as a list of root nodes instead of a single root node if the problem permits forest output. |
Integer overflow in node values if node values are very large. | Use a data type capable of representing the largest possible node value, or throw an exception if a value exceeds the representable range. |
Descriptions contain duplicate parent-child relationships. | The solution should handle duplicates gracefully, either by ignoring redundant entries or by ensuring that duplicate entries do not overwrite existing valid tree structures; a set can prevent redundant tree construction. |