[leetcode]39. Combination Sum

本文介绍了一种使用回溯法解决组合总和问题的方法,通过详细的代码示例展示了如何从给定的候选数字中找出所有可能的组合,这些组合的和等于目标值。文章提供了三种不同的回溯法实现方案,每种方案都详细解释了其工作原理和关键步骤。

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

这是一道典型的回溯法的子集数问题。(因为可以重复,类似与图的m色问题)
每一条路径可以选择的数是candidates的每一个元素。
INPUT:
[2,3,6,7]
[7]
x= [0,0,1,-1]表示解为[2,2,3]

Solution 1:回溯法1
class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
            
        int t=0;
        int sum=0;
        //先排序,这样方便后面去重
        Arrays.sort(candidates);
        //每一个可行解的长度小于等于target/candidates[0]
        //+1是因为 解是在路径上,t只是层数,层数比解个数要多一个。比如4/1=4 maxResLen=5。
        //t=0,1,2,3 ,4,得1+1+1+1,此时sum=4<=target,要在进入下一层t=5,此时满足t==maxResLen,才return
     
        int maxResLen=target/candidates[0]+1;
        //x保存解的下标
        int []x=new int[maxResLen];
        
        //解初始化为-1
        for(int i=0;i<maxResLen;i++)
            x[i]=-1;
        
        List<List<Integer>> res=new ArrayList<>();
       
        backtrack(t,candidates,target,sum,x,maxResLen,res);
        
       
        return res;
    }
    
    private void backtrack(int t,int[] candidates, int target,int sum,int []x,int maxResLen,List<List<Integer>> res){
        if(t==maxResLen)return;
        
        if(sum==target){
            
            List<Integer> oneRes=new ArrayList<Integer>();
            
            for(int i=0;i<maxResLen;i++){
                if(x[i]!=-1){
                    oneRes.add(candidates[x[i]]);
                }
            }
           
           //去重。因为前面已经排了序,所以只有非递减的解才是不重复的
           int flag=1;
           for(int i=1;i<oneRes.size();i++){
               if((int)oneRes.get(i)<(int)oneRes.get(i-1))flag=0;
           }
            
            if(flag==1)res.add(oneRes);
            
            return;
        }
        
        
        for(int i=0;i<candidates.length;i++){
           //试探性先选取这个数
           //sum先加上 
            x[t]=i;
            sum+=candidates[i];
            
            if(sum<=target){
                //如果满足条件,则进入下一层
                backtrack(t+1,candidates,target,sum,x,maxResLen,res);
                //准备回溯
                x[t]=-1;
                sum-=candidates[i];
            }
            else{ //不满足条件,就还原
                x[t]=-1;
                sum-=candidates[i];
            }
        }
    }
    
    
}
Solution 2:回溯法2
class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
            
        int t=0;
        int sum=0;
        Arrays.sort(candidates);
        int maxResLen=target/candidates[0]+1;
        int []x=new int[maxResLen];
        
        for(int i=0;i<maxResLen;i++)
            x[i]=-1;
        
        List<List<Integer>> res=new ArrayList<>();
       
        backtrack(t,candidates,target,sum,x,maxResLen,res);
        
       
        return res;
    }
    
    private boolean backtrack(int t,int[] candidates, int target,int sum,int []x,int maxResLen,List<List<Integer>> res){
        if(t==maxResLen-1)return false;
        
        
        
        for(int i=0;i<candidates.length;i++){//指 2 3 6 7
            

            
            if(sum+candidates[i]<=target){
                
                x[t]=i;//t是位置
                sum+=candidates[i];
               
                if(sum==target){
                    
                    List<Integer> oneRes=new ArrayList<Integer>();
            
                    for(int j=0;j<=t;j++){
                        
                        if(x[j]!=-1){
                            oneRes.add(candidates[x[j]]);
                        }
                    }
                    
                     int flag=1;
                       for(int j=1;j<oneRes.size();j++){
                           if((int)oneRes.get(j)<(int)oneRes.get(j-1))flag=0;
                       }

                    if(flag==1)res.add(oneRes);
                    
                    //这一层直接找到的话,就不用再进下一层了,后续不用继续执行。
                    continue;
            
                }
        
                
                
                
                if(backtrack(t+1,candidates,target,sum,x,maxResLen,res)) return true;
                else{
                    x[t]=-1;
                    sum-=candidates[i];   
                }

            }
           
        }
        
        return false;
    }
    
    
}
Solution 1:回溯法3
class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
            
        int t=0;
        int sum=0;
        Arrays.sort(candidates);
        int maxResLen=target/candidates[0]+1;
        int []x=new int[maxResLen];
        
        for(int i=0;i<maxResLen;i++)
            x[i]=-1;
        
        List<List<Integer>> res=new ArrayList<>();
       
        backtrack(t,candidates,target,sum,x,maxResLen,res);
        
       
        return res;
    }
    
    private void backtrack(int t,int[] candidates, int target,int sum,int []x,int maxResLen,List<List<Integer>> res){
        if(t==maxResLen-1)return;
        
        
        
        for(int i=0;i<candidates.length;i++){//指 2 3 6 7
            

            
            if(sum+candidates[i]<=target){
                
                x[t]=i;//t是位置
                sum+=candidates[x[t]];
               
                if(sum==target){
                    
                    List<Integer> oneRes=new ArrayList<Integer>();
            
                    for(int j=0;j<=t;j++){
                        
                        if(x[j]!=-1){
                            oneRes.add(candidates[x[j]]);
                        }
                    }
                    
                     int flag=1;
                       for(int j=1;j<oneRes.size();j++){
                           if((int)oneRes.get(j)<(int)oneRes.get(j-1))flag=0;
                       }

                    if(flag==1)res.add(oneRes);
                    
                    continue;
            
                }
        
                
                
                
                backtrack(t+1,candidates,target,sum,x,maxResLen,res);
                
                    x[t]=-1;
                    sum-=candidates[i];   
                

            }
           
        }
        
        return ;
    }
    
    
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值