Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions problems/2265-count-nodes-equal-to-average-of-subtree/analysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# 2265. Count Nodes Equal to Average of Subtree

[LeetCode Link](https://leetcode.com/problems/count-nodes-equal-to-average-of-subtree/)

Difficulty: Medium
Topics: Tree, Depth-First Search, Binary Tree
Acceptance Rate: 87.4%

## Hints

### Hint 1

The question asked at every node ("what is the average of my subtree?") depends only on information that lives *below* that node. Whenever a node's answer is a function of its children's answers, think bottom-up recursion — a post-order DFS.

### Hint 2

An average is not something you can combine directly: knowing the left subtree's average and the right subtree's average is not enough to compute the parent's average, because the two subtrees may have different sizes. Ask yourself what pair of numbers *is* composable across children.

### Hint 3

Have the recursion return **two** values per subtree: its sum and its node count. Both compose trivially — `sum = node.Val + leftSum + rightSum` and `count = 1 + leftCount + rightCount`. Once you have them at a node, check `node.Val == sum / count` (Go's integer division on non-negative values already floors, which is exactly the rounding the problem wants) and increment a counter. One traversal computes every node's answer.

## Approach

This is the classic "return an aggregate from DFS" pattern, and the whole trick is choosing an aggregate that merges cleanly.

Define a recursive helper `dfs(node) (sum, count int)`:

1. **Base case:** a `nil` node contributes nothing, so return `(0, 0)`. This makes nodes with one missing child fall out of the general case with no special handling.
2. **Recurse:** get `(leftSum, leftCount)` and `(rightSum, rightCount)` from the children.
3. **Combine:** `sum = node.Val + leftSum + rightSum`, `count = 1 + leftCount + rightCount`.
4. **Check the node itself:** if `node.Val == sum/count`, bump a shared counter. `count` is at least 1 here (the node itself), so the division is always safe.
5. **Return** `(sum, count)` so the parent can build its own aggregate.

The answer is the counter's value after the traversal. A closure over a captured `answer` variable keeps the helper's signature focused on the aggregate rather than threading the count through the return values as well.

Why the floor is free: the constraints guarantee `0 <= Node.val <= 1000`, so every subtree sum is non-negative, and Go's `/` on non-negative integers truncates toward zero — which equals flooring. If values could be negative you would have to floor explicitly, since Go truncates toward zero rather than toward negative infinity.

Walking Example 1, `root = [4,8,5,0,1,null,6]`:

- Node `0` → `(0, 1)`, average `0` → matches, count it.
- Node `1` → `(1, 1)`, average `1` → matches.
- Node `8` → sum `8+0+1 = 9`, count `3`, average `9/3 = 3` ≠ `8` → no.
- Node `6` → `(6, 1)`, average `6` → matches.
- Node `5` → sum `5+6 = 11`, count `2`, average `11/2 = 5` (floored) → matches. This is the case that makes the flooring rule visible.
- Node `4` (root) → sum `24`, count `6`, average `4` → matches.

Total: 5, as expected.

Overflow is a non-issue: at most 1000 nodes with values up to 1000 caps the sum at 1,000,000, far inside `int`.

## Complexity Analysis

Time Complexity: O(n) — each node is visited exactly once and does O(1) work.
Space Complexity: O(h) for the recursion stack, where `h` is the tree height. That is O(log n) for a balanced tree and O(n) in the worst case of a degenerate, list-like tree (with n up to 1000, recursion depth is never a concern here).

## Edge Cases

- **Single node tree** (`[1]`): the subtree average is the node's own value, so the answer is always 1. A good sanity check that the base case returns `(0, 0)` rather than something that skews the sum or count.
- **`nil` children / nodes with only one child**: the `(0, 0)` base case must contribute nothing to either the sum or the count. Returning a count of 1 for `nil` would silently deflate every average above it.
- **Nodes with value 0**: `0 == 0/1` matches, so zero-valued leaves always count. Don't let a truthiness-style check or a "skip empty values" instinct drop them.
- **Flooring matters**: subtrees like `[5, null, 6]` (sum 11, count 2) only match because `11/2` floors to `5`. Using floating-point division and comparing to `node.Val` would reject this node.
- **Skewed trees**: a 1000-node chain gives recursion depth 1000, which is fine in Go; worth noting only if you were asked to handle much deeper trees, where an explicit stack or Morris-style traversal would be needed.

Honest difficulty note: the traversal is short, but the reason this is a Medium rather than an Easy is the "averages don't compose, sums and counts do" insight. If you internalize that — return the raw ingredients from DFS, not the derived value — a whole family of tree problems opens up.
62 changes: 62 additions & 0 deletions problems/2265-count-nodes-equal-to-average-of-subtree/problem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
number: "2265"
frontend_id: "2265"
title: "Count Nodes Equal to Average of Subtree"
slug: "count-nodes-equal-to-average-of-subtree"
difficulty: "Medium"
topics:
- "Tree"
- "Depth-First Search"
- "Binary Tree"
acceptance_rate: 8737.7
is_premium: false
created_at: "2026-09-10T05:00:41.719057+00:00"
fetched_at: "2026-09-10T05:00:41.719057+00:00"
link: "https://leetcode.com/problems/count-nodes-equal-to-average-of-subtree/"
date: "2026-09-10"
---

# 2265. Count Nodes Equal to Average of Subtree

Given the `root` of a binary tree, return _the number of nodes where the value of the node is equal to the**average** of the values in its **subtree**_.

**Note:**

* The **average** of `n` elements is the **sum** of the `n` elements divided by `n` and **rounded down** to the nearest integer.
* A **subtree** of `root` is a tree consisting of `root` and all of its descendants.





**Example 1:**

![](https://assets.leetcode.com/uploads/2022/03/15/image-20220315203925-1.png)


**Input:** root = [4,8,5,0,1,null,6]
**Output:** 5
**Explanation:**
For the node with value 4: The average of its subtree is (4 + 8 + 5 + 0 + 1 + 6) / 6 = 24 / 6 = 4.
For the node with value 5: The average of its subtree is (5 + 6) / 2 = 11 / 2 = 5.
For the node with value 0: The average of its subtree is 0 / 1 = 0.
For the node with value 1: The average of its subtree is 1 / 1 = 1.
For the node with value 6: The average of its subtree is 6 / 1 = 6.


**Example 2:**

![](https://assets.leetcode.com/uploads/2022/03/26/image-20220326133920-1.png)


**Input:** root = [1]
**Output:** 1
**Explanation:** For the node with value 1: The average of its subtree is 1 / 1 = 1.




**Constraints:**

* The number of nodes in the tree is in the range `[1, 1000]`.
* `0 <= Node.val <= 1000`
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package main

// 2265. Count Nodes Equal to Average of Subtree
//
// Post-order DFS that returns the (sum, count) of each subtree. Averages cannot
// be merged across children of different sizes, but sums and counts can, so the
// recursion carries those raw ingredients upward and derives the average at each
// node. Since all values are non-negative, Go's integer division already floors,
// matching the rounding the problem asks for.
//
// Time: O(n), Space: O(h) for the recursion stack.

type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}

func averageOfSubtree(root *TreeNode) int {
answer := 0

var dfs func(node *TreeNode) (sum, count int)
dfs = func(node *TreeNode) (int, int) {
if node == nil {
return 0, 0
}

leftSum, leftCount := dfs(node.Left)
rightSum, rightCount := dfs(node.Right)

sum := node.Val + leftSum + rightSum
count := 1 + leftCount + rightCount

if node.Val == sum/count {
answer++
}

return sum, count
}

dfs(root)

return answer
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package main

import "testing"

// intPtr wraps a value so test trees can spell out LeetCode's null placeholders.
func intPtr(v int) *int { return &v }

// buildTreeLevelOrder builds a binary tree from a LeetCode-style level-order
// slice, where nil entries mark absent children.
func buildTreeLevelOrder(vals []*int) *TreeNode {
if len(vals) == 0 || vals[0] == nil {
return nil
}

root := &TreeNode{Val: *vals[0]}
queue := []*TreeNode{root}
i := 1

for len(queue) > 0 && i < len(vals) {
node := queue[0]
queue = queue[1:]

if i < len(vals) {
if vals[i] != nil {
node.Left = &TreeNode{Val: *vals[i]}
queue = append(queue, node.Left)
}
i++
}
if i < len(vals) {
if vals[i] != nil {
node.Right = &TreeNode{Val: *vals[i]}
queue = append(queue, node.Right)
}
i++
}
}

return root
}

func TestSolution(t *testing.T) {
tests := []struct {
name string
vals []*int
expected int
}{
{
name: "example 1: [4,8,5,0,1,null,6] counts root, 5, 0, 1 and 6",
vals: []*int{intPtr(4), intPtr(8), intPtr(5), intPtr(0), intPtr(1), nil, intPtr(6)},
expected: 5,
},
{
name: "example 2: single node [1] always matches itself",
vals: []*int{intPtr(1)},
expected: 1,
},
{
name: "edge case: empty input returns zero",
vals: nil,
expected: 0,
},
{
name: "edge case: all zeros [0,0,0] counts every node",
vals: []*int{intPtr(0), intPtr(0), intPtr(0)},
expected: 3,
},
{
name: "edge case: flooring makes [5,null,6] match at the root",
vals: []*int{intPtr(5), nil, intPtr(6)},
expected: 2,
},
{
name: "edge case: left-skewed chain [3,2,null,1] counts only the leaf",
vals: []*int{intPtr(3), intPtr(2), nil, intPtr(1)},
expected: 1,
},
{
name: "edge case: [1,2,3] root average of 2 does not match value 1",
vals: []*int{intPtr(1), intPtr(2), intPtr(3)},
expected: 2,
},
{
name: "edge case: max constrained values [1000,1000,1000] all match",
vals: []*int{intPtr(1000), intPtr(1000), intPtr(1000)},
expected: 3,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
root := buildTreeLevelOrder(tt.vals)
if result := averageOfSubtree(root); result != tt.expected {
t.Errorf("averageOfSubtree() = %v, want %v", result, tt.expected)
}
})
}
}