栈
模拟栈,栈是先进后出,只有一个头因此只用一个tt可以记录当前位置
ll st[N],tt;
void push(int x) {//向栈顶插入一个数x
tt++;
st[tt]=x;
}
int pop(){//从栈顶弹出一个数
return st[tt--];
}
int empty(){//判断栈顶是否为空
return tt<=0;
}
int query(){//查询栈顶元素
return st[tt];
}
例题:AcWing828
代码:
这里只写主函数,用到的函数都在上面了
int main(){
ll m;
cin>>m;
while(m--){
string str;
cin>>str;
if(str=="push"){
int x;
cin>>x;
push(x);
}
if(str=="pop") pop();
if(str=="empty"){
if(empty()) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
if(str=="query") cout<<query()<<endl;
}
return 0;
}
队列
队列是先进先出,头出尾进,要两个变量来记录位置,hh为头,tt为尾
例题:AcWing829
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef long long ll;
ll qu[N],hh,tt=-1;
void push(int x)//在队尾插入一个x
{
tt++;
qu[tt]=x;
}
int pop()//队首弹出一个元素
{
hh++;
}
int empty()//检查队列是否为空
{
return hh>tt;
}
int query()//查询队首元素
{
return qu[hh];
}
int main()
{
ll m;
cin>>m;
while(m--)
{
string str;
cin>>str;
if(str=="push")
{
int x;
cin>>x;
push(x);
}
if(str=="empty")
{
if(empty()) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
if(str=="query") cout<<query()<<endl;
if(str=="pop") pop();
}
return 0;
}
单调栈
例题:AcWing830
思路:如果用双循环的话必定超时,单调栈和双指针有点类似,可以先模拟一下暴力做法,左边的数比右边的数大的话必定不会作为输出答案,则可以删除。
例如,3 2 4的答案是-1 -1 2,这里的3、2都比4小,但是4对应位置输出2,不会是3,就是3在左边比2大则不可能用到就可以删除了。
本题可以用单调栈来存,所谓单调栈就是,栈里面的元素都是单调递增的,先进栈的相当于左边的,要是比后进栈的大的话就能删除,比如上面的3 2 4,3进栈,2进栈,3比2大,退栈,再进4…
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
typedef long long ll;
ll st[N],n,tt;
int main(){
scanf("%ld",&n);
for(int i=0;i<n;i++){
ll x;
scanf("%ld",&x);
while(tt&&st[tt]>=x) tt--;
if(tt) printf("%ld ",st[tt]);
else printf("%d ",-1);
st[++tt]=x;
}
return 0;
}
单调队列
例题:AcWing154
思路:如果用双循环的话必定超时,单调队列和双指针有点类似,可以先模拟一下暴力做法,左边的数比右边的数大的话必定不会作为输出答案,则可以删除。
例如,数列1 3 -1 -3 5,k=3的答案是-1 -3 -3,第一个滑动位置1 3 -1这里的1,3都比-1大,且在-1的前面,就是说有-1的时候不可能会将1、3作为答案输出,则可删除1、3,(可以理解为-1寿命最长且势力大),此时队列第二个滑动窗口3 -1 -3 这里的3、-1都比-3大,同理可删除。
本题可以用单调队列来存,所谓单调队列就是,队列里面的元素队首是最小的,之后依次进队,小的进队首,大的pop掉。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
typedef long long ll;
ll n,k,a[N],q[N],hh,tt;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>k;
for(int i=0;i<n;i++) cin>>a[i];
//滑动窗口最小值
hh=0,tt=-1;
for(int i=0;i<n;i++)//i为滑到了哪个数
{
//判断队头是否已经滑出窗口
if(hh<=tt&&i-k+1>q[hh]) hh++;//i每加一次必定有一个前面的数要弹出
//就是先插入的数比先要插入的数大的话
//则肯定轮不到先插入的数输出,就删除掉它tt--
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++)//i为滑到了哪个数
{
//判断队头是否已经滑出窗口
if(hh<=tt&&i-k+1>q[hh]) hh++;//i每加一次必定有一个前面的数要弹出
//就是先插入的数比先要插入的数小的话
//则肯定轮不到先插入的数输出,就删除掉它tt--
while(hh<=tt&&a[q[tt]]<=a[i]) tt--;
q[++tt]=i;//将这数插入进队列
if(i>=k-1) cout<<a[q[hh]]<<" ";
}
cout<<endl;
return 0;
}