Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

662. Maximum Width of Binary Tree - Leetcode Solution

Code Implementation

from collections import deque

class Solution:
    def widthOfBinaryTree(self, root):
        if not root:
            return 0
        max_width = 0
        queue = deque([(root, 0)])  # (node, index)
        while queue:
            level_length = len(queue)
            _, first_index = queue[0]
            for i in range(level_length):
                node, idx = queue.popleft()
                if node.left:
                    queue.append((node.left, 2 * idx))
                if node.right:
                    queue.append((node.right, 2 * idx + 1))
            # last node's index in this level
            last_index = idx
            max_width = max(max_width, last_index - first_index + 1)
        return max_width
      
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
#include <queue>
using namespace std;

class Solution {
public:
    int widthOfBinaryTree(TreeNode* root) {
        if (!root) return 0;
        unsigned long long maxWidth = 0;
        queue<pair<TreeNode*, unsigned long long>> q;
        q.push({root, 0});
        while (!q.empty()) {
            int levelSize = q.size();
            unsigned long long firstIndex = q.front().second;
            unsigned long long lastIndex = 0;
            for (int i = 0; i < levelSize; ++i) {
                auto node_pair = q.front(); q.pop();
                TreeNode* node = node_pair.first;
                unsigned long long idx = node_pair.second;
                lastIndex = idx;
                if (node->left)
                    q.push({node->left, 2 * idx});
                if (node->right)
                    q.push({node->right, 2 * idx + 1});
            }
            maxWidth = max(maxWidth, lastIndex - firstIndex + 1);
        }
        return (int)maxWidth;
    }
};
      
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
import java.util.*;

public class Solution {
    public int widthOfBinaryTree(TreeNode root) {
        if (root == null) return 0;
        int maxWidth = 0;
        Queue<Pair> queue = new LinkedList<>();
        queue.offer(new Pair(root, 0));
        while (!queue.isEmpty()) {
            int size = queue.size();
            int firstIndex = queue.peek().idx;
            int lastIndex = 0;
            for (int i = 0; i < size; ++i) {
                Pair p = queue.poll();
                TreeNode node = p.node;
                int idx = p.idx;
                lastIndex = idx;
                if (node.left != null)
                    queue.offer(new Pair(node.left, 2 * idx));
                if (node.right != null)
                    queue.offer(new Pair(node.right, 2 * idx + 1));
            }
            maxWidth = Math.max(maxWidth, lastIndex - firstIndex + 1);
        }
        return maxWidth;
    }
}

class Pair {
    TreeNode node;
    int idx;
    Pair(TreeNode node, int idx) {
        this.node = node;
        this.idx = idx;
    }
}
      
/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var widthOfBinaryTree = function(root) {
    if (!root) return 0;
    let maxWidth = 0;
    let queue = [[root, 0]];
    while (queue.length > 0) {
        let levelLength = queue.length;
        let firstIndex = queue[0][1];
        let lastIndex = 0;
        for (let i = 0; i < levelLength; i++) {
            let [node, idx] = queue.shift();
            lastIndex = idx;
            if (node.left) queue.push([node.left, 2 * idx]);
            if (node.right) queue.push([node.right, 2 * idx + 1]);
        }
        maxWidth = Math.max(maxWidth, lastIndex - firstIndex + 1);
    }
    return maxWidth;
};
      

Problem Description

Given the root node of a binary tree, you are to find the maximum width of the tree. The width of a binary tree at a particular level is defined as the length between the leftmost and rightmost non-null nodes at that level (including the null nodes in between). The tree may not be complete or balanced, and nodes may be missing at any position.

The input is a binary tree represented by its root node. The output is an integer representing the maximum width among all levels of the tree.

  • Each level's width is calculated as: last_index - first_index + 1, where first_index and last_index are the indices of the leftmost and rightmost non-null nodes at that level, respectively.
  • Indices are assigned as if the tree were a complete binary tree: for a node at index i, its left child is at 2*i and right child at 2*i + 1.
  • There is guaranteed to be at least one node in the tree.

Thought Process

At first glance, you might consider traversing each level and counting the number of nodes. However, because the tree can have missing nodes (not a complete tree), simply counting nodes per level does not give the correct width. Instead, we need a way to account for the "gaps" created by missing nodes.

The key insight is to assign a positional index to each node as if the tree were a complete binary tree. This allows us to calculate the width by comparing the indices of the leftmost and rightmost nodes at each level, even if there are missing nodes between them.

A brute-force approach would involve reconstructing the tree with explicit nulls or traversing all possible positions, but this is inefficient. Instead, we can use a queue for level-order traversal (BFS), keeping track of each node's index. This way, we can efficiently compute the width at each level as we go.

Solution Approach

  • Level-Order Traversal (BFS): We use a queue to traverse the tree level by level, keeping track of each node and its corresponding index as if the tree were complete.
  • Indexing Nodes: For each node, assign an index. The root starts at index 0. For any node at index i:
    • Its left child is at 2 * i
    • Its right child is at 2 * i + 1
    This indexing system ensures that the difference between the first and last indices at a level reflects the actual width, including nulls.
  • Tracking Width: For each level, record the index of the first and last node. The width is last_index - first_index + 1. Update the maximum width found so far.
  • Implementation Details:
    • We use a queue of tuples/pairs: each entry is (node, index).
    • At each level, process all nodes in the queue, enqueue their children with calculated indices.
    • After processing a level, compute the width and update the result if it's the largest seen so far.

Example Walkthrough

Consider the following binary tree:

          1
         / \
        3   2
       /     \
      5       9
     /         \
    6           7
  
  • Level 0: Node 1 at index 0. Width = 1.
  • Level 1: Node 3 at index 0, Node 2 at index 1. Width = 2.
  • Level 2: Node 5 at index 0, Node 9 at index 3 (because 3's left is at 2*0=0, right would be 1, but missing; 2's right is at 2*1+1=3). Width = 3-0+1 = 4.
  • Level 3: Node 6 at index 0, Node 7 at index 7 (5's left is at 2*0=0, 9's right is at 2*3+1=7). Width = 7-0+1 = 8.
  • The maximum width among all levels is 8.

At each step, we record the indices of the first and last nodes in the queue for that level, and compute the width accordingly.

Time and Space Complexity

  • Brute-force Approach: If you tried to reconstruct the tree with explicit nulls or traverse all possible positions, the time and space complexity could grow exponentially with the depth of the tree (O(2^h)), which is infeasible for large trees.
  • Optimized BFS Approach:
    • Time Complexity: Each node is visited once, so the time complexity is O(N), where N is the number of nodes in the tree.
    • Space Complexity: The queue can hold up to O(N) nodes in the worst case (for the last level of a complete binary tree), plus the integer indices, so overall O(N) space.

Summary

The maximum width of a binary tree can be efficiently computed by assigning indices to nodes as if the tree were complete, and performing a level-order traversal while tracking the leftmost and rightmost indices at each level. This approach elegantly handles missing nodes and avoids the inefficiency of brute-force methods. The key insight is using positional indices to represent potential nulls, allowing for a simple and efficient O(N) solution.