HomeLeetcode39. Combination Sum - Leetcode Solutions

39. Combination Sum – Leetcode Solutions

Description:

Given an array of distinct integers candidates and a target integer target, return a list of all unique combinations of candidates where the chosen numbers sum to target. You may return the combinations in any order.

The same number may be chosen from candidates an unlimited number of times. Two combinations are unique if the frequency of at least one of the chosen numbers is different.

The test cases are generated such that the number of unique combinations that sum up to target is less than 150 combinations for the given input.

Examples:

Example 1:

Input: candidates = [2,3,6,7], target = 7
Output: [[2,2,3],[7]]
Explanation:
2 and 3 are candidates, and 2 + 2 + 3 = 7. Note that 2 can be used multiple times.
7 is a candidate, and 7 = 7.
These are the only two combinations.

Example 2:

Input: candidates = [2,3,5], target = 8
Output: [[2,2,2,2],[2,3,3],[3,5]]

Example 3:

Input: candidates = [2], target = 1
Output: []

Solution in Python:

To solve this problem in Python, we will use a backtracking approach. Backtracking is well-suited for exploring all possible combinations and pruning those that do not meet the criteria (i.e., do not sum up to the target).

Python
from typing import List

class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        result = []
        
        def backtrack(remain, comb, start):
            # If remain is zero, we found a valid combination
            if remain == 0:
                result.append(list(comb))
                return
            # If remain is negative, this combination is not valid
            elif remain < 0:
                return
            
            # Loop through the candidates starting from the current position
            for i in range(start, len(candidates)):
                # Add the candidate to the current combination
                comb.append(candidates[i])
                # Since we can use the same element again, we call backtrack with the same start index
                backtrack(remain - candidates[i], comb, i)
                # Remove the last element to backtrack
                comb.pop()
        
        # Sort the candidates to help with pruning (not necessary but can optimize a bit)
        candidates.sort()
        # Start the backtracking process with an empty combination
        backtrack(target, [], 0)
        
        return result

Detailed Explanation:

  1. Initialization:
    • Define a result list to store all the valid combinations.
    • Define a helper function backtrack that takes three parameters:
      • remain: the remaining sum needed to reach the target.
      • comb: the current combination of numbers.
      • start: the current index in candidates from which to consider adding numbers to the combination.
  2. Base Cases:
    • If remain is zero, a valid combination is found. Append a copy of comb to result.
    • If remain is less than zero, the current combination is invalid, so return early.
  3. Recursion:
    • Loop through the candidates starting from the start index.
    • Append the current candidate to comb.
    • Recursively call backtrack with the updated remain (subtract the current candidate from remain), the updated comb, and the same start index (allowing repeated use of the same candidate).
    • After the recursive call, remove the last element from comb to backtrack and try the next candidate.
  4. Sorting:
    • Optionally, sort the candidates to optimize the process by helping with pruning.
  5. Return the Result:
    • After all recursive calls complete, return the result list containing all unique combinations.

This approach ensures that all unique combinations of candidates that sum to the target are found using a depth-first search (DFS) method facilitated by backtracking. The constraints ensure that the problem size is manageable within this approach.

Solution in Javascript:

JavaScript
/**
 * @param {number[]} candidates
 * @param {number} target
 * @return {number[][]}
 */
var combinationSum = function(candidates, target) {
    const result = [];
    
    /**
     * Helper function for backtracking
     * @param {number} remain - the remaining sum needed to reach the target
     * @param {number[]} comb - the current combination of numbers
     * @param {number} start - the current index in candidates from which to consider adding numbers to the combination
     */
    const backtrack = (remain, comb, start) => {
        // If remain is zero, we found a valid combination
        if (remain === 0) {
            result.push([...comb]); // Make a copy of comb and add it to result
            return;
        }
        // If remain is negative, this combination is not valid
        if (remain < 0) {
            return;
        }
        
        // Loop through the candidates starting from the current position
        for (let i = start; i < candidates.length; i++) {
            // Add the candidate to the current combination
            comb.push(candidates[i]);
            // Since we can use the same element again, we call backtrack with the same start index
            backtrack(remain - candidates[i], comb, i);
            // Remove the last element to backtrack
            comb.pop();
        }
    };
    
    // Sort the candidates to help with pruning (not necessary but can optimize a bit)
    candidates.sort((a, b) => a - b);
    // Start the backtracking process with an empty combination
    backtrack(target, [], 0);
    
    return result;
};

