C++单调队列详解

本文详细介绍了单调队列的概念、用途和工作原理,特别是在解决区间最值问题上的应用。通过一个POJ 2823题目实例,阐述了如何使用单调队列以O(n)的时间复杂度找到每个长度为k区间内的最小值/最大值,对比了暴力和线段树方法,突出了单调队列的效率优势。

单调队列到底是什么呢?

简单地按照字面意思来说,单调队列是一种队列(踢飞)

但是这种队列和普通的队列有着很大的区别,怎么说呢:

它的队首和普通的队列一样,只能删除元素。

而它的队尾既可以添加元素也可以删除元素。

通常来说也可以叫做输入受限的双端队列(栈)。


单调队列是做什么用的呢?

简单来说就是用来维护一段区间内的单调上升/下降性质,导出性质就是也可以用来维护一个区间内的最值。

我们先来看一道例题:poj 2823

题目大意

给你n个数字和一个长度k,让你从前到后输出每个长度为k的区间内的最小值/最大值。

思路

1.暴力 

从前到后每个区间遍历一遍,复杂度是O(n*k),铁定超时了

2.线段树维护区间最值

n的范围是10^6,线段树需要4*10^6的空间貌似不会爆,时间复杂度是O(nlogn),可以接受

但是对这道题而言,单调队列显然是优于线段树的另一种思路,先来了解一下它的

工作原理

假设现在我们维护一个递减(最大值)的单调队列,当一个数据想要入队时,需要执行以下操作:

①从队尾到队首开始遍历,如果碰到元素比待入队元素要小,那么这个元素便失去了作用(因为维护的是最大值,如果有一个比当前值小,那它一定不是最大的),将其出队。直到碰到一个比当前值大的元素,跳出循环;

②从队首开始清除已经不符合条件的元素(比如已经“过时”即不在当前区间内的元素,视题目而定);

在执行①之后,队列中所有之前元素都比当前元素大

执行②之后,队列中不再有“过时”元素,即此时队列中只有在区间内而且队首元素就是最大值。

复杂度

因为每个元素都只会入队一次和出队一次,所以复杂度为O(n)。

然后这道题目就可以成功地套上板子做出来了~~

不知道为什么c++提交就可以过,g++会超时,总之poj上还有好多奇怪的问题。。。。

#include<iostream>
#include<cstdio>
#include<vector>
#include<set>
#include<map>
#include<string.h>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
#define LL long long
#define mod 1000000007
#define inf 0x3f3f3f3f
#define sqr(a) (a)*(a)
#define lan(a,b) memset(a,b,sizeof(a))

using namespace std;

int a[1000010];
int num1[1000010];
int num2[1000010];
int b[1000010];

int c[1000010];
int d[1000010];

int main()
{
    int n,k;
    while(~scanf("%d%d",&n,&k))
    {
        lan(a,0);
        lan(b,0);
        lan(c,0);
        lan(d,0);
        lan(num1,0);
        lan(num2,0);
        int h1=0,t1=0;
        int h2=0,t2=0;
        for(int i=1;i<=n;i++)
        {
            int tem;
            scanf("%d",&tem);
            while(h1<t1&&a[t1-1]>tem) t1--;
            a[t1++]=tem;
            num1[t1-1]=i;
            while(i-num1[h1]+1>k) h1++;
            if(i>=k)
                c[i]=a[h1];
            while(h2<t2&&b[t2-1]<tem) t2--;
            b[t2++]=tem;
            num2[t2-1]=i;
            while(i-num2[h2]+1>k) h2++;
            if(i>=k)
                d[i]=b[h2];
        }
        for(int i=k;i<=n;i++)
        {
            if(i==n)
                printf("%d\n",c[i]);
            else
                printf("%d ",c[i]);
        }
         for(int i=k;i<=n;i++)
        {
            if(i==n)
                printf("%d\n",d[i]);
            else
                printf("%d ",d[i]);
        }
    }
    return 0;
}


# C++单调队列与单调栈详解 ## 一、概述 单调队列和单调栈是数据结构中非常重要的两种结构,它们在解决某些特定问题时具有显著的效率优势。通过维护数据的单调性,能够快速找到满足条件的元素。 ## 二、单调栈 ### 1. 定义 单调栈是一种栈结构,其内部元素始终保持单调性(递增或递减)。每次新元素入栈时,都会与栈顶元素进行比较,直到找到合适的位置。 ### 2. 应用场景 - **括号匹配**:判断括号是否正确闭合。 - **最大矩形面积**:求解直方图中最大矩形面积。 - **每日温度**:找出每天之后第一个温度更高的天数。 ### 3. 示例代码 ```cpp #include <iostream> #include <stack> #include <vector> using namespace std; vector<int> dailyTemperatures(vector<int>& temperatures) { stack<int> s; vector<int> result(temperatures.size(), 0); for (int i = 0; i < temperatures.size(); ++i) { while (!s.empty() && temperatures[i] > temperatures[s.top()]) { int index = s.top(); s.pop(); result[index] = i - index; } s.push(i); } return result; } int main() { vector<int> temperatures = {73, 74, 75, 71, 69, 72, 76, 73}; vector<int> result = dailyTemperatures(temperatures); for (int i : result) { cout << i << " "; } return 0; } ``` ## 三、单调队列 ### 1. 定义 单调队列是一种队列结构,其内部元素始终保持单调性(递增或递减)。通常使用双端队列实现,以便在队首和队尾进行高效的插入和删除操作。 ### 2. 应用场景 - **滑动窗口最大值**:求解滑动窗口中的最大值。 - **最长连续递增子序列**:求解最长连续递增子序列的长度。 ### 3. 示例代码 ```cpp #include <iostream> #include <deque> #include <vector> using namespace std; vector<int> maxSlidingWindow(vector<int>& nums, int k) { deque<int> dq; vector<int> result; 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); // 添加窗口最大值到结果中 if (i >= k - 1) { result.push_back(nums[dq.front()]); } } return result; } int main() { vector<int> nums = {1,3,-1,-3,5,3,6,7}; int k = 3; vector<int> result = maxSlidingWindow(nums, k); for (int i : result) { cout << i << " "; } return 0; } ``` ## 四、总结 单调栈和单调队列的核心思想是通过维护数据的单调性,快速找到满足条件的元素。它们在解决某些特定问题时非常高效,尤其是在需要频繁查找最大值或最小值的场景中。 ### 知识点详解 1. **单调栈**:利用栈结构维护元素的单调性,适用于需要快速找到下一个更大或更小元素的问题。 2. **单调队列**:利用双端队列维护窗口内元素的单调性,适用于滑动窗口问题中的最大值查找。 3. **滑动窗口**:一种处理数组的技巧,通过固定大小的窗口遍历数组,结合单调队列可以高效解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值