这几天做LeetCode的每日一题,是关于组合总和的题目。组合总和、组合总和||、组合总和|||。不出意外的话,明天就是组合总和IV了。这种类型的题目算是比较经典,几道题核心内容不变,都是利用回溯法来进行筛选,过程中束缚条件根据题目来稍做修改。
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
题目分析:关键点在于最后一句话,数字可以无限制重复选取,意味着你在回溯递归的时候,当前索引为i,递归索引也为i而不是i+1
具体看代码:
public class Leetcode39_组合总和 {
public static void main(String[] args) {
int[] candidates={
2,3,6,7};
int n=7;
combinationSum(candidates, n);
}
public static List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res=new ArrayList<>();
List<Integer> list=new ArrayList<>();
zuhe(candidates,target,0,list,res);
System.out.println(res);
return res;
}
/**
*
* @param candidates 元素数组
* @param target 目标值,每次递归都会减去添加的元素
* @param index 当前元素数组的索引,每一次递归就是判断这个索引对应的元素要不要加进去
* @param list 临时列表,存储当前一组元素的值
* @param res 结果列表
*/
public static void zuhe(int[] candidates, int target,int index,List<Integer> list,List<List<Integer>> res){
// 如果目标值为0,意味着不需要再添加元素了,到此结束,添加到结果街
if(target==0){
res.add(new ArrayList<>(list));
return;
}
// 从index索引开始,遍历元素数组
for(int i=index;i<candidates.length;i++){
// 如果当前元素大于目标值,就没有添加的必要了,因为元素都是正数,以后不会有等于0的情况
if(target<candidates[i]){
continue;
}
list.add(candidates[i]);
// 核心重点:由于每个元素都可以重复,下一轮搜索的起点依然是i,好好体会一下
zuhe(candidates,target-candidates[i],i,list,res);
// 状态重置,也很关键,不要忽略,这是回溯关键
list.remove(list.size()-1);
}
}
}
输出
代码模板就是 zuhe(int[] candidates, int target,int index,List list,List res)
这里着重注意一下,其中的index取值,这道题是取得i,接下来在来看一下这道题的变种
zuhe(candidates,target-candidates[i],i,list,res);
组合总和II
和上道题唯一不同的是candidates中的每个数字在每个组合中只能使用一次
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]
这里注意一下,从样例可以看出来,数字不能重复使用而且,同时要保证不能有相同数组,例如[1,2,5]和[2,1,5]虽然满足数字不重复,但只保留其中一个。
所以上述代码只需修改以下几处即可:
(1)
public static void main(String[] args) {
int[] candidates={
10,1,2,7,6,1,5};
// 要先进行排序,原因是方便后面的去重。因为list中[1,2,5]和[2,1,5]属于不同元素
Arrays.sort(candidates);
combinationSum(candidates, n);
}
(2)
这里将i改为i+1,保证数字在组合中只用一次
zuhe(candidates,target-candidates[i],i+1,list,res);
和前两题略微不一样,其实大同小异
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
这里限定了
1、组合的数目,必须是k个
2、组合只允许含1-9
3、组合中不含重复数字
这里其实和组合总和II就只有一点不同,组合数目
第2点可以直接自己拟定元素数组candidates即可{1,2,3,4,5,6,7,8,9}
第3点自然满足条件了,因为candidates本身就不含重复元素
代码:
class Solution {
public List<List<Integer>> combinationSum3(int k, int n) {
int[] candidates={
1,2,3,4,5,6,7,8,9};
List<List<Integer>> res=new ArrayList<>();
List<Integer> list=new ArrayList();
zuhe(candidates,n,0,list,res,k);
return res;
}
public void zuhe(int[] candidates,int target,int index,List<Integer> list,List<List<Integer>> res,int k){
// 这里多添加一个约束条件,满足数目的list才被添加到结果集
if(target==0 && list.size()==k){
res.add(new ArrayList<>(list));
return;
}
for(int i=index;i<candidates.length;i++){
if(target<candidates[i])
continue;
list.add(candidates[i]);
zuhe(candidates,target-candidates[i],i+1,list,res,k);
list.remove(list.size()-1);
}
}
}
输出
其实还有一个组合总和IV,但这道题其实和前几道题相关性不大了
是利用动态规划的一道题目,有兴趣的人可以去LeetCode看一下
好了
总结一下,组合问题其实就是一棵树的不断分支剪枝的过程
根据题目要求进行具体操作
希望童鞋们可以好好理解体会~