CMD Guide
HomeDSACompany Practice

medium Combinations

Problem Statement

Given two integers n and k, return all possible combinations of k numbers chosen from the range [1, n].

Each combination must be a unique set of numbers, order of which does not matter.

Examples

Try it yourself

Try solving this question here:

🎯 STRICT STANDOUT — Combinations medium

0. Pattern family

Family: Backtracking combinations C(n,k)

1. Why / judgment (K3)

Choose k numbers from 1..n. Backtrack start index to keep ascending order — avoids permutations of same set.

2. Worked complexity / derivation (K11)

Time Θ(C(n,k)·k) to build; space O(k) stack + output.

3. Pattern + recognition + when-NOT (K12)

Name: COMBINATIONS BACKTRACK

Recognition: all subsets size k from 1..n; orderless.

When-NOT: Permutations P(n,k); combinations with rep; next_combination iterative.

4. Edge hand-run (K13)

k=1; k=n one combo; k=0 empty combo; n=k=0.

5. Interviewer follow-ups & drills

Q1. Why start=i+1?
Model answer: Enforce non-decreasing indices → unique sets.

Q2. Prune?
Model answer: if remain slots > remain numbers break.

Q3. Iterative?
Model answer: Index array simulating digits.

✅ Solution Combinations

Problem Statement

Given two integers n and k, return all possible combinations of k numbers chosen from the range [1, n].

Each combination must be a unique set of numbers, order of which does not matter.

