39、40、组合总和

  1. 39、组合总和
  2. 说明:
    1. 示例 1:
    2. 示例 2:
  3. 解法1:递归回溯,深度优先
  4. 解法2:动态规划
  5. 40、组合总和II
    1. 示例 1:
    2. 示例 2:
  6. 递归回溯,删除重复元素

39、组合总和

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

链接:https://leetcode-cn.com/problems/combination-sum

解法1:递归回溯,深度优先


class Solution {
    List<List<Integer>> ans = new ArrayList<List<Integer>>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        
        Arrays.sort(candidates);// 排好序,方便回溯
        
        
        getAllAnswer(candidates,0,new Stack<>(),target);
        
        return ans;
    }
    
    // 递归 回溯 结合图形编码
    private void getAllAnswer(int[] can,int index,Stack<Integer> res,int target) {
        if (0 == target) {
            List<Integer> ans0 = new ArrayList<Integer>(res);
            ans.add(ans0);
            return;
        } 
        // 剪枝 target - can[i] >= 0
        for (int i = index;i < can.length && target - can[i] >= 0;i++) {
            res.add(can[i]);
            getAllAnswer(can,i,res,target-can[i]);// 递归
            res.pop();// 回溯,利用栈的特性,将最后入栈的元素出栈
        }
    }
}

解法2:动态规划

  • 求出1-target的所有解
// 由@liu-li-8提供
class Solution {
  public List<List<Integer>> combinationSum(int[] candidates, int target) {
      List<List<Integer>> result = new ArrayList<>();
      Map<Integer,Set<List<Integer>>> map = new HashMap<>();
      //对candidates数组进行排序
      Arrays.sort(candidates);
      int len = candidates.length;
      // 逐渐增加目标值
      for(int i = 1;i <= target;i++){
          //初始化map
          map.put(i,new HashSet<>());
          //对candidates数组进行循环
          for(int j = 0;j < len&&candidates[j] <= target;j++){
              // 目标值已存在
              if(i == candidates[j]){
                  //相等即为相减为0的情况,直接加入set集合即可
                  List<Integer> temp = new ArrayList<>();
                  temp.add(i);
                  map.get(i).add(temp);
              }else if(i > candidates[j]){// 目标值
                  //i-candidates[j]是map的key
                  int key = i-candidates[j];
                  //使用迭代器对对应key的set集合进行遍历
                  //如果candidates数组不包含这个key值,对应的set集合会为空,故这里不需要做单独判断
                  for(Iterator iterator = map.get(key).iterator();iterator.hasNext();){
                      List list = (List) iterator.next();
                      //set集合里面的每一个list都要加入candidates[j],然后放入到以i为key的集合中
                      List tempList = new ArrayList<>();
                      tempList.addAll(list);
                      tempList.add(candidates[j]);
                      //排序是为了通过set集合去重
                      Collections.sort(tempList);
                      map.get(i).add(tempList);
                  }
              }
          }
      }
      result.addAll(map.get(target));
      return result;
  }
}

// 作者:chun-meng-da-xiao-yang
// 链接:https://leetcode-cn.com/problems/combination-sum/solution/chao-qiang-gifzhu-ni-shi-yong-dong-tai-gui-hua-qiu/

40、组合总和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]
]

链接:https://leetcode-cn.com/problems/combination-sum-ii

递归回溯,删除重复元素


// 解法 1,稍微修改一下上一题的代码
class Solution {
    Set<List<Integer>> ans = new HashSet<List<Integer>>();// 利用Set去重

    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        // 数组有重复元素,每个元素只能使用一次
        Arrays.sort(candidates);// 排好序,方便回溯
        
        getAllAnswer(candidates,0,new Stack<>(),target);
        
        return new ArrayList<List<Integer>>(ans);// 这里比较耗时
    }
     // 递归 回溯 结合图形编码
    private void getAllAnswer(int[] can,int index,Stack<Integer> res,int target) {
        if (0 == target) {
            List<Integer> ans0 = new ArrayList<Integer>(res);
            ans.add(ans0);
            return;
        } 
        // 剪枝 target - can[i] >= 0
        for (int i = index;i < can.length && target - can[i] >= 0;i++) {
            res.add(can[i]);
            getAllAnswer(can,i+1,res,target-can[i]);// 递归。i+1每个元素智能使用一次
            res.pop();// 回溯,利用栈的特性,将最后入栈的元素出栈
        }
    }
}
// 优化一下
class Solution {
    List<List<Integer>> ans = new ArrayList<List<Integer>>();

    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        // 数组有重复元素,每个元素只能使用一次
        Arrays.sort(candidates);// 排好序,方便回溯
        
        getAllAnswer(candidates,0,new Stack<>(),target);
        
        return ans;
    }
     // 递归 回溯 结合图形编码
    private void getAllAnswer(int[] can,int index,Stack<Integer> res,int target) {
        if (0 == target) {
            List<Integer> ans0 = new ArrayList<Integer>(res);
            ans.add(ans0);
            return;
        } 
        // 剪枝 target - can[i] >= 0
        for (int i = index;i < can.length && target - can[i] >= 0;i++) {
            if(i > index && can[i] == can[i-1]) continue;// can[i]加入结果,从index + 1 开始去重
            res.add(can[i]);
            getAllAnswer(can,i+1,res,target-can[i]);// 递归
            res.pop();// 回溯,利用栈的特性,将最后入栈的元素出栈
        }
    }
}

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1056615746@qq.com

💰

Title:39、40、组合总和

Count:1.3k

Author:攀登

Created At:2020-07-26, 00:19:44

Updated At:2024-06-15, 15:52:32

Url:http://jiafeimao-gjf.github.io/2020/07/26/39%E3%80%8140%E3%80%81%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8C/

Copyright: 'Attribution-non-commercial-shared in the same way 4.0' Reprint please keep the original link and author.

×

Help us with donation