Skip to content

151. Path Sum II

šŸ”— LeetCode Problem: 113. Path Sum II
šŸ“Š Difficulty: Medium
šŸ·ļø Topics: Binary Trees, DFS, Backtracking, Path Tracking, Recursion

Problem Statement

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 = 22
5 + 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


🌳 Visual Understanding - Finding Paths

What is a Root-to-Leaf Path?

Tree:
         5
        / \
       4   8
      /   / \
     11  13  4
    /  \    / \
   7    2  5   1

Root-to-leaf paths:
1. [5, 4, 11, 7]   → sum = 27
2. [5, 4, 11, 2]   → sum = 22 āœ“
3. [5, 8, 13]      → sum = 26
4. [5, 8, 4, 5]    → sum = 22 āœ“
5. [5, 8, 4, 1]    → sum = 18

targetSum = 22
Answer: [[5,4,11,2], [5,8,4,5]]

Key Observations:

1. Must go from ROOT to LEAF
   - Can't stop at internal node
   - Can't start from middle

2. Need to track ENTIRE path
   - Not just sum (like Problem 135)
   - Need actual node values

3. Multiple paths possible
   - Collect ALL valid paths
   - Return as list of lists

This is Path Sum I + path collection! šŸŽÆ

Visual Path Exploration:

Exploring left side:

         5
        /
       4
      /
     11
    /  \
   7    2

Path [5, 4, 11]:
  Not a leaf! Continue...

Path [5, 4, 11, 7]:
  Leaf! Sum = 27 ≠ 22 āœ—

Path [5, 4, 11, 2]:
  Leaf! Sum = 22 = 22 āœ“
  Add to result!

🧠 The AHA Moment - Backtracking Pattern!

The Core Insight:

We need to:
1. Track current path as we go down
2. Check at leaves if sum matches
3. Collect valid paths
4. BACKTRACK to explore other paths

This is BACKTRACKING! šŸŽÆ

Build path going down
Check at leaf
Remove last when coming back up

Backtracking Visualization:

Tree:
       5
      / \
     4   8

State as we traverse:

Going down left:
  path = [5]
  path = [5, 4]
  (explore children)

Coming back up:
  path = [5, 4]
  Remove 4: path = [5]

Going down right:
  path = [5]
  path = [5, 8]
  (explore children)

Coming back up:
  path = [5, 8]
  Remove 8: path = [5]

Add before recurse, remove after recurse! šŸ”‘

Why Backtracking is Needed:

Without backtracking:
  path = [5, 4, 11, 2]
  (returns from this path)
  path still = [5, 4, 11, 2]
  Go to next path: [5, 4, 11, 2, 8, 13]
  WRONG! āœ—

With backtracking:
  path = [5, 4, 11, 2]
  Remove 2: path = [5, 4, 11]
  Remove 11: path = [5, 4]
  Remove 4: path = [5]
  Now can explore right: [5, 8]
  CORRECT! āœ“

🧠 Recursive Thinking - Building the Solution

Base Case:

When to check if path is valid?
  → At LEAF nodes

What's a leaf?
  → node.left == null && node.right == null

If at leaf:
  Check: currentSum == targetSum?
  If yes: Add path to result
  Return

Recursive Case:

For any node:

STEP 1: Add current node to path
  path.add(node.val)

STEP 2: Update remaining sum
  remaining = targetSum - node.val

STEP 3: Check if leaf
  if (leaf && remaining == 0):
    result.add(new ArrayList<>(path))  // Copy path!

STEP 4: Recurse on children
  if (node.left != null):
    dfs(node.left, remaining, path, result)

  if (node.right != null):
    dfs(node.right, remaining, path, result)

STEP 5: BACKTRACK - Remove current node
  path.remove(path.size() - 1)

What Each Node Does:

Each node:
1. Adds itself to path
2. Checks if it's a valid leaf
3. Explores children (if any)
4. Removes itself from path

Returns: Nothing (void)
  - Result built up in result list
  - Path modified by reference

šŸŽÆ Solution 1: DFS with Backtracking

Implementation:

class Solution {
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        List<List<Integer>> result = new ArrayList<>();
        List<Integer> path = new ArrayList<>();

