LeetCode(39):组合总和 Combination Sum(Java)

234 篇文章 1 订阅
177 篇文章 0 订阅

2019.10.10 #程序员笔试必备# LeetCode 从零单刷个人笔记整理(持续更新)

github:https://github.com/ChopinXBP/LeetCode-Babel

今天正式进入LeetCode200题俱乐部~

从前往后思考,可以看出,每一个target组合都可以由小于target的数字i的组合加上target-i构成,可以用动态规划的思路;从后往前思考,可以看出,target每和candidates中的一个数字i做差,target-i又是一个新的子问题,可以走递归回溯的思路。

1.动态规划:dp[i]代表和为i的所有组合数,dp[i]由dp[j] (j<i) 的所有组合+j构成。遍历的重复组合比较多,需要专门借助HashSet进行去重。

2.剪枝回溯:在正常的回溯思路上加上剪枝:1.递归遍历范围从当前数字开始,保证去重,避免重复计算;2.差值小于0的分支提前舍弃。


传送门:组合总和

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.

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

所有数字(包括 target)都是正整数。

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

示例 1:

输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
  [7],
  [2,2,3]
]

示例 2:
输入: candidates = [2,3,5], target = 8,
所求解集为:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
]



import java.util.*;

/**
 *
 * 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.
 * 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
 * candidates 中的数字可以无限制重复被选取。
 *
 */

public class CombinationSum {
    //动态规划:自前向后
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        HashSet<Integer> set = new HashSet<>();
        for(int num : candidates){
            if(num <= target){
                set.add(num);
            }
        }

        //dp[i]代表和为i的所有组合数,dp[i]由dp[j](j<i)的所有组合+j构成
        ArrayList<List<Integer>>[] dp = new ArrayList[target + 1];
        dp[0] = new ArrayList<>();
        dp[0].add(new ArrayList<>());
        for(int i = 1; i <= target; i++){
            dp[i] = new ArrayList<>();
            for(int j = 0; j < i; j++){
                if(set.contains(i - j)){
                    for(List<Integer> list : dp[j]){
                        ArrayList<Integer> newlist = new ArrayList<>();
                        newlist.addAll(list);
                        newlist.add(i - j);
                        dp[i].add(newlist);
                    }
                }
            }
        }

        HashSet<List<Integer>> temp = new HashSet<>();
        for(List<Integer> list : dp[target]){
            Collections.sort(list);
            temp.add(list);
        }
        ArrayList<List<Integer>> result = new ArrayList<>();
        result.addAll(temp);
        return result;
    }

    //剪枝回溯法
    public List<List<Integer>> combinationSum2(int[] candidates, int target){
        Arrays.sort(candidates);
        List<List<Integer>> result = new ArrayList<>();
        Solution(target, 0, new LinkedList<>(), candidates, target, result);
        return result;
    }

    public void Solution(int curSum, int startIdx, LinkedList<Integer> list, int[] candidates, int target, List<List<Integer>> result){
        if(curSum == 0){
            result.add(new LinkedList<>(list));
            return;
        }
        //剪枝:1.递归遍历范围从当前数字开始,保证去重;2.差值小于0的分支提前舍弃
        for(int i = startIdx; i < candidates.length && curSum - candidates[i] >= 0; i++){
            list.add(candidates[i]);
            Solution(curSum - candidates[i], i, list, candidates, target, result);
            list.pollLast();
        }
    }
}




#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值