真是好久没刷题,好久没写博客了,最怪的是最近也没忙什么,秋招在即,此篇仅记录刷题过程,为以后复习做准备
一、组合总和
这道题是个典型的DFS,通过选和不选两种状态进行深搜,由于一个数可以不限次数的选取,所以在递归中,下标无需加一
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<Integer> item = new ArrayList<>();
List<List<Integer>> ans = new ArrayList<List<Integer>>();
dfs(candidates,target,item,ans,0);
return ans;
}
public void dfs(int[] candidates, int target,List<Integer> item,List<List<Integer>> ans,int index){
if(index == candidates.length){
return ;
}
if(target == 0){
ans.add(new ArrayList(item));
return ;
}
//不选
dfs(candidates,target,item,ans,index+1);
//选
if(target-candidates[index] >= 0){
item.add(candidates[index]);
dfs(candidates,target-candidates[index],item,ans,index);
item.remove(item.size()-1);
}
}
}
二、组合总和 II
这道题比上题多加了个限制,就是每一个数只能用一次,并且不能包含重复的集合
(这道题供我们选择数的数组里面会有重复数字,而上一道题无重复元素,需要我们解决重复集合的问题)
1、每一个数用一次:这个只需要在每次递归时让index++就能解决,选取了当前数,就不能在选这个数了
2、不能包含重复的集合:这个我们可以先将数组排好序,在每一层循环中只能选择一种数,例如:1 1 1 2 2 这组数,在第一层循环中如果选择了第一个1,那么就不能选择第二个1,下次选择满足条件的话就只能选择2,不然就会造成重复的问题
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<Integer> item = new ArrayList<>();
List<List<Integer>> ans = new ArrayList<List<Integer>>();
Arrays.sort(candidates);
dfs(candidates,target,item,ans,0);
return ans;
}
public void dfs(int[] candidates, int target,List<Integer> item,List<List<Integer>> ans,int index){
if(target == 0){
ans.add(new ArrayList(item));
return ;
}
for(int i = index ; i <candidates.length ; i++){
if(i!=index&&candidates[i]==candidates[i-1]){//防止同一层循环宣选到一样的数
continue;
}
if(target - candidates[i] >= 0){
item.add(candidates[i]);
dfs(candidates,target-candidates[i],item,ans,i+1);
item.remove(item.size()-1);
}
}
}
}
三、组合总数III
这道题是让求我们从1-9, 9个数字中选取n个数组成大小为k的各个解(每个数只能用一次)
与上面的第一题组合总和很像,都是从特定的数组里选取数字,区别只在于:每个数只能使用一次
我们可以调节递归中,循环的起始值,让下一个数的大小肯定大于上一个数的大小,这样我们就能实现每个数用一次的要求。具体代码如下
class Solution {
private List<List<Integer>> ans = new ArrayList<List<Integer>>();
public List<List<Integer>> combinationSum3(int k, int n) {
List<Integer> item = new ArrayList<>();
dfs(item,k,n,1);
return ans;
}
public void dfs(List<Integer> item,int k,int n,int start){
if(item.size()>k || n<0) return ;
if(n == 0&&item.size()==k){
ans.add(new ArrayList(item));
return ;
}
//把起始值改为start
for(int i=start ; i<=9 ;i++){
{
item.add(i);
dfs(item,k,n-i,i+1);
item.remove(item.size()-1);
}
}
}
}
四、组合总和 Ⅳ
这道题要求我们从给定数组中选取数字,之和得等于target,返回能满足要求的组合总数,由于可以无限制选取(没有一个数只能选一次的限制,并且这道题的target上限有点大:1000)如果我们单纯的用dfs,肯定会超时,就如下代码:
class Solution {
private int ans = 0;
public int combinationSum4(int[] nums, int target) {
dfs(nums,target);
return ans;
}
public void dfs(int[] nums,int target){
if(target<0){
return;
}
if(target == 0){
ans++;
return;
}
for(int i = 0 ; i<nums.length;i++){
target-=nums[i];
dfs(nums,target);
target+=nums[i];
}
}
}
我们此时可以用动态规划解决此类问题:
比如 nums = [1,2,3], target = 4,组成4的总数肯定由组成(4-1)的总数和(4-2)的总数和(4-3)的总数组成;也就是组成3的总数和组成2的总数和组成1的总数
因为组成3的总数里面,加一个1就是4;组成2的总数里面,加一个2就是4;组成1的总数里面,加一个3就是4
dp[1] =dp[1-1]= 1
dp[2] =dp[2-1] + dp[2-2] = 1+1 = 2
dp[3] = dp[3-1] + dp[3-2] +dp[3-3]= 1 + 2 +1= 4
dp[4] = dp[4-1] + dp[4-2] + dp[4-3] + dp[4-4] = 1+2+4=7
正确代码如下:
class Solution {
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 num : nums){
if(i>=num){
dp[i] += dp[i-num];
}
}
}
return dp[target];
}
}