112. Path Sum - Explanation
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: trueExplanation: 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: trueExplanation: 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: falseConstraints:
0 <= The number of nodes in the tree <= 5000.-1000 <= Node.val <= 1000-1000 <= targetSum <= 1000
Topics
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
- Define
dfs(node, curSum)that returns whether a valid path exists from this node. - If
nodeisnull, returnfalse. - Add
node.valtocurSum. - If
nodeis a leaf (no children), return whethercurSum == targetSum. - Otherwise, recursively check the left and right subtrees, returning
trueif either has a valid path. - 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:
- Space complexity: 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
- If
rootisnull, returnfalse. - Subtract
root.valfromtargetSum. - If
rootis a leaf, return whethertargetSum == 0. - Recursively call the function on left and right children with the updated target.
- Return
trueif 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:
- Space complexity: 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
- If
rootisnull, returnfalse. - Initialize a stack with
(root, targetSum - root.val). - 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.
- 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 FalseTime & Space Complexity
- Time complexity:
- Space complexity:
4. Breadth First Search
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
- If
rootisnull, returnfalse. - Initialize a queue with
(root, targetSum - root.val). - 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.
- Return
falseif 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 FalseTime & Space Complexity
- Time complexity:
- Space complexity:
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.
Sign in to join the discussion