题目描述:
给定一个无重复元素的数组 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]
]
提示:
1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate 中的每个元素都是独一无二的。
1 <= target <= 500
解题思路:
标准的回溯模板,在此基础上可以进行一些剪枝的操作,来降低时间复杂度和空间复杂度。详细见代码。
代码:
标准模板,无剪枝操作。
public class LC39 {
//回溯,标准模板
//时间复杂度:O(N) N为所有可行解的长度 空间复杂度:O(target)
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> ansList = new ArrayList<>();
//Arrays.sort(candidates);
backtrack(ansList, candidates, target, 0, new ArrayList<Integer>());
return ansList;
}
//回溯函数
/*
ansList最终结果集 candidates已知数组 target目标值 index搜索起点 tmpList中间结果集
*/
private void backtrack(List<List<Integer>> ansList, int[] candidates, int target, int index, ArrayList<Integer> tmpList) {
//如果当前目标值小于0则结束递归
if (target < 0){
return;
}
//当目标值为0时,将当前中间结果集合加入最终结果集
if (target == 0){
//注意这里一定是要new一个新的才存储,因为回溯到最上层时tmpList会变为空
ansList.add(new ArrayList<>(tmpList));
return;
}
//循环递归
for (int i = index; i < candidates.length; i++){
//更新状态
tmpList.add(candidates[i]);
//注意每一元素都可以循环使用,所以下一轮搜索的起点依旧是i
backtrack(ansList, candidates, target - candidates[i], i,tmpList);
//状态重置
tmpList.remove(tmpList.size() - 1);
}
}
public static void main(String[] args) {
LC39 obj = new LC39();
List<List<Integer>> lists = obj.combinationSum(new int[]{2, 3, 5}, 8);
System.out.println(lists);
}
}
剪枝
//剪枝
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> ansList = new ArrayList<>();
//这里保证已知的操作数组是有序的,以此来提高效率
Arrays.sort(candidates);
backtrack(ansList, candidates, target, 0, new ArrayList<Integer>());
return ansList;
}
private void backtrack(List<List<Integer>> ansList, int[] candidates, int target, int index, ArrayList<Integer> tmpList) {
if (target == 0){
ansList.add(new ArrayList<>(tmpList));
return;
}
for (int i = index; i < candidates.length; i++){
//已知target减去一个数小于0,那他减去更大的数也一定小于0,所以我们不妨将其剪掉,不再进行这些浪费时间和空间的无意义的操作
if(target - candidates[i] < 0){
break;
}
tmpList.add(candidates[i]);
backtrack(ansList, candidates, target - candidates[i], i,tmpList);
tmpList.remove(tmpList.size() - 1);
}
}