Leetcode-39组合总和
给定一个无重复元素的正整数数组
candidates
和一个正整数target
,找出candidates
中所有可以使数字和为目标数target
的唯一组合。
candidates
中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。对于给定的输入,保证和为
target
的唯一组合数少于150
个。示例 1:
输入: candidates = [2,3,6,7], target = 7 输出: [[7],[2,2,3]]
题意分析:给定目标值,找出可能的组合,这类寻找组合的题都可以用到经典__回溯__解法,以下是基本回溯题解
static volatile int size = 0; //递归的次数
static List<List<Integer>> relist = new ArrayList<>();
static LinkedList<Integer> list = new LinkedList<>();
public static void main(String[] args) {
int[] arr = {2,5,6,3,9,7};
System.out.println(combinationSum(arr, 11));
System.out.println(">>>>>>递归总次数:" + size);
}
public static List<List<Integer>> combinationSum(int[] candidates, int target) {
if(candidates == null || candidates.length == 0 || target <= 0){
return relist;
}
dfs(candidates, target, 0);
return relist;
}
/**
* 深度优先搜索
* @param candidates
* @param target
* @param ind 已经检索的数组下标
*/
public static void dfs(int[] candidates, int target, int ind){
if(target < 0 || ind == candidates.length){
return;
}
if(target == 0){
relist.add(new LinkedList(list));
return;
}
for(int i = ind; i < candidates.length; i++){
//添加当前下标到集合
list.addLast(candidates[i]);
System.out.println(">>>>>>递归前:" + list.toString() + ">>>>>剩余:" + (target - candidates[i]));
//深度搜索,注意这里是i,不是i+1,因为每个值可以重复使用
size++;
dfs(candidates, target - candidates[i], i);
System.out.println(">>>>>>递归后:" + list.toString());
//回溯
list.removeLast();
}
}
运行结果:
以上的结果怎样剪枝昵,可以注意到,上面的计算每次递归之后,target与当前下标的数相减小于0之后,还会回溯与这个下标之后的数比较,如果candidates这个数组是有序的,如果target与前面小的下标相减小于0,那么跟后面更大的数相减更会小于0,那么可以剪枝的代码如下:
static volatile int size = 0; //递归的次数
static List<List<Integer>> relist = new ArrayList<>();
static LinkedList<Integer> list = new LinkedList<>();
public static void main(String[] args) {
int[] arr = {2,5,6,3,9,7};
System.out.println(combinationSum(arr, 11));
System.out.println(">>>>>>递归总次数:" + size);
}
public static List<List<Integer>> combinationSum(int[] candidates, int target) {
if(candidates == null || candidates.length == 0 || target <= 0){
return relist;
}
//对数组排序,重要
Arrays.sort(candidates);
dfs(candidates, target, 0);
return relist;
}
/**
* 深度优先搜索
* @param candidates
* @param target
* @param ind 已经检索的数组下标
*/
public static void dfs(int[] candidates, int target, int ind){
if(target < 0 || ind == candidates.length){
return;
}
if(target == 0){
relist.add(new LinkedList(list));
return;
}
for(int i = ind; i < candidates.length; i++){
//剪枝,小于0对后序下标跳过
if(target - candidates[i] < 0){
break;
}
//添加当前下标到集合
list.addLast(candidates[i]);
System.out.println(">>>>>>递归前:" + list.toString() + ">>>>>剩余:" + (target - candidates[i]));
//深度搜索,注意这里是i,不是i+1,因为每个值可以重复使用
size++;
dfs(candidates, target - candidates[i], i);
System.out.println(">>>>>>递归后:" + list.toString());
//回溯
list.removeLast();
}
}
运行结果:
可以看到剪枝之后的递归次数大幅减小,核心在于数据是排过序的