40. Combination Sum 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] ]
- 题目难度:中等。
- 英文网址:40. Combination Sum II 。
- 中文网址:40. 组合总和 II 。
思路分析
求解关键:找到如何在结果集中去除重复的方法。
(1)与第 39 题的区别,第 39 题的数组没有重复数字,可以使用多次;第 40 题的数组有重复数字,只能使用一次,具体就体现在进行下一层递归的时候,起始的那个索引值是多少;
(2)很容易想到,应该先对给出的数组进行排序,排序的目的有两个:其一是,可以提前终止循环,其二是“在递归函数的调用中,同一深度的一个值只能使用一次”,这一处理也几乎成为了标准写法,或许刚刚开始接触的时候并不好理解,应该使用具体的例子画出图来理解,然后多做一些类似练习,理解代码为什么那样写。
参考解答
参考解答1
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
public class Solution {
    private List<List<Integer>> res = new ArrayList<>();
    private int[] candidates;
    private int len;
    // residue 表示剩余,这个值一开始等于 target,基于题目中说明的"所有数字(包括目标数)都是正整数"这个条件
    // residue 在递归遍历中,只会越来越小
    private void findCombinationSum2(int residue, int begin, Stack<Integer> stack) {
        if (residue == 0) {
            res.add(new ArrayList<>(stack));
            return;
        }
        for (int i = begin; i < len && residue - candidates[i] >= 0; i++) {
            // 这一步之所以能够生效,其前提是数组一定是排好序的,这样才能保证:
            // 在递归调用的统一深度(层)中,一个元素只使用一次。
            // 这一步剪枝操作基于 candidates 数组是排序数组的前提下
            if (i > begin && candidates[i] == candidates[i - 1]) {
                continue;
            }
            stack.add(candidates[i]);
            // 【关键】因为元素不可以重复使用,这里递归传递下去的是 i + 1 而不是 i
            findCombinationSum2(residue - candidates[i], i + 1, stack);
            stack.pop();
        }
    }
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        int len = candidates.length;
        if (len == 0) {
            return res;
        }
        this.len = len;
        // 先将数组排序,这一步很关键
        Arrays.sort(candidates);
        this.candidates = candidates;
        findCombinationSum2(target, 0, new Stack<>());
        return res;
    }
    public static void main(String[] args) {
        int[] candidates = {2, 5, 2, 1, 2};
        int target = 5;
        Solution solution = new Solution();
        List<List<Integer>> combinationSum2 = solution.combinationSum2(candidates, target);
        System.out.println(combinationSum2);
    }
}