🧠
Better Approach (BFS with Indexing to Track Positions)
💡 This approach introduces the key insight of assigning indices to nodes to measure width including null gaps, bridging the gap between brute force and optimal.
Intuition
Assign an index to each node as if the tree was a complete binary tree, then calculate width as rightmost index - leftmost index + 1 at each level.
Algorithm
- Initialize a queue with pairs (node, index), starting with (root, 0).
- For each level, record the index of the first node as leftmost.
- Traverse all nodes at the level, updating the rightmost index.
- Calculate width as rightmost - leftmost + 1 and update max width.
- Add children to queue with indices 2*index and 2*index + 1 respectively.
💡 Indexing nodes simulates a complete binary tree, allowing us to count gaps without storing nulls explicitly.
from collections import deque
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def widthOfBinaryTree(root):
if not root:
return 0
max_width = 0
queue = deque([(root, 0)]) # (node, index) pairs for BFS
while queue:
level_length = len(queue)
_, first_index = queue[0] # leftmost index at current level
for i in range(level_length):
node, index = queue.popleft()
index -= first_index # normalize index to prevent overflow
if node.left:
queue.append((node.left, 2 * index)) # left child index
if node.right:
queue.append((node.right, 2 * index + 1)) # right child index
if i == level_length - 1:
max_width = max(max_width, index + 1) # update max width
return max_width
# Driver code
if __name__ == '__main__':
root = TreeNode(1)
root.left = TreeNode(3)
root.right = TreeNode(2)
root.left.left = TreeNode(5)
root.left.right = TreeNode(3)
root.right.right = TreeNode(9)
print(widthOfBinaryTree(root)) # Output: 4
Line Notes
queue = deque([(root, 0)])Start BFS with root node at index 0 to track positions
_, first_index = queue[0]Record leftmost index at current level for normalization
index -= first_indexNormalize indices to prevent integer overflow in deep trees
queue.append((node.left, 2 * index))Assign left child index as 2 * parent index
queue.append((node.right, 2 * index + 1))Assign right child index as 2 * parent index + 1
max_width = max(max_width, index + 1)Update max width using normalized rightmost index
import java.util.*;
class TreeNode {
int val;
TreeNode left, right;
TreeNode(int val) { this.val = val; }
}
public class Solution {
public int widthOfBinaryTree(TreeNode root) {
if (root == null) return 0;
Queue<Pair<TreeNode, Integer>> queue = new LinkedList<>();
queue.offer(new Pair<>(root, 0));
int maxWidth = 0;
while (!queue.isEmpty()) {
int levelSize = queue.size();
int firstIndex = queue.peek().getValue();
int lastIndex = 0;
for (int i = 0; i < levelSize; i++) {
Pair<TreeNode, Integer> pair = queue.poll();
TreeNode node = pair.getKey();
int index = pair.getValue() - firstIndex; // normalize
if (node.left != null) queue.offer(new Pair<>(node.left, 2 * index));
if (node.right != null) queue.offer(new Pair<>(node.right, 2 * index + 1));
if (i == levelSize - 1) lastIndex = index;
}
maxWidth = Math.max(maxWidth, lastIndex + 1);
}
return maxWidth;
}
public static void main(String[] args) {
Solution sol = new Solution();
TreeNode root = new TreeNode(1);
root.left = new TreeNode(3);
root.right = new TreeNode(2);
root.left.left = new TreeNode(5);
root.left.right = new TreeNode(3);
root.right.right = new TreeNode(9);
System.out.println(sol.widthOfBinaryTree(root)); // Output: 4
}
}
// Helper Pair class if not using JavaFX or AbstractMap.SimpleEntry
class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) { this.key = key; this.value = value; }
public K getKey() { return key; }
public V getValue() { return value; }
}
Line Notes
Queue<Pair<TreeNode, Integer>> queue = new LinkedList<>();Queue holds nodes with their indices for BFS traversal
queue.offer(new Pair<>(root, 0));Start BFS with root at index 0
int firstIndex = queue.peek().getValue();Get leftmost index at current level for normalization
int index = pair.getValue() - firstIndex;Normalize index to avoid overflow
queue.offer(new Pair<>(node.left, 2 * index));Assign left child index as 2 * parent index
queue.offer(new Pair<>(node.right, 2 * index + 1));Assign right child index as 2 * parent index + 1
maxWidth = Math.max(maxWidth, lastIndex + 1);Update max width with current level width
#include <iostream>
#include <queue>
#include <utility>
using namespace std;
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
int widthOfBinaryTree(TreeNode* root) {
if (!root) return 0;
queue<pair<TreeNode*, unsigned long long>> q;
q.push({root, 0});
unsigned long long maxWidth = 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, index] = q.front();
q.pop();
index -= firstIndex; // normalize
if (node->left) q.push({node->left, 2 * index});
if (node->right) q.push({node->right, 2 * index + 1});
if (i == levelSize - 1) lastIndex = index;
}
maxWidth = max(maxWidth, lastIndex + 1);
}
return (int)maxWidth;
}
int main() {
TreeNode* root = new TreeNode(1);
root->left = new TreeNode(3);
root->right = new TreeNode(2);
root->left->left = new TreeNode(5);
root->left->right = new TreeNode(3);
root->right->right = new TreeNode(9);
cout << widthOfBinaryTree(root) << endl; // Output: 4
return 0;
}
Line Notes
queue<pair<TreeNode*, unsigned long long>> q;Queue holds nodes with indices for BFS traversal
q.push({root, 0});Start BFS with root at index 0
unsigned long long firstIndex = q.front().second;Get leftmost index at current level for normalization
index -= firstIndex;Normalize index to prevent overflow
q.push({node->left, 2 * index});Assign left child index as 2 * parent index
q.push({node->right, 2 * index + 1});Assign right child index as 2 * parent index + 1
maxWidth = max(maxWidth, lastIndex + 1);Update max width with current level width
class TreeNode {
constructor(val=0, left=null, right=null) {
this.val = val;
this.left = left;
this.right = right;
}
}
function widthOfBinaryTree(root) {
if (!root) return 0;
let queue = [[root, 0]]; // [node, index] pairs for BFS
let maxWidth = 0;
while (queue.length > 0) {
let levelLength = queue.length;
let firstIndex = queue[0][1]; // leftmost index
let lastIndex = 0;
let nextLevel = [];
for (let i = 0; i < levelLength; i++) {
let [node, index] = queue[i];
index -= firstIndex; // normalize
if (node.left) nextLevel.push([node.left, 2 * index]);
if (node.right) nextLevel.push([node.right, 2 * index + 1]);
if (i === levelLength - 1) lastIndex = index;
}
maxWidth = Math.max(maxWidth, lastIndex + 1);
queue = nextLevel;
}
return maxWidth;
}
// Driver code
const root = new TreeNode(1);
root.left = new TreeNode(3);
root.right = new TreeNode(2);
root.left.left = new TreeNode(5);
root.left.right = new TreeNode(3);
root.right.right = new TreeNode(9);
console.log(widthOfBinaryTree(root)); // Output: 4
Line Notes
let queue = [[root, 0]];Initialize queue with root and index 0 for BFS
let firstIndex = queue[0][1];Get leftmost index at current level for normalization
index -= firstIndex;Normalize index to avoid overflow
nextLevel.push([node.left, 2 * index]);Assign left child index as 2 * parent index
nextLevel.push([node.right, 2 * index + 1]);Assign right child index as 2 * parent index + 1
maxWidth = Math.max(maxWidth, lastIndex + 1);Update max width with current level width
queue = nextLevel;Move to next level nodes
Each node is visited once, and queue holds nodes per level with indices.
💡 For n=1000, this approach efficiently calculates width including gaps.
Interview Verdict: Accepted and correct
This approach correctly handles sparse trees by indexing nodes, making it suitable for interviews.