The problem
You get the root of a binary tree and an integer target. Return True if some path from the root down to a leaf has node values that add up to exactly target. A leaf is a node with no children.
Input: root = [5,4,8,11,null,13,4,7,2,null,null,null,1], target = 22
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
Output: TrueThe path 5 → 4 → 11 → 2 adds up to 22.
How to think about it
Start with brute force. Collect every root-to-leaf path into a list, sum each one, and check whether any equals the target. That’s O(n) paths of up to O(h) length each, plus the memory to store them. You don’t need the paths themselves, only their sums.
The key insight: carry the remaining amount down instead of the path. When you step from a node to its child, the child’s subtree must make up target - node.val. So pass the remainder down. At a leaf, the question collapses to one comparison: is the remainder exactly this leaf’s value?
Stop at leaves, not at None. It’s tempting to check target == 0 when you reach an empty child. That’s wrong: a node with only one child would let you “finish” through its missing side, which isn’t a leaf. Checking at real leaves keeps the definition honest.
Which pattern is this? Tree DFS where state flows down (the remaining sum) and a boolean flows up (found or not). The tell is “root-to-leaf path” plus a condition on its values.
Approach
- If the node is empty, return
False(no path through nothing). - If the node is a leaf, return whether
target == node.val. - Otherwise compute
rem = target - node.val. - Return whether the left subtree or the right subtree has a path summing to
rem.
The or short-circuits, so the search stops as soon as one path works.
Solution
def has_path_sum(root, target):
if not root: return False
if not root.left and not root.right: return target == root.val # leaf: does it finish the sum?
rem = target - root.val # what the rest of the path must add up to
return has_path_sum(root.left, rem) or has_path_sum(root.right, rem)
# has_path_sum(tree[5,4,8,11,null,13,4,7,2,null,null,null,1], 22) -> TrueDry run
On the example with target = 22, following the calls until the answer is found:
| Node | Target here | Leaf? | Action |
|---|---|---|---|
| 5 | 22 | no | pass rem = 17 down |
| 4 | 17 | no | pass rem = 13 down |
| 11 | 13 | no | pass rem = 2 down |
| 7 | 2 | yes | 2 == 7? False, try sibling |
| 2 | 2 | yes | 2 == 2? True |
The right half of the tree is never explored, because or already has a True.
Complexity
- Time: O(n). In the worst case (no valid path) every node is visited once.
- Space: O(h). The recursion stack is as deep as the tree height: O(log n) balanced, O(n) skewed.
Edge cases and pitfalls
- Empty tree returns
False, even whentargetis 0. There is no path at all. - Paths must end at a leaf. For
[1, 2]withtarget = 1, the answer isFalse: node 1 has a child, so it’s not a leaf. - Negative values are allowed, so you can’t prune when the running sum passes the target.
[-2, null, -3]withtarget = -5isTrue. - Don’t test at
Nonechildren. That’s the most common wrong version, and it accepts paths that stop at a one-child node.
Related problems
- Binary Tree Maximum Path Sum (LeetCode 124): any path, not just root-to-leaf, and you maximise instead of match.
- Maximum Depth of Binary Tree (LeetCode 104): the same DFS skeleton returning a number.
- Validate Binary Search Tree (LeetCode 98): another problem where state (a range) is passed down.
- 1.Maximum Depth of Binary Tree (LeetCode 104)
- 2.Same Tree (LeetCode 100)
- 3.Invert Binary Tree (LeetCode 226)
- 4.Path Sum (LeetCode 112)
- 5.Subtree of Another Tree (LeetCode 572)
- 6.Diameter of Binary Tree (LeetCode 543)
- 7.Lowest Common Ancestor of a BST (LeetCode 235)
- 8.Lowest Common Ancestor of a Binary Tree (LeetCode 236)
- 9.Validate Binary Search Tree (LeetCode 98)
- 10.Kth Smallest Element in a BST (LeetCode 230)
- 11.Construct Binary Tree from Preorder and Inorder (LeetCode 105)
- 12.Serialize and Deserialize Binary Tree (LeetCode 297)
- 13.Binary Tree Maximum Path Sum (LeetCode 124)