基础算法--尺取法(滑动窗口法)

0 阅读2分钟

尺蠼一种幼虫,因为其移动时“屈伸”的动作与本算法的区间移动方式类似,故取名为尺取法或滑动窗口法。 尺蠼法的核心是,当我们需要寻找符合题目要求的子数组时,设置双指针l和r。

尺取法(滑动窗口).png 在上图中显示,我们应该==首先考虑r的位置,在此基础上再去考虑l的位置,从而找到所有满足题意的区间==。 对于r的位置,我们使用for循环即可。对于l的位置,需要注意的是在绝大多数题目中,当r右移时,l并不回到最左侧,而是呆在原地。


例如,我们要求区间内所有数字之和小于10且数组内所有元素都是正整数,对于上一个r的位置,我们找到了l的极限位置(假设不是第一个位置),即如果l往左移一位,区间内元素之和就会大于等于10。如果当r右移一位之后,我们选择将l移回第一个位置,其结果一定是大于10的。所以我们要让l呆在原地,然后再向右移动,寻找当下情况的极限位置。

尺取法(滑动窗口)-1.png

==但是,并不是所有的题目,都需要让l呆在原地,要根据题目要求进行分析。==


我们来一起看一道题目~ leetcode.cn/problems/mi…

尺取法(滑动窗口)-2.png

分析:对于这种不定长的滑动窗口,一般从第一个元素开始,先构建一个单元素的窗口,然后再去移动右指针。当右指针在不同位置上时,我们让左指针右移,直到数组和小于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;
}