Chaturmind
LearnDSASystem DesignBlogPremium
Sign inGet started
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML

Company

  • Blog
  • Premium
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.

DSA›Recursion & Backtracking›Combination Sum
MediumRecursion & Backtracking

Combination Sum

backtrackingrecursion

Problem

Given a list of distinct positive integers candidates and a target, return all unique combinations where the chosen numbers sum to target. The same number may be chosen from candidates an unlimited number of times.

Examples

Example 1

Input: candidates = [2,3,6,7], target = 7

Output: [[2,2,3],[7]]

Explanation: 2+2+3=7 (2 reused) and 7 alone both work.

Constraints

  • •1 <= candidates.length <= 30
  • •1 <= target <= 40

Hints

Hint 1

The 'unlimited reuse of the same element' requirement is the key difference from Subsets/Permutations — it changes exactly one line in the recursive call.

Hint 2

Prune the moment the running sum exceeds target — no point continuing down a branch that's already invalid.

Hint 3

Sorting candidates first lets you break out of the loop entirely once a candidate alone would exceed the remaining target, rather than checking every remaining candidate individually.

Solutions

public List<List<Integer>> combinationSum(int[] candidates, int target) {
    Arrays.sort(candidates); // enables the early-break optimization below
    List<List<Integer>> result = new ArrayList<>();
    backtrack(candidates, target, 0, new ArrayList<>(), result);
    return result;
}

private void backtrack(int[] candidates, int remaining, int start, List<Integer> current, List<List<Integer>> result) {
    if (remaining == 0) { result.add(new ArrayList<>(current)); return; }
    for (int i = start; i < candidates.length; i++) {
        if (candidates[i] > remaining) break; // sorted array — every later candidate is even bigger, stop entirely
        current.add(candidates[i]);
        backtrack(candidates, remaining - candidates[i], i, current, result); // 'i', NOT 'i + 1' — allows reusing the SAME element
        current.remove(current.size() - 1);
    }
}
Java

Time: O(n^(target/min_candidate)) roughly — exponential, pruned heavily by the sorted early-break · Space: O(target/min_candidate) recursion depth