39. 组合总和
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
- 所有数字(包括 target)都是正整数。
- 解集不能包含重复的组合。
示例 1:
输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
示例 2:
输入:candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
这题还是蛮难的,难的不是得出结果,是去重,还有时间限制,内存限制等,笔者卡在 170/171,最后一个测例始终通不过,超出限制了。
我的思路:
遍历数组,计算sum,大于则返回,小于则继续,等于就进入判断是否存在的逻辑,如果存在则不添加,把结果排序,对比是否完全相等,相等则为存在。然后递归,回溯。
有一点很重要,就是 i = index ,然后把 i 传到递归中,如果不能重复用,则index++,传index即可。
这里需要重复使用,则传 i 。这点很重要。我不能运行出来就是因为传的是0,每次都从头开始,时间超时了。
List<List<Integer>> result = new ArrayList<List<Integer>>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
backtracking(new ArrayList<Integer>(), candidates, target,0);
return result;
}
/**
* @param candidates 选数字的数组
* @param target 数字之和目标值
* @param startIndex
*/
int sum = 0;
public void backtracking(List<Integer> list, int[] candidates, int target,int index) {
//超出结果值就不用加了
if (sum > target) {
return;
}
//返回 和为sum的结果集
if (sum == target) {
List<Integer> temp = new ArrayList<Integer>(list);
//不可在原集合上直接排序,否则会打乱回溯顺序
Collections.sort(temp);
//如果存在已经重复的结果
if (!removeRep(result, temp)) {
return;
}
result.add(new ArrayList<>(temp));
return;
}
for (int i = index; i < candidates.length; i++) {
//累加sum,添加元素到集合
list.add(candidates[i]);
sum += candidates[i];
//递归
backtracking(list, candidates, target,i);
//回溯
sum -= candidates[i];
list.remove(list.size() - 1);
}
}
/**
* 结果集去重
*
* @param result
* @param list
* @return
* false:结果集存在 , 不可添加
* true:结果集不存在,可添加
*/
public boolean removeRep(List<List<Integer>> result, List<Integer> list) {
for (List<Integer> item : result) {
//如果两个 list 完全相同
if (isSameList(list, item)) {
return false;
}
}
//都不相同,可以添加
return true;
}
/**
* 两个集合元素是否完全相同
*
* @param list1
* @param list2
* @return
*/
public boolean isSameList(List<Integer> list1, List<Integer> list2) {
if (list1.size() != list2.size()) {
return false;
}
for (int i = 0; i < list1.size(); i++) {
if (list1.get(i) != list2.get(i)) {
return false;
}
}
return true;
}
答案比我巧妙在不用取出,它是先把数字集排序,那样就不会出现重复的 list 。妙啊。
class Solution {
List<List<Integer>> result = new ArrayList<List<Integer>>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
//排序后生成的列表顺序都一样了,这点很重要,不用再去去重
Arrays.sort(candidates); // 先进行排序
backtracking(new ArrayList<Integer>(), candidates, target, 0);
return result;
}
/**
* @param candidates 选数字的数组
* @param target 数字之和目标值
* @param startIndex
*/
int sum = 0;
public void backtracking(List<Integer> list, int[] candidates, int target, int startIndex) {
//返回 和为sum的结果集
if (sum == target) {
System.out.println(list);
result.add(new ArrayList<>(list));
return;
}
for (int i = startIndex; i < candidates.length; i++) {
//累加sum,添加元素到集合
if (sum + candidates[i] > target) {
break;
}
sum += candidates[i];
list.add(candidates[i]);
//递归 当前元素使用过后,依次往后使用,不再往前来
backtracking(list, candidates, target, i);
//回溯
sum -= candidates[i];
list.remove(list.size() - 1);
}
}
}