作者:额~我那个早过50了,忘记了
言归正传ca
什么是栈?
小李攒钱买了车,可是他家住在胡同的尽头。胡同很窄,只能通过一辆车,而且是死胡同。小李每天都为停车发愁,如果回家早了停在里面,早上上班就要让所有人挪车,先让最外面的车出去,然后挨着一辆一辆的出去,这样小李才能去上班。胡同很窄,只能通过一辆车,而且是死胡同,所以只能从胡同口进出。小汽车呈线性排列,只能从一端进出,后进的汽车先出去,如下图所示:
这种后进先出的线性序列,称为栈。栈是一种线性表,只不过是它是操作受限的线性表,只能在一端进行进出操作。进出的一端称为栈顶(top),另一端称为栈底(base)。假设栈为∶s=(a1,a2,…,an),那么a1为栈底元素,而an是栈顶元素。栈中的元素是按照a1、a2、…、an的顺序进栈,退栈的第一个元素应为栈顶元素。由于其先进后出的特点,栈又称为先进后出(First In Last Out, FILO)的线性表。
入栈和出栈
入栈:入栈前要判断是否栈满,如果栈已满,则入栈失败;否则将元素放入栈顶.
出栈:出栈前要判断是否栈空,如果栈是空的,则出栈失败;否则将栈顶元素暂存给一个变量.
stack简介
栈的英文名就是stack,在C++的STL标准库中提供了一个容器stack来实现栈的基本操作,可以方便的进行出入栈的操作。使用标准库的栈和队列时,先包含相关的头文件#include<stack>定义栈如下:stack<int> s;s.empty() 如果栈为空返回true,否则返回falses.size() 返回栈中元素的个数s.pop() 删除栈顶元素s.top() 返回栈顶的元素s.push() 在栈顶压入新元素**stack没有提供遍历所有元素的方法,不支持下标操作。因此入栈和出栈都需要使用循环实现。下面的代码是利用stack模拟实现1-10个数字的入栈和出栈操作。
#include<iostream>
#include<stack> //栈的头文件
using namespace std;
int main()
{
stack<int> s; //定义栈
for(int i=1; i<=10; i++) //将1-10入栈
{
s.push(i);
}
while(!s.empty()) //首先输出栈顶元素,然后将栈顶元素出栈,循环执行,直到栈为空。
{
cout <<s.top()<<" "; //输出首元素
s.pop(); //栈顶元素出栈
}
return 0;
}
栈相关训练:其实栈的定义以及基本操作都很好理解,但是在一道题目里面要不要用栈,该如何用栈其实还是有一定难度的。我们不妨通过下面几个习题来体会下栈的美妙之处。训练1:括号匹配给定一个只包含左右括号的合法括号序列,按右括号从左到右的顺序输出每一对配对的括号出现的位置(括号序列以0开始编号)。括号序列长度不超过100。输入格式:仅一行,表示一个合法的括号序列。输出格式:设括号序列有n个右括号。则输出包括n行,每行两个整数l,r,表示配对的括号左括号出现在第l位,右括号出现在第r位。输入输出样列:
输入样例:(())()
输出样例:1 20 34 5
解题思路:因为本题出现在栈的训练里面,大家首选用栈去解决,那么如果不是呢?你能看出来这是栈的应用吗?从样例中其实不容易看出规律,我们举一个特殊的例子,例如有这样一个括号((((())))),如果我们想找第一个匹配的括号,其实是找第一个右括号出现的位置,找到右括号的位置之后就是找离它最近的左括号,离他最近的左括号就是在左括号序列里面的最后一个,说到这里,大家是不是感觉有点像栈了,最后进去的左括号先出来,这不就是后进先出的栈吗?想到这里,题目就变得很简单了。我们定义一个栈,然后输入括号字符串,如果是左括号就先保存在栈里面,如果不是(即为右括号)则找离它最近的左括号的位置即栈顶,s.top()表示左括号位置,i表示右括号位置,需要注意的是,当这个左括号完成匹配之后需要出栈。完整的代码如下所示:
#include <iostream>
#include <stack>
#include <string>
using namespace std;
string a; //括号序列
stack<int> s; //定义栈
int main()
{
cin >> a;
for(int i=0; i<a.size(); i++)
{
if(a[i] == '(') s.push(i); //如果是左括号将其位置入栈
else //如果是右括号,则这个时候离它最近的左括号一定在栈顶
{
cout << s.top() << ' ' << i << endl; //s.top()表示最近左括号的位置,i代表右括号的位置
s.pop(); //该左括号匹配完之后需要出栈
}
}
return 0;
}
训练2:括号匹配升级(主题库1466)topscoding才有
假设表达式中允许包含圆括号和方括号两种括号,其嵌套的顺序随意,如 或[([ ][])]等为正确的匹配,[( ])或(或(( )))均为错误的匹配。本题的任务是检验一个给定表达式中的括号是否正确匹配。输人一个只包含圆括号和方括号的字符串,判断字符串中的括号是否匹配,匹配就输出“0K” ,不匹配就输出“Wrong“。输入:一行字符,只含有圆括号和方括号,个数小于255。输出:匹配就输出一行文本“0K",不匹配就输出一行文本“Wrong“。
样例输入:[(])
样例输出:Wrong
解题思路:有了上一题的经验之后,以后在遇到类似括号匹配的问题大家自然而然想到用栈的思想去解决问题,这也就是为什么要多刷题的原因。完整的代码如下:
#include<bits/stdc++.h>
using namespace std;
stack<char> a; //字符型栈
char t;
int main() {
while(cin>>t) {
if(t=='(') a.push(t); //左小括号入a栈
if(t=='[') a.push(t); //左中括号入a栈
if(t==')') { //如果是右小括号
if(a.empty()==1||a.top()!='(') { //如果这个时候a栈是空的,或者a栈的栈顶不是小括号,表示这个时候匹配不上,输出Wrong
cout<<"Wrong";
return 0;
}
a.pop(); //如果匹配上了,则出栈
}
if(t==']') { //同理
if(a.empty()==1||a.top()!='[') {
cout<<"Wrong";
return 0;
}
a.pop();
}
}
if(a.empty()==1) cout<<"OK"; //右边的找完了,这个时候a栈也应该空了,因此空了就是ok,不空就是wrong,该判断易忽略
else cout<<"Wrong";
return 0;
}
队列:
还记得我们之前讲过的买了新车的小李吗?为了解决胡同停车问题,小李跑了无数次居委会,终于将挡在胡同口的建筑清除了。这样住在胡同尽头的小李就可以早早回家把车停在最里面,并且每天都第一个开车去上班了。现在胡同被打通了,但是胡同还是很窄,只能通过一辆小车。小汽车呈线性排列,只能从一端进,另一端出,如下图所示,先进先出。
这种先进先出(First In First Out,FIFO) 的线性序列,称为“队列”。队列也是一种线性表,只不过它是操作受限的线性表,只能在两端操作:一端进,一端出。进的一端称为队尾(rear),出的一端称为队头(front)。 队列可以用顺序存储,也可以用链式存储。这里我们只侧重讲队列的顺序存储,队列的链式存储了解即可。假设队列为∶q=(a1,az,…,an),那么a1就是队头元素,而an是队尾元素。队列中的元素是按照 a1、a2、…、an的顺序进入的,退出队列时也只能按照这个顺序依次退出,也就是说只有在a1,a2,…,an-1都离开队列以后,an才能退出队列。
queue简介队列的英文名就是queue,使用队列时,先包含相关的头文件:#include<queue>定义队列如下:queue<int> q;q.empty() 如果队列为空返回true,否则返回falseq.size() 返回队列中元素的个数q.pop()删除队列首元素q.front()返回队首元素的值q.push()在队尾压入新元素q.back() 返回队列尾元素的值**注意∶跟stack类似,queue没有提供遍历所有元素的方法,不支持下标操作。如果需要遍历queue中所有元素,需要一遍调用front(),一边调用pop()。队列相关训练:其实队列的定义以及基本操作都很好理解,但是在一道题目面前要不要用队列,该如何用队列其实还是有一定难度的。我们不妨通过下面几个习题来体会下栈的美妙之处。
例题1:队列的使用
使用queue,实现输入n个数,输出他们的头元素、尾元素、当前队列中元素个数、当前队列是否为空。输入格式: 第一行输入n,第二行输入n个不同的数输出格式: 输出他们的头元素、尾元素、当前队列中元素个数、当前队列是否为空。
样例输入:
5
4 8 7 2 6
样例输出:
4 6 5 0
参考代码:
#include<bits/stdc++.h>
using namespace std;
queue<int> q;
int main(){
int n,t;
cin >>n;
for(int i=1;i<=n;i++){
cin >> t;
q.push(t);
}
cout << q.front()<< " ";
cout << q.back()<< " ";
cout << q.size()<< " ";
cout << q.empty();
return 0;
}
例题3:拓尔思舞会2(主题库2627)
和上题一样,男士们和女士们进入舞厅时,各自排成一队配舞伴。跳完一轮后的男女接到队伍尾部继续排队。不同的是,匹配的过程中,如果发生男女的舞力值相差超过10,那么舞力值低的那一位会离开舞厅,舞力值高的留在原地继续等待匹配下一个舞伴。如果在k支舞曲结束前,有一只队伍全员离开了,舞会就结束了,输出“Over”。输入格式:第1行两个正整数,表示男士人数m和女士人数n (1≤m,n≤1000)第2行m个正整数,表示每位男士的舞力值,第3行n个正整数,表示每位女士的舞力值,第4行一个正整数,表示舞曲的数目k (k≤1000)。输出格式:不超过k行,每行为两个数,用一个空格隔开表示配对舞伴的序号,男士在前,女士在后。如果匹配截止输出Over
样例输入:
2 3
1 15
2 6 11
7
样例输出:
1 2
15 6
1 11
15 6
1 11
15 6
1 11
解题思路:请自行补充
#include<bits/stdc++.h>
using namespace std;
int main()
{
int m,n,i,j,t,k;
cin>>m>>n;
queue<int>q1,q2;
for(i=1;i<=m;i++){ cin>>t; q1.push(t); }
for(i=1;i<=n;i++){ cin>>t; q2.push(t); }
cin>>k;
while(k--)
{ int t1=q1.front(),t2=q2.front();
if(abs(t1-t2)<=10) //如果舞力值相差小于10和上一题思想一致
{ cout<<t1<<" "<<t2<<endl;
q1.pop();q2.pop();
q1.push(t1);q2.push(t2);
}
else //如果舞力值相差大于10
{ if(t1<t2) q1.pop();
else q2.pop(); //较小的出队列
if(q1.size()==0||q2.size()==0)
{ cout<<"Over";return 0; } //如果没人了就输出“Over”
k++;
}
}
}
参考答案
#include <bits/stdc++.h>
using namespace std;
int main()
{
int m,n,k,i,t;
queue<int>boy,girl;
cin>>m>>n;
for(i=1;i<=m;i++)
{
cin>>t;
boy.push(t);
}
for(i=1;i<=n;i++)
{
cin>>t;
girl.push(t);
}
cin>>k;
while(k--)
{
if(boy.empty()==1||girl.empty()==1)
{
cout<<"Over";
return 0;
}
int t1=boy.front(),t2=girl.front();
if(abs(t1-t2)<=10)
{
cout<<t1<<" "<<t2<<endl;
boy.push(t1);girl.push(t2);boy.pop();girl.pop();
}else
{
if(t1<t2)
{
boy.pop();
}
else
{
girl.pop();
}
k++;
}
}
}