单调队列问题

单调队列,即单调递减或单调递增的队列。

单调队列的作用:用来解决“滑动窗口”问题。

1438. 绝对差不超过限制的最长连续子数组

2762. 不间断子数组 

239. 滑动窗口最大值

P1886 滑动窗口 /【模板】单调队列

题中说满足任意两个数的差值的绝对值<=limit,那么只需要让这个数组中的最大值与最小值的差值<=limit即可,关键就是如何寻找一个数组内的最大值与最小值,同时数组一直在改变。并且当最大值与最小值的差值>limit时,我们要按照给出的数字的顺序将最小值或者最大值pop掉,直到满足<=limit,更新元素的个数。

代码:

class Solution {
public:
    int longestSubarray(vector<int>& nums, int limit) {
        int n=nums.size();
        int left=0,right=0,ans=0;
        deque<int>quen,quex;
        while(right<n)
        {
            while(!quex.empty()&&nums[right]>quex.back())
            quex.pop_back();

            while(!quen.empty()&&nums[right]<quen.back())
            quen.pop_back();
            
            quex.push_back(nums[right]);
            quen.push_back(nums[right]);
            right++;
            while(quex.front()-quen.front()>limit)
            {
                if(quex.front()==nums[left]) quex.pop_front(); //最大值
                if(quen.front()==nums[left]) quen.pop_front(); //最小值
                left++;
            }
           
            ans=max(ans,right-left);
        }
        return ans;
    }
};

 

思路与上一题大致相同,只不过该题需要求出有几个子数组满足当前条件,例如:当数组为[4,2]s时,添加一个4,数组会有[4,2,4],[2,4],[4]种情况,对应添加了i-left+1个数组。一般对两者之差有限制的话,都两个队列,一个为存放最大值的队列,一个为存放最小值的队列。 

 代码:

class Solution {
public:
    long long continuousSubarrays(vector<int>& nums) {
        int n=nums.size();
        long long ans=0;
        int left=0,right=0;
        deque<int>quex,quen;
        while(right<n)
        {
            while(!quex.empty()&&nums[right]>quex.back())
            quex.pop_back();
            while(!quen.empty()&&nums[right]<quen.back())
            quen.pop_back();
            quex.push_back(nums[right]);
            quen.push_back(nums[right]);

            while(quex.front()-quen.front()>2)
            {
                if(nums[left]==quex.front()) quex.pop_front();
                if(nums[left]==quen.front()) quen.pop_front();
                left++;
            }
            ans+=right-left+1;
            right++;
        } 
        return ans; 
    }
};

先插入k个元素,其中que.front()永远为这个窗口中元素的最大值,那么如何push元素与如何pop元素成为关键,因为que.front()为最大元素值,那么要插入的这个值比队列中的值大的话,那么把队列中小的给pop掉,使得队首为长度为k的元素中的最大值,最后答案为每次操作后队列的队首。

代码:

class Solution {
private:
    class MyQueue {
    public:
       deque<int>que;    
       void pop(int val)
       {
           if(!que.empty()&&val==que.front())
           que.pop_front();
       }      
       void push(int val)
       {
           while(!que.empty()&&val>que.back())
           que.pop_back();
           que.push_back(val);
       }
       int front()
       {
           return que.front();
       }
    }; 
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
       MyQueue que;
       vector<int>ans;
       int n=nums.size();
       for(int i=0;i<k;i++)
       {
           que.push(nums[i]);
       }
       ans.push_back(que.front());
       for(int i=k;i<n;i++)
       {
           que.pop(nums[i-k]);
           que.push(nums[i]);
           ans.push_back(que.front());
       }
       return ans;
    }
};

P1886 滑动窗口 /【模板】单调队列

 

 代码:

#include<bits/stdc++.h>
using namespace std;
int a[1000009];
int n;
void quemax(int a[],int k)
{
	deque<int>que;
	vector<int>ans;
	for(int i=0;i<k;i++)
	{
		while(!que.empty()&&a[i]>que.back())
		que.pop_back();
		que.push_back(a[i]);
	}
	ans.push_back(que.front());
	for(int i=k;i<n;i++)
	{
		if(!que.empty()&&a[i-k]==que.front())	
		que.pop_front(); 
		while(!que.empty()&&a[i]>que.back())
		que.pop_back();
		que.push_back(a[i]);
		ans.push_back(que.front());
	} 
	for(int i=0;i<ans.size()-1;i++)
	{
		cout<<ans[i]<<' ';
	}
	cout<<ans[ans.size()-1]<<endl;
}
void quemin(int a[],int k)
{
	deque<int>que;
	vector<int>ans;
	for(int i=0;i<k;i++)
	{
		while(!que.empty()&&a[i]<que.back())
		que.pop_back();
		que.push_back(a[i]);	
	} 	
	ans.push_back(que.front());
	for(int i=k;i<n;i++)
	{
		if(!que.empty()&&a[i-k]==que.front())
		que.pop_front();
		while(!que.empty()&&a[i]<que.back())
		que.pop_back();
		que.push_back(a[i]);
		ans.push_back(que.front());
	}
	for(int i=0;i<ans.size()-1;i++)
	{
		cout<<ans[i]<<' ';
	}
	cout<<ans[ans.size()-1]<<endl;
}
int main()
{
	int k;
	cin>>n>>k;
	for(int i=0;i<n;i++)
	{
		cin>>a[i];	
	} 
	quemin(a,k);
	quemax(a,k);
	return 0;	
} 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值