Practice LeetCode 39 · Combination Sum · GFG · Backtracking
RecursionBacktrackingDecision TreePruning

Backtracking · The Template

Every backtracking problem is a walk down a decision tree: choose an option, explore deeper, then un-choose to try the next. Pruning kills dead branches early. Here it's subsets, cut whenever the running sum blows past a target.

Choose · Explore · Unchoose

At each node you make a choice (add an element to the path), recurse to explore what follows, then undo the choice before trying the next option. That undo is the "backtrack".

The recursion tree is the search space. Every root-to-leaf path is one candidate.

Pruning & state

  • Carry running state (here: path + sum)
  • Before recursing, test a constraint
  • Violated → prune: skip that whole subtree
  • Pruning is what makes backtracking fast

Walk the decision tree

Unvisited Active (on path) Leaf kept Pruned
current path:
slowfast
Step 0
Nodes explored
0
Subsets kept
0
Branches pruned
0
Depth
0

C++

// generic backtracking skeleton (here: subsets with a prune)
void backtrack(int i, vector<int>& nums, int sum, int target,
               vector<int>& path, vector<vector<int>>& out) {
    if (sum > target) return;          // PRUNE: dead branch
    if (i == nums.size()) {           // leaf: a complete candidate
        out.push_back(path);
        return;
    }
    // choice 1: include nums[i]
    path.push_back(nums[i]);          // CHOOSE
    backtrack(i + 1, nums, sum + nums[i], target, path, out);  // EXPLORE
    path.pop_back();                  // UNCHOOSE (backtrack)

    // choice 2: skip nums[i]
    backtrack(i + 1, nums, sum, target, path, out);
}
Time
O(2ⁿ · n)

Up to 2ⁿ leaves, each copying an O(n) path. Pruning cuts this in practice.

Space
O(n)

Recursion depth and the path, ignoring the output list itself.

Shape
a tree

Branching factor × depth defines the search space you traverse.

  • The three lines that matter: choose (mutate state), explore (recurse), unchoose (restore state). Forget the unchoose and every branch corrupts the next.
  • Prune early, prune often. Testing the constraint before recursing skips entire subtrees, the difference between fast and timeout.
  • Pass state by reference and undo it, or pass copies. Reference + undo is faster but easier to get wrong.
  • Subsets, permutations, combinations, word search, N-Queens, all the same skeleton with different choices and prunes.
  • Collect results at leaves (complete candidates) or at every node (all prefixes), depending on the problem.