Path Sum II⚓︎
Description⚓︎
Given the root of a binary tree and an integer targetSum, return all root-to-leaf paths where the sum of the node values in the path equals targetSum. Each path should be returned as a list of the node values, not node references.
A root-to-leaf path is a path starting from the root and ending at any leaf node. A leaf is a node with no children.
Example 1:
- Input:
root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22 - Output:
[[5,4,11,2],[5,8,4,5]] - Explanation:
- There are two paths whose sum equals targetSum:
5 + 4 + 11 + 2 = 225 + 8 + 4 + 5 = 22
Example 2:
- Input:
root = [1,2,3], targetSum = 5 - Output:
[]
Example 3:
- Input:
root = [1,2], targetSum = 0 - Output:
[]
Constraints:
- The number of nodes in the tree is in the range
[0, 5000]. -1000 <= Node.val <= 1000-1000 <= targetSum <= 1000
Solution⚓︎
Way 1⚓︎
Easier way of writing:
See reference.
Way 2 (Pass by Value)⚓︎
- Time Complexity: \(O(N\cdot H)\), where \(N\) is the number of nodes and \(H\) is the average length of the path. The extra factor of H comes from the time taken to copy the path vector at each node. In the worst case, the top half of the tree is in the form of a chain, the bottom half is in the form of a fully binary tree, and the paths from the root node to each leaf node meet the requirements of the question. In this case, the number of paths is \(O(N)\) and the number of nodes in each path is also \(O(N)\), so the time complexity to add all these paths to the answer is \(O(N^2)\);
- Space complexity: \(O(H)\) for each recursive call, but since they are not shared, the maximum simultaneous space used is still \(O(H)\). In the worst case, it is \(O(N)\).
Note⚓︎
Way 1: Not Using Return After Adding Path
In Way 1, the path vector is a class member and is modified in place. Here's the corresponding section:
- Why
returnis Not Used: After adding a valid path tores, the function does not return immediately. Instead, it continues to the end, wherepath.pop_back()is called. This is crucial to maintain the correct state ofpathfor other branches of the recursion. - Control Flow Management: The absence of
returnhere allows the function to reachpath.pop_back()at the end, which is necessary to backtrack correctly in the recursive tree.
Way 2: Returning After Adding Path
In Way 2, the path vector is passed by value. Here's the key section:
- Why
returnis Used: Once a valid path is found (i.e., a leaf node where the sum equals the target), this path is added tores, and the function returns immediately. This return is crucial because the function's further execution (calling left and right subtrees) is unnecessary—the path has been completed and recorded. - Effect of
return: It prevents further unnecessary recursive calls for the current path.
Summary:
- Way 1: Does not use
returnimmediately after finding a path, as it needs to reach thepop_backoperation to correctly manage the state of the sharedpathvector for ongoing recursion. - Way 2: Uses
returnto avoid unnecessary recursive calls after a complete path is found, as each recursive call works on its own copy ofpath.