Leetcode-团灭组合问题

目录

1.问题描述

2.解题思路

2.1 算法思路

3.源代码

4.其他组合总和问题 

4.1 Leetcode40-组合总和 II

4.2 Leetcode216-组合总和 III


1.问题描述

给定一个无重复元素的数组 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

2.解题思路

一看问题的描述就知道这个题目的基本解法是回溯法,然后注意问题的描述就是candidates 中的数字可以无限制重复被选取。

2.1 算法思路

对于这类寻找所有可行解的题,我们都可以尝试用「搜索回溯」的方法来解决。

回到本题,我们定义递归函数

 private   void  dfs(int []candidates,int  index,int targetSum,int sum)

index表示 数组candidates的第 index位,sum表示当前数组数组累加的和,targetsum 表示我们需要的目标和。递归的终止条件为 sum==targetSum 。

 List<List<Integer>> res=new ArrayList<>();
    List<Integer> ans=new ArrayList<>();

res表示最后存储的解集的集合,然后ans表示当前解集的集合

本题搜索的过程抽象成树形结构如下:

3.源代码

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class CombinationSum {
    List<List<Integer>> res=new ArrayList<>();
    List<Integer> ans=new ArrayList<>();
    public List<List<Integer>> combinationSum(int[] candidates, int target)
    {
        dfs(candidates,0,target,0);
        return res;
    }
     private   void  dfs(int []candidates,int  index,int targetSum,int sum){
          if(sum>targetSum){
               return  ;
          }
          if(sum==targetSum){
               res.add(new ArrayList<>(ans));
                 return  ;
          }

          for(int i=index;i<candidates.length;i++ ){

              sum=sum+candidates[i];
              ans.add(candidates[i]);
              dfs(candidates,i,targetSum,sum);
              sum=sum-candidates[i];
              ans.remove(ans.size() - 1);
         }
     }
       public static  void main(String[] args) {  
             int  []nums={2,3,5};
             int  target=8;
               CombinationSum cm=new CombinationSum();
               List<List<Integer>> res=new ArrayList<>();
               res=cm.combinationSum(nums,target);
               System.out.println(res.toString());
       }
}

编译器运行通过:

Leetcode 上运行截图:

4.其他组合总和问题 

4.1 Leetcode40-组合总和 II

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

candidates 中的每个数字在每个组合中只能使用一次。

说明:

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

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
]

 示例2:

输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[
  [1,2,2],
  [5]
]

分析该问题,当把问题与上面的问题比较的时候,我们发现唯一的区别就是元素不能重复使用。解决问题的基本解法还是回溯法,在上面的代码基础上我们还是要如何解决元素重复的问题。避免重复的核心代码:

 for(int i=index;i<candidates.length;i++ ){
                   if(i>index&&candidates[i] == candidates[i-1]){
                         continue;
                   }

这样可以避免相同的情况筛选两次(一次原生For循环,一次是递归)。

还有就是递归调用dfs的时候我们需要修改条件:

  dfs(candidates,i+1,targetSum,sum);

这里的i+1可以避免元素的重复使用。当解决这些问题之后我们直接上源码:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class CombinationSum {
    List<List<Integer>> res=new ArrayList<>();
    List<Integer> ans=new ArrayList<>();
    public List<List<Integer>> combinationSum(int[] candidates, int target)
    {
        dfs(candidates,0,target,0);
        return res;
    }
     private   void  dfs(int []candidates,int  index,int targetSum,int sum){
          if(sum>targetSum){
               return  ;
          }
          if(sum==targetSum){
               res.add(new ArrayList<>(ans));
                 return  ;
          }

          for(int i=index;i<candidates.length;i++ ){
                   if(i>index&&candidates[i] == candidates[i-1]){
                         continue;
                   }
              sum=sum+candidates[i];
              ans.add(candidates[i]);
              dfs(candidates,i+1,targetSum,sum);
              sum=sum-candidates[i];
              ans.remove(ans.size() - 1);
         }
     }
       public static  void main(String[] args) {  
             int  []nums={10,1,2,7,6,1,5};
             int  target=8;
               CombinationSum cm=new CombinationSum();
               List<List<Integer>> res=new ArrayList<>();
               res=cm.combinationSum(nums,target);
               System.out.println(res.toString());
       }
}

编译器运行结果:

我们发现原来结果集有重复的情况,仔细想想,递归之前排序就可以解决了。源代码如下:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class CombinationSum {
    List<List<Integer>> res=new ArrayList<>();
    List<Integer> ans=new ArrayList<>();
    public List<List<Integer>> combinationSum(int[] candidates, int target)
    {
        Arrays.sort(candidates);
        dfs(candidates,0,target,0);
        return res;
    }
     private   void  dfs(int []candidates,int  index,int targetSum,int sum){
          if(sum>targetSum){
               return  ;
          }
          if(sum==targetSum){
               res.add(new ArrayList<>(ans));
                 return  ;
          }

          for(int i=index;i<candidates.length;i++ ){
                   if(i>index&&candidates[i] == candidates[i-1]){
                         continue;
                   }
              sum=sum+candidates[i];
              ans.add(candidates[i]);
              dfs(candidates,i+1,targetSum,sum);
              sum=sum-candidates[i];
              ans.remove(ans.size() - 1);
         }
     }
       public static  void main(String[] args) {  
             int  []nums={10,1,2,7,6,1,5};
             int  target=8;
               CombinationSum cm=new CombinationSum();
               List<List<Integer>> res=new ArrayList<>();
               res=cm.combinationSum(nums,target);
               System.out.println(res.toString());
       }
}

编译器运行结果:

运行结果显示结果没有重复的集合。直接提交leetcode。

 

4.2 Leetcode216-组合总和 III

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

说明:

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

示例1:

输入: k = 3, n = 7
输出: [[1,2,4]]

 示例2:

输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]

解决问题的基本思路还是回溯法。

首先定义递归函数:

void dfs(int  k,int index,int  targetSum,int  sum)

k 代表数组解集的位数,index代表数组解集的元素值。targetSum代表目标和,sum代表当前解集的和。

递归终止条件为:

ans.size()==k&&sum==targetSum

完整代码如下:

import java.util.ArrayList;
import java.util.List;
public class CombinationSum3 {
      List<List<Integer>> lists=new ArrayList<>();
      List<Integer> ans=new ArrayList<>();
      public List<List<Integer>> combinationSum3(int k, int n) {
          List<Integer> list = new ArrayList<>();
          dfs(k,1,n,0);
           return lists;
    }
     void dfs(int  k,int index,int  targetSum,int  sum){
               if(ans.size()==k){
                     if(sum==targetSum)
                          lists.add(new ArrayList<>(ans));
                          return ;
              }
               for(int  i=index;i<=9;i++)
               {
                   sum = sum + i;
                   ans.add(i);
                   dfs(k,i+1,targetSum,sum);
                   sum=sum-i;
                   ans.remove(ans.size()-1);//回溯
               }
     }
         public static   void main(String[] args){
           List<List<Integer>> lists=new ArrayList<>();
           int  k=3;
           int  n=9;
           CombinationSum3  cm=new CombinationSum3();
           lists=cm.combinationSum3(k,n);
           System.out.println(lists.toString());
           }


}

运行结果:

直接提交Leetcode

 惊喜哈哈哈!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值