LeetCode-40-组合总和 II

5 篇文章 1 订阅
5 篇文章 0 订阅
该博客主要探讨了如何使用回溯算法解决组合总和II的问题,强调了有序数组在剪枝中的作用,避免重复元素的策略,并提供了详细的解题代码。通过排序数组,利用回溯法在搜索过程中进行剪枝,确保每个数字在组合中仅使用一次。此外,还解释了如何通过避免在同一层级出现相同元素来消除重复组合。
摘要由CSDN通过智能技术生成

题目

<中等> 组合总和 II
来源:LeetCode.

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

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

注意:解集不能包含重复的组合。 

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]
示例 2:
输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]

提示:

  • 1 < = c a n d i d a t e s . l e n g t h < = 100 1 <= candidates.length <= 100 1<=candidates.length<=100
  • 1 < = c a n d i d a t e s [ i ] < = 50 1 <= candidates[i] <= 50 1<=candidates[i]<=50
  • 1 < = t a r g e t < = 30 1 <= target <= 30 1<=target<=30

接下来看一下解题思路:
参考 回溯算法 + 剪枝

思路:回溯:

    与 组合之和 的差别在于:

  • 组合之和candidates 中的数字可以无限制重复被选取;
  • 本题: candidates 中的每个数字在每个组合中只能使用一次。
如何去掉重复的元素

在这里插入图片描述
在这里插入图片描述
    数组 candidates 有序,也是 回溯 过程中实现「剪枝」的前提。
    将数组先排序的思路来自于这个问题:去掉一个数组中重复的元素。很容易想到的方案是:先对数组 升序 排序,重复的元素一定不是排好序以后相同的连续数组区域的第 1 1 1 个元素。
    也就是说,剪枝发生在:同一层数值相同的结点第 2 2 2 3 3 3 … 个结点,因为数值相同的第 1 1 1 个结点已经搜索出了包含了这个数值的全部结果,同一层的其它结点,候选数的个数更少,搜索出的结果一定不会比第 1 1 1 个结点更多,并且是第 1 1 1 个结点的子集。

class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<>();
        List<Integer> combine = new ArrayList<>();
        if (candidates.length <= 0) {
            return result;
        }
        // 排序
        Arrays.sort(candidates);
        dfs(candidates, target, result, combine, 0);
        return result;
    }
    void dfs(int[] candidates, int target, List<List<Integer>> result, List<Integer> combine, int begin) {
        if (target == 0) {
            result.add(new ArrayList<>(combine));
            return;
        }

        for (int i = begin; i < candidates.length; ++i) {
            // 大剪枝:减去 candidates[i] 小于 0,减去后面的 candidates[i + 1]、candidates[i + 2] 肯定也小于 0,因此用 break
            if (target - candidates[i] < 0) {
                break;
            }
			// 小剪枝:同一层相同数值的结点,从第 2 个开始,候选数更少,结果一定发生重复,因此跳过,用 continue
            if (i > begin && candidates[i] == candidates[i - 1]) {
                continue;
            }

            combine.add(candidates[i]);
            // 因为元素不可以重复使用,这里递归传递下去的是 i + 1 而不是 i
            dfs(candidates, target - candidates[i], result, combine, i + 1);
            combine.remove(combine.size() - 1);
        }
    }
}
避免重复的思想:

    可以让同一层级,不出现相同的元素,但是父子结构可以出现相同的结构。同一层级相同的元素,表示同一个元素多次使用,父子结构相同的元素,表示同等的不同元素。
    避免重复的是这条语句:

i > begin && candidates[i] == candidates[i - 1]
  • 在一个for循环中,所有被遍历到的数都是属于一个层级的。我们要让一个层级中,
    必须出现且只出现一个2,那么就放过第一个出现重复的2,但不放过后面出现的2。
    第一个出现的2的特点就是 i == begin. 第二个出现的 2 特点是 i> begin.
  • 首先 c a n d i d a t e s [ i ] = = c a n d i d a t e s [ i − 1 ] candidates[i] == candidates[i - 1] candidates[i]==candidates[i1] 是用于判定当前元素是否和之前元素相同的语句。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值