通关数据结构 day_05 -- 单调队列

单调队列

经典应用:滑动窗口里的最大值/最小值

举例

假设有序列:

1 3 -1 -3 5 3 6 7

第一次滑动窗口是 【1 3 -1】最小值是 -1

第二次滑动窗口是 【3 -1 -3】最小值是 -3

以此类推最后一次滑动窗口是 【3 6 7】 最小值是 3

我们用队列来维护这个窗口,保证队列中存储的时时刻刻都是我们窗口中的元素

每一次求极值的时候,暴力做法是遍历窗口中的元素,时间复杂度是O(k)

单调队列定律:当一个选手比你小还比你强的时候,你永远无法超越他

那么只要我们的队列中存在如下情况:

前面有一个数,比我后面的数大,那么前面的点一定没有用

因此,我们就可以把大的点删了,最后会变成一个严格单调上升的队列

模板

常见模型:找出滑动窗口中的最大值/最小值
int hh = 0, tt = -1;
for (int i = 0; i < n; i ++ )
{
    while (hh <= tt && check_out(q[hh])) hh ++ ;  // 判断队头是否滑出窗口
    while (hh <= tt && check(q[tt], i)) tt -- ;
    q[ ++ tt] = i;
}

练习

154. 滑动窗口 - AcWing题库

给定一个大小为 n≤106 的数组。

有一个大小为 k 的滑动窗口,它从数组的最左边移动到最右边。

你只能在窗口中看到 k个数字。

每次滑动窗口向右移动一个位置。

以下是一个例子:

该数组为 [1 3 -1 -3 5 3 6 7],kk 为 33。

窗口位置 最小值 最大值
[1 3 -1] -3 5 3 6 7 -1 3
1 [3 -1 -3] 5 3 6 7 -3 3
1 3 [-1 -3 5] 3 6 7 -3 5
1 3 -1 [-3 5 3] 6 7 -3 5
1 3 -1 -3 [5 3 6] 7 3 6
1 3 -1 -3 5 [3 6 7] 3 7

你的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。

输入格式

输入包含两行。

第一行包含两个整数 n 和 k,分别代表数组长度和滑动窗口的长度。

第二行有 n 个整数,代表数组的具体数值。

同行数据之间用空格隔开。

输出格式

输出包含两个。

第一行输出,从左至右,每个位置滑动窗口中的最小值。

第二行输出,从左至右,每个位置滑动窗口中的最大值。

输入样例:

8 3
1 3 -1 -3 5 3 6 7

输出样例:

-1 -3 -3 -3 3 3
3 3 5 5 6 7
#include<iostream>
using namespace std;

const int N = 1000010;
int n,k;
int a[N],q[N];

int main()
{
    cin.tie(0);
    ios::sync_with_stdio(false);
    
    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++)
    {
        //  判断队头是否滑出窗口
        if(hh <= tt && i-k+1>q[hh])
        {
            hh++;
        }
        
        while(hh <= tt && a[q[tt]] >= a[i])
        {
            tt--;
        }
        
        q[++tt] = i;
        if(i >= k-1)
        {
            cout << a[q[hh]] << " ";
        }
        
    }
    
    cout << endl;
    
    hh = 0,tt = -1;
    
    for(int i = 0;i < n;i++)
    {
        //  判断队头是否滑出窗口
        if(hh <= tt && i-k+1>q[hh])
        {
            hh++;
        }
        
        while(hh <= tt && a[q[tt]] <= a[i])
        {
            tt--;
        }
        
        q[++tt] = i;
        if(i >= k-1)
        {
            cout << a[q[hh]] << " ";
        }
        
    }
    
    return 0;
}
posted @   ShibuyaKanon  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示