题例:
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
此题的暴力解法是双层遍历,即O(n^2)的时间复杂度。这里提出一种O(n)的方法,即滑动窗口。
滑动窗口
滑动窗口使用两个移动指针,指针范围内的子数组就是一个窗口;不断调整窗口的大小和位置,使窗口内的子数组符合我们的要求,就是滑动窗口的使用目的。
在此题中,我们的窗口初始大小为 1 ,窗口的左边界和右边界都指向nums[0]。此时窗口内的总值sum = nums[0],在总值没有达到target之前,我们不断地将有窗口向右移动,更新sum。若某一时刻窗口内的sum == target,那么返回窗口的大小即可。
若窗口内的sum > target,说明此时以左边界起始的窗口 不能达到要求,我们将左窗口向右边,即下一个元素移动,更新窗口的左边界(此时要把原本左窗口的值从sum中减去)。
code:
扩展题:
这道题的思路也比较清晰,窗口的右边界向右扩展,记此时水果数量(即窗口的长度)。若此时窗口内的元素种类超过2种,左边界就要向右收缩,知道窗口内的水果种类为2为止,比较两种情况下水果数量谁更多。
这题的难点在于记录窗口内的水果种类,用unordered-map来记录会比较方便(若用数组或者变量来记录会分不清哪个种类先摘哪个后摘)
code:
class Solution {
public:
int totalFruit(vector<int>& fruits) {
int total=INT_MIN;
int i=0;
int j=0;
//int type=1;
//int temp=0;
unordered_map<int, int> cnt; //种类->数量,方便遍历时判断这个种类是否已经存在
for(j;j<fruits.size();++j)
{
++cnt[fruits[j]];//该种类的水果数量++
while(cnt.size() > 2)//种类满了后,将同种类退出
{
// --cnt[fruits[i]];
// i++;
//为了指定代表i指向的水果种类,必须用指针来指向这个键值对
auto it = cnt.find(fruits[i]);//指针指向相应键的键值对
-- it -> second;
if(it -> second == 0)
cnt.erase(it);
i++;
}
total = total > j-i+1 ? total : j-i+1;//权值为1,则窗口长度就是数量
}
return total;
}
};