        // Start DFS from root
        dfs(root, targetSum, path, result);

        return result;
    }

    private void dfs(TreeNode node, int remaining, 
                    List<Integer> path, List<List<Integer>> result) {
        // Base case: null node
        if (node == null) {
            return;
        }

        // STEP 1: Add current node to path
        // Building path as we go down
        path.add(node.val);

        // STEP 2: Check if this is a leaf with target sum
        // CRITICAL: Must be LEAF (both children null)
        if (node.left == null && node.right == null) {
            // Check if sum matches
            if (remaining == node.val) {
                // Found valid path! Add copy to result
                // WHY copy? Path will be modified later!
                result.add(new ArrayList<>(path));
            }
        }

        // STEP 3: Explore left subtree
        // Pass remaining sum minus current node
        if (node.left != null) {
            dfs(node.left, remaining - node.val, path, result);
        }

        // STEP 4: Explore right subtree
        // Pass remaining sum minus current node
        if (node.right != null) {
            dfs(node.right, remaining - node.val, path, result);
        }

        // STEP 5: BACKTRACK
        // Remove current node from path
        // WHY? Allow exploration of other paths!
        path.remove(path.size() - 1);
    }
}

Why New ArrayList When Adding to Result?

// āŒ WRONG - Adds reference
result.add(path);

// Problem: path will be modified!
// All entries in result point to same list
// Final result: [[empty], [empty], ...]

// āœ“ CORRECT - Adds copy
result.add(new ArrayList<>(path));

// Creates snapshot of current path
// Immune to future modifications
// Each result entry is independent

šŸŽÆ Solution 2: Cleaner Version (No Null Checks)

Implementation:

class Solution {
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        List<List<Integer>> result = new ArrayList<>();
        List<Integer> path = new ArrayList<>();
        dfs(root, targetSum, path, result);
        return result;
    }

    private void dfs(TreeNode node, int remaining, 
                    List<Integer> path, List<List<Integer>> result) {
        // Base case: null node
        if (node == null) {
            return;
        }

        // Add current node to path
        path.add(node.val);

        // Leaf check and sum check together
        if (node.left == null && node.right == null && 
            remaining == node.val) {
            result.add(new ArrayList<>(path));
        }

        // Recurse on both children (null check in recursive call)
        // This simplifies code - no explicit null checks here
        dfs(node.left, remaining - node.val, path, result);
        dfs(node.right, remaining - node.val, path, result);

        // Backtrack
        path.remove(path.size() - 1);
    }
}

šŸ” Detailed Dry Run - Complete Backtracking

Tree:
       5
      / \
     4   8
    /   / \
   11  13  4
  /  \      \
 7    2      1

targetSum = 22

Complete Execution with Path Tracking:

═══════════════════════════════════════════════════
BACKTRACKING TRACE - SHOWING PATH CHANGES
═══════════════════════════════════════════════════

Start: path = [], result = []

────────────────────────────────────────────────────
Call: dfs(5, 22, [], [])
────────────────────────────────────────────────────

