Given a set of candidate numbers (candidates) (without duplicates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.
The same repeated number may be chosen from candidates unlimited number of times.
Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
Example 1:
Input: candidates = [2,3,6,7], target = 7,
A solution set is:
[
[7],
[2,2,3]
]
给出一个数组和一个目标和target,让找出所有和为target的元素组合
可以使用重复元素
思路:
DFS和combination
DFS是排列,个数为N!combination为组合,个数为
C
N
N
C_{N}^N
CNN
DFS
if cur.length == N
result.add(cur);
//每次遍历所有元素,找出所有排列,有重复
for every element i in candidates
if (used[i]) continue;
used[i] = true;
cur.push(candidates[i])
dfs(candidates, i, cur, result);
cur.pop()
used[i] = false;
combination:
if cur.length == N
result.add(cur);
//start为传入参数,指定从哪个index开始遍历
for i = start to candidates.length
cur.push(candidates[i])
//注意这里从i+1开始,只挑后面的数,无重复
combination(candidates, i+1, cur, result);
cur.pop()
此题是combination sum,因此选combination的思路
但是因为可以重复选择同一元素,
//下次改为从i开始,可重复同一元素
combination(candidates, i, cur, result);
同时为了降低时间复杂度,将原数组candidates排序,这样做有一个好处,当到某处的和已经大于target时,就不需要再遍历后面的元素
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
if(candidates == null || candidates.length == 0) {
return result;
}
int n = candidates.length;
Stack<Integer> cur = new Stack<>();
Arrays.sort(candidates);
combination(candidates, 0, target, result, cur);
return result;
}
public void combination(int[] candidates, int start, int target, List<List<Integer>> result, Stack<Integer> cur) {
if(target < 0) {
return;
}
if(target == 0) {
//这里一定要新建一个list,如果传参照的话后面元素会变empty
result.add(new ArrayList<Integer>(cur));
return;
}
for(int i = start; i < candidates.length; i++) {
if(candidates[i] > target) {
break;
}
cur.push(candidates[i]);
//从i开始而不是i+1,因为可以使用重复数字
combination(candidates, i, target - candidates[i], result, cur);
cur.pop();
}
}
40题,仍然是组合,但是这次不能使用重复数字
那么就不需要改combination思路,直接用组合思路
combination:
if cur.length == N
result.add(cur);
//start为传入参数,指定从哪个index开始遍历
for i = start to candidates.length
cur.push(candidates[i])
//注意这里从i+1开始,只挑后面的数,无重复
combination(candidates, i+1, cur, result);
cur.pop()
而且还有一个问题,虽然不使用相同index的数字,但是原数组candidates中是有重复数字的
这时可以用排序然后跳过重复元素的思路,但是这里注意比较的边界,因为我们每次是从i+1开始的下一次,所以下一次检查要从i+1往后开始检查重复,而不能把i+1这个边界之前的数字也拿来比较
比如[1,1,6]
第一次选1,然后下一次从第2个1开始,[1,6]
这时比较重复只在[1,6]这个小范围比较,而不能把上一step的1也拿来比较,那样的话就会跳过这一阶段的1,而导致解变少
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
if(candidates == null || candidates.length == 0) {
return result;
}
Stack<Integer> cur = new Stack<>();
Arrays.sort(candidates);
combination(candidates, target, 0, result, cur);
return result;
}
public void combination(int[] candidates, int target, int start,
List<List<Integer>> result, Stack<Integer> cur) {
if(target < 0) {
return;
}
if(target == 0) {
result.add(new ArrayList<Integer>(cur));
return;
}
for(int i = start; i < candidates.length; i++) {
if(candidates[i] > target) {
break;
}
//注意比较start之后的部分,不能把边界之前的也拿来比较
if(i > start && candidates[i] == candidates[i-1]) {
continue;
}
cur.push(candidates[i]);
combination(candidates, target - candidates[i], i+1, result, cur);
cur.pop();
}
}