Solution in Java:

Java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<>(); // Store all valid combinations
        
        // Sort the candidates array to help with pruning (not necessary but can optimize a bit)
        Arrays.sort(candidates);
        
        // Helper function for backtracking
        backtrack(candidates, target, new ArrayList<>(), 0, result);
        
        return result;
    }
    
    /**
     * Helper function for backtracking
     * @param candidates - array of candidate numbers
     * @param remain - remaining sum needed to reach the target
     * @param comb - current combination of numbers
     * @param start - current index in candidates from which to consider adding numbers to the combination
     * @param result - list to store all valid combinations
     */
    private void backtrack(int[] candidates, int remain, List<Integer> comb, int start, List<List<Integer>> result) {
        // Base case: If remain is zero, we found a valid combination
        if (remain == 0) {
            result.add(new ArrayList<>(comb)); // Make a copy of comb and add it to result
            return;
        }
        // If remain is negative or we have exhausted all candidates, this combination is not valid
        if (remain < 0 || start == candidates.length) {
            return;
        }
        
        // Loop through the candidates starting from the current position
        for (int i = start; i < candidates.length; i++) {
            // Add the current candidate to the current combination
            comb.add(candidates[i]);
            // Recursively call backtrack with the updated remain, updated combination, and the same start index
            backtrack(candidates, remain - candidates[i], comb, i, result);
            // Remove the last element to backtrack
            comb.remove(comb.size() - 1);
        }
    }
    
   
}

Solution in C#:

C#
using System;
using System.Collections.Generic;

public class Solution {
    public IList<IList<int>> CombinationSum(int[] candidates, int target) {
        IList<IList<int>> result = new List<IList<int>>(); // Store all valid combinations
        
        // Sort the candidates array to help with pruning (not necessary but can optimize a bit)
        Array.Sort(candidates);
        
        // Helper function for backtracking
        Backtrack(candidates, target, new List<int>(), 0, result);
        
        return result;
    }
    
    /**
     * Helper function for backtracking
     * @param candidates - array of candidate numbers
     * @param remain - remaining sum needed to reach the target
     * @param comb - current combination of numbers
     * @param start - current index in candidates from which to consider adding numbers to the combination
     * @param result - list to store all valid combinations
     */
    private void Backtrack(int[] candidates, int remain, List<int> comb, int start, IList<IList<int>> result) {
        // Base case: If remain is zero, we found a valid combination
        if (remain == 0) {
            result.Add(new List<int>(comb)); // Make a copy of comb and add it to result
            return;
        }
        // If remain is negative or we have exhausted all candidates, this combination is not valid
        if (remain < 0 || start == candidates.Length) {
            return;
        }
        
        // Loop through the candidates starting from the current position
        for (int i = start; i < candidates.Length; i++) {
            // Add the current candidate to the current combination
            comb.Add(candidates[i]);
            // Recursively call backtrack with the updated remain, updated combination, and the same start index
            Backtrack(candidates, remain - candidates[i], comb, i, result);
            // Remove the last element to backtrack
            comb.RemoveAt(comb.Count - 1);
        }
    }
    
    

    // Helper method to print the result
    private static void PrintResult(IList<IList<int>> result) {
        foreach (var comb in result) {
            Console.Write("[");
            Console.Write(string.Join(", ", comb));
            Console.WriteLine("]");
        }
    }
}

Subscribe
Notify of

0 Comments
Inline Feedbacks
View all comments

Popular