ā”œā”€ Add 5: path = [5]
ā”œā”€ Not leaf (has children)
│
ā”œā”€ LEFT: dfs(4, 17, [5], []) ─────────────────────┐
│                                                  │
│  ā”œā”€ Add 4: path = [5, 4]                       │
│  ā”œā”€ Not leaf                                    │
│  │                                               │
│  ā”œā”€ LEFT: dfs(11, 13, [5,4], []) ─────────┐   │
│  │                                         │   │
│  │  ā”œā”€ Add 11: path = [5, 4, 11]         │   │
│  │  ā”œā”€ Not leaf                           │   │
│  │  │                                      │   │
│  │  ā”œā”€ LEFT: dfs(7, 2, [5,4,11], []) ─┐  │   │
│  │  │                                  │  │   │
│  │  │  ā”œā”€ Add 7: path = [5,4,11,7]    │  │   │
│  │  │  ā”œā”€ IS LEAF!                     │  │   │
│  │  │  ā”œā”€ Check: 2 == 7? NO           │  │   │
│  │  │  ā”œā”€ No children to explore       │  │   │
│  │  │  └─ Remove 7: path = [5,4,11]   │  │   │
│  │  │                                  │  │   │
│  │  ā”œā”€ RIGHT: dfs(2, 2, [5,4,11], []) ┤  │   │
│  │  │                                  │  │   │
│  │  │  ā”œā”€ Add 2: path = [5,4,11,2]    │  │   │
│  │  │  ā”œā”€ IS LEAF!                     │  │   │
│  │  │  ā”œā”€ Check: 2 == 2? YES! āœ“        │  │   │
│  │  │  ā”œā”€ Add copy: result = [[5,4,11,2]]  │
│  │  │  ā”œā”€ No children                  │  │   │
│  │  │  └─ Remove 2: path = [5,4,11]   │  │   │
│  │  │                                  │  │   │
│  │  └─ Remove 11: path = [5, 4]       │  │   │
│  │                                         │   │
│  ā”œā”€ RIGHT: dfs(null, 13, [5,4], [...]) → return
│  │                                               │
│  └─ Remove 4: path = [5]                        │
│                                                  │
ā”œā”€ RIGHT: dfs(8, 17, [5], [[5,4,11,2]]) ─────────┐
│                                                  │
│  ā”œā”€ Add 8: path = [5, 8]                       │
│  ā”œā”€ Not leaf                                    │
│  │                                               │
│  ā”œā”€ LEFT: dfs(13, 9, [5,8], [...]) ───────┐   │
│  │                                         │   │
│  │  ā”œā”€ Add 13: path = [5, 8, 13]         │   │
│  │  ā”œā”€ IS LEAF!                           │   │
│  │  ā”œā”€ Check: 9 == 13? NO                │   │
│  │  ā”œā”€ No children                        │   │
│  │  └─ Remove 13: path = [5, 8]          │   │
│  │                                         │   │
│  ā”œā”€ RIGHT: dfs(4, 9, [5,8], [...]) ───────┤   │
│  │                                         │   │
│  │  ā”œā”€ Add 4: path = [5, 8, 4]           │   │
│  │  ā”œā”€ Not leaf                           │   │
│  │  │                                      │   │
│  │  ā”œā”€ LEFT: dfs(5, 5, [5,8,4], [...]) ┐ │   │
│  │  │                                   │ │   │
│  │  │  ā”œā”€ Add 5: path = [5,8,4,5]      │ │   │
│  │  │  ā”œā”€ IS LEAF!                      │ │   │
│  │  │  ā”œā”€ Check: 5 == 5? YES! āœ“         │ │   │
│  │  │  ā”œā”€ Add copy: result =           │ │   │
│  │  │  │   [[5,4,11,2], [5,8,4,5]]     │ │   │
│  │  │  ā”œā”€ No children                   │ │   │
│  │  │  └─ Remove 5: path = [5,8,4]     │ │   │
│  │  │                                   │ │   │
│  │  ā”œā”€ RIGHT: dfs(1, 5, [5,8,4], [...]) │   │
│  │  │                                   │ │   │
│  │  │  ā”œā”€ Add 1: path = [5,8,4,1]      │ │   │
│  │  │  ā”œā”€ IS LEAF!                      │ │   │
│  │  │  ā”œā”€ Check: 5 == 1? NO            │ │   │
│  │  │  ā”œā”€ No children                   │ │   │
│  │  │  └─ Remove 1: path = [5,8,4]     │ │   │
│  │  │                                   │ │   │
│  │  └─ Remove 4: path = [5, 8]         │   │
│  │                                         │   │
│  └─ Remove 8: path = [5]                       │
│                                                  │
└─ Remove 5: path = []                            │

═══════════════════════════════════════════════════
FINAL: result = [[5,4,11,2], [5,8,4,5]]
═══════════════════════════════════════════════════

Path State at Each Node:

Node 5:   path = [5]
Node 4:   path = [5, 4]
Node 11:  path = [5, 4, 11]
Node 7:   path = [5, 4, 11, 7] → leaf, sum ≠ 22
          path = [5, 4, 11]    (backtracked)
