{x}
blog image

Check Completeness of a Binary Tree

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:

  • The number of nodes in the tree is in the range [1, 100].
  • 1 <= Node.val <= 1000

Solution Explanation:

This problem asks to determine if a given binary tree is a complete binary tree. A complete binary tree is a binary tree in which every level, except possibly the last, is completely filled, and all nodes in the last level are as far left as possible.

The most efficient approach to solve this problem is using Breadth-First Search (BFS). BFS visits nodes level by level. We leverage this property to check for the completeness criteria.

Algorithm:

  1. Initialization: Create a queue q and initialize it with the root node.
  2. BFS Traversal: While the queue is not empty:
    • Dequeue a node from the queue.
    • If the node is null, it means we've encountered a missing node and should proceed to check the remaining queue elements.
    • Otherwise (node is not null):
      • Enqueue the node's left child.
      • Enqueue the node's right child.
  3. Completeness Check: After the BFS traversal:
    • If there are any non-null nodes remaining in the queue, the tree is incomplete (because we encountered nulls, but later encountered other non-null nodes).
    • If all remaining nodes are null, the tree is complete.

Time Complexity Analysis:

The BFS traversal visits each node in the tree exactly once. Therefore, the time complexity is O(N), where N is the number of nodes in the tree.

Space Complexity Analysis:

The space complexity is O(W), where W is the maximum width of the binary tree. In the worst-case scenario (a complete binary tree), W can be proportional to N (number of nodes), resulting in O(N) space complexity. In a balanced tree, W is log₂(N).

Code Explanation (Python3):

from collections import deque
 
# 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  #Encountered a missing node
            q.append(node.left)
            q.append(node.right)
        return all(node is None for node in q) #Check if all remaining are None
 

The Python code directly implements the algorithm described above. It uses collections.deque for efficient queue operations. The all(node is None for node in q) check elegantly verifies the completeness condition after the BFS.

Code Explanation (Java):

import java.util.*;
 
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();
    }
}

The Java code mirrors the Python solution's logic. LinkedList is used as a deque, offering efficient offer (enqueue) and poll (dequeue) operations. The loop continues until the first null node is encountered. The second loop removes trailing nulls, and finally, q.isEmpty() determines completeness.

The C++, and Go solutions follow the same algorithmic approach with appropriate data structure usage for their respective languages. The core logic remains consistent across all provided implementations.