滑动窗口类型题

文章介绍了如何使用滑动窗口解决三个不同的数组问题:找到大于等于目标值的最短子数组、和为s的连续正数序列以及计算滑动窗口内的最大值。通过C++代码展示了具体的实现方法,包括维护区间和、更新最小子数组长度以及使用双端队列存储最大值。

剑指 Offer 和大于等于 target 的最短子数组

今天主要学习的是滑动窗口就用滑动窗口方法做,首先很多人不知道什么是滑动窗口,滑动窗口看成如下图一样数组框的一部分在这个区间里面找合适的结果。

在这里插入图片描述

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
int ans=INT_MAX,sum=0;
int s=0,e=0;//定义左区间和右区间
while(e<nums.size()){
    sum+=nums[e];
    while(sum>=target){//当和大于等于target则说明要求该区间的和可能满足
        ans=min(e-s+1,ans);//找最小的区间长度
        sum-=nums[s];
        s++;
    }
    e++;
}
return ans==INT_MAX?0:ans;//若ans的值未变化则说明没找到否则找到最短的
    }
};

剑指 Offer II 和为s的连续正数序列

在这里插入图片描述

class Solution {
public:
vector<vector<int>> findContinuousSequence(int target) {
int l=1,r=1;//定义左边界,和右边界
int sum=1;//让区间左闭右闭
vector<vector<int>>ans;
while(l<=target/2){//因为序列中至少包含两个数字所以左区间可以到target/2
    if(target>sum){//target比sum大则让右区间动起来
        r++; //因为开始没考虑r所以要先加r
        sum+=r;
   
    }
    else if(target<sum){//target比sum小则让左区间动起来
     sum-=l;
        l++;
    }
    else{
        vector<int>a;
        for(int k=l;k<=r;k++){//左闭右闭区间复制
            a.push_back(k);
        }
        ans.push_back(a);
    sum-=l;
        l++;
    }

}
return ans;
    }
};

滑动窗口的最大值

在这里插入图片描述