Node 2:   path = [5, 4, 11, 2] → leaf, sum = 22! āœ“
          path = [5, 4, 11]    (backtracked)
          path = [5, 4]        (backtracked)
          path = [5]           (backtracked)
Node 8:   path = [5, 8]
Node 13:  path = [5, 8, 13]    → leaf, sum ≠ 22
          path = [5, 8]        (backtracked)
Node 4:   path = [5, 8, 4]
Node 5:   path = [5, 8, 4, 5]  → leaf, sum = 22! āœ“
          path = [5, 8, 4]     (backtracked)
Node 1:   path = [5, 8, 4, 1]  → leaf, sum ≠ 22
          path = [5, 8, 4]     (backtracked)
          path = [5, 8]        (backtracked)
          path = [5]           (backtracked)
          path = []            (done!)

šŸ“Š Complexity Analysis

Time Complexity: O(n²)

Visit each node: O(n)

At each node:
  - Add to path: O(1)
  - Copy path (worst case): O(n)
  - Remove from path: O(1)

Worst case: Skewed tree, all paths valid
  O(n) nodes Ɨ O(n) copy = O(n²)

Average case: Balanced tree
  O(n) nodes Ɨ O(h) copy = O(n log n)

Space Complexity: O(n)

Recursion stack: O(h)
  Best: O(log n)
  Worst: O(n)

Current path: O(h)
  Max length = height

Result storage: O(n Ɨ h)
  Worst case: all paths stored

Total: O(n) dominated by result storage


āš ļø Common Mistakes

Mistake 1: Not Making Copy When Adding Path

// āŒ WRONG - Adds reference, not copy
if (node.left == null && node.right == null && remaining == node.val) {
    result.add(path);  // WRONG!
}

// Problem: path is modified after adding!
// All entries point to same list
// Final result: all empty or all same

// Example:
//   After adding [5,4,11,2]: result = [[5,4,11,2]]
//   After backtracking: path = []
//   result now = [[]]  āœ— (pointing to empty path!)

// āœ“ CORRECT - Add copy
result.add(new ArrayList<>(path));  āœ“

Mistake 2: Forgetting to Backtrack

// āŒ WRONG - No backtracking
private void dfs(TreeNode node, int remaining, 
                List<Integer> path, List<List<Integer>> result) {
    if (node == null) return;

    path.add(node.val);

    if (node.left == null && node.right == null && 
        remaining == node.val) {
        result.add(new ArrayList<>(path));
    }

    dfs(node.left, remaining - node.val, path, result);
    dfs(node.right, remaining - node.val, path, result);

    // Missing: path.remove(path.size() - 1);
}

// Problem: path keeps growing!
// Tree:   1
//        / \
//       2   3
// After left: path = [1, 2]
// Go right: path = [1, 2, 3] āœ— (should be [1, 3])

// āœ“ CORRECT - Always backtrack
path.remove(path.size() - 1);  āœ“

Mistake 3: Not Checking Both Children are Null

// āŒ WRONG - Only checks remaining == node.val
if (remaining == node.val) {
    result.add(new ArrayList<>(path));
}

// Problem: Adds internal nodes!
// Tree:  5
//       /
//      4
//     /
//    11
// Path [5, 4] has sum 9
// If target = 9, would add [5, 4] āœ— (not a leaf!)

// āœ“ CORRECT - Check leaf condition
if (node.left == null && node.right == null && 
    remaining == node.val) {  āœ“
    result.add(new ArrayList<>(path));
}

Mistake 4: Wrong Remaining Sum Calculation

// āŒ WRONG - Subtracts before checking
private void dfs(TreeNode node, int remaining, ...) {
    if (node == null) return;

    remaining = remaining - node.val;  // WRONG placement!
    path.add(node.val);

    if (node.left == null && node.right == null && remaining == 0) {
        result.add(new ArrayList<>(path));
    }
    // ...
}

// Problem: Modifies remaining before checking
// Should check: remaining == node.val
// Not: (remaining - node.val) == 0

// āœ“ CORRECT - Check remaining == node.val
if (remaining == node.val) {  āœ“
    // OR pass remaining - node.val to children
    dfs(node.left, remaining - node.val, ...);
}

