Leetcode刷题记录(数组)组合总和、组合总和 II、组合总数III、组合总和 Ⅳ

本文解析了四个经典的组合问题,包括组合总和I、II、III及组合总和IV,详细介绍了每种问题的特点及对应的解决方案,如DFS深度优先搜索算法和动态规划方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

真是好久没刷题,好久没写博客了,最怪的是最近也没忙什么,秋招在即,此篇仅记录刷题过程,为以后复习做准备
一、组合总和
这道题是个典型的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];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值