Algorithm

Path Sum

Trees Pattern

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.

CONSTRAINTS
  • 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
EXAMPLE 1
Input: root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
Output: true
The path 5, 4, 11, 2 adds to 22 and ends at the leaf 2. Only one qualifying path is needed — the others may miss the target entirely.
EXAMPLE 2
Input: root = [1,2,3], targetSum = 1
Output: false
The root alone is worth 1, but the root is not a leaf, so stopping there is not a legal path. The two real paths total 3 and 4.
EXAMPLE 3
Input: root = [1,-2,3], targetSum = -1
Output: true
1 + (-2) = -1 at the leaf -2. Negative values mean the running total can fall as you descend, so a total that has already overshot the target may still come back.
EXAMPLE 4
Input: root = [], targetSum = 0
Output: false
There are no leaves, so there is no path whose values could sum to anything — even a target of 0 has nothing to match.
Must the path end at a leaf?
Yes, and this is the trap in the problem. Hitting the target at an internal node does not count, so the check has to be tied to leaf-ness, not to the total alone.
Can values be negative?
Yes, from -1000 up. That rules out any 'stop early once the running sum exceeds the target' pruning, since a later negative can bring it back down.
What is the answer for an empty tree with target 0?
False. It is tempting to say the empty path sums to 0, but the problem requires an actual root-to-leaf path, and an empty tree has none.
Do I need to return the path itself?
No, just true or false. The follow-up that asks for every qualifying path (Path Sum II) needs backtracking on top of this, which changes the cost.

We want to know: does some path from the root down to a leaf add up to exactly the target?

text
        5
       / \
      4   8         target = 22
     /
    11
   /  \
  7    2

  5 + 4 + 11 + 7 = 27   (misses)
  5 + 4 + 11 + 2 = 22   at leaf 2   ->  true

Read the picture: follow each route from the root down to a leaf and add the values on it. The left-most route (5, 4, 11, 7) totals 27, but the route (5, 4, 11, 2) lands exactly on 22 — so the answer is true. We only need one winning path.

The number has to come from above

Think about a leaf. Can it tell whether the path ending on it hits the target? No — it has no idea what was already added above it on the way down. That running total lives in its ancestors, not below it. So there's nothing useful to ask the children for; instead, I have to carry the number down from the root and use it the instant I arrive at a node. Doing a node's work as soon as you land on it, before turning to the children, is pre-order.

The number I carry is simple: how much is still owed to reach the target. It starts at the full target. At each node I subtract this node's value — I "pay" it — and hand the leftover down to my children.

- Empty node? There's no route through nothing. Return false.
- A leaf? The route ends right here. It succeeds only if nothing is still owed — the leftover is exactly 0. Not close to 0; exactly.
- Otherwise I'm just a stop along the way: either child might finish the job, so I ask both and take an OR.
python
def hasPathSum(node, need):
    if node is None:
        return False                 # no route through nothing
    need -= node.val                 # pay this node on arrival (pre-order)
    if node.left is None and node.right is None:
        return need == 0             # a leaf: must land exactly on zero
    return hasPathSum(node.left, need) or hasPathSum(node.right, need)

One thing to hold onto: the empty-node check and the leaf check are different jobs — don't merge them. If you let an empty node answer need == 0, a node with one missing child could report success for a path that stops in mid-air at an internal node. The path must end at a leaf, so tie the success test to being a leaf, not to the total alone.

One trap: no early exit on overshoot

It is tempting to abandon a branch the moment need goes negative — "we already overshot." That is only safe when every value is non-negative. Here values go down to -1000, so a route that overshot by 50 can be rescued by a -50 further down. So we cannot prune on overshoot; we visit everything. (Quitting early on success is fine — or stops the instant one route works.)

Worked example:tree rooted at 5, target 22
5 → children 4 and 8; 4 → left child 11; 11 → children 7 and 2.
- At 5: need = 22 - 5 = 17. Not a leaf → go left.
- At 4: need = 17 - 4 = 13. Descend to 11.
- At 11: need = 13 - 11 = 2. Try left first.
- At 7 (leaf): need = 2 - 7 = -5, not 0 → false.
- Back at 11, try right. At 2 (leaf): need = 2 - 2 = 0 → true.
- That true rides straight back up; the subtree under 8 is never touched.

O(N) time, O(H) stack space.

The idea to keep

The number this problem needs — the running total — lives above each node, not below it. So instead of asking the children and combining on the way up, I carry the number down as an argument and use it the moment I arrive. That's the pre-order shape: work on arrival, then descend. The one neat touch is carrying what's still owed (counting down toward 0) rather than adding up a total and comparing at the end — one number in flight, not two. O(N) time, O(H) stack.

Interactive Strategy Visualization

Path Sum Calculus

Subtraction Logic: Goal (22)

54117281341
Root (5). Remaining required: 22 - 5 = 17.
Recursive State
Target Check
22
Remaining needed for leaf

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."

O(N × H) Build Every Path Then Sum
O(N) Time · O(H) Space Carry The Remainder Down