介绍
LeetCode300题小目标达成
这是一类问题,都可以用backtrack(回溯法)来求,这里放在一起比较一下异同。首先是Combination Sum I,问题:给定一个int数组,不包含重复数字,现在需要从里面可重复地取出一些数使得它们的和为target,返回所有不同的取法。这里可以从两个角度考虑:1.每次往arr里面添加数据,比如添加1,那么剩下的就是在数据集中找到和为target-1的所有可能,并保存数据,如果最后的目标小于0,那么返回,等于0则添加至list;2.同样的每次添加数据,剩下的起点则从添加的数据开始,比如1,直到最终结果=target,则添加至list中。这里为了防止出现保存重复的结果,每次都从数组当前位置往后遍历。(见代码I中变量start)。
然后是Combination Sum II。和第一题不同的是,数组包含重复数字,但是不能重复取。方法大致不变,仍然是回溯法,但是这里每次回溯的起始位置就需要变成start+1了。还有一点不同,这里包含重复数据,为了防止记录重复的结果,这里我们把数组先从小到大排序,并且用HashSet去除重复结果。代码见下2。
最后是Combination Sum III。现在需要从1~9中选择k个不同的数字使得它们的和为n。这样一来,限定了每个可能结果的长度,即是k。不怕,我们在回溯中再添加一个变量,用于记录当前结果的长度,或者直接用当前结果arr的长度来和目标k比较,回溯的大致思路不变,只是每次回溯的判定和边界条件发生变化。代码见下3。
总结起来就是万变不离其宗,我们想要求f(target),那么我们就去尝试先试着添加一个元素a,那么剩下的结果就变成了求f(target-a),依次类推,直到最后为0,这样就算一次满足条件的组合。
还有一道Combination Sum IV,不过这个用DP就可以解,代码见下4。
Java代码
Combination Sum I代码:
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> list = new ArrayList();
dfs(list, new ArrayList<Integer>(), candidates,target,0,0);
return list;
}
public void dfs(List<List<Integer>> list, List<Integer> arr, int[] candidates, int target, int sum, int start){
if(sum >target) return;
if(sum==target){
list.add( new ArrayList<>(arr));
return;
}
else{
for(int i=start;i<candidates.length;i++){
arr.add(candidates[i]);
dfs(list,arr,candidates,target,sum+candidates[i],i);
arr.remove(arr.size()-1);
}
}
}
Combination Sum II:
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> list = new ArrayList();
Arrays.sort(candidates);
dfs(list, new ArrayList<Integer>(), candidates,target,0,0);
HashSet hs = new HashSet(list);
list.clear();
list.addAll(hs);
return list;
}
public void dfs(List<List<Integer>> list, List<Integer> arr, int[] candidates, int target, int sum, int start){
if(sum >target) return;
if(sum==target){
list.add( new ArrayList<>(arr));
return;
}
else{
for(int i=start;i<candidates.length;i++){
arr.add(candidates[i]);
dfs(list,arr,candidates,target,sum+candidates[i],i+1);
arr.remove(arr.size()-1);
}
}
}
Combination Sum III:
public List<List<Integer>> combinationSum3(int k, int n) {
List<List<Integer>> list = new ArrayList();
dfs(list, new ArrayList<Integer>(), n, 1, k);
return list;
}
public void dfs(List<List<Integer>> list, List<Integer> arr, int tar, int start, int num){
if(tar<0||arr.size()>num) return;
if(tar==0&&arr.size()==num){
list.add(new ArrayList<>(arr));
return;
}
else{
for(int i=start;i<=tar&&i<=9;i++){
arr.add(i);
dfs(list,arr,tar-i,i+1,num);
arr.remove(arr.size() - 1);
}
}
}
Combination Sum IV:
public int combinationSum4(int[] nums, int target) {
int dp[] = new int[target+1];
dp[0]=1;
for(int i=1;i<=target;i++){
for(int j=0;j<nums.length;j++){
if(i>=nums[j]) dp[i] += dp[i-nums[j]];
}
}
return dp[target];
}