首页 > 其他分享 >单调队列

单调队列

时间:2023-02-17 15:32:50浏览次数:47  
标签:10 队列 back int front 单调


单调队列是什么呢?可以直接从问题开始来展开。
Poj 2823
给定一个数列,从左至右输出每个长度为m的数列段内的最小数和最大数。
数列长度:N<=106,m<=N

暴力解

很直观的一种解法,那就是从数列的开头,将窗放上去,然后找到这最开始的k个数的最大值,然后窗最后移一个单元,继续找到k个数中的最大值。

#include <iostream>
#include <cstdio>
using namespace std ;
const int MAX = 105 ;
struct node{
int index ;
int minn ;
int maxx ;
};

int main()
{

int a[MAX] ;
node ans[MAX] ;
int n ,m ;
int k = 1 ;
cin >> n >> m ;
for(int i = 1 ; i<=n ;i++)
{
cin >>a[i] ;
}
for(int i = 1 ; i<=n-m+1; i++)
{
int maxn = -0x3f3f3f3f ;
int minn = 0x3f3f3f3f ;
for(int j = i; j<=i+m-1 ;j++)
{

maxn = max(a[j],maxn);
minn = min(a[j],minn) ;

}
node s ;
s.index = i ;
s.minn = minn ;
s.maxx = maxn ;
ans[k++] =s;

}
for(int i = 1 ; i<=k-1 ;i++)
{
cout<<ans[i].index <<" : " <<" min :"<< ans[i].minn<<" max : "<<ans[i].maxx <<endl ;
}



return 0 ;
}

单调队列解: 

对于单调队列,我们这样子来定义:

  • 1、维护区间最值
  • 2、去除冗杂状态 如上题,区间中的两个元素a[i],a[j](假设现在再求最大值)
    若 j>i且a[j]>=a[i] ,a[j]比a[i]还大而且还在后面(目前a[j]留在队列肯定比a[i]有用,因为你是往后推, 核心思想 !!!)
  • 3、保持队列单调,最大值是单调递减序列,最小值反之
  • 4、最优选择在队首

单调队列实现的大致过程:
1、维护队首(对于上题就是如果队首已经是当前元素的m个之前,则队首就应该被删了,head++)
2、在队尾插入(每插入一个就要从队尾开始往前去除冗杂状态,保持单调性)

简单举例应用
数列为:6 4 10 10 8 6 4 2 12 14
N=10,K=3;
那么我们构造一个长度为3的单调递减队列:
首先,那6和它的位置0放入队列中,我们用(6,0)表示,每一步插入元素时队列中的元素如下
插入6:(6,0);
插入4:(6,0),(4,1);
插入10:(10,2);
插入第二个10,保留后面那个:(10,3);
插入8:(10,3),(8,4);
插入6:(10,3),(8,4),(6,5);
插入4,之前的10已经超出范围所以排掉:(8,4),(6,5),(4,6);
插入2,同理:(6,5),(4,6),(2,7);
插入12:(12,8);
插入14:(14,9);
那么f(i)就是第i步时队列当中的首元素:6,6,10,10,10,10,8,6,12,14
同理,最小值也可以用单调队列来做。

单调队列的时间复杂度是O(N),因为每个数只会进队和出队一次,所以这个算法的效率还是很高的。
注意:建议直接用数组模拟单调队列,因为系统自带容器不方便而且不易调试,同时,每个数只会进去一次,所以,数组绝对不会爆,空间也是S(N),优于堆或线段树等数据结构。

更重要的:单调是一种思想,当我们解决问题的时候发现有许多冗杂无用的状态时,我们可以采用单调思想,用单调队列或类似于单调队列的方法去除冗杂状态,保存我们想要的状态.

#include <iostream>
#include <cstdio>
using namespace std ;
const int MAX = 1005 ;
struct Num{
int index ;// 需要记录单调队列内的下标
int x ;// 大小 ;
};
int a[MAX]; // 原始队列
Num q[MAX]; // 单调队列
int n ,m ;

void Getmax()
{
// 求最大值,是单调递减,队首就是最大值
// 1 每次加一个新数,就要维护一次递减队列,
// 2 从队尾开始向队首弹比当前加入的新数要小的元素 ,
// 3 保证队列长度为 m
int front , back ;
front = 1 ;
back= 0 ;
for(int i = 1 ; i<=n ; i++)
{
if(front >back)
// 队列为空
printf("0 ");
else
{
if(q[front].index +m <i)
front++;
printf("%d ",q[front].x);

}
while(front<=back && q[back].x<=a[i])
// 当队列非空的时候 ,不断弹出队尾比当前小的元素,维护一个单调
// 递减的队列 ;
back-- ;
back++ ;
q[back].x = a[i] ;// 加入队列
q[back].index = i ;

}



}
void getmin()
{
int front , back ;
front = 1 ;
back= 0 ;
for(int i = 1 ; i<=n ; i++)
{
if(front >back)
printf("0 ");
else
{
if(q[front].index +m <i)
front++;
printf("%d ",q[front].x);

}
while(front<=back && q[back].x>=a[i])

back-- ;
back++ ;
q[back].x = a[i] ;// 加入队列
q[back].index = i ;

}


}
int main()
{

cin >> n >> m ;
for(int i = 1 ; i<=n; i++)
cin >>a[i] ;
Getmax() ;// 求最大 , 维护递减队列


return 0 ;
}

 模板题 ​​https://cn.vjudge.net/problem/POJ-2823​

