Question
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.
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]
]


本题难度medium。

【注意】
本题允许重复抽取同一个数字。(The same repeated number may be chosen from C unlimited number of times.)

【思路】
我刚开始准备采用3sum的办法,利用双指针向中间查找。这个办法不是不能用,效率也高,但是它的缺点就是要分别对candidates含有1个、2个、2个以上情况,以及还要另外写一个函数针对结果只有1个元素的情况:

ie. 
input:[1 2 3] 2
output中的[2]就是1个元素

这个缺点的原因在于这个办法的要求是​​candidates的length > 2​​。

所以采用深度优先搜索算法(DFS)。虽然效率未必高,但是代码简洁不会出错。DFS的思路是把解空间按照从小到大含有某个元素的方式进行划分,例如:

假设该题的解空间:
[1 1 1 1] [1 1 2] [1 3] [4]

划分为:
含有1
[1 1 1 1] [1 1 2] [1 3]
含有4
[4]

我首先深度优先搜索含1的所有解,然后当我在深度优先搜索2时,就不必再考虑含有1的情况。这就是28行含有index的原因:

for(int i=index;i<size;i++)

【提示】
如果不好理解,就从树的角度来理解DFS。

DFS

【复杂度】
时间 O(N!) 空间 O(N) 递归栈空间

【注意】
1、candidates可能没有排过序
2、要越过重复的元素(第29行),否则就会出现重复的结果。(不过不加也不影响AC,估计是测试用例中没有重复元素的用例)例如:

input:[1 1 1 1 1] 3
if(i!=index&&c[i-1]==c[i])
continue;

【代码】

public class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
//require
List<List<Integer>> ans=new ArrayList<>();
if(candidates==null){
return ans;
}
int size=candidates.length;
if(size<1)
return ans;
//sort
Arrays.sort(candidates);
//invariant
List<Integer> preList=new ArrayList<Integer>();
helper(preList,0,candidates,target,ans);
//ensure
return ans;

}
public void helper(List<Integer> list,int index,int[] c,int target,List<List<Integer>> ans){
if(target<0)
return;
if(target==0){
List<Integer> oneComb= new ArrayList<Integer>(list);
ans.add(oneComb);
}else if(target>0){
int size=c.length;
for(int i=index;i<size;i++){
list.add(c[i]);
helper(list,i,c,target-c[i],ans);
list.remove(list.size()-1);
}

}


}
}

【follow up】
我曾想通过采用“DFS+二分查找法”提高效率,将上面的代码改为:先利用二分查找法看看本级有没有element等于target,然后再向下级遍历。实际上这样会造成结果出现duplicates。因为DFS本身就可以搜索到所有解,所以必然会出现duplicates。

同时,要注意DFS本身是末端节点处理,当处在中间的节点时并不进行处理。

参考

​​[Leetcode] Combination Sum 组合数之和​​