Average of Levels in Binary Tree
Average of Levels in Binary Tree
Return the average value of the nodes on each level in a binary tree.
Why this is worth learning
A level average requires a per-level accumulator and a frozen count. Integer division and cross-level sums are independent sources of subtle errors.
Before coding: one-minute experiment
For a level containing values 2 and 3, what is the average and why is integer division wrong?
Function signature
func AverageOfLevels(root *TreeNode) []float64
Inputs / outputs
root: root of the tree.- Return: slice where each entry is the average of one level.
Example
3
/ \
9 20
/ \
15 7
Output: [3.0, 14.5, 11.0]
Constraints
- 0 <= number of nodes <= 100_000
- node values fit in 32-bit signed int
Core idea (near-solution)
Collecting every level and then averaging works, but the calculation only needs two summaries: sum and count. Save the frontier size before adding children and accumulate exactly those values. A divisor based on a changing queue length would mix depths. Integer division would discard the fraction, so convert operands before dividing. The signed-32-bit values and at most 100000 nodes bound the sum safely in int64 and below 2^53 in magnitude before its float64 conversion.
Invariant: the queue holds exactly the nodes for the current level when you start processing it.
Algorithm (step-by-step)
- If root is nil, return empty.
- Push root into queue.
- While queue not empty:
- Let
nbe queue size. - Pop
nnodes, add their values tosum, enqueue children. - Append
float64(sum)/float64(n)to result.
- Let
Correctness sketch
At each level boundary, freeze the number of nodes and reset the sum to zero. Processing exactly that many queued nodes adds each value from that depth once and no values from other depths. The arithmetic mean is therefore this sum divided by the frozen count, converted to floating point. Enqueuing children preserves the next-level order and coverage invariant but does not change the current divisor. Induction supplies the correct mean for every depth. For the stated 32-bit node values and at most 100000 nodes, an int64 or exact float64 integer accumulation is safe for each level sum.
Trace (hand walkthrough)
Example input:
3
/ \
9 20
/ \
15 7
Output: [3.0, 14.5, 11.0]
Walk:
- level 0 sum=3 count=1 -> avg 3.0
- level 1 sum=9+20=29 count=2 -> avg 14.5
- level 2 sum=15+7=22 count=2 -> avg 11.0
Pitfalls to avoid
- Forgetting to reset the sum and count for each level.
- Using
intfor sums when node values can overflow; useint64.
Debugging lens
Use unequal-width levels, negative values, and large 32-bit values. Compare floating results with an explicit tolerance; do not require a particular decimal string formatting.
Complexity
- Time:
O(n) - Extra space:
O(n).
Implementation notes (Go)
- Use
int64for sums, then convert tofloat64. - Reset sum/count each level; capture level size up front.
Reconstruction checkpoint
A level contains [−3,2,2]. What sum, divisor, and result should be used? Would float64(sum/count) and float64(sum)/float64(count) agree?
Model answer
The sum is 1, count is 3, and the mean is 1/3 within floating-point tolerance. Integer sum/count truncates to 0 before conversion, so the first expression is wrong. Convert before division. Reset the sum for the next level and use the saved level size, not a queue length that includes children.