【总结】队列与栈

进出在同一侧,且满足后进先出,先进后出

车厢调度(train)
有一个火车站,铁路如图所示,每辆火车从A驶入,再从B方向驶出,同时它的车厢可以重新组合。假设从A方向驶来的火车有n节(n≤1000),分别按照顺序编号为1,2,3,…,n。假定在进入车站前,每节车厢之间都不是连着的,并且它们可以自行移动到B处的铁轨上。另外假定车站C可以停放任意多节车厢。但是一旦进入车站C,它就不能再回到A方向的铁轨上了,并且一旦当它进入B方向的铁轨,它就不能再回到车站C。
在这里插入图片描述

负责车厢调度的工作人员需要知道能否使它以a1,a2,…,an的顺序从B方向驶出,请来判断能否得到指定的车厢顺序。

解析:
观察发现,整个调度过程其实是在模拟入栈出栈的过程,而这个过程中,我们可以分成三种状态:栈前、栈中、栈后。我们可以发现,当某个数字出栈了,说明比它小的数字要么已经出栈了,要么还在栈里,不能是入栈前状态,并且在栈中的顺序是从大到小的(从栈顶往栈底看),比如出5,那么1,2,3,4要么已经在5之前出了,要么还在栈中(假如1,3,4在栈中,从栈顶往栈底看依次为4,3,1),不能是入栈前的状态。如果某个数字要出栈,那么当前在栈中的数字都必须小于它,否则就与栈的性质矛盾,不合法,于是我们可以这样解决:

从第一个数字开始扫描,a[i]表示当前出栈的数字,如果有比a[i]大的数字还在栈中,那么就产生矛盾,输出“NO”;否则,标记当前数字a[i]为栈后状态,那么[1, a[i]-1]这些数字如果还没出栈,标记为栈中状态。具体我们可以用0表示为确定状态,1表示栈中状态,2表示栈后状态。

#include<cstdio> #include<stack> using namespace std; stack<int> liu; int n,a[1005],b[1005]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&b[i]); for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(b[i]==j) a[j]=2; if(a[j]==2) continue; else if(b[i]>j) { if(a[j]==0) a[j]=1; } else if(b[i]<j) { if(a[j]>0) { printf("NO"); return 0; } } } } printf("YES"); }

字符串匹配问题(strs)
字符串中只含有括号 (),[],<>,{},判断输入的字符串中括号是否匹配。如果括号有互相包含的形式,从内到外必须是<>,(),[],{},例如。输入: [()] 输出:YES,而输入([]),([)]都应该输出NO。

解析:简历四个栈,记录四种符号

  • 由于有优先级,所以当一个右括号匹配时,比他优先级低的括号必须已经匹配完成才行,左括号入栈即可,最后判断是否匹配完
#include<cstdio> #include<stack> #include<cstring> using namespace std; stack<int> a,b,c,d; char s[300]; int n,len,flag; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { flag=1; scanf("%s",s); len=strlen(s); for(int i=0;i<len;i++) { if(s[i]=='{') { a.push(1); } if(s[i]=='}') { if(a.empty()==1) { flag=0; break; } a.pop(); if(b.empty()==0||c.empty()==0||d.empty()==0) { flag=0; break; } } if(s[i]=='[') { b.push(1); } if(s[i]==']') { if(b.empty()==1) { flag=0; break; } b.pop(); if(c.empty()==0||d.empty()==0) { flag=0; break; } } if(s[i]=='(') { c.push(1); } if(s[i]==')') { if(c.empty()==1) { flag=0; break; } c.pop(); if(d.empty()==0) { flag=0; break; } } if(s[i]=='<') { d.push(1); } if(s[i]=='>') { if(d.empty()==1) { flag=0; break; } d.pop(); } } //判断是否匹配完 if(a.empty()==0||b.empty()==0||c.empty()==0||d.empty()==0) { flag=0; } if(flag) printf("YES\n"); else {//多组数据,要清空 printf("NO\n"); while(a.empty()==0) a.pop(); while(b.empty()==0) b.pop(); while(c.empty()==0) c.pop(); while(d.empty()==0) d.pop(); } } }

