尺蠼一种幼虫,因为其移动时“屈伸”的动作与本算法的区间移动方式类似,故取名为尺取法或滑动窗口法。 尺蠼法的核心是,当我们需要寻找符合题目要求的子数组时,设置双指针l和r。
在上图中显示,我们应该==首先考虑r的位置,在此基础上再去考虑l的位置,从而找到所有满足题意的区间==。
对于r的位置,我们使用for循环即可。对于l的位置,需要注意的是在绝大多数题目中,当r右移时,l并不回到最左侧,而是呆在原地。
例如,我们要求区间内所有数字之和小于10且数组内所有元素都是正整数,对于上一个r的位置,我们找到了l的极限位置(假设不是第一个位置),即如果l往左移一位,区间内元素之和就会大于等于10。如果当r右移一位之后,我们选择将l移回第一个位置,其结果一定是大于10的。所以我们要让l呆在原地,然后再向右移动,寻找当下情况的极限位置。
==但是,并不是所有的题目,都需要让l呆在原地,要根据题目要求进行分析。==
我们来一起看一道题目~ leetcode.cn/problems/mi…
分析:对于这种不定长的滑动窗口,一般从第一个元素开始,先构建一个单元素的窗口,然后再去移动右指针。当右指针在不同位置上时,我们让左指针右移,直到数组和小于target,此时去更新ans即最小子数组的长度。
#include <iostream>
#include <vector>
using namespace std;
int minSubArrayLen(int target, vector<int>& nums) {
int ans = INT_MAX;
int n = nums.size();
int l, r;
int sum = 0;
l = r = 0;
while(r <= n-1) {
sum += nums[r];
if(sum >= target) {
while(l <= r && sum - nums[l] >= target) {
sum -= nums[l];
++l;
}
ans = min(ans, r - l + 1);
}
++r;
}
if(ans == INT_MAX) {
ans = 0;
}
return ans;
}
int main(){
int n, target, num, ans;
vector<int> nums;
cin >> n >> target;
for(int i = 0; i < n; ++i) {
cin >> num;
nums.push_back(num);
}
ans = minSubArrayLen(target, nums);
cout << ans << endl;
return 0;
}