数组模拟队列&栈
栈
#include<iostream>
using namespace std;
const int N=100010;
int stk[N],tt;//栈一般定义为stk[N],tt为栈点下标,栈习惯上从0开始
//插入
skt[++tt]=x;//表示在栈顶加入一个新的元素
//弹出
tt--;
//判断栈是否为空
if(tt>0)//说明不空
else //empty
//栈顶元素
stk[tt];
队列
//模拟队列
int q[N],hh,tt=-1;//队列习惯上从-1开始
//插入
q[++tt]=x;
//弹出
hh++;//将队头的指针向后移动一位则为弹出
//判断是否为空
if(hh<=tt) not empty
else empty
//取出队头元素
q[hh];
//还可以取出队尾
q[tt];
单调栈
题目:
输入一串数字,找到每一个数字所对应的在他左边离他最近且比他小的数,如果有这个数,则输出,若无,则输出-1
#include<iostream>
using namespace std;
const int N=100010;
int n;
int stk[N],tt;
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
int x;
cin>>x;
while(tt&&stk[tt]>=x) tt--;//当栈非空并且栈顶元素大于此时读入的这个x,那么这个栈顶元素就再也不会被用到了,因为要让序列为单调递增
if(tt) cout<<stk[tt]<<' ';//若栈非空,则说明该栈顶元素为离他最近的一个小于它的元素
else cout<<-1<<' ';//否则说明找不到这样的数,则返回-1
stk[++tt]=x;//最后要记得把该数再插回到队列当中
}
return 0;
}
单调队列
题目:
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回滑动窗口中的最小值和最大值。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出: [-1,-3,-3,-3,3,3]
[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
思路:
用一个队列来存,当窗口在滑动时,将即将要滑到的元素插入到队尾,将即将要划走的元素从队头弹出来。
在找到最值时,若用遍历枚举的话,会造成时间复杂度高,则此处和单调栈相同思路,先用暴力做法,再看在栈和队列中有哪些元素是没有用到的,将这些元素删掉后,再去看剩下的元素是否具有单调性,即可优化:
若取极值则取端点,若取某个点则用二分法
代码:
#include<iostream>
using namespace std;
const int N=1000010;
int n,k;
int a[N],q[N];//a存原来的值,q存的是单调队列
int main()
{
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++) scanf("%d",&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) printf("%d ",a[q[hh]]);//
}
puts("");
//最大值与最小值完全一致
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) printf("%d ",a[q[hh]]);//
}
puts("");
return 0;
}