Last Updated: April 3, 2026
We have a binary tree and we need to compute the average of all node values at each depth level. Level 0 is the root, level 1 is the root's children, level 2 is the grandchildren, and so on.
The word "each level" is the key here. We are not computing a single average over the whole tree. We need to group nodes by their depth, compute a separate average for each group, and return those averages in order from top to bottom.
This naturally leads us to think about level-order traversal. If we can process all nodes at the same depth together, computing averages becomes straightforward: sum the values, divide by the count. The question is just how we group them, and there are two clean ways to do it.
1 <= number of nodes <= 10^4 -> With up to 10,000 nodes, O(n) and O(n log n) approaches are both fine. Even O(n^2) would work within limits, but there is no reason to go there.-2^31 <= Node.val <= 2^31 - 1 -> Node values can be as large as Integer.MAX_VALUE or as small as Integer.MIN_VALUE. If we sum multiple such values, we can overflow a 32-bit integer. We need to use long (or equivalent) for the running sum at each level.The most natural way to compute averages per level is to process the tree level by level. BFS does exactly this. We use a queue, and at each step, we know how many nodes are in the current level (it is the current size of the queue). We dequeue all of them, sum their values, count them, and compute the average. Then we enqueue their children, which form the next level.
The trick that makes BFS work level-by-level is capturing the queue size at the start of each level. Before we start dequeuing, we check how many nodes are in the queue right now. That number tells us exactly how many nodes belong to the current level. We process exactly that many, and anything we enqueue during processing belongs to the next level.
BFS naturally visits nodes in level order. The key insight is using the queue size at the start of each iteration to know exactly how many nodes belong to the current level. Everything we enqueue during that iteration belongs to the next level, so there is a clean separation between levels without needing any extra markers or sentinel values.
The queue only ever holds at most two consecutive levels of nodes. For a balanced binary tree, the widest level has about n/2 nodes, so the queue peaks at roughly n/2 elements. For a skewed tree, the queue holds just 1 node at a time.
BFS is already optimal in terms of time, but the queue can hold up to half the tree in memory for wide trees. What if we tracked level information during a DFS instead, using only O(h) stack space?
DFS does not process nodes level by level, but it does know the depth of every node it visits. If we pass the current depth along during recursion, we can accumulate the sum and count for each level in two parallel arrays. After the DFS finishes, we just divide each level's sum by its count to get the average.
Think of it this way: BFS groups nodes by level explicitly through the queue. DFS groups them implicitly by tagging each node with its depth and using that depth as an index into our tracking arrays. The end result is the same, but the traversal order is different.
This approach shines when the tree is very tall and narrow (like a skewed tree), because the call stack depth is O(h) and h could be much smaller than the queue width in BFS.
DFS visits every node exactly once, just like BFS. Since we tag each node with its depth and accumulate into level-indexed arrays, the order of visits does not matter. Whether we visit node 9 before or after node 20 is irrelevant because both contribute to level 1's sum and count.
The depth == sums.size() check is a neat trick. Because DFS goes left-first and top-down, the first time we reach any given depth is always through the leftmost path. So the sums and counts lists grow in order, and we never skip a depth.
sums to store the running sum at each level, and counts to store the number of nodes at each level.sums, this is the first time we have reached this depth. Append the node's value to sums and 1 to counts.sums[depth] and increment counts[depth].sums[i] by counts[i] for each level.