Examples

  • Example 1:

    • Input: n = 3, k = 2
    • Expected Output: [[1, 2], [1, 3], [2, 3]]
    • Justification: [[1, 2], [1, 3], [2, 3]] are all combinations of size 2, which we can create using [1, 2, 3].
  • Example 2:

    • Input: n = 4, k = 1
    • Expected Output: [[1], [2], [3], [4]]
    • Justification: [[1], [2], [3], [4]] are all combinations of size 1, which we can create using `[1, 2, 3, 4].
  • Example 3:

    • Input: n = 5, k = 3
    • Expected Output: [[1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5], [1, 4, 5], [2, 3, 4], [2, 3, 5], [2, 4, 5], [3, 4, 5]]
    • Justification: We unique triplets using numbers 1 to 5, yielding ten different combinations.

Solution

To solve this problem, we can employ a backtracking approach. Backtracking is a recursive strategy for solving problems by exploring all possible solutions and backtrack whenever a solution that's currently being explored no longer seems viable. This approach is particularly effective for generating combinations because it allows us to build combinations one element at a time and backtrack as soon as we've either found a valid combination or need to make a different choice to continue exploring possibilities.

Backtracking works here by choosing a start number for a combination, then recursively choosing the next number, ensuring each number is greater than the previous to maintain uniqueness and avoid duplication. This method efficiently explores all potential combinations of k numbers within the given range, ensuring no possibilities are missed and no invalid combinations are included.

Step-by-step Algorithm

  1. Initialize an empty list (or array) result to store all the combinations.
  2. Define a helper function backtrack that takes the current combination (tempList), the starting number (start), 3. n, and k as parameters. Begin the process by calling backtrack with an empty tempList, start as 1, n, and k.
  3. Check if the current combination's size equals k. If so, add a copy of tempList to result and return.
  4. Iterate from the current start number to n:
    • Add the current number i to tempList.
    • Recursively call backtrack with i + 1 as the new start, to ensure each combination is unique and respects the ascending order.
    • Remove the last number added to tempList to backtrack, exploring other possible combinations by trying the next number in the sequence.
  5. Once all combinations are explored, return the result.

Algorithm Walkthrough

Let's consider the input n = 5 and k = 3:

  1. Initialize result as an empty list to store combinations.
  2. Call backtrack with an empty list as tempList, start = 1, n = 5, and k = 3.
  3. First Call to Backtrack:
    • tempList = [], start = 1
    • Loop from i = 1 to 5:
      • Add 1 to tempList and call backtrack with start = 2.
        • Second Call to Backtrack:
          • tempList = [1], start = 2
          • Loop from i = 2 to 5:
            • Add 2 to tempList and call backtrack with start = 3.
              • Third Call to Backtrack:
                • tempList = [1, 2], start = 3
                • Loop from i = 3 to 5:
                  • Add 3 to tempList making it [1, 2, 3] and since tempList.size() == k, add [1, 2, 3] to result.
                  • Backtrack: Remove 3, making tempList = [1, 2].
                  • Add 4 to tempList making it [1, 2, 4] and since tempList.size() == k, add [1, 2, 4] to result.
                  • Backtrack: Remove 4, making tempList = [1, 2].
                  • Add 5 to tempList making it [1, 2, 5] and since tempList.size() == k, add [1, 2, 5] to result.
                  • Backtrack: Remove 5, making tempList = [1, 2].
                • Backtrack to the second call: tempList = [1].
            • Next, i = 3 in the second call, add 3 to tempList making it [1, 3] and proceed similarly.
            • Continue this process until all combinations starting with 1 are explored.
      • Backtrack and try next numbers as the starting point, [2, 3, 4], [2, 3, 5], [2, 4, 5], [3, 4, 5] will be generated in subsequent calls.
  4. After exploring all possible combinations starting from 1 to 5, the result will contain all unique combinations of size 3 from numbers 1 to 5.
  5. Return result which now holds all the required combinations: [[1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5], [1, 4, 5], [2, 3, 4], [2, 3, 5], [2, 4, 5], [3, 4, 5]].

Code

java
import java.util.ArrayList;
import java.util.List;

public class Solution {

  // Function to generate all combinations
  public List<List<Integer>> combine(int n, int k) {
    List<List<Integer>> result = new ArrayList<>();
    // Start the backtracking process
    backtrack(result, new ArrayList<>(), 1, n, k);
    return result;
  }

  // Helper function for backtracking
  private void backtrack(
    List<List<Integer>> result,
    List<Integer> tempList,
    int start,
    int n,
    int k
  ) {
    // Base case: if the combination is complete
    if (k == 0) {
      // Add a copy of tempList to the result
      result.add(new ArrayList<>(tempList));
      return;
    }

    // Iterate through possible starts
    for (int i = start; i <= n; i++) {
      tempList.add(i); // Add current number to the combination
      // Move to the next element and decrease k by 1
      backtrack(result, tempList, i + 1, n, k - 1);
      // Remove the last element to backtrack
      tempList.remove(tempList.size() - 1);
    }
  }

  // Main method to test the algorithm with example inputs
  public static void main(String[] args) {
    Solution solution = new Solution();
    System.out.println(solution.combine(3, 2));
    System.out.println(solution.combine(4, 1));
    System.out.println(solution.combine(5, 3));
  }
}

Complexity Analysis

Time Complexity

  • : The primary factor affecting the time complexity is the number of combinations generated, denoted as C(n, k), where C(n, k) is the binomial coefficient representing the number of ways to choose k elements out of n options. For each combination, we perform operations that are proportional to k (to add elements to a temporary list or array). Therefore, the total time complexity is .

Space Complexity

  • : The space required to store all combinations is directly proportional to the number of combinations times the size of each combination, which is k. This accounts for the space needed to store the output.

🎯 STRICT STANDOUT — Solution Combinations

0. Pattern family

Family: Backtracking / combinations C(n,k) with start-index discipline

1. Why / judgment (K3)

Combinations are unordered subsets of fixed size k. The standout discipline is the start index: always append from i..n and recurse with i+1 so each set is built in strictly ascending order. That kills permutations-of-the-same-set without a visited array. Judgment: if order mattered it would be permutations; if k were free it would be subsets; here size is locked to k.

2. Worked complexity / derivation (K11)

Output size Θ(C(n,k)·k) to write answers.
Search tree: at depth d, branching ≤ n−start+1; total internal nodes O(C(n,k)·k) in the
classic analysis (each combination prefix is charged once).
Time Θ(C(n,k)·k) dominated by copying each length-k combo into the answer.
Space O(k) recursion depth + O(C(n,k)·k) for output.
Naive generate-all-permutations-then-dedup: O(P(n,k)·k) — strictly worse; start-index is the fix.

3. Pattern + recognition + when-NOT (K12)

Name: COMBINATIONS BACKTRACK (start = i+1, stop at len==k)

Recognition: choose k from 1..n; order irrelevant; classic “pick / skip with ascending start”.

When-NOT: If order matters → permutations (swap or used[]). If any size → subsets (include/exclude). If reuse allowed → combination sum (start stays i). If only count C(n,k) → DP/Pascal, not DFS.

4. Edge hand-run (K13)

n=3,k=2 → [[1,2],[1,3],[2,3]] (not [2,1]).
n=1,k=1 → [[1]]. k=0 → [[]] convention; constraints usually k≥1.
n=4,k=4 → [[1,2,3,4]] one path. n=4,k=5 → [] prune when remaining &lt; needed.

5. Interviewer follow-ups & drills

Q1. Why start=i+1 not start=i?
Model answer: start=i reuses the same number and produces multisets / perm-duplicates; combinations need each index once, ascending.

Q2. Prune early?
Model answer: If remaining numbers n−i+1 < k−len(path), return — cannot fill the combo.

Q3. How does this transfer to Combination Sum?
Model answer: Same DFS skeleton; change start to i (reuse) and stop on remaining target 0 / negative.

🧩 Pattern · Backtracking

Recognize it: Enumerate all subsets / permutations / combinations under constraints → recurse, choose, un-choose.

▶ Visualize this problem (step it, predict each fork)
⛶ Open this problem debugger in explore mode
🤖 Don't fully get this? Learn it with Claude

Stuck on Combinations? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🪜 Hint ladder (no spoilers)

Progressively stronger hints — you still solve it.

I'm working on the problem **Combinations** (DSA). Give me a HINT LADDER: start with the tiniest nudge, then wait. Only reveal the next, stronger hint when I ask. Do NOT show the full solution unless I type 'show solution'. Keep me doing the thinking. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🎨 Explain the approach visually

See the technique, not just code.

Explain the optimal approach to **Combinations** with a VISUAL walkthrough: trace it on a small concrete example using ASCII art / a step-by-step diagram, narrate what changes each step, then give time & space complexity with a one-line derivation. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🔍 Review my solution

Catch bugs, edge cases, sub-optimality.

I'll paste my solution to **Combinations**. Review it for correctness, missed edge cases, and time/space complexity, then coach me toward the optimal — don't just rewrite it. Ask me to paste my code now. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🔁 Drill the pattern

Lock in recognition with look-alikes.

Give me 2 problems that use the SAME underlying pattern as **Combinations**. For each, let me attempt first, then review my answer and name the trigger signal that reveals the pattern. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes