回溯法经典问题——组合总和问题系列总结---代码模板(举一反三)

这几天做LeetCode的每日一题,是关于组合总和的题目。组合总和、组合总和||、组合总和|||。不出意外的话,明天就是组合总和IV了。这种类型的题目算是比较经典,几道题核心内容不变,都是利用回溯法来进行筛选,过程中束缚条件根据题目来稍做修改。

组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

  • 所有数字(包括 target)都是正整数。
  • 解集不能包含重复的组合。

输入: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);
        }
    }
}

输出
回溯法经典问题——组合总和问题系列总结---代码模板(举一反三)_第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);

输出:
回溯法经典问题——组合总和问题系列总结---代码模板(举一反三)_第2张图片
接下来趁热看组合总和III

和前两题略微不一样,其实大同小异

找出所有相加之和为 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);
        }
    }
}

输出
回溯法经典问题——组合总和问题系列总结---代码模板(举一反三)_第3张图片
其实还有一个组合总和IV,但这道题其实和前几道题相关性不大了
是利用动态规划的一道题目,有兴趣的人可以去LeetCode看一下

好了
总结一下,组合问题其实就是一棵树的不断分支剪枝的过程
根据题目要求进行具体操作
希望童鞋们可以好好理解体会~

你可能感兴趣的:(IT,刷题,java)