leetcode题解-40. Combination Sum II && 216. Combination Sum III

40,题目:

Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.

Each number in C may only be used once in the combination.

Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.
For example, given candidate set [10, 1, 2, 7, 6, 1, 5] and target 8, 
A solution set is: 
[
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
]

本题与上一题Combination Sum基本上是一样的,不同的是这里的数组可能包含重复的数字,但是在求和的过程中不能重复使用数组里的元素。所以首先我们使用上道题目的思路,直接使用回溯法,但是为了解决数组元素不能重复使用的问题,我们需要做一些小的修改,代码如下所示,有两证改进办法:

    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(candidates);
        dfs(candidates, res, new ArrayList<>(), target, 0);
        return res;
    }

    public void dfs(int[] candidates, List<List<Integer>> res, List<Integer> tmp, int target, int idx){
        if(target<0)
            return;
        else if(target == 0) {
        //第一种方法,在将list添加到res之前判断re中是否已经包含,比较直观
            if(!res.contains(tmp))
                res.add(new ArrayList<>(tmp));
        }
        else{
            for(int i=idx; i<candidates.length; i++) {
                //第二种方法,在遍历的过程中加一个判断语句,看前后元素是否相等,如果想等的话则跳过
                //if(i > idx && candidates[i] == candidates[i-1]) continue;
                tmp.add(candidates[i]);
                //此外,为了不使用重复元素,在递归调用时直接将idx+1即可;
                dfs(candidates, res, tmp, target-candidates[i], i+1);
                tmp.remove(tmp.size()-1);
            }
        }
    }

此外,还有一种改进的方法,这种方法在循环过程中加入了判断语句,可以省去一些不必要的循环。如下所示:

    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        List<List<Integer>> results = new ArrayList<>();
        calcCombinationSum2(candidates, 0, new int[candidates.length], 0, target, results);
        return results;
    }

    private void calcCombinationSum2(int[] candidates, int cindex, int[] list, int lindex, int target, List<List<Integer>> results) {
        if (target == 0) {
            List<Integer> result = new ArrayList<>();
            for (int i = 0; i < lindex; i++) {
                result.add(list[i]);
            }
            results.add(result);
            return;
        }

        int prev = 0;
        for (int i = cindex; i < candidates.length; i++) {
            if (candidates[i] != prev) {
                if (target - candidates[i] < 0) {
                    break;
                }

                list[lindex] = candidates[i];
                calcCombinationSum2(candidates, i + 1, list, lindex + 1, target - candidates[i], results);
                prev = candidates[i];
            }
        }
    }

216, Combination Sum III,题目:

Find all possible combinations of k numbers that add up to a number n, given that only numbers from 1 to 9 can be used and each combination should be a unique set of numbers.


Example 1:

Input: k = 3, n = 7

Output:

[[1,2,4]]

Example 2:

Input: k = 3, n = 9

Output:

[[1,2,6], [1,3,5], [2,3,4]]

这道题目相比前两道就简单了,因为只是求1-9之间和为n的k个数,且不可重复使用。首先仍然使用上述思路,使用回溯法,不同的是,这次既要求元素不可重复使用,又规定了k个数求和的限定条件。所以我们按照下述方法进行修改即可:

    public static List<List<Integer>> combinationSum3(int k, int n) {
        List<List<Integer>> res = new ArrayList<>();
        int[] nums = {1,2,3,4,5,6,7,8,9};
        dfs(nums, res, new ArrayList<>(), n, k, 0);
        return res;
    }

    public static void dfs(int[] nums, List<List<Integer>> res, List<Integer> tmp, int target, int k, int index){
    //加上k个数的限制,每加一个数,k减1
        if(k < 0 || target < 0)
            return;
        else if(k == 0 && target == 0){
            res.add(new ArrayList<>(tmp));
            return;
        }else{
            for(int i=index; i<nums.length; i++) {
                tmp.add(nums[i]);
                dfs(nums, res, tmp, target - nums[i], k - 1, i + 1);
                tmp.remove(tmp.size()-1);
            }
        }
    }

此外我们还可以不用回溯法,直接使用循环的方式求解,代码入下:

public List<List<Integer>> combinationSum3(int k, int n) {
    return helper2(k, n, 9);
}

private List<List<Integer>> helper2(int k, int n, int end) {
    List<List<Integer>> collection = new ArrayList<List<Integer>>();
    //用于记录一次成功的结果
    int[] sum = new int[k];
    while (true) {
        //首先,将1-9分配到sum中,便利所有可能组合,要求是k个数和为n
        while (k > 0 && n > 0 && end > 0) {
            end = end < n ? end : n;
            sum[(k--) - 1] = end;
            n -= end--;
        }
        //如果上面的循环满足了k==0且n==0,则将sum记录到res中
        if (k == 0 && n == 0) {
            List<Integer> list = new ArrayList<Integer>();
            for (int i : sum)
                list.add(i);
            collection.add(list);
        }
        //如果sum不满足或者已经记录,则k++,即给sum重新安排数字
        if (++k > sum.length)
            break;
        //将end和n分别更新
        end = sum[k - 1];
        n += end--;
    }
    return collection;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值