Mistake 5: Modifying Path in Wrong Order

// āŒ WRONG - Removes before children explored
private void dfs(TreeNode node, int remaining, ...) {
    if (node == null) return;

    path.add(node.val);

    if (/* leaf check */) {
        result.add(new ArrayList<>(path));
        path.remove(path.size() - 1);  // WRONG! Too early!
        return;
    }

    path.remove(path.size() - 1);  // WRONG! Too early!
    dfs(node.left, remaining - node.val, ...);
    dfs(node.right, remaining - node.val, ...);
}

// Problem: Removes before recursing!
// Children won't have parent in path!

// āœ“ CORRECT - Remove after all recursion
dfs(node.left, ...);
dfs(node.right, ...);
path.remove(path.size() - 1);  āœ“ After everything!

šŸŽÆ Pattern Recognition - Path Collection Problems

Core Pattern: DFS + Backtracking

Template for path collection:

private void dfs(TreeNode node, ..., List<Integer> path, ...) {
    if (node == null) return;

    // 1. Add current to path
    path.add(node.val);

    // 2. Check if condition met
    if (condition) {
        result.add(new ArrayList<>(path));
    }

    // 3. Recurse on children
    dfs(node.left, ..., path, ...);
    dfs(node.right, ..., path, ...);

    // 4. Backtrack
    path.remove(path.size() - 1);
}

1. Binary Tree Paths (Problem 137)

Collect ALL root-to-leaf paths
Similar structure!
No sum checking, just collect all

2. Path Sum (Problem 135)

Check if ANY path has target sum
Similar logic, but boolean return
Don't need to collect paths

3. Path Sum III

Paths can start ANYWHERE (not just root)
More complex: need prefix sums
But still uses backtracking

4. Sum Root to Leaf Numbers

Build numbers from paths
Similar backtracking structure
Different calculation logic

When to Use This Pattern:

āœ“ Need to collect paths
āœ“ Explore all possibilities
āœ“ Path depends on ancestors
āœ“ Need to undo choices (backtrack)
āœ“ DFS with state restoration

šŸ“ Quick Revision Notes

šŸŽÆ Core Concept

Find ALL root-to-leaf paths with targetSum. Use DFS with backtracking: (1) add node to path, (2) check if leaf with correct sum, (3) recurse on children, (4) BACKTRACK by removing node. Must create copy when adding path to result (path will be modified). Check both children null for leaf (not just remaining == 0).

⚔ Quick Implementation

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }

    public static TreeNode buildTree(Integer[] arr) {
        if (arr == null || arr.length == 0 || arr[0] == null) {
            return null;
        }

        TreeNode root = new TreeNode(arr[0]);
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);

        int i = 1;
        while (!queue.isEmpty() && i < arr.length) {
            TreeNode current = queue.poll();

            // Process left child
            if (i < arr.length) {
                if (arr[i] != null) {
                    current.left = new TreeNode(arr[i]);
                    queue.offer(current.left);
                }
                i++;
            }

            // Process right child
            if (i < arr.length) {
                if (arr[i] != null) {
                    current.right = new TreeNode(arr[i]);
                    queue.offer(current.right);
                }
                i++;
            }
        }

        return root;
    }

    public static void print(TreeNode root) {
        if (root == null) {
            System.out.println("[]");
            return;
        }

        List<String> result = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);

        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            if (node != null) {
                result.add(String.valueOf(node.val));
                queue.offer(node.left);
                queue.offer(node.right);
            } else {
                result.add("null");
            }
        }

        while (!result.isEmpty() && result.get(result.size() - 1).equals("null")) {
            result.remove(result.size() - 1);
        }

        System.out.println(result);
    }    
}

class Solution {
  public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
    List<List<Integer>> res = new ArrayList<>();

    pathSum(root, targetSum, new ArrayList<>(), res);

