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.
Given the root of a binary tree, determine if it is a complete binary tree.
In a complete binary tree, every level, except possibly the last, is completely filled, and all nodes in the last level are as far left as possible. It can have between 1 and 2h nodes inclusive at the last level h.
Example 1:
Input: root = [1,2,3,4,5,6]
Output: true
Explanation: Every level before the last is full (ie. levels with node-values {1} and {2, 3}), and all nodes in the last level ({4, 5, 6}) are as far left as possible.
Example 2:
Input: root = [1,2,3,4,5,null,7] Output: false Explanation: The node with value 7 isn't as far left as possible.
Constraints:
[1, 100].1 <= Node.val <= 1000Problem summary: Given the root of a binary tree, determine if it is a complete binary tree. In a complete binary tree, every level, except possibly the last, is completely filled, and all nodes in the last level are as far left as possible. It can have between 1 and 2h nodes inclusive at the last level h.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Tree
[1,2,3,4,5,6]
[1,2,3,4,5,null,7]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #958: Check Completeness of a Binary Tree
/**
* 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 Solution {
public boolean isCompleteTree(TreeNode root) {
Deque<TreeNode> q = new LinkedList<>();
q.offer(root);
while (q.peek() != null) {
TreeNode node = q.poll();
q.offer(node.left);
q.offer(node.right);
}
while (!q.isEmpty() && q.peek() == null) {
q.poll();
}
return q.isEmpty();
}
}
// Accepted solution for LeetCode #958: Check Completeness of a Binary Tree
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func isCompleteTree(root *TreeNode) bool {
q := []*TreeNode{root}
for q[0] != nil {
root = q[0]
q = q[1:]
q = append(q, root.Left)
q = append(q, root.Right)
}
for len(q) > 0 && q[0] == nil {
q = q[1:]
}
return len(q) == 0
}
# Accepted solution for LeetCode #958: Check Completeness of a Binary Tree
# 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 Solution:
def isCompleteTree(self, root: TreeNode) -> bool:
q = deque([root])
while q:
node = q.popleft()
if node is None:
break
q.append(node.left)
q.append(node.right)
return all(node is None for node in q)
// Accepted solution for LeetCode #958: Check Completeness of a Binary Tree
struct Solution;
use rustgym_util::*;
use std::collections::HashSet;
trait Postorder {
fn postorder(&self, id: u32, nodes: &mut HashSet<u32>) -> usize;
}
impl Postorder for TreeLink {
fn postorder(&self, id: u32, nodes: &mut HashSet<u32>) -> usize {
if let Some(node) = self {
let node = node.borrow();
let left = node.left.postorder(id << 1, nodes);
let right = node.right.postorder((id << 1) | 1, nodes);
nodes.insert(id);
left + right + 1
} else {
0
}
}
}
impl Solution {
fn is_complete_tree(root: TreeLink) -> bool {
let mut nodes: HashSet<u32> = HashSet::new();
let count = root.postorder(1, &mut nodes);
nodes.len() == count && nodes.into_iter().all(|x| x <= count as u32)
}
}
#[test]
fn test() {
let root = tree!(1, tree!(2, tree!(4), tree!(5)), tree!(3, tree!(6), None));
let res = true;
assert_eq!(Solution::is_complete_tree(root), res);
let root = tree!(1, tree!(2, tree!(4), tree!(5)), tree!(3, None, tree!(7)));
let res = false;
assert_eq!(Solution::is_complete_tree(root), res);
}
// Accepted solution for LeetCode #958: Check Completeness of a Binary Tree
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #958: Check Completeness of a Binary Tree
// /**
// * 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 Solution {
// public boolean isCompleteTree(TreeNode root) {
// Deque<TreeNode> q = new LinkedList<>();
// q.offer(root);
// while (q.peek() != null) {
// TreeNode node = q.poll();
// q.offer(node.left);
// q.offer(node.right);
// }
// while (!q.isEmpty() && q.peek() == null) {
// q.poll();
// }
// return q.isEmpty();
// }
// }
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.