Backtracking šŸ¤Æ

https://leetcode.com/problems/subsets/

Given an integer array nums of unique elements, return all possible subsets (the power set).

The solution set must not contain duplicate subsets. Return the solution in any order.

Example 1:

Input: nums = [1,2,3] Output: [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

Example 2:

Input: nums = [0] Output: [[],[0]]

Solution

function subsets(nums: number[]): number[][] {
  const result: number[][] = [];

  function dfs(start: number, currentSubset: number[]) {
    console.log("start", start, "currentSubset", currentSubset);
    result.push([...currentSubset]); // important: create a copy of currentSubset instead of adding it directly
    // console.log("result", result);

    for (let i = start; i < nums.length; i++) {
      currentSubset.push(nums[i]); // [1]
      dfs(i + 1, currentSubset);
      console.log("popped", currentSubset.pop(), currentSubset);
    }
  }

  dfs(0, []);
  return result;
}

console.log(subsets([1, 2, 3]));

The Decision Tree

  • Each level represents a decision: We either include or exclude an element.
  • Backtracking removes elements before trying a new path.
  • All possible subsets are explored without duplicates.

Backtracking is just DFS on tree except there's no pre-defined tree. You have to build your own tree by passing the states through parameters.

For example, normally when you do pre-order traversal, you go root -> root.left -> root.right. In backtracking involving choosing a number, left tree will be choosing the number and right tree not choosing. So you go left first by adding the number to path, call dfs(root.left). Pop it out of path (un-choosing) then dfs(root.right).

                         []
               ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
             [1]                    []
       ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”          ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”
    [1,2]           [1]        [2]           []
   ā”Œā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”    ā”Œā”€ā”€ā”“ā”€ā”€ā”    ā”Œā”€ā”€ā”“ā”€ā”€ā”       ā”Œā”€ā”€ā”“ā”€ā”€ā”
[1,2,3]   [1,2]  [1,3]  [1]  [2,3]  [2]   [3]   []
   āœ…       ā¬†ļøŽ     āœ…     ā¬†ļøŽ     āœ…   ā¬†ļøŽ     āœ…    āœ…
        Backtrack    Backtrack    Backtrack    (end)

Call stacks

  1. dfs(0, [])
  2. ā”œā”€ā”€ dfs(1, [1])
  3. ā”‚ ā”œā”€ā”€ dfs(2, [1,2])
  4. ā”‚ ā”‚ ā”œā”€ā”€ dfs(3, [1,2,3])
  5. ā”‚ ā”‚ ā””ā”€ā”€ Returns to dfs(2, [1,2])
  6. ā”‚ ā”œā”€ā”€ dfs(3, [1,3])
  7. ā”‚ ā””ā”€ā”€ Returns to dfs(1, [1])
  8. ā”œā”€ā”€ dfs(2, [2])
  9. ā”‚ ā”œā”€ā”€ dfs(3, [2,3])
  10. ā”‚ ā””ā”€ā”€ Returns to dfs(2, [2])
  11. ā”œā”€ā”€ dfs(3, [3])
  12. ā””ā”€ā”€ Returns to dfs(0, [])

Paths

1st Path (Include Everything)

[]  ā†’ [1] ā†’ [1,2] ā†’ [1,2,3]  āœ… (added to result)
                   ā¬†ļøŽ backtrack (remove 3)
           ā†’ [1,2]  āœ… (added to result)
           ā¬†ļøŽ backtrack (remove 2)
     ā†’ [1,3]  āœ… (added to result)
     ā¬†ļøŽ backtrack (remove 3)
ā†’ [1] āœ… (added to result)
ā¬†ļøŽ backtrack (remove 1)

2nd Path (Start with 2)

[]  ā†’ [2] ā†’ [2,3]  āœ… (added to result)
           ā¬†ļøŽ backtrack (remove 3)
     ā†’ [2]  āœ… (added to result)
ā¬†ļøŽ backtrack (remove 2)

3rd Path (Start with 3)

[]  ā†’ [3] āœ… (added to result)
ā¬†ļøŽ backtrack (remove 3)

haochen xu