Path Sum
Given the root of a binary tree and an integer targetSum, return true if there exists a root-to-leaf path whose node values add up to exactly targetSum, and false otherwise. The path must start at the root and end at a leaf (a node with no children); a partial path that happens to reach the target partway down does not count. An empty tree contains no path at all, so it returns false for every target, including 0.
- The number of nodes in the tree is in the range [0, 5000]
- -1000 <= Node.val <= 1000
- -1000 <= targetSum <= 1000
- The path must end at a leaf, not at an internal node
root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22trueroot = [1,2,3], targetSum = 1falseroot = [1,-2,3], targetSum = -1trueroot = [], targetSum = 0falseWe want to know whether some root-to-leaf route adds up to the target. This is a search over routes, and the route information again comes from above — a leaf has no idea what was added on the way to it — so this is the downward-flowing shape from Binary Tree Paths, with a number carried instead of a list.
You could carry the running sum accumulated so far, and compare against the target on arrival at a leaf. That works. The slightly cleaner variant carries what is still owed: start with the target, and at each node subtract that node's value before descending. Then the two children inherit the amount remaining to be covered by the rest of the route, and there is only one number to pass instead of two.
At a leaf, the route is finished. It qualifies precisely when nothing is left owing — the remainder is exactly 0. Not "at most 0", not "close to 0": the sum must land on the target exactly.
For a node that is not a leaf, the answer is that either child can rescue it:
canReach(node, need) = canReach(node.left, need - node.val) OR canReach(node.right, need - node.val)and canReach(null, anything) = false — an absent subtree offers no route.
def hasPathSum(node, need):
if node is None:
return False # no route through nothing
need -= node.val # pay this node out of what is owed
if node.left is None and node.right is None:
return need == 0 # a leaf: the route ends here, so it must balance exactly
return hasPathSum(node.left, need) or hasPathSum(node.right, need)need == 0, then a node with exactly one child gets asked about its missing side — with the full remainder still outstanding — and if that remainder happens to be 0, you return true for a route that stops in mid-air at an internal node. The second example is exactly this trap: the root alone equals the target, and any solution that does not insist on a leaf reports true.A tempting optimisation: abandon a branch as soon as the remainder goes negative, since we have already overshot. That is valid only when every value is non-negative, because then the running sum can never come back down. Here values go down to -1000, so a route that has overshot by 50 can be rescued by a -50 further along, and pruning would discard genuine answers. The precondition matters more than the trick — before pruning a search, always ask what property of the input makes the discarded region provably useless. Here there is no such property, so we visit everything.
Short-circuiting on success is still fine, though: or stops as soon as the left subtree reports true, so the search ends the moment one qualifying route is found. That is a genuine saving on lucky inputs and costs nothing.
Worst case every node is visited once, doing constant work: O(N) time. Space is the recursion stack, O(H) — logarithmic on a balanced tree, but up to 5000 frames on a chain. Nothing is stored per path, because we only ever needed one number in flight, which is precisely why this is cheaper than Binary Tree Paths despite the identical traversal.
The transferable move is the reframing from "accumulate and compare at the end" to "carry what remains." It converts a two-value comparison into a single-value target of zero, and it generalises well beyond trees — it is the same substitution that makes subset-sum and coin-change recursions readable. And keep the leaf-versus-null distinction in the front of your mind: the base case that stops you falling off the structure is never the same thing as the condition that makes an answer complete. Sum Root to Leaf Numbers carries a number down in the same way, but builds it by multiplying by ten instead of subtracting — same skeleton, different arithmetic in the one slot that varies.
Path Sum Calculus
Subtraction Logic: Goal (22)
Subtracting node value from goal as we go deeper.
"Success if target becomes 0 at a node with no children."
Strategy
Recursive decomposition: `hasPathSum(root, sum) = hasPathSum(root.left, sum - val) || hasPathSum(root.right, sum - val)`.
"Reduce target at every step."