39. Combination Sum

本文详细解析了LeetCode上的组合总和问题,通过回溯算法和动态规划两种方法,探讨了如何寻找候选数集合中所有可能的组合,使这些组合的和等于目标数。文章对比了两种算法的实现思路,并提供了清晰的代码示例。

题目

Given a set of candidate numbers (candidates) (without duplicates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.

The same repeated number may be chosen from candidates unlimited number of times.

Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.

我的想法

这个题目必须枚举才能求解,因此,可以用backtrack。不过每次用backtrack都心惊胆战的,生怕写错。以后需要把常用的算法整理出自己的模版,隔两天就练一练

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> ls = new ArrayList<>();
        backtrack(candidates, new ArrayList<Integer>(), ls, 0, 0, target);
        return ls;
    }

    private void backtrack(int[] candidates, List<Integer> temp, List<List<Integer>> ls, int start, int count, int target) {
        if(count == target) {
            ls.add(new ArrayList<Integer>(temp));
            return;
        }
        for(int i = start; i < candidates.length; i++) {
        	//因为输入是有序数组,如果此时已经大于target,后面也无需遍历了
            if(count + candidates[i] > target) continue;
            
            temp.add(candidates[i]);
            count += candidates[i]; //temp每添加一个新的元素,计入count
            backtrack(candidates, temp, ls, i, count, target);
            count -= temp.remove(temp.size() - 1); //每删除一个元素,也需从count中减去其值
        }
    }
}

因为累加法是在之前的结果上做加法,所以必须把前面的结果都存下来,对于需要输出所有结果的题来说是可以的。但在这里只需要特定的结果,如果使用累加法还需要耗费额外的空间存储之前的结果

解答

leetcode soluton: DP
这个问题可以看作一个背包问题,存储对于每一个小于等于target的数的解答。
理解cands[j] <= l.get(0)
假设[2,3] target = 5,则
1: null
2: [2]
3: [3]
4: [2,2]
5: [2,3]
如果没有这个判断,当cands[j] = 3时,会重复输出[3,2]

public class Solution {
    public List<List<Integer>> combinationSum(int[] cands, int target) {
        Arrays.sort(cands);
        //最外层的list存储每一个小于target的数的解答
        List<List<List<Integer>>> dp = new ArrayList<>();

        for (int i = 1; i <= target; i++) { //每一个小于target的数的组合方式都要解出来
            List<List<Integer>> iList = new ArrayList();

            for (int j = 0; j < cands.length && cands[j] <= i; j++) {
                //如果当前的数cands[j]就是当前求解的i(不一定是目标target),
                //单独创建一列,不需要循环
                if (i == cands[j]) iList.add(Arrays.asList(cands[j]));

                //当前所需差值 = 当前求解数i - 被遍历到的cands[j],
                //因为数组从0开始存储需-1
                else for (List<Integer> l : dp.get(i-cands[j]-1)) {
                    //滤去重复项
                    if (cands[j] <= l.get(0)) {
                        List cl = new ArrayList<>();
                        cl.add(cands[j]); cl.addAll(l);
                        iList.add(cl);
                    }
                }
            }
            dp.add(iList);
        }
        return dp.get(target-1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值