Introduction
Binary tree problems often become interesting when the answer for a node depends on everything below that node.
This problem is exactly that.
For every node, the task is to calculate the average of all values in its subtree and check whether that average is equal to the value of the node itself.
At first, this looks like a simple DFS problem. But there is an important optimization opportunity hidden inside it.
To calculate the average of a subtree, two things are required:
- The sum of all nodes in the subtree
- The number of nodes in the subtree
The first approach calculates these values directly. Then, by looking at the repeated work, the solution can be improved further.
Question Link
LeetCode 2265 – Count Nodes Equal to Average of Subtree
Understanding the Problem
For every node:
The average is rounded down.
If the calculated average is equal to the current node's value, that node is counted.
Consider this tree:
For node 5:
Since the average is 5, node 5 contributes to the answer.
The same calculation has to be performed for every node.
The Core Observation
For each node, the required information is:
Once these two values are available, calculating the average is straightforward.
The natural question is:
How can the sum and count of every subtree be calculated efficiently?
A recursive DFS is a natural fit because the information of a subtree can be obtained by processing its children first.
Approach 1: Calculate Subtree Sum and Count Separately
The first approach is the most direct way to think about the problem.
For every node:
- Calculate the sum of its entire subtree.
- Calculate the number of nodes in that subtree.
- Calculate the average.
- Compare it with the current node.
- Repeat for the left and right children.
A separate count() function can be used to count the nodes.
This approach follows the problem statement very closely, which makes it a good starting point for understanding the solution.
Code
Here is the complete approach, including the separate count() idea:
How This Approach Works
The interesting part is sumdfs().
Initially, a separate count() function can be used:
At the same time, another traversal calculates the sum.
But there is no need to traverse the same subtree again just to count its nodes.
The sumdfs() function is already visiting every node, so the counter can be increased during the same traversal.
That is why the following idea works:
Whenever a node is visited, co increases by one.
After sumdfs(root) finishes:
Now:
and the average can be calculated.
Dry Run
Consider:
Start from node 4.
Node 0
So node 0 is counted.
Node 1
Node 1 is counted.
Node 8
Its subtree is:
Therefore:
Since:
node 8 is not counted.
Node 6
Node 6 is counted.
Node 5
Its subtree is:
So:
Node 5 is counted.
Node 4
The complete subtree is:
Therefore:
Node 4 is counted.
The final answer is:
An Important Complexity Observation
The co variable removes the need for a separate count() traversal.
However, there is still a bigger issue.
Look at what happens when solve() processes the root.
It calls:
which visits the entire tree.
Then solve() moves to the left child and calls:
which visits that subtree again.
Then the same thing happens for the right subtree.
So the same nodes can be visited many times.
For example, in a skewed tree:
The first call processes:
The second processes:
The third:
and so on.
Therefore, although the co optimization is cleaner than using a separate count() traversal, the overall worst-case complexity can still reach:
This observation leads directly to the optimal solution.
Approach 2: Calculate Everything During One Postorder Traversal
Instead of recalculating every subtree, the better idea is:
Calculate the subtree information once and return it to the parent.
For every node, the DFS can return two values:
Suppose a node has:
Then the current node can calculate:
and:
Now the average is immediately available.
After checking the current node, the pair:
is returned to its parent.
This means every node is processed exactly once.
Why Postorder Traversal?
The traversal order is:
This is called postorder traversal.
It fits perfectly because the parent needs information from its children before it can calculate its own subtree information.
For example:
The algorithm first calculates information for:
and then combines them at:
This is a very common pattern in tree problems.
Optimal Java Solution
A small Pair class can be used to return both the subtree sum and node count together.
Why This Solution Is Optimal
Consider node 4.
Instead of recalculating its subtree from scratch, the algorithm receives:
and:
Then:
and:
Therefore:
The information is calculated once and passed upward.
No subtree needs to be recalculated.
Complexity Analysis
Approach 1
The sumdfs() traversal can be repeated for every node.
Time Complexity
in the worst case.
Space Complexity
The recursion stack depends on the height of the tree:
Approach 2: One Postorder DFS
Every node is visited exactly once.
At each node, only constant-time operations are performed.
Time Complexity
Space Complexity
where h is the height of the tree.
For a balanced tree:
For a skewed tree:
A Small Java Improvement
There is also a small simplification in the original code.
The following expression:
is unnecessary here.
Because tot and cot are integers:
already performs integer division.
For example:
which is exactly the required rounded-down result because all node values are non-negative.
Therefore, the comparison can simply be:
The optimal solution uses:
which is cleaner and easier to read.
Approach Comparison
| Approach | Main Idea | Time | Space |
| Separate sum + count | Calculate subtree information directly | O(n²) worst case | O(h) |
Global co counter | Count nodes during sum traversal | O(n²) worst case | O(h) |
| One postorder DFS | Return sum + count to parent | O(n) | O(h) |
The second approach is a useful improvement over the first, but the third approach is the real optimization.
What Can Be Learned From This Problem?
This problem teaches an important binary-tree pattern:
If a parent needs information about its entire subtree, calculate that information from the results returned by its children.
Instead of repeatedly asking:
the children calculate their information once and return it.
The parent simply combines the results.
This pattern appears in many tree problems involving:
- Subtree sums
- Subtree sizes
- Tree height
- Diameter
- Balanced tree checking
- Maximum path calculations
- Counting nodes satisfying a condition
- Tree dynamic programming
A useful mental template is:
Once this pattern becomes familiar, many seemingly complicated tree problems become much easier to approach.
Interview Tip
If an interviewer asks for the straightforward solution first, it is perfectly reasonable to start with the direct recursive approach.
But after getting it working, look for repeated subtree calculations.
A strong follow-up thought process is:
This is often the difference between an acceptable recursive solution and an optimal tree solution.
Conclusion
The straightforward approach is a natural way to solve this problem: for each node, calculate the sum and number of nodes in its subtree, find the average, and check whether it matches the node's value.
The first improvement is to notice that the same DFS used for calculating the sum can also maintain the node count using a shared counter instead of running a separate count() traversal.
However, the bigger optimization comes from noticing that subtrees are still being recalculated.
The optimal solution solves this by using one postorder DFS. Each node receives the sum and count from its children, calculates its own subtree information, checks the average, and passes the result to its parent.
This changes the worst-case time complexity from:
to:
The most valuable takeaway is the pattern behind the solution:
When a tree problem asks for information about a subtree, try to calculate that information once and return it upward through postorder DFS.




