题目
题目链接:
输入一个正整数 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
解题思路
熟悉这类题的人第一反应应该是滑动窗口。简单说一下什么是滑动窗口?
顾名思义,滑动窗口就是可以滑动的窗口,在代码里就是用双指针代表可以滑动的窗口,在两个指针间是一个窗口,指针的范围是一个大的窗户框架。通过两个指针的移动可以取到可移动范围内的任何值和任何小范围。如下图:
现在来看这一题,我们可以通过滑动窗口的方式寻找我们想要的范围,那么如何寻找?
窗口范围
从样例可以看出,随着窗口越往后移,窗口的大小(范围)越来越小。当右指针等于目标数target
的一半时,左右指针指向同一个位置(目标数为偶数时),此时窗口不能继续向后移动了,所以这个值就是窗口能到达的最大值。窗口的范围确定了,接下来就是如何移动窗口。
移动窗口
窗口的移动是控制左右指针的左右移动完成的,所以首先应该让左右指针指向最开始的位置1
。由于窗口内的值是在变化的,所以我们需要一个变量res
存储窗口值的大小,反应窗口的变化和判断窗口需要移动方向,res
的初始值为1
(左右指针的初始位置)。
由于窗口整体是一直向后移动的,所以应该规定右指针r
的范围不能超过target/2 + 1
,这里就可以用一个循环表示。在循环内,窗口的移动不是随意的或固定的,需要根据窗口的值res
和target
的大小确定。这里就有了三种情况:
- 当
res
和target
相等时,直接把窗口的值放入数组中,然后将窗口平行向后移动一格 - 当
res
大于target
时,说明此时窗口的值需要减去一些值,将左指针向后移动一格 - 当
res
小于target
时,说明此时窗口的值需要加上一些值,将右指针向后移动一格
注意:当左右指针移动时,表示窗口的值rse
也需要变化
伪代码:
while (右指针 <= target / 2 + 1) {
if (窗口值等于target) {
将窗口的值放入数组
窗口平行向后移动
}
if (窗口值大于target) {
窗口值减去左指针所指的值
左指针向后移动
}
if (窗口值小于target) {
右指针向后移动
窗口值加上右指针所指值
}
}
基本的思路就是以上,下面是代码实现:
代码(C++)
class Solution {
public:
vector<vector<int>> findContinuousSequence(int target) {
int l = 1, r = 1, res = 1;
vector<vector<int>> nums;
vector<int> num;
while (r <= target / 2 + 1) {
if (res == target) {
num.clear();
for (int i = l; i <= r; ++ i) {
num.push_back(i);
}
nums.push_back(num);
r ++;
res += r;
res -= l;
l ++;
}
if (res > target) {
res -= l;
l ++;
}
if (res < target) {
r ++;
res += r;
}
}
return nums;
}
};
总结
滑动窗口理解起来并不难,这种算法的题型大都可以一眼看出需要用到滑动窗口。在解题时,需要注意的是左右指针的范围和一些边界值的处理。