🧠
Iterative DFS with Stack and Prefix Sum Map
💡 This approach shows how to implement the prefix sum method iteratively, useful for candidates who prefer iterative solutions or want to avoid recursion stack overflow.
Intuition
Use an explicit stack to simulate DFS traversal while maintaining prefix sums and a hash map to count valid paths, similar to the recursive approach.
Algorithm
- Use a stack to perform DFS traversal, storing nodes along with current prefix sum.
- Maintain a hash map of prefix sums and their counts.
- At each node, update the result by checking prefix sums that satisfy the target condition.
- After processing children, decrement prefix sum count to backtrack.
💡 Managing prefix sums and backtracking manually in iterative code is more complex but reinforces understanding of recursion.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def pathSum(self, root, targetSum):
if not root:
return 0
prefix_counts = {0: 1}
result = 0
stack = [(root, 0, False)] # node, current_sum, visited_children
while stack:
node, current_sum, visited = stack.pop()
if node is None:
continue
if not visited:
current_sum += node.val
result += prefix_counts.get(current_sum - targetSum, 0)
prefix_counts[current_sum] = prefix_counts.get(current_sum, 0) + 1
stack.append((node, current_sum, True)) # Mark node as visited
if node.right:
stack.append((node.right, current_sum, False))
if node.left:
stack.append((node.left, current_sum, False))
else:
prefix_counts[current_sum] -= 1
return result
# Example usage:
# root = TreeNode(10, TreeNode(5, TreeNode(3), TreeNode(2)), TreeNode(-3, None, TreeNode(11)))
# print(Solution().pathSum(root, 8)) # Output: 3
Line Notes
stack = [(root, 0, False)]Initialize stack with root and sum 0, visited flag False
if not visited:Process node first time: update sums and push children
result += prefix_counts.get(current_sum - targetSum, 0)Count valid paths ending at current node
prefix_counts[current_sum] = prefix_counts.get(current_sum, 0) + 1Add current prefix sum count
prefix_counts[current_sum] -= 1Backtrack prefix sum count after children processed
import java.util.*;
public class TreeNode {
int val;
TreeNode left, right;
TreeNode(int x) { val = x; }
}
public class Solution {
public int pathSum(TreeNode root, int targetSum) {
if (root == null) return 0;
Map<Long, Integer> prefixCounts = new HashMap<>();
prefixCounts.put(0L, 1);
int result = 0;
Deque<Object[]> stack = new ArrayDeque<>();
stack.push(new Object[]{root, 0L, false});
Deque<Long> pathStack = new ArrayDeque<>(); // Optional for clarity
while (!stack.isEmpty()) {
Object[] top = stack.pop();
TreeNode node = (TreeNode) top[0];
long currentSum = (Long) top[1];
boolean visited = (Boolean) top[2];
if (node == null) continue;
if (!visited) {
currentSum += node.val;
result += prefixCounts.getOrDefault(currentSum - targetSum, 0);
prefixCounts.put(currentSum, prefixCounts.getOrDefault(currentSum, 0) + 1);
stack.push(new Object[]{node, currentSum, true});
if (node.right != null) stack.push(new Object[]{node.right, currentSum, false});
if (node.left != null) stack.push(new Object[]{node.left, currentSum, false});
pathStack.push(currentSum); // Track prefix sums for backtracking
} else {
prefixCounts.put(currentSum, prefixCounts.get(currentSum) - 1);
pathStack.pop();
}
}
return result;
}
// Example usage:
// public static void main(String[] args) {
// TreeNode root = new TreeNode(10);
// root.left = new TreeNode(5);
// root.right = new TreeNode(-3);
// root.left.left = new TreeNode(3);
// root.left.right = new TreeNode(2);
// root.right.right = new TreeNode(11);
// Solution sol = new Solution();
// System.out.println(sol.pathSum(root, 8)); // Output: 3
// }
}
Line Notes
Map<Long, Integer> prefixCounts = new HashMap<>();Map to track prefix sums and counts
stack.push(new Object[]{root, 0L, false});Initialize stack with root node and sum 0
if (!visited) {Process node first time: update sums and push children
result += prefixCounts.getOrDefault(currentSum - targetSum, 0);Count valid paths ending at current node
prefixCounts.put(currentSum, prefixCounts.getOrDefault(currentSum, 0) + 1);Add current prefix sum count
prefixCounts.put(currentSum, prefixCounts.get(currentSum) - 1);Backtrack prefix sum count after children processed
pathStack.push(currentSum);Track prefix sums for backtracking (optional)
pathStack.pop();Remove prefix sum after backtracking (optional)
#include <iostream>
#include <unordered_map>
#include <stack>
#include <tuple>
using namespace std;
struct TreeNode {
int val;
TreeNode *left, *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
class Solution {
public:
int pathSum(TreeNode* root, int targetSum) {
if (!root) return 0;
unordered_map<long long, int> prefixCounts;
prefixCounts[0] = 1;
int result = 0;
stack<tuple<TreeNode*, long long, bool>> stk;
stk.push({root, 0, false});
while (!stk.empty()) {
auto [node, currentSum, visited] = stk.top();
stk.pop();
if (!node) continue;
if (!visited) {
currentSum += node->val;
result += prefixCounts[currentSum - targetSum];
prefixCounts[currentSum]++;
stk.push({node, currentSum, true});
if (node->right) stk.push({node->right, currentSum, false});
if (node->left) stk.push({node->left, currentSum, false});
} else {
prefixCounts[currentSum]--;
}
}
return result;
}
};
// Example usage:
// int main() {
// TreeNode* root = new TreeNode(10);
// root->left = new TreeNode(5);
// root->right = new TreeNode(-3);
// root->left->left = new TreeNode(3);
// root->left->right = new TreeNode(2);
// root->right->right = new TreeNode(11);
// Solution sol;
// cout << sol.pathSum(root, 8) << endl; // Output: 3
// return 0;
// }
Line Notes
unordered_map<long long, int> prefixCounts;Map to store prefix sums and counts
stk.push({root, 0, false});Initialize stack with root and sum 0
if (!visited) {Process node first time: update sums and push children
result += prefixCounts[currentSum - targetSum];Count valid paths ending at current node
prefixCounts[currentSum]++;Add current prefix sum count
prefixCounts[currentSum]--;Backtrack prefix sum count after children processed
auto [node, currentSum, visited] = stk.top();Structured binding requires C++17 or later
function TreeNode(val, left = null, right = null) {
this.val = val;
this.left = left;
this.right = right;
}
var pathSum = function(root, targetSum) {
if (!root) return 0;
let prefixCounts = new Map();
prefixCounts.set(0, 1);
let result = 0;
let stack = [[root, 0, false]]; // node, currentSum, visited
while (stack.length > 0) {
let [node, currentSum, visited] = stack.pop();
if (!node) continue;
if (!visited) {
currentSum += node.val;
result += prefixCounts.get(currentSum - targetSum) || 0;
prefixCounts.set(currentSum, (prefixCounts.get(currentSum) || 0) + 1);
stack.push([node, currentSum, true]);
if (node.right) stack.push([node.right, currentSum, false]);
if (node.left) stack.push([node.left, currentSum, false]);
} else {
prefixCounts.set(currentSum, prefixCounts.get(currentSum) - 1);
}
}
return result;
};
// Example usage:
// let root = new TreeNode(10, new TreeNode(5, new TreeNode(3), new TreeNode(2)), new TreeNode(-3, null, new TreeNode(11)));
// console.log(pathSum(root, 8)); // Output: 3
Line Notes
let prefixCounts = new Map();Map to track prefix sums and counts
stack = [[root, 0, false]];Initialize stack with root node and sum 0
if (!visited) {Process node first time: update sums and push children
result += prefixCounts.get(currentSum - targetSum) || 0;Count valid paths ending at current node
prefixCounts.set(currentSum, (prefixCounts.get(currentSum) || 0) + 1);Add current prefix sum count
prefixCounts.set(currentSum, prefixCounts.get(currentSum) - 1);Backtrack prefix sum count after children processed
TimeO(n)
SpaceO(n) for stack and hash map
Each node is processed twice (enter and exit), and prefix sums updated in O(1) average time.
💡 This iterative method is as efficient as recursive but requires careful stack and map management.
Interview Verdict: Accepted
This approach is useful when recursion depth is a concern or interviewer requests iterative solution.