滑动窗口的重要性质是:窗口的左边界和右边界永远只能向右移动,而不能向左移动。这是为了保证滑动窗口的时间复杂度是 O(n)O(n)。如果左右边界向左移动的话,这叫做“回溯”,算法的时间复杂度就可能不止 O(n)O(n)。
在这道题中,我们关注的是滑动窗口中所有数的和。当滑动窗口的右边界向右移动时,也就是 j = j + 1,窗口中多了一个数字 j,窗口的和也就要加上 j。当滑动窗口的左边界向右移动时,也就是 i = i + 1,窗口中少了一个数字 i,窗口的和也就要减去 i。滑动窗口只有 右边界向右移动(扩大窗口) 和 左边界向右移动(缩小窗口) 两个操作,所以实际上非常简单。
如何用滑动窗口解这道题
要用滑动窗口解这道题,我们要回答两个问题:
- 第一个问题,窗口何时扩大,何时缩小?
- 第二个问题,滑动窗口能找到全部的解吗?
对于第一个问题,回答非常简单:
- 当窗口的和小于 target 的时候,窗口的和需要增加,所以要扩大窗口,窗口的右边界向右移动
- 当窗口的和大于 target 的时候,窗口的和需要减少,所以要缩小窗口,窗口的左边界向右移动
- 当窗口的和恰好等于 target 的时候,我们需要记录此时的结果。设此时的窗口为 [i, j)[i,j),那么我们已经找到了一个 ii 开头的序列,也是唯一一个 ii 开头的序列,接下来需要找 i+1开头的序列,所以窗口的左边界要向右移动
class Solution {
public int[][] findContinuousSequence(int target) {
int left = 1;
int right = 1;
int sum = 0;
List<int[]> list = new ArrayList<>();
while(left <= target /2){ //题目要求最少两个数, target/2 + target/2+1 就超过taiget了
if(sum < target){
sum += right;
right++;
}else if(sum > target){
sum-=left;
left++;
}else{
int[] arr = new int[right-left];
for(int k = left;k < right; k++){
arr[k-left] = k;
}
list.add(arr);
sum -= left;
left++;
}
}
return list.toArray(new int[list.size()][]);
}
}