AcWing
  • 首页
  • 课程
  • 题库
  • 更多
    • 竞赛
    • 题解
    • 分享
    • 问答
    • 应用
    • 校园
  • 关闭
    历史记录
    清除记录
    猜你想搜
    AcWing热点
  • App
  • 登录/注册

AcWing 154. 滑动窗口    原题链接    简单

作者: 作者的头像   摘星1-1 ,  2025-06-05 21:16:06 · 四川 ,  所有人可见 ,  阅读 4


0


普通队列超时版

/*
滑动窗口的大小为k, 每一次维护一个区间的最小和最大值, 每次新加入的元素若是比之前的大,那么就更新一下,如果没有那么就不更新
数据结构的选取: 队列
特殊的, 当滑动窗口的大小为 n或1 的时候那么最小值和最大值就是整个数组的最小最大值,前者输出一个,后者输出n个
*/
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n,k;
int a[N];
vector<long long> qmi;
vector<long long> qmx;
queue<long long> q;

// 默写快读模板
int rd(){
    // 初始化k
    int k = 0;
    // 循环吞掉非数字符号
    char c = getchar();
    while(!isdigit(c)){
        c = getchar();
    }
    // 循环转换数字符号为数字
    while(isdigit(c)){
        k = (k << 1) + (k << 3) + (c ^ '0'), c = getchar();
    }
    // 返回数字
    return k;
}


int main(){
    n = rd();
    k = rd();
    for(int i = 0; i < n ; i ++){
        cin >> a[i];
    }
    // 判断特殊情况
    // 找到数组的最小值和最大值
    long long mi = *min_element(a,a+n);
    long long mx = *max_element(a,a+n);
    if(k == 1){
        for(int i = 0 ; i < n ; i ++ ){
            cout << a[i] << " ";
        }
        cout << endl;
        for(int i = 0 ; i < n ; i ++ ){
            cout << a[i] << " ";
        }
        return 0;
    }
    if(k == n){
        cout << mi << endl << mx;
        return 0;
    }
    // 滑动窗口
    // 清空mi和mx
    mi = *min_element(a,a+k);
    mx = *max_element(a,a+k);
    // 存储最大最小元素
    qmi.push_back(mi);
    qmx.push_back(mx);
    // 队列中需要时刻保持拥有k个元素
    for(int i = 0 ; i < k ; i ++){
        q.push(a[i]);
    }
    for(int i = 1; i <= n - k; i ++){ // 枚举左端点(从1开始)
        // 入队 
        q.push(a[i+k-1]);
        // 队列的尾元素(新元素)假如大于当前的最小值那么就更新一下
        if(q.back() <= mi) mi = q.back();
        if(q.back() >= mx) mx = q.back();
        // 出队
        // 如果即将出队的元素是最大值或者最小值,那么重新选举最大值或最小值
        if(q.front() == mi){
            mi = *min_element(a+i+1,a+i+k+1);
        }
        if(q.front() == mx){
            mx = *max_element(a+i+1,a+i+k+1);
        }
        // 元素出队
        q.pop();
        // 维护最小最大元素
        qmi.push_back(mi);
        qmx.push_back(mx);
    }

    for(int i=0;i<qmi.size();i++){
        cout << qmi[i] << " ";
    }
    cout << endl;

    for(int i = 0; i <qmx.size(); i++){
        cout << qmx[i] << " ";
    }
    return 0;
}

单调队列版

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e6 + 10;
ll n,k;
ll a[N],q[N];// 定义原数组和单调队列


int main(){
    ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
    cin>>n>>k;
    for(int i=0;i<n;i++){
        cin>>a[i];
    }

    // 单调队列[最小值]
    // 初始化队列
    int hh = 0,tt = -1;
    // 循环遍历每一个元素
    for(int i=0;i<n;i++){
        // 是否头出?前人退休 (i - k + 1)代表当前的窗口在数组中的首地址
        // if(hh <= tt && hh < i - k + 1) hh ++;
        // hh 和 (i - k + 1)没有直接逻辑关系,因为 hh 是队列指针,而 i - k + 1 是数组下标。
        if(hh <= tt && q[hh] < i - k + 1) hh ++;
        // 循环新来的更强(因为是求最小值,所以更小)则退位 // 比最大的还要小故为最小
        while(hh <= tt && a[q[tt]] >= a[i]) tt--;
        // 取而代之
        q[++ tt] = i; // 存入下标
        // 当前元素至少大于等于k(第一个窗口大小)则输出头元素(头元素是最小的)
        // 我的理解是日拱一卒,每次新增元素导致都是一次移动,每次移动如果是新人更强则清除前人,如果是前人更强则不管新人如何累积,直到退休就在头队出队让仅此的后人即可
        if(i >= k - 1) cout << a[q[hh]] << " ";
    }

    cout << endl; // 回车

    // 单调队列[最大值]
    hh = 0, tt = -1;
    for(int i = 0 ; i < n ;i ++){
        if(hh <= tt && q[hh] < i - k + 1) hh ++; // 退休
        while(hh <= tt && a[q[tt]] <= a[i]) tt--; // 让贤
        q[++ tt] = i; // 贤进
        if(i >= k - 1) cout << a[q[hh]] << " ";
    }
    return 0;
}

0 评论

App 内打开
你确定删除吗?
1024
x

© 2018-2025 AcWing 版权所有  |  京ICP备2021015969号-2
用户协议  |  隐私政策  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标 qq图标
请输入绑定的邮箱地址
请输入注册信息