题目描述:
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
示例1:
输入:target = 9
输出:[[2,3,4],[4,5]]
示例2:
输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]
解题思路:
1、滑动窗口:
设滑动窗口(i,j)
的左边界为i
,右边界为j
,窗口内数据之和为sum
。
滑动窗口的性质:
(1)滑动窗口一般为一个左闭右开区间[i,j)
,即保存的数据是nums[i,j - 1]
(不包括nums[j]
);
(2)滑动窗口只有右边界向右移动(扩大窗口)和左边界向右移动(缩小窗口) 两个操作。
找到连续正整数的过程:
(1)当sum < target
时,窗口的和需要增加,所以要扩大窗口,窗口的右边界向右移动,即j++
,同时更新窗口的和,即sum += j
;
(2)当sum > target
时,窗口的和需要减少,所以要缩小窗口,窗口的左边界向右移动,即i++
,同时更新窗口的和,即sum -= i
;
(3)当sum == target
时,记录窗口中的数据,同时窗口的左边界向右移动(因为以左边界i
为开头的序列已经没有了),即i++
,同时更新窗口的和,即sum -= i
;
当窗口的左边界大于target
的一半时,说明连续子序列已经遍历完了,这是因为两个以上的大于target / 2
的数之和肯定大于target
。
注: 当sum == target
时,记录窗口中的数据后,更新左边界时可以将左边界i
向右移动两步,即i += 2
,同时更新sum
,即sum -= 2 * i + 1
,这是因为当存在以i
开头的子序列时,以i + 1
开头的子序列肯定不符合条件。
2、等差数列:
我们知道,等差数列的求和公式为sum = n * a + n(n - 1)d / 2
(其中a为首项,d为公差,n为项数),在这道题中,由于是连续子序列,因此公差为1,因此求和公式可以表示为target = n * a + n(n - 1) / 2
,从而得出a = (target - n * (n - 1) / 2) / n
。
由于题目要求子序列中至少要有两个数字,因此n >= 2
。所以我们可以从n = 2
开始遍历,根据公式算出a
的值,如果a
是正整数,说明以a
开头的连续子序列之和为target
,如果a
不是正整数,n
继续遍历,直到 a <= 0
时遍历结束。(因为随着n递增,a递减)
注: 这样算出来的子序列的长度是递增的,因此最后需要用Collections.reverse(res)
将子序列存放的顺序反转,这样才符合题目的提交要求。
实现代码:
//解法1:滑动窗口
class Solution {
public int[][] findContinuousSequence(int target) {
int left = 1, right = 2;
int sum = 3;
List<int[]> res = new ArrayList<>();
while(left <= target / 2) {
if(sum < target) {
right++;
sum += right;
}else if(sum > target) {
sum -= left;
left++;
}else {
int[] list = new int[right - left + 1];
for(int i = left; i <= right; i++) {
list[i - left] = i;
}
res.add(list);
sum -= 2 * left + 1;
left += 2;
}
}
return res.toArray(new int[res.size()][]);
}
}
//解法2:等差数列求和公式: target = n * a + n(n - 1)d / 2(其中d=1)
public int[][] findContinuousSequence1(int target) {
int n = 2;
ArrayList<int[]> res = new ArrayList<>();
while (true){
int temp = target - n * (n - 1) / 2;
if(temp < n)//a <= 0
break;
if(temp % n == 0){
int a = temp / n;
int[] arr = new int[n];
for(int i = 0; i < n; i++)
arr[i] = i + a;
res.add(arr);
}
n++;
}
Collections.reverse(res);
return res.toArray(new int[res.size()][]);
}