【必拿下】回溯法解决LeetCode40组合总和II

链接: 题目链接


题目:
给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。


不能包含重复的组合(意思可以通过下面的例子了解一下):
输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]


相信聪明的你发现了,其实就元素在一个组合之间是可以重复的(前提是candidates里面有多个值相同的元素),而组合之间不能相同.
那么如何去重呢?
其实去重也就是使用过的元素不能再选取.
这里就要狠狠地夸一下carl哥的同一树层上不能选取和同一树枝上不能选取.
要了解同一树层或同一树枝的概念,还要从把回溯当做N叉树来看待.
图片: Alt如上所示:回溯的for循环里面中代表的是同一树层,而递归代表同一枝干.现在大家概念应该很清楚了.
而我们这一题所对应的就是同一树层上相同值不能重复选取.
其实参考上面的[1,1,6]和[1,7]大家就可以发现了,毕竟没有出现两个[1,7]嘛
那么(一些细节代码说明了)代码1如下:

class Solution {
   List<List<Integer>> res;
    List<Integer> path;
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        //题目速读:
        //组合问题,res中的组合不能重复,同时每个数字再每个组合中只能使用一次
        //对应出招:
        //每个数字每个组合中只能使用一次:设置startIndex来限制for里面对candidates遍历的范围
        //不能有重复的组合:弄一个boolean数组or直接借用startIndex(这里直接借用的是startIndex)
        res=new ArrayList<>();
        path=new ArrayList<>();
        //为了剪枝,将candidates排序
        //也同时让我们能够更好地去重
        Arrays.sort(candidates);
        backTracking(candidates,target,0);
        return res;
    }
    private void backTracking(int[] candidates, int target,int startIndex){
        if(target==0){
            res.add(new ArrayList<>(path));
            return;
        }Integer w;
        for (int i = startIndex; i < candidates.length; i++) {
            //过滤掉N叉树同层的值相同的元素
            if(i>0&&candidates[i-1]==candidates[i]&&i!=startIndex){
                //和我们使用一个Boolean数组一样的效果,核心就在于
                //我们排了序,相同的元素肯定是相邻的,所以i!=startIndex就可以完美的处理掉同一层的相同值
                //而如果是同一个枝干的则会是i==startIndex
                continue;
            }
            w=candidates[i];
            if(target-candidates[i]<0)return;
            else{
            path.add(w);
            backTracking(candidates,target-candidates[i],i+1);
            path.remove(w);
            }
            
        }

    }
}

代码2如下:

class Solution {
   List<List<Integer>> res;
    List<Integer> path;
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        //不能包含重复的组合,每个数字只能使用一次
        //参数:candidates,target,startIndex,返回值:void
        //退出条件:当target==0,target小于0用来剪枝
        //确定单层递归的逻辑
        res=new ArrayList<>();
        path=new ArrayList<>();
        Arrays.sort(candidates);
        boolean[]used=new boolean[candidates.length];
        Arrays.fill(used,false);
        backTracking(candidates,target,0,used);
        return res;
    }
    private void backTracking(int[] candidates,int target,int startIndex,boolean[]used){
        if(target==0){
            res.add(new ArrayList<>(path));
        }
        for (int i = startIndex; i <candidates.length ; i++) {
            if(i>0&&candidates[i]==candidates[i-1]&&used[i-1]==false)continue;
            Integer w=candidates[i];
            if(target-w<0){
                return;
            }
            path.add(w);
            used[i]=true;
            backTracking(candidates,target-w,i+1,used);
            path.remove(w);
            used[i]=false;
        }
    }
}

绝大多数参考自代码随想录

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值