输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
如
输入:target = 9
输出:[[2,3,4],[4,5]]
如
输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]
地址:和为s的连续整数序列
或直转到:https://leetcode-cn.com/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof/
我的题解:
int maxdigit(int target){//以1为阶差的等差数列长度肯定为最长,这个就是求最长的,之后用这个一个一个减就可以
int i;
i=0;
for(i=1;i*i+i<=2*target;i++){//头项加末项*项数/2经典求值
/*空就可以搞定*/
}
i--;
return i;
}//已验证成功可用
int** creat(int a){//二维数组的动态创建
int** p;
int i;
if(NULL==(p=(int**)malloc(a*sizeof(int*)))){
exit(1);
}
for(i=0;i<a;i++){
if(NULL==(p[i]=(int*)malloc(a*sizeof(int)))){
exit(1);
}
}
return p;
/*
for(i=0;i<a;i++){
free(p[i]);
}
free(p);
*/
}
int ThisMayMid(int target,int thisdigit){//奇数项返回的是中间的值,偶数项返回的是第thisdigit/2项的值
int midnum;
int i;
i=0;
midnum=0;
if(thisdigit%2==0){
i=target-thisdigit/2;
if((i%thisdigit)==0){//少了个括号,傻逼
midnum=(target-(thisdigit/2))/thisdigit;
}//偶数情况有点特殊哦,得处理一下
}
else{//奇数情况下就直接处理了
if(target%(thisdigit)==0){
midnum=target/thisdigit;
}
}
return midnum;
}
int** findContinuousSequence(int target, int* returnSize, int** returnColumnSizes){
int begindigit;
int i,j;//经典循环因子
int startnum;
int row;
int** my2D;
int* columnsize;
int* replace;
startnum=0;
i=0;
j=0;
row=0;
begindigit=0;
begindigit=maxdigit(target);//找到可能的最长位数
my2D=creat(begindigit);//创建x*x二dimonsion数组
columnsize=(int*)malloc(sizeof(int)*target);
replace=columnsize;
//现在出现一个小问题,就是二维数组指针怎样初始化
for(i=begindigit;i>1;i--){
//everyrow(target,i);//这里缺少一个接受一维数组的二维数组中的一行
if(ThisMayMid(target,i)!=0){
if(i%2==0){//偶数情况
startnum=ThisMayMid(target,i)-i/2+1;
//遍历行输入
for(j=0;j<i;j++){
my2D[row][j]=startnum+j;
}
//
*columnsize=i;
columnsize=columnsize+1;
//
row++;
}
else{//奇数情况
startnum=ThisMayMid(target,i)-(i-1)/2;
//遍历行输入
for(j=0;j<i;j++){
my2D[row][j]=startnum+j;
}
//
*columnsize=i;
columnsize=columnsize+1;
//
row++;
}
}
}
*returnSize=row;
*returnColumnSizes=replace;//有点迷
return my2D;
}//目标函数
- 介绍函数:
1、maxdigit:最大位数,根据题意,想要构成符合题意的数列,需要找到连续的一组数字,那么理所应当想到从1到n跨度为1的数列,即最大长度,因为起始数字最小。就算找不到相等的,可以找到比target小的和中最大的那组数列对应的长度,这个就是最大长度了。
2、creat:创建二维数组,为了节约空间,我们在创建数组的时候要限制创建空间的大小,不能溢出也不能太小,这里取到上面maxdigit函数得到的最大长度。注意创建的二维数组中每一行也只是指向一维数组的指针,也就是说还没有装东西,只有一堆指针。
3、ThisMayMid:用来判断对应位数的数列和能否为target。这了我分了奇数偶数项,因为中间项的选择很重要,举个例子:若为奇数项和,奇数项和的值一定等于奇数项中间项与项数的乘积,等差数列。若为偶数项的和,就需要再进行判断。这里我发现了偶数项的数用和减去thisdigit/2的值可以被thisdigit整除,因此就用了这个判断方法,其实还可以用中间两项的和乘以thisdigit/2来判断,都可以的。
- 主要入口函数逻辑:
1、找到最长位数
2、创建二维数组接收可能的数列
3、动态创建一维数组存储符合题意的数列
4、取到这个地址,利用columnsize同值指针replace
5、由for循环从maxdigit一直判断到2看看是否可以求和
6、由于ThisMayMid函数只能返回中间数的值,开始的数字还要由外部方法来判断,因此在这里还要分奇偶性,取到开始数字后直接用for循环对应位数次就能得到一个单调递增数列,符合题意。
7、奇数情况同上
8、其实可以更加聚合,不过我个人的变成思想就是从上到下一路想出来的,没有先写大框架。就显得代码不是那么有规则。 - 反思
由于算法学的很少,我像评论区写到的滑动窗口我都不会,我想可能思路和我的差不多,不过用更高效方便的方法实现了,且我的奇偶分的貌似不是那么高效美观
大佬的代码:
int** findContinuousSequence(int target, int* returnSize, int** returnColumnSizes){
int** result = (int**)malloc(sizeof(int*) * target);
int* col = (int*)malloc(sizeof(int) * target);
int i = 1, m = 0, t = target;
while (t - i >= 0)
{
t -= i++;
}
while (t < target && i > 2)
{
t += --i;
if (t % i == 0)
{
col[m] = i;
result[m] = (int*)malloc(sizeof(int) * i);
for (int k = t / i, j = 0; k < t / i + i; k++, j++)
{
result[m][j] = k;
}
m++;
}
}
*returnSize = m;
*returnColumnSizes = col;
return result;
}
//作者:PonderYao
思路:
1、动态创建一个二维数组,一个保留列宽的一维数组,每次需要增加子列的时候就给前面二维数组的子数组动态分配相应的空间,分配的规律很简单,你会发现他除以2的话子列长度就是2,除以3子列长度就是3,以此类推,这部分在下面重点的for循环那里;
2、子列数计数器:其实这个本来是不需要的,没办法人家的题目要求子列是从最长到最短,没有这第一个while循环配合的话就会导致结果反了,他不认啊。如果用最后再反转数组的方法无疑是最蠢的,但一时我也没想到怎么用1个while就可以将他从宽到窄的排列,只好增加一个小小的循环,然后再把计数器原路返回(相当于把前面计数过程的结果拿来反过来计数一遍),便可以得到我想要的排列;
3、数学思想的话,我看有些是用奇偶来分的(一开始我也是这么想的),但我也看见其他的思路似乎更有亮点,就是统一成只需变动我的target(在代码中是变动t,从t 到target),在这个随着i–而t+的过程中我的t和i自然而然就会经历奇数和偶数,但并不是把t/i当做对称轴即子列中心,而是作为子列的起点,依次增加i个连续+1的元素形成子列。这样就不用考虑那么复杂了,不是吗?
我有一个遗留问题,就是大佬代码中的
while (t - i >= 0)
{
t -= i++;
}
while (t < target && i > 2)
{
t += --i;
if (t % i == 0)
{
第一个while我懂,可第二个while中t=t+(–i)后我不清楚t和i的关系,为什么可以进行整除?整除了又怎么了呢?