Problem:
Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
The same repeated number may be chosen from C unlimited number of times.
Note:
- All numbers (including target) will be positive integers.
- Elements in a combination (a1, a2, … , ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak).
- The solution set must not contain duplicate combinations.
For example, given candidate set 2,3,6,7
and target 7
,
A solution set is: [7]
[2, 2, 3]
Analysis:
This problem is a typical DFS problem, it is simple, but it is very very useful. It actually shares same skills we have used in two sum problem: 1. How to take advantage of a sorted array to aovid duplicates? 2. How to guarantee the results in no-descending order? They all could solve through the skill we have used in three sum problem: sort the array first. But there is also an obvious difference between this problem with two sum problem. ------------------------------------------------------------------------------------------------------------------------- 3 sum problem: there could be duplicates in the array, we should use some skills to avoid the elements with value appear was used on the same index many times. ------------------------------------------------------------------------------------------------------------------------- combinaionSum problem: no duplicates in the candidates set, and we allow to use each element many times. (This free us from checking iff nums[i] == nums[i-1]). ------------------------------------------------------------------------------------------------------------------------- Basic idea: step 1: sort the candidates in no-descending order. Arrays.sort(candidates); step 2: get a number from the sorted array, then continue the search along the elements after it(inclusive, since each element could be used multi times) Note: the "inclusive" would allow the same element to be used at other indexes, but not repeat on the same index. A tree structure is good start for understanding! for (int i = start; i < candidates.length; i++) { item.add(candidates[i]); searchPath(candidates, i, target-candidates[i], item, ret); item.remove(item.size()-1); } Good programming habit: Don't do the complex checking when forking!!! Stop the wrong froking branches at base case!!! if (target == 0) ret.add(new ArrayList<Integer> (item)); if (target < 0) return; Two repeatives in such problem: 1. the element with the same value were used on the same index many times. 1.1 iff there are duplicates in array (nums[i] != num[i-1]) 1.2 iff there are not duplicates in array (easy, but should allow it at next index) 2. mix the order of the same result. [2 3] [3 2] The problem was caused by following logic: for (int i = 0; i < candidates.length; i++) { ... searchPath(candidates, i, target-candidates[i], item, ret); } It must cause duplicates! The great way to solve above prolem is using sorting candidates array! And use the fix: for (int i = start; i < candidates.length; i++) { ... } start from "start", rather than the elements before start.
Solution:
public class Solution { public List<List<Integer>> combinationSum(int[] candidates, int target) { if (candidates == null) throw new IllegalArgumentException("candidates is null"); List<List<Integer>> ret= new ArrayList<List<Integer>> (); if (candidates.length == 0 || target < 0) return ret; ArrayList<Integer> item = new ArrayList<Integer> (); Arrays.sort(candidates); searchPath(candidates, 0, target, item, ret); return ret; } private void searchPath(int[] candidates, int start, int target, List<Integer> item, List<List<Integer>> ret) { if (target == 0) ret.add(new ArrayList<Integer> (item)); if (target < 0) return; for (int i = start; i < candidates.length; i++) { item.add(candidates[i]); searchPath(candidates, i, target-candidates[i], item, ret); item.remove(item.size()-1); } } }