- 题目描述
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
说明:
- candidates 中的数字可以无限制重复被选取。
- 所有数字(包括 target)都是正整数。
- 解集不能包含重复的组合。
提示:
1 <= candidates.length <= 30, 1 <= candidates[i] <= 200, 1 <= target <= 500。
candidate 中的每个元素都是独一无二的。
来源:LeetCode
- 示例
- 示例 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] ]
- 思路分析
用回溯枚举所有情况,回溯过程中进行剪枝去掉那些从目前来看已经不可能的情况。
需要注意的是List和数组一样,表示地址,因此需要用深拷贝来存储一个list,防止被后续操作更改。
//浅拷贝
public class Test {
public static void main(String args[]) {
List<Integer> list = new ArrayList<>();
list.add(1);
List<Integer> copy = list; //不同之处
list.add(2);
System.out.println("list.size():"+list.size());
System.out.println("elements of list:");
for(int i:list) System.out.print(i+" ");
System.out.println();
System.out.println("copy.size():"+copy.size());
System.out.println("elements of copy:");
for(int i:copy) System.out.print(i+" ");
}
}
/*控制台输出
list.size():2
elements of list:
1 2
copy.size():2
elements of copy:
1 2
*/
//深拷贝
public class Test {
public static void main(String args[]) {
List<Integer> list = new ArrayList<>();
list.add(1);
List<Integer> copy = new ArrayList<>(list); //不同之处
list.add(2);
System.out.println("list.size():"+list.size());
System.out.println("elements of list:");
for(int i:list) System.out.print(i+" ");
System.out.println();
System.out.println("copy.size():"+copy.size());
System.out.println("elements of copy:");
for(int i:copy) System.out.print(i+" ");
}
}
/*控制台输出:
list.size():2
elements of list:
1 2
copy.size():1
elements of copy:
1
*/
- JAVA实现
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> L = new ArrayList();
int len = candidates.length, sum;
List<Integer> l1 = new ArrayList();
Arrays.sort(candidates); //排序,方便后面进行剪枝
bT(target,candidates,0,l1,L); //回溯开始
return L;
}
public void bT(int target, int[] candidates, int i, List<Integer> l1, List<List<Integer>> L) {
//int i: 从candidates[i]开始看是否可能是target的一部分,前面的数都已经考虑过了,确保不会重复
//List<Integer> l1: 上一轮决定的可以加入list的元素
//List<List<Integer>> L: 总的list
int len = candidates.length, sum = target;
for(int k=i;k<len;k++) {
if(target<candidates[k]) return; //剪枝,说明从candidates[k]开始的数都不能放进list里,因为都大于target
else {
l1.add(candidates[k]); //这个数有可能是答案的一部分,放进list里
sum = target - candidates[k];
if(sum==0) { //找到了一个答案
L.add(new ArrayList<>(l1)); //新创建一个list,值等于l1;不能直接传l1!
l1.remove(l1.size()-1); //去掉这一轮加入的数,回到上一轮
break;
}
else { //sum>0
bT(sum, candidates,k,l1,L); //进入下一轮循环
l1.remove(l1.size()-1); //去掉这一轮加入的数,回到上一轮
}
}
}
}
}