Leetcode每日一题——40. 组合总和 II(回溯)

本文解析了如何通过回溯法解决40.组合总和II问题,重点介绍了利用used数组进行去重的方法,以及如何将问题抽象成树形结构进行递归操作。通过实例演示了如何在递归过程中实现终止条件、单层逻辑和状态初始化,确保组合的唯一性和正确性。
摘要由CSDN通过智能技术生成

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

这道题涉及回溯和去重(因为给定的candidates数组中可以有重复的元素),难点是去重。可以先把题目抽象成树形结构(回溯题抽象成树形结构方便理解):

40.组合总和II

如上图(图片摘自《代码随想录》)

我们要做的是利用上used数组对同一树层进行去重以达到不同组合数字不同的效果;利用used数组也可以对同一树枝进行去重达到同一组合不存在重复数字的效果。具体判断条件在下面的代码有

由于是递归,可以参考递归四部曲:

递归作用:从cadidates中的下标为startIndex开始取数,直到取到终止条件

递归参数:candidates和target意义同题意,startIndex表示这一层取数的起始下标

终止条件:sum > target或sum==target

单层逻辑:在这一层从startIndex开始取一个数,取完这个数后再进入下一层,下一层从这一层的取数索引加1开始取,直到取到终止条件。这里定义操作前的状态为回溯的初始状态

代码如下:

class Solution {

    List<List<Integer>> result = null;//记录所有的组合
    List<Integer> path = null;//记录目前的组合
    int sum = 0;
    boolean[] used = null;//去重用的数组

    void combinationSum1(int[] candidates, int target, int startIndex) {
        //递归作用:从cadidates中的下标为startIndex开始取数,直到取到终止条件
        //递归参数:candidates和target意义同题意,startIndex表示这一层取数的起始下标
        //终止条件:sum > target或sum==target
        //单层逻辑:在这一层从startIndex开始取一个数,取完这个数后再进入下一层,下一层从这一层的取数索引加1开始取,直到取到终止条件。这里定义操作前的状态为回溯的初始状态

        if(sum > target) return;//由于candidates[i]>0,在这一层sum>target后,那么在后面的层sum都将大于target;
        if(sum == target){//符合条件
            result.add(new ArrayList(path));//因为add添加的是引用,所以得重新new一个对象,如果直接add(path),那么add的将是同一个引用,对应同一个对象
            return;
        }
        for(int i = startIndex; i < candidates.length; i++){
            if(i > 0 && candidates[i] == candidates [i - 1] && used[i - 1] == false)//如果是used[i - 1] == false就是对树枝去重,效果相当于每个组合中不能出现相同的元素。其实这道题去掉used数组也可以,i > 0 && candidates[i] == candidates [i - 1]就够了,只不过used数组在对树结构的题目的去重有普适性,值得我们去研究
            continue;
        sum += candidates[i];
        path.add(candidates[i]);
        used[i] = true;//表示这个元素被取了
        combinationSum1(candidates, target, i + 1);//下一层从i+1开始取,因为candidates 中的每个数字在每个组合中只能使用一次
        sum -= candidates[i];
        path.remove(path.size() - 1);
        used[i] = false;//回溯,将各个状态量初始化
        }
    }

    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        //先排序方便去重,用used数组去重,抽象成树结构后是对同一树层去重,即同一层不能重复取到一个数字
        used = new boolean[candidates.length];//使用new boolean[]初始化,默认值为false
        result = new ArrayList<>();
        path = new ArrayList<>();
        Arrays.sort(candidates);//排序方便去重
        combinationSum1(candidates, target, 0);
        return result;
    }
}

回溯算法的话记得操作完这一层后对各个状态量进行初始化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

翔空中,策人生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值