A binary tree is given using the helper class Node. Each node has a value and two pointers, left and right, that point to other nodes in the tree. Additionally, each node might have a third pointer, random, which also points to any node in the tree, or null.
Clone the given tree.
Here is an example of the Node class:
class Node {
int val;
Node left;
Node right;
Node random;
Node() {}
Node(int val) { this.val = val; }
Node(int val, Node left, Node right, Node random) {
this.val = val;
this.left = left;
this.right = right;
this.random = random;
}
}
Example 1:
Input: root = [[7,null,null,null],[13,0,null,null],[11,null,4,null],[10,7,null,null],[1,0,null,null],[4,2,null,null]] Output: [[7,null,null,null],[13,0,null,null],[11,null,4,null],[10,7,null,null],[1,0,null,null],[4,2,null,null]]
Example 2:
Input: root = [[1,null,null,null],[2,0,null,null]] Output: [[1,null,null,null],[2,0,null,null]]
Constraints:
[0, 1000].-1000 <= Node.val <= 1000When 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 challenge is to make a complete copy of a tree, but with an extra twist: each node has a special 'random' pointer that might point to any other node in the original tree. The brute force method makes a new tree and then painstakingly finds the corresponding random node for each node in the copied tree.
Here's how the algorithm would work step-by-step:
class Node:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
self.random = None
def clone_binary_tree_with_random_pointer_brute_force(root):
if not root:
return None
# Create a deep copy of the tree structure
copied_root = _clone_tree_structure(root)
# Assign random pointers in the copied tree
_assign_random_pointers(root, copied_root)
return copied_root
def _clone_tree_structure(root):
if not root:
return None
copied_node = Node(root.value)
copied_node.left = _clone_tree_structure(root.left)
copied_node.right = _clone_tree_structure(root.right)
return copied_node
def _assign_random_pointers(original_root, copied_root):
if not original_root:
return
# Need to process the current node first
_process_current_node(original_root, copied_root)
_assign_random_pointers(original_root.left, copied_root.left)
_assign_random_pointers(original_root.right, copied_root.right)
def _process_current_node(original_node, copied_node):
if original_node.random:
# Must find corresponding node in copied tree
copied_node.random = _find_corresponding_node(original_node.random, copied_node)
def _find_corresponding_node(original_random_node, copied_root):
if not original_random_node:
return None
return _brute_force_search(original_random_node, copied_root)
def _brute_force_search(original_random_node, copied_node):
if not copied_node:
return None
if copied_node.value == original_random_node.value:
# If values match, assume it's the same node
return copied_node
# Recursively search left and right subtrees
left_result = _brute_force_search(original_random_node, copied_node.left)
if left_result:
return left_result
return _brute_force_search(original_random_node, copied_node.right)We need to create a perfect copy of a binary tree, but this tree has a special pointer that points to a random node. The trick is to efficiently link the random pointers in the cloned tree to their corresponding nodes in the cloned tree.
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
self.random = None
def clone_binary_tree_with_random_pointer(root):
if not root:
return None
node_map = {}
# Create clone nodes and store them in the map
def create_clones(node):
if not node:
return
node_map[node] = TreeNode(node.value)
create_clones(node.left)
create_clones(node.right)
create_clones(root)
# Set left and right pointers of the cloned nodes
def assign_left_right(node):
if not node:
return
cloned_node = node_map[node]
if node.left:
cloned_node.left = node_map[node.left]
if node.right:
cloned_node.right = node_map[node.right]
assign_left_right(node.left)
assign_left_right(node.right)
assign_left_right(root)
# Assign random pointers of the cloned nodes
def assign_random_pointers(node):
if not node:
return
cloned_node = node_map[node]
# Must check if random pointer is not null
if node.random:
cloned_node.random = node_map[node.random]
assign_random_pointers(node.left)
assign_random_pointers(node.right)
assign_random_pointers(root)
# Return the cloned root
return node_map[root]| Case | How to Handle |
|---|---|
| Null root node | Return null immediately if the root is null, indicating an empty tree. |
| Single node tree | Create a new node with the same value and set its random pointer to null, then return the new node. |
| Tree with all nodes having the same value | The hash map will correctly map all original nodes to new nodes with the same value, ensuring a correct clone. |
| Tree with skewed structure (e.g., only left or right children) | Recursion handles this case without stack overflow, as each node is still visited and cloned. |
| Random pointers forming cycles or pointing to nodes outside the subtree | The hash map ensures that the clone of a node already exists and is reused if the random pointer points to it. |
| Large tree (potential for stack overflow with recursion) | Use iterative DFS with a stack to avoid stack overflow for large trees. |
| Original tree modified during cloning | The hashmap stores mapping of old node to new node, thus not being affacted by modifications in original tree. |
| Memory constraints for extremely large trees | Consider using an external memory store or a more memory-efficient data structure if the cloned tree becomes too large to fit in memory. |