Forgetting null/base-case handling
Wrong move: Recursive traversal assumes children always exist.
Usually fails on: Leaf nodes throw errors or create wrong depth/path values.
Fix: Handle null/base cases before recursive transitions.
Move from brute-force thinking to an efficient approach using tree strategy.
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.Problem summary: 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.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Tree · Design
["CBTInserter","insert","insert","get_root"] [[[1,2]],[3],[4],[]]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #919: Complete Binary Tree Inserter
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class CBTInserter {
private List<TreeNode> tree = new ArrayList<>();
public CBTInserter(TreeNode root) {
Deque<TreeNode> q = new ArrayDeque<>();
q.offer(root);
while (!q.isEmpty()) {
for (int i = q.size(); i > 0; --i) {
TreeNode node = q.poll();
tree.add(node);
if (node.left != null) {
q.offer(node.left);
}
if (node.right != null) {
q.offer(node.right);
}
}
}
}
public int insert(int val) {
TreeNode p = tree.get((tree.size() - 1) / 2);
TreeNode node = new TreeNode(val);
tree.add(node);
if (p.left == null) {
p.left = node;
} else {
p.right = node;
}
return p.val;
}
public TreeNode get_root() {
return tree.get(0);
}
}
/**
* Your CBTInserter object will be instantiated and called as such:
* CBTInserter obj = new CBTInserter(root);
* int param_1 = obj.insert(val);
* TreeNode param_2 = obj.get_root();
*/
// Accepted solution for LeetCode #919: Complete Binary Tree Inserter
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
type CBTInserter struct {
tree []*TreeNode
}
func Constructor(root *TreeNode) CBTInserter {
q := []*TreeNode{root}
tree := []*TreeNode{}
for len(q) > 0 {
for i := len(q); i > 0; i-- {
node := q[0]
q = q[1:]
tree = append(tree, node)
if node.Left != nil {
q = append(q, node.Left)
}
if node.Right != nil {
q = append(q, node.Right)
}
}
}
return CBTInserter{tree}
}
func (this *CBTInserter) Insert(val int) int {
p := this.tree[(len(this.tree)-1)/2]
node := &TreeNode{val, nil, nil}
this.tree = append(this.tree, node)
if p.Left == nil {
p.Left = node
} else {
p.Right = node
}
return p.Val
}
func (this *CBTInserter) Get_root() *TreeNode {
return this.tree[0]
}
/**
* Your CBTInserter object will be instantiated and called as such:
* obj := Constructor(root);
* param_1 := obj.Insert(val);
* param_2 := obj.Get_root();
*/
# Accepted solution for LeetCode #919: Complete Binary Tree Inserter
# Definition for a binary tree node.
# 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: Optional[TreeNode]):
self.tree = []
q = deque([root])
while q:
for _ in range(len(q)):
node = q.popleft()
self.tree.append(node)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
def insert(self, val: int) -> int:
p = self.tree[(len(self.tree) - 1) // 2]
node = TreeNode(val)
self.tree.append(node)
if p.left is None:
p.left = node
else:
p.right = node
return p.val
def get_root(self) -> Optional[TreeNode]:
return self.tree[0]
# Your CBTInserter object will be instantiated and called as such:
# obj = CBTInserter(root)
# param_1 = obj.insert(val)
# param_2 = obj.get_root()
// Accepted solution for LeetCode #919: Complete Binary Tree Inserter
use rustgym_util::*;
struct CBTInserter {
stack: Vec<TreeLink>,
}
impl CBTInserter {
fn new(root: TreeLink) -> Self {
let mut stack: Vec<TreeLink> = vec![root];
let mut i = 0;
while i < stack.len() {
let left = stack[i].as_mut().unwrap().borrow_mut().left.clone();
let right = stack[i].as_mut().unwrap().borrow_mut().right.clone();
if left.is_some() {
stack.push(left);
}
if right.is_some() {
stack.push(right);
}
i += 1;
}
CBTInserter { stack }
}
fn insert(&mut self, v: i32) -> i32 {
let link = tree!(v);
let n = self.stack.len();
self.stack.push(link.clone());
let mut parent = self.stack[(n - 1) / 2].as_mut().unwrap().borrow_mut();
if n % 2 == 1 {
parent.left = link;
} else {
parent.right = link;
}
parent.val
}
fn get_root(&self) -> TreeLink {
self.stack[0].clone()
}
}
#[test]
fn test() {
let mut obj = CBTInserter::new(tree!(1));
assert_eq!(obj.insert(2), 1);
assert_eq!(obj.get_root(), tree!(1, tree!(2), None));
}
// Accepted solution for LeetCode #919: Complete Binary Tree Inserter
/**
* Definition for a binary tree node.
* class TreeNode {
* val: number
* left: TreeNode | null
* right: TreeNode | null
* constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
* }
*/
class CBTInserter {
private tree: TreeNode[] = [];
constructor(root: TreeNode | null) {
if (root === null) {
return;
}
const q: TreeNode[] = [root];
while (q.length) {
const t: TreeNode[] = [];
for (const node of q) {
this.tree.push(node);
node.left !== null && t.push(node.left);
node.right !== null && t.push(node.right);
}
q.splice(0, q.length, ...t);
}
}
insert(val: number): number {
const p = this.tree[(this.tree.length - 1) >> 1];
const node = new TreeNode(val);
this.tree.push(node);
if (p.left === null) {
p.left = node;
} else {
p.right = node;
}
return p.val;
}
get_root(): TreeNode | null {
return this.tree[0];
}
}
/**
* Your CBTInserter object will be instantiated and called as such:
* var obj = new CBTInserter(root)
* var param_1 = obj.insert(val)
* var param_2 = obj.get_root()
*/
Use this to step through a reusable interview workflow for this problem.
BFS with a queue visits every node exactly once — O(n) time. The queue may hold an entire level of the tree, which for a complete binary tree is up to n/2 nodes = O(n) space. This is optimal in time but costly in space for wide trees.
Every node is visited exactly once, giving O(n) time. Space depends on tree shape: O(h) for recursive DFS (stack depth = height h), or O(w) for BFS (queue width = widest level). For balanced trees h = log n; for skewed trees h = n.
Review these before coding to avoid predictable interview regressions.
Wrong move: Recursive traversal assumes children always exist.
Usually fails on: Leaf nodes throw errors or create wrong depth/path values.
Fix: Handle null/base cases before recursive transitions.