112. Path Sum
112. Path Sum
Problem Statement
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.
Examples
Example 1:
Input: root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
Output: true
Explanation: The path 5 → 4 → 11 → 2 sums to 22.
Example 2:
Input: root = [1,2,3], targetSum = 5
Output: false
Explanation: There is no root-to-leaf path with sum = 5.
Example 3:
Input: root = [], targetSum = 0
Output: false
Explanation: Empty tree has no paths.
Constraints
- The number of nodes in the tree is in the range
[0, 5000]. -1000 <= Node.val <= 1000-1000 <= targetSum <= 1000
Clarification Questions
Before diving into the solution, here are 5 important clarifications and assumptions to discuss during an interview:
-
Path definition: What defines a valid path - root to leaf only? (Assumption: Yes - path must be from root to a leaf node, not any internal node)
-
Leaf node: What is considered a leaf node? (Assumption: A node with no children - both left and right are null)
-
Empty tree: What should we return for an empty tree? (Assumption: Return false - empty tree has no paths)
-
Negative values: Can node values and targetSum be negative? (Assumption: Yes - both can be negative per constraints)
-
Path sum calculation: Should we include the root node in the sum? (Assumption: Yes - sum includes all nodes from root to leaf)
Interview Deduction Process (10 minutes)
Step 1: Brute-Force Approach (2 minutes)
Find all root-to-leaf paths, calculate the sum of each path, and check if any sum equals the target. Use DFS to collect all paths, then check each path’s sum. This approach requires storing all paths, which uses O(n × h) space where h is the height, and O(n) time to traverse and check. This works but uses unnecessary space.
Step 2: Semi-Optimized Approach (3 minutes)
Use DFS with backtracking to build paths incrementally. When reaching a leaf node, check if the current path sum equals the target. This avoids storing all paths but still requires maintaining the current path, which uses O(h) space for the recursion stack and path storage.
Step 3: Optimized Solution (5 minutes)
Use recursive DFS without storing paths. Pass the remaining sum (targetSum - current node value) to child nodes. When reaching a leaf node, check if the remaining sum equals 0 (meaning the path sum equals targetSum). This eliminates the need to store paths entirely, using only O(h) space for the recursion stack. The key insight is that we don’t need to know the actual path values - we only need to track whether a valid path exists, which can be determined by tracking the remaining sum.
Solution Approach
This problem requires checking if there exists a root-to-leaf path with a specific sum. We can use DFS to traverse all paths and check if any path sums to the target.
Key Insights:
- Root-to-Leaf Path: Must start at root and end at a leaf node
- Backtracking: Subtract current node value from target sum as we traverse
- Leaf Check: Only check sum when we reach a leaf node
- Early Termination: Return true as soon as we find a valid path
Solution: Recursive DFS with Backtracking
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
// From root to leaf, any leaf's value equal (target - path value)
if(!root) return false;
if(!root->left && !root->right) {
return root->val == targetSum;
}
return hasPathSum(root->left, targetSum - root->val) ||
hasPathSum(root->right, targetSum - root->val);
}
};
Algorithm Explanation:
- Base Case - Empty Node:
- If
rootisnullptr, returnfalse(no path exists)
- If
- Base Case - Leaf Node:
- If both children are null, we’ve reached a leaf
- Check if
root->val == targetSum(remaining sum equals node value) - Return
trueif match,falseotherwise
- Recursive Case:
- For non-leaf nodes, subtract current node value:
targetSum - root->val - Recursively check left subtree:
hasPathSum(root->left, targetSum - root->val) - Recursively check right subtree:
hasPathSum(root->right, targetSum - root->val) - Return
trueif either subtree contains a valid path (OR operation)
- For non-leaf nodes, subtract current node value:
Why This Works?
The algorithm uses backtracking by:
- Subtracting the current node’s value from
targetSumas we go deeper - At each leaf, checking if the remaining
targetSumequals the leaf’s value - This effectively checks if the path sum equals the original
targetSum
Example Walkthrough:
Input: root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
Tree structure:
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
Path: 5 → 4 → 11 → 2
hasPathSum(5, 22):
root = 5, has children
Check left: hasPathSum(4, 22 - 5 = 17)
root = 4, has children
Check left: hasPathSum(11, 17 - 4 = 13)
root = 11, has children
Check left: hasPathSum(7, 13 - 11 = 2)
root = 7, leaf node
Check: 7 == 2? No → return false
Check right: hasPathSum(2, 13 - 11 = 2)
root = 2, leaf node
Check: 2 == 2? Yes → return true ✓
return true
return true
return true ✓
Input: root = [1,2,3], targetSum = 5
Tree structure:
1
/ \
2 3
hasPathSum(1, 5):
root = 1, has children
Check left: hasPathSum(2, 5 - 1 = 4)
root = 2, leaf node
Check: 2 == 4? No → return false
Check right: hasPathSum(3, 5 - 1 = 4)
root = 3, leaf node
Check: 3 == 4? No → return false
return false || false = false ✓
Complexity Analysis:
- Time Complexity: O(n)
- Visit each node at most once
- n = number of nodes in tree
- Early termination when path found
- Space Complexity: O(h)
- Recursion call stack depth = height of tree
- Best case (balanced): O(log n)
- Worst case (skewed): O(n)
Alternative Approaches
Solution 2: Iterative DFS with Stack
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
if (!root) return false;
stack<pair<TreeNode*, int>> st;
st.push({root, targetSum});
while (!st.empty()) {
auto [node, remaining] = st.top();
st.pop();
if (!node->left && !node->right && node->val == remaining) {
return true;
}
if (node->right) {
st.push({node->right, remaining - node->val});
}
if (node->left) {
st.push({node->left, remaining - node->val});
}
}
return false;
}
};
Complexity: Same as recursive DFS
Solution 3: Iterative BFS with Queue
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
if (!root) return false;
queue<pair<TreeNode*, int>> q;
q.push({root, targetSum});
while (!q.empty()) {
auto [node, remaining] = q.front();
q.pop();
if (!node->left && !node->right && node->val == remaining) {
return true;
}
if (node->left) {
q.push({node->left, remaining - node->val});
}
if (node->right) {
q.push({node->right, remaining - node->val});
}
}
return false;
}
};
Complexity:
- Time: O(n)
- Space: O(w) where w is maximum width
Key Insights
- Backtracking Pattern: Subtract node value from target as we traverse
- Leaf Node Check: Only validate sum at leaf nodes (not internal nodes)
- Early Termination: Return true immediately when valid path found
- OR Logic: Only need one valid path, not all paths
- Path Definition: Must be root-to-leaf (complete path)
Edge Cases
- Empty tree:
root = null,targetSum = 0→ returnfalse - Single node:
root = [1],targetSum = 1→ returntrue - Single node mismatch:
root = [1],targetSum = 2→ returnfalse - Negative values:
root = [-2,null,-3],targetSum = -5→ returntrue - Zero sum:
root = [1,-1],targetSum = 0→ returntrue(if path exists) - No valid path:
root = [1,2,3],targetSum = 5→ returnfalse
Common Mistakes
- Checking at internal nodes: Validating sum before reaching leaf
// WRONG: if (root->val == targetSum) return true; // ❌ Internal node check - Not handling empty tree: Forgetting null check
- Wrong leaf check: Not checking both children are null
- Accumulating instead of subtracting: Adding values instead of subtracting from target
- Not using OR: Using AND instead of OR for subtree checks
Comparison with Related Problems
| Problem | Key Difference |
|---|---|
| LC 112: Path Sum | Check if any root-to-leaf path equals target |
| LC 113: Path Sum II | Return all root-to-leaf paths that equal target |
| LC 437: Path Sum III | Count paths with target sum (any node to any node) |
| LC 124: Binary Tree Maximum Path Sum | Find maximum path sum (can start/end anywhere) |
Related Problems
- LC 112: Path Sum - This problem (check existence)
- LC 113: Path Sum II - Return all paths
- LC 437: Path Sum III - Count paths (any node to any node)
- LC 124: Binary Tree Maximum Path Sum - Maximum path sum
- LC 129: Sum Root to Leaf Numbers - Sum all root-to-leaf numbers
- LC 257: Binary Tree Paths - Return all root-to-leaf paths
This problem demonstrates backtracking in tree traversal. The key insight is subtracting the current node’s value from the target sum as we traverse, then checking if the remaining sum equals the leaf node’s value at the end of each path.