    return res;
  }

  private void pathSum(TreeNode root, int targetSum, ArrayList<Integer> pathSum, List<List<Integer>> res) {
    // reached end of the path.
    if(root == null) {
      return;
    }

    // Or else, check subtrees now.
    // Add the current element
    pathSum.add(root.val);

    // Suppose I am at the leaf node and that node satisfies the target sum.
    // Have to put this below above line after adding the root.val to the path sum list. Else, that element will not be present in the result list.
    if(root.left == null && root.right == null && root.val == targetSum) {
      res.add(new ArrayList<>(pathSum));
    }    

    // Traverse LST.
    pathSum(root.left, targetSum - root.val, pathSum, res);

    // Traverse RST.
    pathSum(root.right, targetSum - root.val, pathSum, res);

    // remove the added element for next
    pathSum.remove(pathSum.size() - 1);
  }

  public static void main(String[] args) {
    Solution s = new Solution();

    System.out.println(s.pathSum(TreeNode.buildTree(new Integer[] {5,4,8,11,null,13,4,7,2,null,null,5,1}), 22));
    System.out.println(s.pathSum(TreeNode.buildTree(new Integer[] {1,2,3}), 5));
  }
}

šŸ”‘ Key Insights

Backtracking Necessity:

Without backtracking:
  path = [5, 4, 11, 2]  (after left path)
  path = [5, 4, 11, 2, 8]  (going right) āœ—

With backtracking:
  path = [5, 4, 11, 2]  (at leaf)
  Remove 2: [5, 4, 11]
  Remove 11: [5, 4]
  Remove 4: [5]
  path = [5, 8]  (going right) āœ“

Backtracking restores state! šŸ”‘

Copy vs Reference:

WRONG:
  result.add(path);  // Adds reference!

  After: result = [[5,4,11,2]]
  Path modified: path = []
  Result now: [[]]  āœ— (all entries point to same list!)

CORRECT:
  result.add(new ArrayList<>(path));  // Copy!

  Snapshot of current state
  Independent of future changes āœ“

Always copy when storing mutable objects! šŸŽÆ

Leaf Check is Critical:

WRONG:
  if (remaining == node.val) {
    result.add(path);
  }

  Adds internal nodes! āœ—

CORRECT:
  if (node.left == null && node.right == null && 
      remaining == node.val) {
    result.add(path);
  }

  Only adds complete root-to-leaf paths! āœ“

Must check BOTH children null! šŸ”‘

Backtracking Order:

Order matters:

1. Add to path
2. Check condition / recurse
3. Remove from path

Not:
1. Add to path
2. Remove from path  āœ— (too early!)
3. Recurse  āœ— (children won't see current!)

Remove AFTER all recursion completes! ⚔

šŸŽŖ Memory Aid

"Add, recurse, remove (backtrack)!"
"Copy when storing (new ArrayList)!"
"Leaf = both children null!"
"Backtrack restores state!" ✨

āš ļø Don't Forget

  • Add copy to result (new ArrayList!)
  • Backtrack after recursion (remove last!)
  • Check leaf properly (both children null!)
  • Pass remaining - node.val (to children!)
  • Remove in correct order (after all recursion!)
  • Path modified by reference (same list throughout!)

šŸŽ‰ Congratulations!

You've mastered backtracking on trees!

What You Learned:

āœ… Backtracking pattern - Add/recurse/remove
āœ… Path collection - Copying vs reference
āœ… State restoration - Undoing choices
āœ… Leaf checking - Both children null
āœ… DFS with state - Passing mutable path
āœ… Multiple solutions - Collecting all paths

The Backtracking Pattern:

Classic backtracking structure:

1. Make choice (add to path)
2. Explore (recurse on children)
3. Undo choice (remove from path)

Universal pattern for:
  - Path problems
  - Combination problems
  - Permutation problems
  - Constraint satisfaction

Fundamental algorithm pattern! šŸŽÆ

Interview Mastery:

Interviewer: "Find all root-to-leaf paths with sum"

Your response:
  "Use DFS with backtracking.

   Key steps:
   1. Add current node to path
   2. Check if leaf with target sum
   3. Recurse on both children
   4. Backtrack: remove current node

   Critical: Copy path when adding to result
   (path will be modified later)

   Time: O(n²) worst case, Space: O(n)"

Shows algorithm mastery! šŸ’Æ

You've learned a fundamental pattern used across many problems! šŸ†āœØšŸŽÆ