中缀表达式值(expr)
前缀和后缀表达式都很容易求值,我们将中缀表达式转换成后缀表达式,且后缀表达式不需要括号。复杂度O(n)

  1. 建立一个符号栈,一个数字栈
  2. 如果遇到一个数,加入数字栈
  3. 如果遇到左括号,把左括号入符号栈
  4. 如果遇到右括号,不断取出符号栈顶并在数字栈中运算,直到栈顶为左括号,把左括号出栈
  5. 如果遇到运算符,**只要栈顶符号的优先级不低于新符号,就不断取出栈顶并运算,最后把新括号入栈。**优先级乘除乘方>加减>左括号
  6. 最终数字栈剩下的数字就是结果。
    分析:第五点保证了先进先出(优先级相同时)以及符号相互的优先级,第四点其实就是处理括号内的运算,在符号栈中越先出去的,在后缀运算中优先级更高
#include<cstdio> #include<cmath> #include<stack> #include<cstring> using namespace std; //stack<char> f; const int MAXN=1e6+5; char a[MAXN],f[MAXN]; long long n,m,t,b[MAXN]; int main() { scanf("%s",a); n=strlen(a); for(int i=0;i<n;i++) { if(a[i]>='0'&&a[i]<='9') { int w=0; while(a[i]>='0'&&a[i]<='9') w=w*10+(a[i]-'0'),i++; b[++m]=w; } if(a[i]=='(') f[++t]='('; else if(a[i]==')') { while(f[t]!='(') { char x=f[t]; t--; if(x=='+') b[m-1]=b[m-1]+b[m],m--; if(x=='-') b[m-1]=b[m-1]-b[m],m--; if(x=='*') b[m-1]=b[m-1]*b[m],m--; if(x=='/') b[m-1]=b[m-1]/b[m],m--; if(x=='^') b[m-1]=pow(b[m-1],b[m]),m--; } t--; } else if(a[i]=='*'||a[i]=='/'||a[i]=='^') { while(f[t]=='*'||f[t]=='/'||f[t]=='^') { char x=f[t]; t--; if(x=='*') b[m-1]=b[m-1]*b[m],m--; if(x=='/') b[m-1]=b[m-1]/b[m],m--; if(x=='^') b[m-1]=pow(b[m-1],b[m]),m--; } f[++t]=a[i]; } else if(a[i]=='+'||a[i]=='-') { while(f[t]=='+'||f[t]=='-'||f[t]=='*'||f[t]=='/'||f[t]=='^') { char x=f[t]; t--; if(x=='*') b[m-1]=b[m-1]*b[m],m--; if(x=='/') b[m-1]=b[m-1]/b[m],m--; if(x=='^') b[m-1]=pow(b[m-1],b[m]),m--; if(x=='+') b[m-1]=b[m-1]+b[m],m--; if(x=='-') b[m-1]=b[m-1]-b[m],m--; } f[++t]=a[i]; } /*for(int i=1;i<=m;i++) printf("%d ",b[i]); printf("\n");*/ } for(int i=t;i>=1;i--) { char x=f[i]; if(x=='+') b[m-1]=b[m-1]+b[m],m--; if(x=='-') b[m-1]=b[m-1]-b[m],m--; if(x=='*') b[m-1]=b[m-1]*b[m],m--; if(x=='/') b[m-1]=b[m-1]/b[m],m--; if(x=='^') b[m-1]=pow(b[m-1],b[m]),m--; } printf("%lld",b[1]); }

括号画家
写累了,直接看代码吧:
错误代码

#include<cstdio> #include<cmath> #include<stack> #include<cstring> #include<algorithm> using namespace std; stack<int> f; const int MAXN=100005; char s[MAXN]; int n,ans,t; int main() { scanf("%s",s); n=strlen(s); for(int i=0;i<n;i++) { t++; if(s[i]=='('||s[i]=='['||s[i]=='{') f.push(s[i]); else if(f.empty()==1||(s[i]==')'&&f.top()!='(')||(s[i]==']'&&f.top()!='[')||(s[i]=='}'&&f.top()!='{')) { t=0; while(f.empty()==0) f.pop(); } else f.pop(); int m=f.size(); ans=max(ans,t-m); } printf("%d",ans); }

