题目
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
示例 1:
输入:target = 9
输出:[[2,3,4],[4,5]]
示例 2:
输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]
限制:
1 <= target <= 10^5
解题思路
解法一:数学 + 间隔法
设数列第一项为t,项数为n,则由等差数列求和公式 [t+(t+n-1)]*n/2 = s,
化简得 t = [s-n(n-1)/2]/n,因为t为正整数,所以 s>n(n-1)/2,[s-n(n-1)/2] % n = 0。又项数至少为2,所以n>=2。
步骤:
1)初始化项数 n=2,结果数列res = [];
2)进行循环,当 n(n-1)/2 < s时,即 while(n(n-1)/2 < s):
2.1)判断[s-n(n-1)/2]能否整除n,即[s-n(n-1)/2] % n = 0,是则进行如下操作:求出 t,并根据当前项数n求出整个数列,加入结果数列中。
2.2)否则项数加1,继续循环;
3)由于项数是从小到大,所以数列是以从大到小的顺序存入结果数列的,最后需要进行反转操作。
解法二:滑动窗口(双指针)
主要步骤:
1)初始化:双指针i=1, j=2,当前和为 cursum=i+j。
2)循环搜索:当指针i大于 target/2 时跳出循环。
2.1)当窗口的和小于 target 的时候,窗口的和需要增加,所以要扩大窗口,窗口的右边界向右移动;
2.2)当窗口的和大于 target 的时候,窗口的和需要减少,所以要缩小窗口,窗口的左边界向右移动;
2.3)当窗口的和恰好等于 target 的时候,记录此时的结果,并将指针i右移一位(或者指针j右移一位)。
序列至少要包括两个数字,所以指针i最多增加到 target / 2,因为(target/2) + (target/2)+1>=target。
代码
解法一:数学 + 间隔法
Python代码如下:
class Solution:
def findContinuousSequence(self, target: int) -> List[List[int]]:
#初始化项数为2
n = 2
res = []
while n*(n-1)/2 < target: # 必须为正数
if (target - n*(n-1)/2) % n == 0: #判断是否能够整除
t = int((target - n*(n-1)/2) / n) # float转int
res.append(list(range(t, t+n)))
n += 1
# 数列反转
return res[::-1]
解法二:滑动窗口(双指针)
Python代码如下:
class Solution:
def findContinuousSequence(self, target: int) -> List[List[int]]:
i = 1
j = 2
cursum = i+j
res = []
while i <= target//2:
if cursum == target:
res.append(list(range(i, j+1)))
# j += 1
# cursum += j
# 或者
cursum -= i
i += 1
elif cursum < target:
j += 1
cursum += j
else:
cursum -= i
i += 1
return res
Java代码如下:
class Solution {
public int[][] findContinuousSequence(int target) {
int i = 1;
int j = 2;
int cursum = i+j;
List<int[]> res = new ArrayList<>();
while(i<=target/2){
if(cursum == target){
int[] arr = new int[j-i+1];
for(int k=i; k<=j; k++){
arr[k-i] = k;
}
res.add(arr);
// j++;
// cursum += j;
// 或者
cursum -= i;
i++;
}else if(cursum < target){
j++;
cursum += j;
}else{
cursum -= i;
i++;
}
}
return res.toArray(new int[res.size()][]);
}
}