A complete binary tree is a binary tree in which every level, except possibly the last, is completely filled, and all nodes are as far left as possible.
Design an algorithm to insert a new node to a complete binary tree keeping it complete after the insertion.
Implement the CBTInserter class:
CBTInserter(TreeNode root) Initializes the data structure with the root of the complete binary tree.int insert(int v) Inserts a TreeNode into the tree with value Node.val == val so that the tree remains complete, and returns the value of the parent of the inserted TreeNode.TreeNode get_root() Returns the root node of the tree.Example 1:
Input ["CBTInserter", "insert", "insert", "get_root"] [[[1, 2]], [3], [4], []] Output [null, 1, 2, [1, 2, 3, 4]] Explanation CBTInserter cBTInserter = new CBTInserter([1, 2]); cBTInserter.insert(3); // return 1 cBTInserter.insert(4); // return 2 cBTInserter.get_root(); // return [1, 2, 3, 4]
Constraints:
[1, 1000].0 <= Node.val <= 5000root is a complete binary tree.0 <= val <= 5000104 calls will be made to insert and get_root.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 problem involves adding new values to a binary tree, making sure it stays 'complete'. A brute force solution involves checking all potential locations for the new value, level by level, until we find an available spot.
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
class CBTInserter:
def __init__(self, root):
self.root = root
def insert(self, value):
queue = [self.root]
while queue:
level_size = len(queue)
# Traverse the current level
for _ in range(level_size):
node = queue.pop(0)
# If the left child is empty, insert the new node there
if not node.left:
node.left = TreeNode(value)
return node.value
# If the right child is empty, insert the new node there
if not node.right:
node.right = TreeNode(value)
return node.value
# Add the children to the queue for the next level
queue.append(node.left)
queue.append(node.right)
def get_root(self):
return self.rootThe goal is to add new values to a binary tree, filling it level by level from left to right. The key idea is to keep track of which nodes might need children added to them, so we don't have to search the whole tree every time we add a new value.
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
class CBTInserter:
def __init__(self, root):
self.root_node = root
self.potential_parents = []
queue = [root]
while queue:
node = queue.pop(0)
if not node.left or not node.right:
self.potential_parents.append(node)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
def insert(self, value):
# Select the first potential parent.
parent_node = self.potential_parents[0]
new_node = TreeNode(value)
if not parent_node.left:
parent_node.left = new_node
else:
parent_node.right = new_node
# Parent is full, so remove from potential parents.
self.potential_parents.pop(0)
# New node could need children later.
self.potential_parents.append(new_node)
return parent_node.value
def get_root(self):
return self.root_node| Case | How to Handle |
|---|---|
| Null root node in constructor | Handle null root by initializing an empty tree and queue, or throwing an exception if null root is invalid. |
| Inserting into a perfectly complete tree. | The solution should correctly add nodes to the next level upon filling the last node in the deepest level. |
| Very large number of insert calls causing integer overflow if tracked improperly | Use a data type with sufficient range (e.g., long) for tracking insertion count or use the tree structure itself to guide insertion. |
| Tree with a single root node initially. | Insertion should correctly add left and right children to the root node and update the queue. |
| Nodes with extremely large values (close to max int). | Ensure the `insert` method can handle large integer values without overflow during comparison or arithmetic operations, but this should be naturally handled by Java unless doing specific manual operations. |
| Skewed tree (e.g., all nodes only have left children). | The algorithm should still correctly add nodes to the appropriate position based on the complete tree property, even with a skewed tree. |
| Inserting a node with a value that already exists in the tree. | The problem doesn't explicitly state uniqueness, so handle duplicate values by simply inserting the new node as per the complete tree insertion rules. |
| Extremely deep tree with many nodes, potentially causing memory issues if the queue grows too large | The queue should only contain nodes that are missing children, so memory usage should be proportional to the number of incomplete nodes, not the total number of nodes, which should be acceptable. |