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.For example:
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 <= 5000
root
is a complete binary tree.0 <= val <= 5000
10^4
calls will be made to insert
and get_root
.A brute force approach to solve this problem would involve traversing the entire tree for each insertion to find the appropriate place to add the new node. This would involve:
insert
function that traverses the tree.The problem with this approach is the time complexity. In the worst-case scenario, we might have to traverse a large portion of the tree to find the correct insertion point.
Big O Runtime: O(n) for each insertion, where n is the number of nodes in the tree, because in the worst case, we may have to search a large part of the tree. Big O Space Usage: O(w), where w is the maximum width of the tree because a queue is used for level order traversal.
To optimize, we can keep track of nodes that are potentially the parent of the next inserted node. A queue can be used to store these candidate parent nodes.
Here is the detailed algorithm:
CBTInserter
initialization, perform a level-order traversal of the existing tree.p
from the queue.p
if p
does not have a left child.p
is now full, so remove it from the queue.p
.Edge Cases:
Code:
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class CBTInserter:
def __init__(self, root: TreeNode):
self.root = root
self.queue = deque()
q = deque([root])
while q:
node = q.popleft()
if not node.left or not node.right:
self.queue.append(node)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
def insert(self, val: int) -> int:
node = TreeNode(val)
parent = self.queue[0]
if not parent.left:
parent.left = node
else:
parent.right = node
self.queue.popleft()
self.queue.append(node)
return parent.val
def get_root(self) -> TreeNode:
return self.root
Big O Runtime:
CBTInserter
: O(n), where n is the number of nodes in the initial tree (due to the level-order traversal).insert
: O(1) on average, as we only perform a constant number of operations.get_root
: O(1)Big O Space Usage:
CBTInserter
: O(w), where w is the maximum width of the tree (to store nodes in the queue).insert
: O(1)get_root
: O(1)