[ 热题 HOT 100]---39. 组合总和➕40. 组合总和 II -- 回溯法+剪枝

1 题目描述

    1. 组合总和

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

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

说明:

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

    1. 组合总和 II

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

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

说明:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combination-sum-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

2 解题思路

    1. 组合总和
  • 方法:回溯法+剪枝
    思路:根据示例 1:输入: candidates = [2,3,6,7],target = 7。

候选数组里有 2 ,如果找到了 7 - 2 = 5 的所有组合,再在之前加上 2 ,就是 7 的所有组合;
同理考虑 3,如果找到了 7 - 3 = 4 的所有组合,再在之前加上 3 ,就是 7 的所有组合,依次这样找下去;
上面的思路就可以画成下面的树形图。
[ 热题 HOT 100]---39. 组合总和➕40. 组合总和 II -- 回溯法+剪枝_第1张图片
说明:

蓝色结点表示:尝试找到组合之和为该数的所有组合,怎么找呢?逐个减掉候选数组中的元素即可

  • 以 target = 7 为根结点,每一个分支做减法;
  • 减到 0 或者负数的时候,到了叶子结点;
  • 减到 0 的时候结算,这里 “结算” 的意思是添加到结果集
  • 从根结点到叶子结点(必须为 0)的路径,就是题目要我们找的一个组合。

代码来自评论区,感觉大佬的代码还是有待于学习,后面会认真学习观看哦

作者:liweiwei1419
链接:https://leetcode-cn.com/problems/combination-sum/solution/hui-su-suan-fa-jian-zhi-python-dai-ma-java-dai-m-2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    1. 组合总和 II
      [ 热题 HOT 100]---39. 组合总和➕40. 组合总和 II -- 回溯法+剪枝_第2张图片
  • 方法:回溯法+剪枝

[ 热题 HOT 100]---39. 组合总和➕40. 组合总和 II -- 回溯法+剪枝_第3张图片

[ 热题 HOT 100]---39. 组合总和➕40. 组合总和 II -- 回溯法+剪枝_第4张图片

3 解决代码

    1. 组合总和
  • 方法:回溯法+剪枝《不排序Java代码》–评论区
class Solution {
    //全局变量,格式根据结果的需求来确定
    List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        if(candidates == null){
            return res;
        }
        dfs(target, 0, new Stack<Integer>(), candidates);
        return res;

    }
    //dfs函数,要注意传的参数
    public void dfs(int target, int index, Stack<Integer> stack, int[] candidates){
        //如果等于0的话,就是我们要的结果,将stack添加add到结果集res后面
        if(target == 0){
            res.add(new ArrayList<> (stack));
            return;
        }
        //主要内容,遍历,index为本分支上一节点减数的下标
        for(int i = index; i < candidates.length; i++){
            if(candidates[i] <= target){
                //小于的话就入栈,大的就不要了,有剪枝效果
                stack.push(candidates[i]);
                //目标值减去元素值
                dfs(target - candidates[i], i, stack, candidates);
                //操作的最后,每次回溯都要将上次最后加入的元素删掉
                stack.pop();
            }
        }
    }
}
  • 方法:回溯法+剪枝《排序Java代码》–
public class Solution {

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        int len = candidates.length;

        // 排序是为了提前终止搜索
        Arrays.sort(candidates);

        dfs(candidates, len, target, 0, new ArrayDeque<>(), res);
        return res;
    }

    /**
     * @param candidates 数组输入
     * @param len        输入数组的长度,冗余变量
     * @param residue    剩余数值
     * @param begin      本轮搜索的起点下标
     * @param path       从根结点到任意结点的路径
     * @param res        结果集变量
     */
    private void dfs(int[] candidates,
                     int len,
                     int residue,
                     int begin,
                     Deque<Integer> path,
                     List<List<Integer>> res) {
        if (residue == 0) {
            // 由于 path 全局只使用一份,到叶子结点的时候需要做一个拷贝
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = begin; i < len; i++) {

            // 在数组有序的前提下,剪枝
            if (residue - candidates[i] < 0) {
                break;
            }

            path.addLast(candidates[i]);
            dfs(candidates, len, residue - candidates[i], i, path, res);
            path.removeLast();

        }
    }
}
    1. 组合总和 II
  • 方法:回溯法+剪枝《排序Java代码》–
class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        int len = candidates.length;
        List<List<Integer>> res = new ArrayList<>();
        if(len == 0){
            return res;
        }
        //排序,方便去重,这一步很重要
        Arrays.sort(candidates);
        Deque<Integer> path = new ArrayDeque<>(len);
        dfs(candidates, len, 0, target, path, res);
        return res;

    }
    public void dfs(int[] candidates, int len, int begin, int residue, Deque<Integer> path, List<List<Integer>> res){
        //如果剩下的值为0了,证明已经到达叶子结点,加入结果集中即可
        if (residue == 0){
            res.add(new ArrayList<>(path));
            return;
        }
        //for循环,去访问节点
        for(int i = begin; i < len; i++){
            //大剪枝,结果
            if(residue - candidates[i] < 0){
                break;
            }
            //小剪枝
            if(i > begin && candidates[i] == candidates[i - 1]){
                continue;
            }
            path.addLast(candidates[i]);
            // 因为元素不可以重复使用,这里递归传递下去的是 i + 1 而不是 i
            dfs(candidates, len, i + 1, residue - candidates[i], path, res);
            //回溯法的话要记得删除上次加入的节点
            path.removeLast();
        }    
    }
}

你可能感兴趣的:(力扣LeetCode)