Given a binary tree with the following rules:
root.val == 0treeNode:
treeNode.val has a value x and treeNode.left != null, then treeNode.left.val == 2 * x + 1treeNode.val has a value x and treeNode.right != null, then treeNode.right.val == 2 * x + 2Now the binary tree is contaminated, which means all treeNode.val have been changed to -1.
Implement the FindElements class:
FindElements(TreeNode* root) Initializes the object with a contaminated binary tree and recovers it.bool find(int target) Returns true if the target value exists in the recovered binary tree.Example 1:
Input ["FindElements","find","find"] [[[-1,null,-1]],[1],[2]] Output [null,false,true] Explanation FindElements findElements = new FindElements([-1,null,-1]); findElements.find(1); // return False findElements.find(2); // return True
Example 2:
Input ["FindElements","find","find","find"] [[[-1,-1,-1,-1,-1]],[1],[3],[5]] Output [null,true,true,false] Explanation FindElements findElements = new FindElements([-1,-1,-1,-1,-1]); findElements.find(1); // return True findElements.find(3); // return True findElements.find(5); // return False
Example 3:
Input ["FindElements","find","find","find","find"] [[[-1,null,-1,-1,null,-1]],[2],[3],[4],[5]] Output [null,true,false,false,true] Explanation FindElements findElements = new FindElements([-1,null,-1,-1,null,-1]); findElements.find(2); // return True findElements.find(3); // return False findElements.find(4); // return False findElements.find(5); // return True
Constraints:
TreeNode.val == -120[1, 104]find() is between [1, 104]0 <= target <= 106When 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 strategy involves two main phases. First, we completely rebuild the tree by visiting every single node and calculating its correct value based on the given rules. Then, to find a specific number, we simply check every single one of the newly calculated values to see if we can find a match.
Here's how the algorithm would work step-by-step:
class FindElements:
def __init__(self, root_node: TreeNode):
self.recovered_root_node = root_node
# We must first traverse the entire tree to replace the contaminated values with correct ones.
self._recover(self.recovered_root_node, 0)
def _recover(self, current_node, assigned_value):
if current_node is None:
return
current_node.val = assigned_value
# The recovery rule dictates a specific value calculation for left and right children.
self._recover(current_node.left, 2 * assigned_value + 1)
self._recover(current_node.right, 2 * assigned_value + 2)
def find(self, target_value: int) -> bool:
# Each find operation must re-scan the entire tree from the top to locate the target.
return self._search(self.recovered_root_node, target_value)
def _search(self, current_node, value_to_find):
if current_node is None:
return False
# If the current node's value matches, we have successfully found the target in the tree.
if current_node.val == value_to_find:
return True
# If not found, the search must continue down both the left and right paths from this node.
return self._search(current_node.left, value_to_find) or self._search(current_node.right, value_to_find)The key idea is to first 'heal' the tree by calculating the correct value for every existing node in a one-time setup process. We then store all these correct values in a special collection that allows for super-fast lookups, which avoids having to search the entire tree every single time we need to find a number.
Here's how the algorithm would work step-by-step:
class FindElements:
def __init__(self, root):
# Using a set is crucial for achieving the near-instant lookup time required.
self.recovered_values_set = set()
# We must traverse the entire tree once to calculate and store the correct value for each node.
self._recover_tree_values(root, 0)
def _recover_tree_values(self, current_node, calculated_value):
if not current_node:
return
self.recovered_values_set.add(calculated_value)
# The formula for children's values reconstructs the tree's intended structure.
if current_node.left:
self._recover_tree_values(current_node.left, (calculated_value * 2) + 1)
if current_node.right:
self._recover_tree_values(current_node.right, (calculated_value * 2) + 2)
def find(self, target_value):
# This check is fast because all values were pre-calculated and stored in a hash set.
return target_value in self.recovered_values_set| Case | How to Handle |
|---|---|
| Constructor is initialized with a null root. | The implementation should handle this by treating the tree as empty, causing all subsequent find calls to return false. |
| The tree consists of only a single root node. | The recovery process correctly identifies the root's value as 0, so find(0) returns true while any other target returns false. |
| A degenerate tree that resembles a linked list, for example, all nodes are only left children. | The chosen data structure, like a hash set, is unaffected by the tree's shape, ensuring consistent performance. |
| Finding a target value that is missing in the given sparse tree but would exist in a complete tree. | The recovery process only stores values for existing nodes, so searching for a value corresponding to a null child path correctly returns false. |
| The find method is called with a target of 0. | This corresponds to the root of the tree, which is guaranteed to exist by the constraints, so the solution should always return true. |
| The tree has the maximum allowed height of 20, leading to potentially large node values. | The largest possible node value, approximately 2^20, fits within a standard 32-bit integer, preventing overflow during value calculation. |
| The system is tested against the maximum constraints: 10^4 nodes and 10^4 find calls. | A pre-computation approach using a hash set provides O(1) average time for each find call, which is necessary to pass within time limits. |
| The find method is called multiple times with different targets on the same initialized object. | The recovered values are stored in a persistent data structure during initialization, allowing repeated queries to be answered efficiently without re-traversing the tree. |