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一秒钟。留个言点个赞呗,谢谢你#