Skip to content

Backtracking: Code Templates for Permutations and Subsets

If you have not read the concept guide yet, start there for the intuition and complexity analysis. This page gives you the code you can memorize and adapt during an interview.

1. The Generic Backtracking Template

This is the skeleton of almost every backtracking algorithm. It follows a recursive pattern of making a choice, exploring its consequences, and then undoing that choice.

function backtrack(result, current, data) {
    // 1. Base case: Is the current solution complete?
    if (isValid(current)) {
        result.push([...current]); // Add a copy
        return;
    }

    // 2. Iterate through available candidates
    for (let candidate of getCandidates(data, current)) {
        if (isPossible(candidate, current)) {
            // 3. Make a choice (State Change)
            current.push(candidate);

            // 4. Recurse to explore this branch
            backtrack(result, current, data);

            // 5. Backtrack (Undo State Change)
            current.pop();
        }
    }
}
Templates only help if you can recall them without staring at the screen. Turn these snippets into flashcards for daily mobile review.

Code Breakdown

The template relies on three main markers:

  • State: Usually represented by current, the path taken so far.
  • Choices: The for loop that explores all possible next steps.
  • Constraints: The isValid check that decides when to stop.
    graph LR
    A["State: []"] --> B[Choice: 1]
    B --> C["State: [1]"]
    C --> D[Explore Sub-trees]
    D -- "Backtrack" --> E["State: []"]
    E --> F[Choice: 2]
    F --> G["State: [2]"]
  

2. Common Variations

Most interview problems are specific versions of this generic template.

A. Permutations

Used when you need all possible orderings. The key addition is a used array that tracks which elements have already been placed, since permutations use every element exactly once. See Problem 1: Permutations .

Visual Logic:

    graph TD
    Root["[]"] --> A["[1]"]
    Root --> B["[2]"]
    A --> AB["[1,2]"]
    A --> AC["[1,3]"]
    AB -- "Backtrack" --> A
    AC -- "Backtrack" --> A
  
function permute(nums) {
    const result = [];
    const used = new Array(nums.length).fill(false);

    function backtrack(current) {
        if (current.length === nums.length) {
            result.push([...current]);
            return;
        }
        for (let i = 0; i < nums.length; i++) {
            if (used[i]) continue;
            used[i] = true;
            current.push(nums[i]);
            backtrack(current);
            current.pop();
            used[i] = false;
        }
    }
    backtrack([]);
    return result;
}

B. Subsets

Used when you need every possible combination regardless of order. The start index prevents duplicate sets by only looking forward. See Problem 2: Subsets .

Visual Logic:

    graph TD
    Root["[]"] --> S1["[1]"]
    S1 --> S12["[1,2]"]
    S12 --> S123["[1,2,3]"]
    Root --> S2["[2]"]
    S2 --> S23["[2,3]"]
    Root --> S3["[3]"]
  
function subsets(nums) {
    const result = [];
    function backtrack(start, current) {
        result.push([...current]);
        for (let i = start; i < nums.length; i++) {
            current.push(nums[i]);
            backtrack(i + 1, current);
            current.pop();
        }
    }
    backtrack(0, []);
    return result;
}
Each variation above is a separate review card in the app. Drill them individually so you do not mix up the conditions under pressure.

Now head to the practice problems to apply these templates to real interview questions.

Now that you have the templates, the next step is application. Head to the practice problems or start reviewing these templates with spaced repetition .