#include <vector>
class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size) {
        vector<int>ret;
        deque<int>a;//用双端队列来维护最大值
        if(num.size()==0||size==0)
        return {};//判特殊情况
        for(int i=0;i<num.size();i++){
            while(!a.empty()&&num[a.back()]<num[i]){//如果比队列最后一个值大则删除最后的元素
                a.pop_back();
            }
            a.push_back(i);//插入当前最大值的下标
            if(a.front()+size<=i){//若旧下标失效
                a.pop_front();//则头删
            }
            if(i+1>=size){//判断是否形成窗口
                ret.push_back(num[a.front()]);
            }
        }
        return ret;
    }
};
<think>我们正在处理用户关于滑动窗口算法C++中的实现的问。用户要求提供示例代码。根据引用,我们有几个相关的代码片段。引用[1]展示了使用滑动窗口解决“最大连续1的个数III”问的代码。它维护一个窗口,其中最多允许k个0,通过移动左右指针来扩展和收缩窗口。引用[2]提供了滑动窗口模板,用于解决最小覆盖子串问LeetCode76)。它使用两个哈希表(need和window)来跟踪需要匹配的字符和当前窗口中的字符,并通过valid变量来检查当前窗口是否满足条件。引用[3]来自AcWing154,它使用一个单调队列来解决滑动窗口的最大值和最小值问。代码分别计算了每个长度为k的窗口中的最小值和最大值。引用[4]展示了使用双端队列(deque)解决滑动窗口最大值问的代码。它维护一个单调递减队列,队列的头部始终是当前窗口的最大值。引用[5]简要介绍了滑动窗口的核心思想、典型步骤和应用场景,并提到C++代码示例(但未给出具体代码)。用户的需求是“滑动窗口算法C++实现示例代码”。我们可以提供几个典型的滑动窗口示例:1.固定窗口大小的滑动窗口(如求每个窗口的最大值)2.可变窗口大小的滑动窗口(如满足条件的最长子数组)由于引用中有多个例子,我们选择两个经典问来展示:1.固定窗口大小:滑动窗口最大值(LeetCode239,引用[4]提供了代码)2.可变窗口大小:最大连续1的个数III(LeetCode1004,引用[1]提供了代码)我们将分别给出这两个问C++实现,并解释代码。注意:引用[3]的代码也是滑动窗口最大值,但使用了数组模拟队列,引用[4]使用了deque,两者思想一致,我们选择使用deque的版本,因为更常见。另外,引用[2]的模板适用于可变窗口且需要哈希表记录状态的问(如子串匹配)。因此,我们提供两个示例:示例1:固定窗口大小(滑动窗口最大值)示例2:可变窗口大小(最大连续1的个数III)同时,我们也会简要说明滑动窗口算法的核心思想和适用场景。最后,按照要求生成相关问。</think>### 滑动窗口算法 C++ 实现详解 #### 算法核心思想 滑动窗口算法通过维护动态窗口处理数组/字符串的连续子序列问,核心步骤: 1. **初始化指针**:左指针 `left` 和右指针 `right` 定义窗口边界 2. **扩展窗口**:`right` 右移纳入新元素 3. **收缩窗口**:当窗口不满足条件时,`left` 右移排除元素 4. **更新结果**:在窗口满足条件时记录最优解 时间复杂度通常为 $O(n)$,空间复杂度 $O(1)$ 或 $O(k)$。 --- #### 示例 1:固定窗口大小(求最大值) **问**:给定数组和窗口大小 $k$,求每个窗口的最大值(LeetCode 239) **实现**:使用双端队列维护单调递减序列 ```cpp #include <iostream> #include <deque> #include <vector> using namespace std; vector<int> maxSlidingWindow(vector<int>& nums, int k) { deque<int> dq; // 存储元素下标(保证队首为最大值) vector<int> res; for (int i = 0; i < nums.size(); i++) { // 移除超出窗口的队首元素 if (!dq.empty() && dq.front() == i - k) dq.pop_front(); // 维护单调递减队列 while (!dq.empty() && nums[dq.back()] < nums[i]) dq.pop_back(); dq.push_back(i); // 当前元素入队 // 从第 k-1 个元素开始记录结果 if (i >= k - 1) res.push_back(nums[dq.front()]); } return res; } int main() { vector<int> nums = {1,3,-1,-3,5,3,6,7}; int k = 3; vector<int> res = maxSlidingWindow(nums, k); for (int num : res) cout << num << " "; // 输出: 3 3 5 5 6 7 return 0; } ``` **关键点**: - 队列存储下标而非值,便于判断窗口位置 - 当新元素 $nums[i]$ 大于队尾时弹出队尾,保持单调性 - 时间复杂度 $O(n)$,每个元素进出队列各一次[^4] --- #### 示例 2:可变窗口大小(最大连续 1 的个数 III) **问**:将最多 $k$ 个 0 翻转为 1,求最长连续 1 的子数组(LeetCode 1004) **实现**:动态调整窗口使窗口内 0 数 $\leq k$ ```cpp #include <vector> #include <algorithm> using namespace std; int longestOnes(vector<int>& nums, int k) { int left = 0, right = 0; int zeroCount = 0; // 窗口内 0 的个数 int maxLen = 0; // 最大长度 while (right < nums.size()) { if (nums[right] == 0) zeroCount++; // 更新 0 计数 right++; // 扩展窗口 // 当 0 超限时收缩窗口 while (zeroCount > k) { if (nums[left] == 0) zeroCount--; left++; // 左指针右移 } // 更新结果:right - left 为当前窗口长度 maxLen = max(maxLen, right - left); } return maxLen; } ``` **关键点**: - `zeroCount` 记录窗口内 0 的数量 - 当 `zeroCount > k` 时收缩左边界 - 窗口长度 $right - left$ 始终满足 $\leq k$ 个 0 的条件[^1] --- #### 应用场景与模板 | 场景类型 | 经典问 | 窗口特征 | |----------------|---------------------------|------------------------| | 固定窗口大小 | 滑动窗口最大值 | 窗口大小恒定 | | 可变窗口大小 | 最小覆盖子串(LeetCode 76)| 动态调整边界 | | 计数条件约束 | 最大连续 1 的个数 III | 统计特定元素数量 | **通用模板**: ```cpp int slidingWindow(vector<int>& nums, int k) { int left = 0, right = 0; int condition = 0; // 窗口状态(如计数、和值等) int result = 0; // 存储结果 while (right < nums.size()) { // 1. 扩展窗口并更新状态 condition += updateState(nums[right]); right++; // 2. 当不满足条件时收缩窗口 while (condition > k) { condition -= removeState(nums[left]); left++; } // 3. 更新结果(满足条件时) result = max(result, right - left); } return result; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值