🧠
Brute Force (Pure Recursion with Path Tracking)
💡 Starting with brute force helps understand the problem deeply by exploring all root-to-leaf paths without optimization. It builds intuition about recursion and backtracking, which is essential before optimizing.
Intuition
Traverse the tree from root to leaf, keep track of the current path and sum, and whenever a leaf is reached, check if the sum matches targetSum. If yes, record the path.
Algorithm
- Start DFS from the root with an empty path and initial sum 0.
- At each node, add its value to the current path and sum.
- If the node is a leaf, check if the sum equals targetSum; if yes, add the path to results.
- Recursively explore left and right children, then backtrack by removing the current node from the path.
💡 The challenge is to correctly add and remove nodes from the path during recursion to avoid mixing paths and to only add valid paths at leaves.
from typing import List, Optional
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def pathSum(root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
res = []
def dfs(node, current_path, current_sum):
if not node:
return
current_path.append(node.val)
current_sum += node.val
if not node.left and not node.right:
if current_sum == targetSum:
res.append(current_path[:])
else:
dfs(node.left, current_path, current_sum)
dfs(node.right, current_path, current_sum)
current_path.pop()
dfs(root, [], 0)
return res
# Example usage:
# Construct the tree from example
# root = TreeNode(5, TreeNode(4, TreeNode(11, TreeNode(7), TreeNode(2))), TreeNode(8, TreeNode(13), TreeNode(4, TreeNode(5), TreeNode(1))))
# print(pathSum(root, 22))
Line Notes
def dfs(node, current_path, current_sum):Defines the recursive DFS helper function with current node, path, and sum to explore all paths
if not node:Base case: if node is None, stop recursion to avoid errors
current_path.append(node.val)Add current node's value to path to track the current traversal
if not node.left and not node.right:Check if current node is a leaf to decide if path can be considered
if current_sum == targetSum:If leaf and sum matches target, record a copy of the current path
current_path.pop()Backtrack by removing the current node before returning to explore other paths
import java.util.*;
class TreeNode {
int val;
TreeNode left, right;
TreeNode(int val) { this.val = val; }
}
public class Solution {
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
List<List<Integer>> res = new ArrayList<>();
dfs(root, targetSum, new ArrayList<>(), 0, res);
return res;
}
private void dfs(TreeNode node, int targetSum, List<Integer> path, int currentSum, List<List<Integer>> res) {
if (node == null) return;
path.add(node.val);
currentSum += node.val;
if (node.left == null && node.right == null) {
if (currentSum == targetSum) {
res.add(new ArrayList<>(path));
}
} else {
dfs(node.left, targetSum, path, currentSum, res);
dfs(node.right, targetSum, path, currentSum, res);
}
path.remove(path.size() - 1);
}
// Example main to test
public static void main(String[] args) {
TreeNode root = new TreeNode(5);
root.left = new TreeNode(4);
root.right = new TreeNode(8);
root.left.left = new TreeNode(11);
root.left.left.left = new TreeNode(7);
root.left.left.right = new TreeNode(2);
root.right.left = new TreeNode(13);
root.right.right = new TreeNode(4);
root.right.right.left = new TreeNode(5);
root.right.right.right = new TreeNode(1);
Solution sol = new Solution();
System.out.println(sol.pathSum(root, 22));
}
}
Line Notes
public List<List<Integer>> pathSum(TreeNode root, int targetSum)Entry point to start DFS and collect all valid paths
if (node == null) return;Base case to stop recursion on null nodes to prevent errors
path.add(node.val);Add current node to path to track traversal
if (node.left == null && node.right == null)Check if current node is a leaf to consider path validity
res.add(new ArrayList<>(path));Add a copy of current path to results to avoid mutation issues
path.remove(path.size() - 1);Backtrack by removing last node before returning to explore other paths
#include <iostream>
#include <vector>
using namespace std;
struct TreeNode {
int val;
TreeNode *left, *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
class Solution {
public:
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
vector<vector<int>> res;
vector<int> path;
dfs(root, targetSum, 0, path, res);
return res;
}
private:
void dfs(TreeNode* node, int targetSum, int currentSum, vector<int>& path, vector<vector<int>>& res) {
if (!node) return;
path.push_back(node->val);
currentSum += node->val;
if (!node->left && !node->right) {
if (currentSum == targetSum) {
res.push_back(path);
}
} else {
dfs(node->left, targetSum, currentSum, path, res);
dfs(node->right, targetSum, currentSum, path, res);
}
path.pop_back();
}
};
// Example usage:
// int main() {
// TreeNode* root = new TreeNode(5);
// root->left = new TreeNode(4);
// root->right = new TreeNode(8);
// root->left->left = new TreeNode(11);
// root->left->left->left = new TreeNode(7);
// root->left->left->right = new TreeNode(2);
// root->right->left = new TreeNode(13);
// root->right->right = new TreeNode(4);
// root->right->right->left = new TreeNode(5);
// root->right->right->right = new TreeNode(1);
// Solution sol;
// vector<vector<int>> result = sol.pathSum(root, 22);
// for (auto &path : result) {
// for (int v : path) cout << v << " ";
// cout << endl;
// }
// return 0;
// }
Line Notes
void dfs(TreeNode* node, int targetSum, int currentSum, vector<int>& path, vector<vector<int>>& res)Recursive DFS helper with current node, sum, path, and results to explore all paths
if (!node) return;Stop recursion on null nodes to avoid errors
path.push_back(node->val);Add current node to path to track traversal
if (!node->left && !node->right)Check if node is leaf to consider path validity
res.push_back(path);Add current path to results if sum matches target
path.pop_back();Backtrack by removing last node before returning to explore other paths
function TreeNode(val, left = null, right = null) {
this.val = val;
this.left = left;
this.right = right;
}
var pathSum = function(root, targetSum) {
const res = [];
function dfs(node, currentPath, currentSum) {
if (!node) return;
currentPath.push(node.val);
currentSum += node.val;
if (!node.left && !node.right) {
if (currentSum === targetSum) {
res.push([...currentPath]);
}
} else {
dfs(node.left, currentPath, currentSum);
dfs(node.right, currentPath, currentSum);
}
currentPath.pop();
}
dfs(root, [], 0);
return res;
};
// Example usage:
// const root = new TreeNode(5,
// new TreeNode(4, new TreeNode(11, new TreeNode(7), new TreeNode(2))),
// new TreeNode(8, new TreeNode(13), new TreeNode(4, new TreeNode(5), new TreeNode(1)))
// );
// console.log(pathSum(root, 22));
Line Notes
function dfs(node, currentPath, currentSum)Recursive DFS helper to explore all root-to-leaf paths with current node, path, and sum
if (!node) return;Stop recursion on null nodes to avoid errors
currentPath.push(node.val);Add current node to path to track traversal
if (!node.left && !node.right)Check if node is leaf to consider path validity
res.push([...currentPath]);Add a copy of current path to results if sum matches target
currentPath.pop();Backtrack by removing last node before returning to explore other paths
TimeO(N * L) where N is number of nodes and L is average path length
SpaceO(L) recursion stack + O(K * L) for storing paths, K is number of valid paths
We visit each node once, but copying paths to results takes extra time proportional to path length. The recursion stack and path list use space proportional to tree height.
💡 For a tree with 20 nodes and height 5, this means roughly 20 visits and storing paths of length up to 5, which is manageable but can grow large for bigger trees.
Interview Verdict: Accepted
This approach is straightforward and accepted for the problem constraints, but may be inefficient for very large trees with many paths.