On this page
Description
A tree has the children sum property when every node's value equals the sum of its children's values. Leaves are exempt, since they have no children. Two tasks come with it. Checking whether a tree already has the property is one read-only pass. Repairing a tree that doesn't, by only ever increasing values, takes a walk down and a walk back up.
We'll go from simple to more technical:
- The pattern: the repair - push down, recurse, sum up.
- A second flavor: the read-only check.
- Where rollup rules like this hold in production.
- The mistakes: skipping the upward pass, writing to missing children, and leaves.
The approach (repairing a tree)
You may only increase values. So when a node is bigger than its children's sum, you can't lower it. Instead, raise a child to the node's value and let the fix carry on below. When a node's children already add up to at least its value, the node simply takes their sum. Then, after both subtrees are repaired, set each node to its children's sum on the way back up. That last step matters, because a change deep down can make every ancestor wrong.
Here's a tree before and after the repair:
| Step | At node | What happens |
|---|---|---|
| 1 | 20 (root) | children sum 4 + 6 = 10, less than 20: raise the left child from 4 to 20 |
| 2 | 20 (was 4) | children sum 1 + 2 = 3, less than 20: raise its left child from 1 to 20 |
| 3 | the leaves 20, 2 and 6 | nothing to fix |
| 4 | back at the left child | its children sum to 20 + 2: set it to 22 |
| 5 | back at the root | its children sum to 22 + 6: set it to 28 |
Without steps 4 and 5, the root would still say 20 while its children add up to 28.
Java
void changeTree(TreeNode root) {
if (root == null) return;
int childSum = (root.left != null ? root.left.val : 0) + (root.right != null ? root.right.val : 0);
if (childSum >= root.val) {
root.val = childSum; // children already dominate - adopt their sum
} else if (root.left != null) {
root.left.val = root.val; // push the excess down onto whichever child exists
} else if (root.right != null) {
root.right.val = root.val;
}
changeTree(root.left);
changeTree(root.right);
// bottom-up: re-derive this node's value now that its children are finalized
int total = (root.left != null ? root.left.val : 0) + (root.right != null ? root.right.val : 0);
if (root.left != null || root.right != null) root.val = total;
}
Python
def change_tree(root: TreeNode | None) -> None:
if root is None:
return
child_sum = (root.left.val if root.left else 0) + (root.right.val if root.right else 0)
if child_sum >= root.val:
root.val = child_sum # children already dominate - adopt their sum
elif root.left:
root.left.val = root.val # push the excess down onto whichever child exists
elif root.right:
root.right.val = root.val
change_tree(root.left)
change_tree(root.right)
# bottom-up: re-derive this node's value now that its children are finalized
if root.left or root.right:
root.val = (root.left.val if root.left else 0) + (root.right.val if root.right else 0)
A second flavor: checking without changing anything
Checking whether a tree already has the property is much simpler. There's no pushing and no second pass. Each node compares its value with its children's sum, and every subtree must pass too. In the example, the "After" tree passes and the "Before" tree fails at the root.
Java
boolean checkChildrenSumProperty(TreeNode node) {
if (node == null) return true;
if (node.left == null && node.right == null) return true; // a leaf trivially satisfies it
int childSum = (node.left != null ? node.left.val : 0) + (node.right != null ? node.right.val : 0);
return node.val == childSum
&& checkChildrenSumProperty(node.left)
&& checkChildrenSumProperty(node.right);
}
Python
def check_children_sum_property(node: TreeNode | None) -> bool:
if node is None:
return True
if node.left is None and node.right is None:
return True # a leaf trivially satisfies it
child_sum = (node.left.val if node.left else 0) + (node.right.val if node.right else 0)
return (node.val == child_sum
and check_children_sum_property(node.left)
and check_children_sum_property(node.right))
The leaf check is what makes this correct. A leaf's children sum to 0. Without the check, every leaf would have to hold 0 to pass.
Where you'll see it
Budgets and ledgers often follow this rule: a department's budget is the sum of its teams' budgets. When someone edits a team's number, every total above it has to be recomputed, bottom up. Auditing whether the totals already add up is the simpler check.
Which tool for which job?
| You need | Use |
|---|---|
| Does every node equal its children's sum? | one pass, leaves always pass |
| Make it hold by only increasing values | push down the shortfall, recurse, then sum up |
| Does every node equal its whole subtree's sum? | return subtree sums from one recursion |
| Totals that keep changing | recompute along the path to the root on each edit |
| Large values | sums in long |
Common mistakes
| Mistake | What happens | Do this instead |
|---|---|---|
| Skipping the upward pass | parents don't reflect deeper changes | set each node to its children's sum after recursing |
| Writing to a missing child | NullPointerException, or a made-up node | push onto a child that exists |
| Checking leaves against 0 | every non-zero leaf fails | leaves always satisfy it |
| A subtree-sum helper at every node | O(n^2) on chains | return sums from one recursion |
int sums | the root overflows | long |
Check yourself
Try to answer before opening each one - recalling beats re-reading.
1. A repair pushes a node's value down to a child and never comes back to that node. What goes wrong?
Show answer
The push can set off more pushes further down, and those raise values below the child. The node never sees them, so it no longer equals its children's sum. In the example, the root would stay 20 while its children add up to 28.
2. A repair writes root.left.val = root.val without checking that root.left exists. What happens on a node with only a right child?
Show answer
A NullPointerException, because there's no left child to write to. A node with one child must push onto the child it actually has. Check which one exists first.
3. A check computes the children's sum as 0 for leaves and compares it with the leaf's value. What does it report for a correct tree?
Show answer
That the tree fails, as soon as any leaf holds a non-zero value. Almost every real tree has such leaves. Treat a node with no children as an automatic pass.
Key takeaways
- The repair has two parts: push values down on the way in, then set each node to its children's sum on the way back up.
- The upward pass isn't optional. A deep change only reaches the ancestors through it.
- A node with one child pushes onto that child, never onto a missing one.
- The read-only check is one pass with no changes, but it needs an explicit leaf case.