C++数据结构6:滑动窗口

1、概念

动窗口(Sliding Window)是一种常用的算法技巧,主要用于处理数组或列表中的连续子序列问题。它通过维护一个大小固定或可变的"窗口"来高效地解决问题,避免了不必要的重复计算。

2、实战例子

给定一个数组,给定滑动窗口k,从左到右,求出每个窗口中最小值和最大值。

思路:通过一维数组存储数据;通过双端队列存储滑动窗口中的最值。

#include <iostream>
#include <deque>
#include <algorithm>
using namespace std;
const int N = 1e6 + 10;  // 定义数组最大大小
int a[N];  // 存储输入数据的数组
int main()
{
    int n, k;  // n-数组长度,k-滑动窗口大小
    cin >> n >> k;  // 输入数组长度和窗口大小
    for (int i = 1; i <= n; i++)   cin >> a[i];
    deque<int> q;  // 使用双端队列维护滑动窗口
    // 第一部分:求滑动窗口最小值
    for(int i = 1; i <= n; i++)
    {
        // 维护单调递增队列:移除队尾大于当前元素的元素
        while(q.size() && q.back() > a[i])  q.pop_back(); 
        q.push_back(a[i]);  // 将当前元素加入队尾
        // 如果队首元素是窗口左侧刚移出的元素,则移除它
        if(i - k >= 1 && q.front() == a[i - k])   q.pop_front(); 
        // 当窗口完全在数组内时,输出当前窗口最小值
        if(i >= k) cout << q.front() << ' ';
    }
    q.clear();  // 清空队列,准备下一轮计算
    cout << endl;  // 输出换行
    // 第二部分:求滑动窗口最大值
    for(int i = 1; i <= n; i++)
    {
        // 维护单调递减队列:移除队尾小于当前元素的元素
        while(q.size() && q.back() < a[i])   q.pop_back();
        q.push_back(a[i]);  // 将当前元素加入队尾 
        // 如果队首元素是窗口左侧刚移出的元素,则移除它
        if(i - k >= 1 && q.front() == a[i - k])  q.pop_front();
        // 当窗口完全在数组内时,输出当前窗口最大值
        if(i >= k)  cout << q.front() << ' ';
    } return 0;
}

难点分析:

1、while(q.size() && q.back() > a[i])  q.pop_back(); 

求最小值,维护的是递增的双端队列,头部是最小值;在进行新插入元素比较时,首先队列中需要有元素,其次就是比较插入元素和栈顶元素;

2、if(i - k >= 1 && q.front() == a[i - k])   q.pop_front();
当i == k的时候,说明刚好形成了第一个窗口;这个时候是不需要从队列中移除任何数据的,直接输出队列中的最小值即可;

当i > k的时候,说明滑动窗口已经开始移动了,那么在数组的视角,只要滑动窗口向前移动一步,那么一定有一个新数据加到滑动窗口,同时有一个旧的数据脱离滑动窗口;因此:当移动一步的时候,我们输出的最小值一定不能是刚脱离滑动窗口的这个数

而每次窗口的位置是[i - k + 1, i],所以a[i - k]位置的数一定不能作为当前窗口的输出!

q.front() == a[i - k],这里的a[i-k]是从a[1]开始判断的,当i = 4的时候,a[1]不能作为输出;此时队列的最小值可以是a[2]或a[3]或a[4],但就是不能是a[1]。而且,a[1]是第一个进行加入队列操作的,它要么在队列第一个元素,要么被比较而移除出去了,一定不会出现在队列的其他位置;所以通过这个式子就可以彻彻底底确定当前窗口的输出不会是a[1]。后续就是类似了。。。

3、双端队列

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值