TeachingBee

Combination Sum LeetCode 39 Solution

Combination Sum leetcode

In this article we will solve Combination Sum LeetCode Problem which is a very good backtracking question.

Difficulty: Medium, Asked-in: Amazon, Adobe.

Problem Statement

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

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

It is guaranteed that the number of unique combinations that sum up to target is less than 150 combinations for the given input.

Examples:

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.

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

Input: candidates = [2], target = 1
Output: []
Explanation: No combinations are possible to generate as array as only 1 element i.e. 2

Constraints:

  • 1 <= candidates.length <= 30
  • 2 <= candidates[i] <= 40
  • All elements of candidates are distinct.
  • 1 <= target <= 40

Solution

  1. Recursion (Take/NoTake Approach)

1. Recursion

Intuition

Whenever the question asks to find permutations or printing subsequences or something similar, think of recursion/backtracking. This problem uses the “pick and non-pick” approach. The idea is similar to subset sum problem.

Algorithm

We select each number from candidates array and recursively consider two possible cases:

  • if(candidates[i]<target) then we have two cases:
    • The selected number is part of the solution and included in the list.
    • The selected number is not part of the solution and not included in the list.
  • if(candidates[i]>target) then we have only 1 cases of not taking.
Combination Sum

Base Case: There will be two base cases:

  • if(target==0) it means we found the answer so the list that we were carrying in recursive function we will store in answer 2D list.
  • if(index>=candidates.length) it means we haven’t found any possible answer so just return back.

Dry Run

We start with an empty data structure i.e. list in our case, the target value, and the index == 0. At each index, as discussed above there are two options:

  1. Include the element: Add the current element to the data structure. As duplicates are allowed, remain at the same index but reduce the target by the current element’s value.
  2. Exclude the element: Do not add the element and move on to the next index, keeping the target unchanged.

We recursively explore both options. When including an element, we make a recursive call at the same index after updating the target. The target value will be target-candidates[index] (if => candidates[i]<target). When excluding, the recursive call goes to the next index.

Before backtracking, we remove the element added in the previous call. This ensures we generate all permutations with and without the element at a given index.

The recursion terminates when the index>=candidates.length for a call or when target==0. The data structure then contains a valid combination summing to target.

Combination Sum dry run

By recursively exploring all include/exclude choices at each index, we generate all possible combinations that sum up to the target. The backtracking and removal of elements ensures correct subsets.

Code Implementation

Here is the code implementation of combination sum in C++, Java, Python.

// Combination Sum C++ Solution

#include <vector>
#include <algorithm>

class Solution {
public:
    void help(int index, std::vector<int>& nums, std::vector<int>& list, std::vector<std::vector<int>>& res, int target) {
        // Answer found, store in res 2D vector.
        if (target == 0) {
            res.push_back(list);
            return;
        }
        
        // No possible answer, return back.
        if (index == nums.size()) return;
        
        // Take call.
        if (nums[index] <= target) {
            list.push_back(nums[index]);
            help(index, nums, list, res, target - nums[index]);
            list.pop_back(); // Backtracking step.
        }
        
        // No take call.
        help(index + 1, nums, list, res, target);
    }
    
    std::vector<std::vector<int>> combinationSum(std::vector<int>& candidates, int target) {
        std::vector<std::vector<int>> res;
        std::vector<int> list;
        
        help(0, candidates, list, res, target);
        return res;
    }
};

// Combination Sum Java Solution

class Solution {
    private void help(int index, int[] nums, List<Integer> list, List<List<Integer>> res, int target){
        
        // Answer found store in res 2D list..
        if(target==0){
             res.add(new ArrayList<>(list));
            return;
        }
           
        // No possible ans.. return back...
        if(index==nums.length)
            return;

        // Take call...
        if(nums[index]<=target) {
            list.add(nums[index]);
            help(index,nums,list,res,target-nums[index]);
            list.remove(list.size()-1); // backtracking step...
        }
            // No take call...
        help(index+1,nums,list,res,target);
    }
    
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res= new ArrayList<>();
        List<Integer> list = new ArrayList<>();
        
        help(0,candidates,list,res,target);
        return res;
    }
}
// Combination Sum Python Solution

class Solution:
    def help(self, index, nums, list, res, target):
        # Answer found, store in res list of lists.
        if target == 0:
            res.append(list[:])
            return
        
        # No possible answer, return back.
        if index == len(nums):
            return
        
        # Take call.
        if nums[index] <= target:
            list.append(nums[index])
            self.help(index, nums, list, res, target - nums[index])
            list.pop()  # Backtracking step.
        
        # No take call.
        self.help(index + 1, nums, list, res, target)
    
    def combinationSum(self, candidates, target):
        res = []
        self.help(0, candidates, [], res, target)
        return res

Complexity

Time Complexity: O(2^t * n) where t is the target, n is the average length

Space Complexity:  O(n*x), n is the average length and x is the no. of combinations

I hope You liked the post ?. For more such posts, ? subscribe to our newsletter. Try out our free resume checker service where our Industry Experts will help you by providing resume score based on the key criteria that recruiters and hiring managers are looking for.

90% of Tech Recruiters Judge This In Seconds! 👩‍💻🔍

Don’t let your resume be the weak link. Discover how to make a strong first impression with our free technical resume review!

Related Articles

Hello World! in C HackerRank Solution 

Problem Statement In this challenge, we will learn some basic concepts of C that will get you started with the language. You will need to use the same syntax to

Why Aren’t You Getting Interview Calls? 📞❌

It might just be your resume. Let us pinpoint the problem for free and supercharge your job search. 

Newsletter

Don’t miss out! Subscribe now

Log In