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;
}