L39,组合总和 L40-回溯法

回溯法的应用

  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] ]

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new LinkedList<>();
        Arrays.sort(candidates);//先排序,生成的结果自然有序
        dfs(candidates, target, 0, 0, res, new LinkedList<>());
        return res;
    }
    void dfs(int[] nums, int target, int index, int sum, List<List<Integer>> res, List<Integer> list){
        //设计一个函数,就要确定它的变量,index, sum控制进度, res放结果,list放每次的结果,target作对比
        if(sum == target){//边界条件
            res.add(new LinkedList<>(list));
            return;
        }
        if(sum > target) return;//及时剪枝,没有这个,会爆栈,因为不一定正好等于target
        //每个元素可以重复多次,也可以不选,招商那题是每个元素都会选取,但是符号却又很多种可能,所以是对符号的可能情况进行了讨论
        //同理对应的边界条件也变化,不再是index == nums.length;

        //开头有很多种可能性,利用for循环
        int len = nums.length;
        if(index >= len) return;//其实这句不加也行,一般不会越界
        for(int i = index; i < len; i++){//同时也控制了数组不会越界,与字符的全排列类似
            list.add(nums[i]);//注意这里都是i,不是index
            dfs(nums, target, i, sum + nums[i], res, list);//可以重复,所以仍以index = i;
            //注意sum是在函数里面变化的,如果是在外面,需要加一层回溯
            list.remove(list.size() - 1);
        }
        //这题同时要与全排列比较,全排列对应的for循环是从0-len,同时加了一个状态变量,保证每一个元素都被使用,同时边界条件为index== len, 而这里只需要和为给定的值即可

    }
}

2.对上述代码进行优化,改变剪枝的位置

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new LinkedList<>();
        Arrays.sort(candidates);//先排序,生成的结果自然有序
        
        dfs(candidates, target, 0, 0, res, new LinkedList<>());
        return res;
    }
    void dfs(int[] nums, int target, int index, int sum, List<List<Integer>> res, List<Integer> list){
        
        if(sum == target){//边界条件
            res.add(new LinkedList<>(list));
            return;
        }
        //if(sum > target) return;//及时剪枝
        
        int len = nums.length;
        for(int i = index; i < len; i++){
            if(sum + nums[i] > target) break;//此时效率会得到极大提高
            list.add(nums[i]);
            dfs(nums, target, i, sum + nums[i], res, list);
            list.remove(list.size() - 1);
        }

    }
}

3.换用减法,同样的思路

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new LinkedList<>();
        Arrays.sort(candidates);

        dfs(candidates, target, 0, target, res, new LinkedList<>());
        return res;
    }
    void dfs(int[] nums, int target, int index, int sum, List<List<Integer>> res, List<Integer> list){
        //此时sum表示剩余的和
        if(sum == 0){//边界条件
            res.add(new LinkedList<>(list));
            return;
        }

        int len = nums.length;
        for(int i = index; i < len; i++){
            if(nums[i] > sum) break;
            list.add(nums[i]);
            dfs(nums, target, i, sum - nums[i], res, list);
            list.remove(list.size() - 1);
        }
    }
}

其实可以少一个参数

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> ans = new ArrayList<>();
        Arrays.sort(candidates);
        dfs(candidates, 0, target, ans, new ArrayList<Integer>());
        return ans;
    }
    void dfs(int[] candidate, int index,  int target, List<List<Integer>> ans, ArrayList<Integer> list){
        if(0 == target){
            ans.add(new ArrayList<>(list));
            return;
        }
        if(0 > target) return;

        int len = candidate.length;
        if(index > len) return;
        for(int i = index; i < len; i++){
            if( candidate[i] > target) break;
            list.add(candidate[i]);
            dfs(candidate, i,  target - candidate[i], ans, list);
            list.remove(list.size() - 1);
        }
    }
}

L40

1.数组元素可重复
2.结果中每个数字只能使用一次

  1. 组合总和 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] ]

注意每个数字智能使用一次,回溯的时候需要注意是i+1,不是i
1.做减法

class Solution {
    
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> res = new LinkedList<>();
        Arrays.sort(candidates);

        dfs(candidates, target, 0, target, res, new LinkedList<>());
        return res;
    }
    void dfs(int[] nums, int target, int index, int sum, List<List<Integer>> res, List<Integer> list){
        //此时sum表示剩余的和
        if(sum == 0){//边界条件
            res.add(new LinkedList<>(list));
            return;
        }

        int len = nums.length;
        for(int i = index; i < len; i++){
            if(nums[i] > sum) break;
            //if(i > index && nums[i] == nums[i - 1]) break;//去重,注意不是跳出
            //注意这里与全排列的区别,那个是i > 0,同时还有状态的判断
            if(i >index && nums[i] == nums[i - 1]) continue;
            list.add(nums[i]);
            dfs(nums, target, i + 1, sum - nums[i], res, list);//第二个区别,只能使用一次
            list.remove(list.size() - 1);
        }

    }
}

2.加法

class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> res = new LinkedList<>();
        Arrays.sort(candidates);//先排序,生成的结果自然有序

        dfs(candidates, target, 0, 0, res, new LinkedList<>());
        return res;
    }
    void dfs(int[] nums, int target, int index, int sum, List<List<Integer>> res, List<Integer> list){

        if(sum == target){//边界条件
            res.add(new LinkedList<>(list));
            return;
        }
        //if(sum > target) return;//及时剪枝

        int len = nums.length;
        for(int i = index; i < len; i++){
            if(sum + nums[i] > target) break;//此时效率会得到极大提高
            if(i > index && nums[i] == nums[i - 1]) continue;
            list.add(nums[i]);
            dfs(nums, target, i+1, sum + nums[i], res, list);
            list.remove(list.size() - 1);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值