LJS的数据:
( ( ( { } [ ] ( ( ( )
正确输出:4
我的输出:6

为什么会这样呢?会发现我并没有考虑多出来的没有匹配的括号,计算时去掉了f.size()即栈的长度,如果在最左边,这种算法是成立的;而如果未匹配到的在中间,就是不满足题意,而我相当于把两个满足要求的子序列合并在一起了。只需判断当前子序列末尾与栈的是否完全相同即可

正解:

#include<cstdio> #include<cmath> #include<stack> #include<cstring> #include<algorithm> using namespace std; stack<int> f; const int MAXN=100005; char s[MAXN]; int n,ans,t; int main() { scanf("%s",s); n=strlen(s); for(int i=0;i<n;i++) { t++; if(s[i]=='('||s[i]=='['||s[i]=='{') f.push(s[i]); else if(f.empty()==1||(s[i]==')'&&f.top()!='(')||(s[i]==']'&&f.top()!='[')||(s[i]=='}'&&f.top()!='{')) { //说明以后的子序列不能延续这以前的部分了,直接删除 t=0; while(f.empty()==0) f.pop(); } else f.pop(); int m=f.size(); bool flag=1; if(m!=0) { int b[MAXN]={},len=0; while(f.empty()==0) { b[++len]=f.top(); f.pop(); } for(int j=i-t+1;j<=i-t+m;j++) { if(s[j]!=b[m-(j-(i-t+1))]) flag=0; } for(int i=len;i>=1;i--) f.push(b[i]); if(flag) ans=max(ans,t-m); } if(flag) ans=max(ans,t-m); } printf("%d",ans); }

队列

进出在异侧,且满足先进先出,后进后出

占卜DIY

#include <cstdio> #include <iostream> #include <queue> #include <algorithm> using namespace std; //队列 //上次没有在操作完后统计,必须操作完后再统计 const int MAXN = 105; queue<int> a[MAXN]; int x, ans, b[MAXN]; char c[MAXN]; //这道题从一边进入,另一边取出,且进入和取出的方向是固定的,所以用队列 //因为栈是从同一边进入和取出 int main() { for (int i = 1; i <= 12; i++) { for (int j = 1; j <= 4; j++) { cin >> c[j]; if (c[j] == '0') c[j] = 10; else if (c[j] > '0' && c[j] <= '9') c[j] -= '0'; else if (c[j] == 'A') c[j] = 1; else if (c[j] == 'J') c[j] = 11; else if (c[j] == 'Q') c[j] = 12; else if (c[j] == 'K') c[j] = 13; } for (int j = 4; j >= 1; j--) a[i].push(c[j]); //注意队列的方向 } for (int j = 1; j <= 4; j++) { cin >> c[j]; if (c[j] == '0') c[j] = 10; else if (c[j] > '0' && c[j] <= '9') c[j] -= '0'; else if (c[j] == 'A') c[j] = 1; else if (c[j] == 'J') c[j] = 11; else if (c[j] == 'Q') c[j] = 12; else if (c[j] == 'K') c[j] = 13; a[13].push(c[j]); //注意队列的方向 } for (int i = 1; i <= 4; i++) { x = a[13].front(), a[13].pop(); while (1) { if (x >= 100) x -= 100; if (x == 13) { break; } int t = x; a[t].push(x + 100); x = a[t].front(); a[t].pop(); } } for (int i = 1; i <= 12; i++) { while (a[i].empty() == 0) { int t = a[i].front(); a[i].pop(); if (t > 100) b[t - 100]++; } } for (int i = 1; i <= 12; i++) { if (b[i] == 4) ans++; } printf("%d", ans); }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530439.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(11)  评论(0编辑  收藏  举报  
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示