// File Name: deque.cpp
// Author: xiaxiaosheng
// Created Time: 2018年12月21日 星期五 23时16分47秒

#include<deque>
#include<stack>
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>

using namespace std;
const int MAX = 1e6+10 ;

struct node{
int x ;
int index ;
}q[MAX];
int a[MAX] ;
int n , m ;
int mx[MAX] ,mn[MAX] ;
void getmin()
{
int i , head = 1 , tail = 0 ;
for(i = 1 ; i<m ; i++)
{
// 先将前m-1个数放入队列 , 每次都要维护递增队列,
while(head<=tail && tail && q[tail].x >=a[i]) tail-- ;
q[++tail].x = a[i] ;
q[tail].index = i ;
}
for(; i<=n ;i++)
{
while(head<=tail && q[tail].x >=a[i] ) tail-- ;
q[++tail].x = a[i] ;
q[tail].index = i ;
while(q[head].index <i -m + 1)
head++ ;
mn[i-m+1] = q[head].x ;
}
return ;
}
void getmax()
{
int i ;
int head = 1 , tail = 0 ;
for(i = 1 ;i<m ; i++)
{
while(head<=tail && q[tail].x<=a[i]) tail-- ;
q[++tail].x = a[i] ;
q[tail].index = i ;
}
for(;i<=n;i++)
{
while(head<=tail&& q[tail].x <=a[i] ) tail-- ;
q[++tail].x = a[i ] ;
q[tail].index = i ;
while(q[head].index <i-m+1 ) head++ ;
mx[i-m+1] = q[head].x ;
}
return ;
}

int main()
{
cin >> n >> m ;
for(int i = 1 ; i<=n ; i++)
cin >> a[i] ;
getmin();
getmax();
for(int i = 1 ; i<= n-m+1 ; i++ )
{
if(i ==1 )
printf("%d",mn[i]);
else
printf(" %d",mn[i]) ;
}
printf("\n");
for(int i = 1 ; i<=n-m+1 ;i++)
{
if(i ==1 )
{
printf("%d",mx[i] );
}
else
printf(" %d",mx[i]) ;

}
return 0;
}

考试周,先学这些吧.... 

 

 

标签:10,队列,back,int,front,单调
From: https://blog.51cto.com/u_15970235/6064293

相关文章

  • 堆-- 神奇的优先队列
    堆是什么?是一种特殊的完全二叉树,就像树一样。有没有发现这棵二叉树有一个特点?就是所有的父节点都比子节点小(PS:就是圆圈的数值,圆圈外面的编号是这个节点的编号,)那么符合这样......
  • 【LeetCode栈与队列#06】前K个高频元素,以及pair、priority_queue的使用
    前K个高频元素力扣题目链接(opensnewwindow)给定一个非空的整数数组,返回其中出现频率前k高的元素。示例1:输入:nums=[1,1,1,2,2,3],k=2输出:[1,2]示......
  • 【数据结构】栈与队列的实际应用——球钟问题
    球钟:球钟为一种计时工具,其主要原理为通过小球的移动来进行实践的记录。它有三个能容纳若干球的指示器:小时指示器、五分钟指示器、分钟指示器。如果小时、五分钟、分钟指示......
  • 【数据结构】队列
    队列队列是限制在两端进行数据插入或删除的线性表,其特点为“先入先出,后入后出”。 允许进行入队的一端称为“队尾”,允许进行出兑的一端称为“队首”。顺序队相关代码:......
  • 【LeetCode栈与队列#05】滑动窗口最大值
    滑动窗口最大值力扣题目链接(opensnewwindow)给定一个数组nums,有一个大小为k的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的k个数字。......
  • 【LeetCode队列#04】逆波兰表达式求值(仍然是经典的栈操作)
    逆波兰表达式求值力扣题目链接(opensnewwindow)根据逆波兰表示法,求表达式的值。有效的运算符包括+,-,*,/。每个运算对象可以是整数,也可以是另一个逆波兰表达......
  • 从上至下遍历二叉树---队列的性质
    问题:从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。剑指Offer32-I.从上到下打印二叉树-力扣(LeetCode)思路:利用队列先入先出的性质,可以依次打......
  • 【LeetCode队列#03】删除字符串中所有的相邻重复项
    删除字符串中所有的相邻重复项力扣题目链接(opensnewwindow)给出由小写字母组成的字符串S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。在S上反复执行重......
  • MQ 消息队列 比较
    为什么需要消息队列削峰业务系统在超高并发场景中,由于后端服务来不及同步处理过多、过快的请求,可能导致请求堵塞,严重时可能由于高负荷拖垮Web服务器。为了能支持最高峰......
  • 【LeetCode队列#02】有效括号
    有效括号力扣题目链接(opensnewwindow)给定一个只包括'(',')','{','}','[',']'的字符串,判断字符串是否有效。有效字符串需满足:左括号必须用相同类型的右括号闭合。左......