回溯题目集合——贰

1.组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。

题目链接

解析:
1.需要传递的参数有:
临时数组用来临时保存获得的元素。
二维数组,当条件满足时将临时数组内的元素拷贝至二维数组。
计数器,用来统计当前临时数组内的元素总和

2.寻找剪枝条件
通过题目知道数组是无重复元素的,且都是正整数,因此可以得出递归出口和剪枝条件,当我们选的数组的总和等于target时,就可以将临时数组内的元素拷贝至二维数组,当我们临时数组内的元素的总和大于target时则可以直接进行剪枝

3.构建递归方程
回溯方程通常都有一个for循环来进行每个元素的遍历,然后我们通过题目可知每个元素都可重复使用,因此每次递归for循环都可以从前面一样的起点开始。

又题目是求组合不是求排列,因此我们的解之中不能有重复的答案,怎么来保证答案不会重复呢?很显然,当我们以一个数字作为起点找出含有它的所有答案之后,我们就不再使用它,因此我们的for寻找中给出一个变量sub,用来保存我们的起点遍历的位置,用过一个起点之后就不再含有这个元素,这样就保证了我们的答案之中不会有重复的;

void GetNum(int* candidates, int candidatesSize, int target, int* returnSize, int** returnColumnSizes,int **ret,int *temp,int temp_sub,int count,int sub)
{
    if(count==target)//达到满足条件
    {
        ret[*returnSize]=(int *)malloc(sizeof(int)*temp_sub);
        memcpy(ret[*returnSize],temp,temp_sub*sizeof(int));
        returnColumnSizes[0][*returnSize]=temp_sub;
        (*returnSize)++;
        return;
    }
    if(count>target)//剪枝
        return ;

        for(int i=sub;i<candidatesSize;i++)//sub保证不会有重复的答案
        {
            temp[temp_sub]=candidates[i];
            count+=candidates[i];//计数器,记录当前数组内所有元素的总和
        
        //temp_sub+1,保证了数组内元素往后叠加,回溯时回到原来的位置
        //count保证了当前进行判断时,count内是当前数组内所有元素的总和
        //sub保证了调用的子函数也是从一样的位置开始出发 
GetNum(candidates, candidatesSize,  target,  returnSize,  returnColumnSizes,ret,temp,temp_sub+1,count,i);
            count-=candidates[i];//回溯
        }
}

int** combinationSum(int* candidates, int candidatesSize, int target, int* returnSize, int** returnColumnSizes){
   
    //准备二维数组
    int **ret=(int **)malloc(sizeof(int*)*1000);
    *returnColumnSizes=(int *)calloc(1000,sizeof(int));
    *returnSize=0;

    int *temp=(int *)malloc(sizeof(int)*1000);//临时数组
    int count=0;//计数器

    GetNum(candidates, candidatesSize,  target,  returnSize,  returnColumnSizes,ret,temp,0,0,0);

    return ret;

}

2.组合总和贰

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明:
所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。

题目链接

解析:
此题是上题的变种问题,在参数传递方面同样需要传递临时数组,二维数组,计数器。
递归出口也是target等于我们计数器的值时便将临时数组中的内容拷贝至二维数值之中。
剪枝条件也是当计数器count大于target时直接退出当前递归完成剪枝。

在递归构建方面,我们同样需要一个for循环来进行控制,由于每个元素只能使用一次,因此需要一个for循环的控制下标变量sub,每次递归sub的值都要+1,保证下一次不会取到同样的数字。

如此一来,便达成了初步要求,即将所有等于目标值的解都拷贝至数组之中。

但是现在还有一个问题,就是我们的数组之中可能含有相同的元素,因此就算我们控制了下标不会使用,用过的元素,但是当含有相同的元素的时候还是会出现重复的解,我们的解决办法是将数组进行排序,并且给定一个标记数组,在给临时数组赋值时进行判断,如果当前元素和前一个元素相等,并且前一个元素没有被标记,说明前一个元素已经被使用过了且进行了回溯,因此我们进行剪枝,将当前元素进行抛弃

能用这种方法排重的原理如下图所示:
在这里插入图片描述

void GetNum(int* candidates, int candidatesSize, int target, int* returnSize, int** returnColumnSizes,int **ret,int *temp,int *flag,int temp_sub,int count,int sub)
{
    if(count>target)//都是正整数
    return;
    if(count==target)//满足条件的出口
    {
        ret[*returnSize]=(int *)malloc(sizeof(int )*temp_sub);//给二维数组的一维数组开辟空间
        memcpy(ret[*returnSize],temp,temp_sub*sizeof(int));//拷贝
        returnColumnSizes[0][*returnSize]=temp_sub;
        (*returnSize)++;
        return;
    }

        for(int i=sub;i<candidatesSize;i++)
        {
            if(i-1>=0&&flag[i-1]==0&&candidates[i-1]==candidates[i])
                continue;
            
            temp[temp_sub]=candidates[i];
            flag[i]=1;//进行标记
            count+=candidates[i];

GetNum(candidates,candidatesSize,target, returnSize,returnColumnSizes,ret,temp,flag,temp_sub+1,count,i+1);
            count-=candidates[i];//回溯
            flag[i]=0;//取消标记

        }

}

int Compar(int *x,int *y)
{
    return *x-*y;
}


int** combinationSum2(int* candidates, int candidatesSize, int target, int* returnSize, int** returnColumnSizes){

qsort(candidates,candidatesSize,sizeof(int),Compar);

int **ret=(int **)malloc(sizeof(int*)*1000);
 *returnSize=0;
*returnColumnSizes=(int *)calloc(1000,sizeof(int));
int *temp=(int *)malloc(sizeof(int)*1000);

int flag[1000]={0};

GetNum(candidates,candidatesSize,target, returnSize,returnColumnSizes,ret,temp,flag,0,0,0);

    return ret;
}

3.组合总和叁

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:
所有数字都是正整数。
解集不能包含重复的组合。

题目链接

解析
通过题目可知信息,组合中只允许1-9个正整数,则可以当作一维数组不重复的9个元素。
相加之和为n和k个数的组合则是递归出口和剪枝条件。
不存在重复的数字,说明进行递归时,需要一个下标变量sub,来保证不会取到重复的值

 void GetNum(int k, int n, int* returnSize, int** returnColumnSizes,int **ret,int *temp,int count,int temp_sub,int sub)
 {
     if(count>n||temp_sub>k)//剪枝
        return ;

    if(count==n&&temp_sub==k)//满足条件进行拷贝
    {
        ret[*returnSize]=(int *)malloc(sizeof(int)*k);//开空间
        memcpy(ret[*returnSize],temp,sizeof(int)*k);//拷贝
        returnColumnSizes[0][*returnSize]=k;//二维数组中一维数组元素个数
        (*returnSize)++;//二维数组行增加
        return ;
    }

    for(int i=sub;i<10;i++)//元素1-9,且不出现重复元素
    {
        temp[temp_sub]=i;
        count+=i;//计数器
       
        GetNum( k,n, returnSize, returnColumnSizes,ret,temp,count,temp_sub+1,i+1);//i+1保证不会出现重复元素
       
        count-=i;//回溯
    }
            
 }
int** combinationSum3(int k, int n, int* returnSize, int** returnColumnSizes){

    //开辟数组
    int **ret=(int **)malloc(sizeof(int*)*1000);
    *returnSize=0;
    *returnColumnSizes=(int *)calloc(1000,sizeof(int));
    int *temp=(int *)malloc(sizeof(int)*9);

    int count=0;//计数器

    GetNum( k,n, returnSize, returnColumnSizes,ret,temp,count,0,1);

    return ret;

}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值