递归与回溯7:LeetCode40. 组合总和 II(不可重复)

Posted 纵横千里,捭阖四方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了递归与回溯7:LeetCode40. 组合总和 II(不可重复)相关的知识,希望对你有一定的参考价值。

本题与LeetCode39的区别就是这里candidates 中的每个元素在每个组合中只能使用 一次 。但是candidats数组里的元素是可以重复的。有点绕,我们看一下例子:

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

题目说candidates是一个数组,里面有两个1,因此输出的结果里可以出现1,1的情况。严格来说这里说candidates是一个集合,是不严谨的,因为集合里元素是不能重复的。我们理解就好。

这个题与39题都相同的一点就是解集不能包含重复的组合。例如,不能出现一个解为1,2,5,另一个为5,2,1的情况。

这个题的关键在于如何去重,就是使用过的元素不能重复选取。 这里补充一点,“使用过”在回溯的树形结构上是有两个维度的,一个维度是同一树枝上使用过,一个维度是同一树层上使用过。这两个层面上的“使用过” 很明显是不一样的。

那么,我们是要同一树层上使用过,还是同一树枝上使用过呢?看一下题目,元素在同一个组合内是可以重复的,怎么重复都没事,但两个组合不能相同。所以我们要去重的是同一树层上的“使用过”,同一树枝上的都是一个组合里的元素,不用去重。

这里为了好处理,我们最好先将所有元素排序一下,假如candidates = [1, 1, 2], target = 3,Carl画的这个图非常形象 ,我们直接拿过来看一看:

 可以看到图中,每个节点都多加了used数组,这个就是去重的关键。要去重的是“同一树层上的使用过”,如果判断同一树层上元素(相同的元素)是否使用过了呢。

如果candidates[i] == candidates[i - 1] 并且 used[i - 1] == false,就说明前一个树枝,使用了candidates[i - 1],也就是说同一树层使用过candidates[i - 1]。此时for循环里就应该做continue的操作。如下图所示:(引自carl博客)

图中将used的变化用橘黄色标注上,可以看出在candidates[i] == candidates[i - 1]相同的情况下:

  •  used[i - 1] == true,说明同一树支candidates[i - 1]使用
  •  used[i - 1] == false,说明同一树层candidates[i - 1]使用过

 其他部分的处理方式与39题基本一致,完整实现如下:

class Solution 
    List<List<Integer>> lists = new ArrayList<>();
    Deque<Integer> deque = new LinkedList<>();
    int sum = 0;

    public List<List<Integer>> combinationSum2(int[] candidates, int target) 
        //为了好处理重复,所以先排序,这里可以直接调基础方法
        Arrays.sort(candidates);
        //加标志数组,用来辅助判断同层节点是否已经遍历
        boolean[] used = new boolean[candidates.length];
        backTracking(candidates, target, 0, used);
        return lists;
    

    public void backTracking(int[] arr, int target, int index, boolean[] used) 
        if (sum == target) 
            lists.add(new ArrayList(deque));
            return;
        
        for (int i = index; i < arr.length && arr[i] + sum <= target; i++) 
            //出现重复节点,同层的第一个节点已经被访问过,所以直接跳过
            if (i > 0 && arr[i] == arr[i - 1] && !used[i - 1]) 
                continue;
            
            used[i] = true;
            sum += arr[i];
            deque.push(arr[i]);
            //每个节点仅能选择一次,所以从下一位开始
            backTracking(arr, target, i + 1, used);
            int temp = deque.pop();
            used[i] = false;
            sum -= temp;
        
    

 

以上是关于递归与回溯7:LeetCode40. 组合总和 II(不可重复)的主要内容,如果未能解决你的问题,请参考以下文章

递归与回溯6:LeetCode39组合总和(可重复使用)

leetcode 39. 组合总和---回溯篇2

leetcode 40. 组合总和 II---回溯篇3

40. 组合总和 II-LeetCode

LeetCode40. 组合总和 II

216. 组合总和 III-----回溯篇4