112. Path Sum - Explanation

Problem Link

Description

You are given the root of a binary tree and an integer targetSum, return true if the tree has a root-to-leaf path such that adding up all the values along the path equals targetSum.

A leaf is a node with no children.

Example 1:

Input: root = [1,2,3], targetSum = 3

Output: true

Explanation: The root-to-leaf path with the target sum is 1 -> 2.

Example 2:

Input: root = [-15,10,20,null,null,15,5,-5], targetSum = 15

Output: true

Explanation: The root-to-leaf path with the target sum is -15 -> 20 -> 15 -> -5.

Example 3:

Input: root = [1,1,0,1], targetSum = 2

Output: false

Constraints:

  • 0 <= The number of nodes in the tree <= 5000.
  • -1000 <= Node.val <= 1000
  • -1000 <= targetSum <= 1000


Topics

Company Tags

Please upgrade to NeetCode Pro to view company tags.



Prerequisites

Before attempting this problem, you should be comfortable with:

  • Binary Trees - Understanding tree structure, root, leaf nodes, and traversal concepts
  • Depth First Search (DFS) - Recursive tree traversal to explore paths from root to leaves
  • Recursion - Using recursive function calls to traverse tree structures

1. Depth First Search - I

Intuition

We traverse the tree from root to leaves, accumulating the sum along the way. When we reach a leaf node, we check if the accumulated sum equals the target. dfs naturally explores all root-to-leaf paths, making it ideal for this problem.

Algorithm

  1. Define dfs(node, curSum) that returns whether a valid path exists from this node.
  2. If node is null, return false.
  3. Add node.val to curSum.
  4. If node is a leaf (no children), return whether curSum == targetSum.
  5. Otherwise, recursively check the left and right subtrees, returning true if either has a valid path.
  6. Call dfs(root, 0) to start the search.
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
        def dfs(node, curSum):
            if not node:
                return False

            curSum += node.val
            if not node.left and not node.right:
                return curSum == targetSum

            return dfs(node.left, curSum) or dfs(node.right, curSum)

        return dfs(root, 0)

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(n)O(n) for recursion stack.

2. Depth First Search - II

Intuition

Instead of accumulating a sum, we subtract each node's value from the target. When we reach a leaf, we check if the remaining target is zero. This approach avoids passing an extra parameter and keeps the logic clean.

Algorithm

  1. If root is null, return false.
  2. Subtract root.val from targetSum.
  3. If root is a leaf, return whether targetSum == 0.
  4. Recursively call the function on left and right children with the updated target.
  5. Return true if either subtree finds a valid path.
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
        if not root:
            return False

        targetSum -= root.val
        return (self.hasPathSum(root.left, targetSum) or
                self.hasPathSum(root.right, targetSum) or
                (not targetSum and not root.left and not root.right))

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(n)O(n) for recursion stack.

3. Iterative DFS

Intuition

We can simulate the recursive dfs using an explicit stack. Each stack entry stores a node and the remaining sum needed to reach the target from that node. This approach avoids recursion depth issues for very deep trees.

Algorithm

  1. If root is null, return false.
  2. Initialize a stack with (root, targetSum - root.val).
  3. While the stack is not empty:
    • Pop a node and its remaining sum.
    • If it's a leaf and the remaining sum is zero, return true.
    • Push the right child (if exists) with its updated remaining sum.
    • Push the left child (if exists) with its updated remaining sum.
  4. If the stack empties without finding a valid path, return false.
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
        if not root:
            return False

        stack = [(root, targetSum - root.val)]
        while stack:
            node, curr_sum = stack.pop()
            if not node.left and not node.right and curr_sum == 0:
                return True
            if node.right:
                stack.append((node.right, curr_sum - node.right.val))
            if node.left:
                stack.append((node.left, curr_sum - node.left.val))
        return False

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(n)O(n)

Intuition

bfs explores the tree level by level. We use a queue to store each node along with the remaining sum needed. When we reach a leaf, we check if the target has been met. BFS guarantees we explore all paths systematically.

Algorithm

  1. If root is null, return false.
  2. Initialize a queue with (root, targetSum - root.val).
  3. While the queue is not empty:
    • Dequeue a node and its remaining sum.
    • If it's a leaf and the remaining sum is zero, return true.
    • Enqueue the left child (if exists) with its updated remaining sum.
    • Enqueue the right child (if exists) with its updated remaining sum.
  4. Return false if no valid path is found.
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
        if not root:
            return False

        queue = deque([(root, targetSum - root.val)])
        while queue:
            node, curr_sum = queue.popleft()
            if not node.left and not node.right and curr_sum == 0:
                return True
            if node.left:
                queue.append((node.left, curr_sum - node.left.val))
            if node.right:
                queue.append((node.right, curr_sum - node.right.val))
        return False

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(n)O(n)

Common Pitfalls

Checking Sum at Non-Leaf Nodes

A frequent mistake is checking if the current sum equals the target at every node rather than only at leaf nodes. The problem specifically requires a root-to-leaf path, so internal nodes should not trigger a true result even if the running sum matches the target. Always verify that both left and right children are null before comparing sums.

Returning True on Null Nodes When Target is Zero

Another pitfall is incorrectly returning true when reaching a null node if targetSum happens to be zero. An empty tree (null root) should return false regardless of the target value. The base case for null nodes should always return false, and the sum check should only occur at actual leaf nodes.