0
点赞
收藏
分享

微信扫一扫

信息学集训 | 08 栈与队列实战



戳一戳!和我一起走进信息学的世界

导读

信息学能够有助于孩子未来工作发展,提升孩子的综合能力。


前两节课,我们讲了栈和队列的理论,讲了简单的代码实现,今天我们深入了解STL库中的栈和队列,并且通过几道题目来更好地掌握STL库吧。

往期回顾

【NOIP竞赛/CSP认证】

▶  ​​赛前必看!信息学名师带你复习NOIP竞赛初赛及CSP认证初赛​​


【信息学精华帖】

▶  ​​收藏!交流会内容全公开,让你陪孩子更好地学习信息学​​

▶  ​​信息学的万般好处!附C++必备基础知识总结​​

▶  ​​信息学提高班知识体系详解与家长常见问题解答!让孩子赢在提高班学习的起跑线!​​

▶  ​​再回首,最全提高班知识总结,做更优秀的自己!​​

▶  ​​早知道!信息学集训班大揭秘!你想知道的的都在这!​​


信息学集训

▶  ​​01 温故知新,以更好状态学习数据结构和算法​​

▶  ​​02 信息学初赛必备计算机知识大串讲​​

▶  ​​03 位运算与进制初步​​

▶  ​​04 进制进阶与编码​​

▶  ​​05 字符串进阶操作​​

▶  ​​06 栈理论与实战​​

▶  ​​07 队列理论与实战​​


【数据结构前导课】

▶  ​​1 温故知新——一篇文章领略信息学C++知识结构​​

▶  ​​2 披荆斩棘——只学C++,可以做哪些竞赛题​​

▶  ​​3 运筹帷幄——一篇文章,让指针学起来也很简单!​​

▶  ​​4 初试锋芒——顺序表写起来也很简单​​

▶  ​​5 小试牛刀——STL库之vector数组​​

▶  ​​6 触类旁通——链表基本理论与信息学竞赛必考点​​

▶  ​​7 更进一步——STL库之List链表​​

▶  ​​8 知“人”善任——深入理解顺序表和链表的区别与应用​​


【C++提高班教程】

▶  ​​C++强化 | 01 新学期再出发!温故知新!​​

▶  ​​C++强化 | 02 继续前行,三大结构终极介绍​​

▶  ​​C++强化 | 03 一维数组入门​​

▶  ​​C++强化 | 04 数组越界​​

▶  ​​C++强化 | 05 一维数组经典应用​​

▶  ​​C++强化 | 06 一篇文章带你掌握字符数组​​

▶  ​​C++强化 | 07 二维数组​​

▶  ​​C++强化 | 08 二维数组经典案例​​

▶  ​​C++强化 | 09 一篇文章带你探索函数的奥秘​​

▶  ​​C++强化 | 10 函数进阶必备​​

▶  ​​C++强化 | 11 这样学递归,才不会觉得难​​

​​▶  C++强化 | 12 格式化输入输出与文件操作​​

​​▶  ​​​​C++强化 | 13 结构体入门​​

​​  ​​​​C++强化 | 14 结构体进阶​​


【C++基础班教程】

▶  ​​C++总结 | 01 程序的世界​​

▶  ​​C++总结 | 02 输出、换行与注释​​

▶  ​​C++总结 | 03 变量定义、赋值与运算​​

▶  ​​C++总结 | 04 算术运算符与赋值运算符​​

▶  ​​C++总结 | 05 cin语句​​

▶  ​​C++总结 | 06 程序中的数据类型​​

▶  ​​C++总结 | 07 数据类型补充​​

▶  ​​C++总结 | 08 顺序结构​​

▶  ​​C++总结 | 09 if 和 if-else​​

▶  ​​C++总结 | 10 if嵌套与逻辑运算符​​

▶  ​​C++总结 | 11 开关语句switch-case​​

▶  ​​C++总结 | 12 for循环及其应用​​

▶  ​​C++总结 | 13 数据范围与数据类型​​

▶  ​​C++总结 | 14 break与continue​​

▶  ​​C++总结 | 15 while与do-while​​

▶  ​​C++总结 | 16 循环嵌套及其应用​​




1 栈与队列

首先让我们来看一下栈和队列。


栈和队列都是受限制的线性表,只能在两端做操作。


栈是一个先进后出的结构,队列是一个先进先出的结构。


在下面,我们就要开始接触代码啦!

2 STL库中的栈和队列

首先先让我们一起来了解一下STL中的栈和队列!

1 deque容器

deque 是 double-ended queue 的缩写,又称双端队列容器。


前面我们讲队列,讲了普通队列和循环队列,这两个队列都是在一端进,在另一端出。双端队列,就是说可以在两端进或者出:


信息学集训 | 08 栈与队列实战_#include


当然还是限制只能在两端做操作。


deque需要用到如下头问题和命名空间,才能使用:


#include <deque>
using namespace std;


我们创建一个具体的双端队列方式有如下几种:


//创建一个没有任何元素的空 deque 容器 
std::deque<int> d;

//创建一个具有 n 个元素的 deque 容器,默认值为0
std::deque<int> d(10);

//创建一个具有 n 个元素的 deque 容器,并为每个元素都指定初始值为5
std::deque<int> d(10, 5);

//拷贝普通数组,创建deque容器
int a[] = { 1,2,3,4,5 };
std::deque<int>d(a, a + 5);


注意:上面这些方法中用到的std::,如果我们在前面已经使用命名空间,实际使用中,就不用再添加std::了


deque的常用操作如下:


函数

功能

begin()

返回指向容器中第一个元素的迭代器。

end()

返回指向容器最后一个元素所在位置后一个位置的迭代器。

rbegin()

返回指向最后一个元素的迭代器。

rend()

返回指向第一个元素所在位置前一个位置的迭代器。

size()

返回实际元素个数。

empty()

判断容器中是否为空。

front()

返回第一个元素的引用。

back()

返回最后一个元素的引用。

push_back()

在序列的尾部添加一个元素。

push_front()

在序列的头部添加一个元素。

pop_back()

移除容器尾部的元素。

pop_front()

移除容器头部的元素。

clear()

清空容器。

swap()

交换两个容器的所有元素。


我们通过示例来看一下用法:


#include <iostream>
#include <deque>
using namespace std;
int main()
{
//初始化一个空deque容量
deque<int> d;
if(d.empty()){
cout<<"d是一个空的双端队列"<<endl;
}
//向d容器中的尾部依次添加 1234
for(int i = 1;i<5;i++){
d.push_back(i);
}
//向d容器的头部添加 0
d.push_front(0); //{0,1,2,3}
//调用 size() 成员函数输出该容器存储的字符个数。
printf("元素个数为:%d\n", d.size());

//使用迭代器遍历容器
for (auto i = d.begin(); i < d.end(); i++) {
cout << *i << " ";
}
cout << endl;

d.pop_front(); //去掉队头元素
cout<<"当前队头元素:"<<d.front()<<endl;
d.pop_back(); //去掉队尾元素
cout<<"当前队尾元素:"<<d.back()<<endl;

return 0;
}


执行结果如下:


信息学集训 | 08 栈与队列实战_#include_02


2 stack容器适配器

stack和后面的queue都是容器适配器,容器适配器可以理解为在基础容器的基础上构建的具有独特特性的容器。就像我们可以在普通类型基础上去定义结构体一样。


stack容器适配器实现的功能,就是我们前面的栈的功能。


我们前面讲过栈的一些概念,也自己实现过栈,我们在这里就直接开始讲解stack容器适配器的功能。


首先需要的就是头文件和命名空间:


#include <stack>
using namespace std;


最简单的定义stack的方法如下:


// 创建一个不包含任何元素的 stack 适配器,并采用默认的 deque 基础容器
std::stack<int> values;


我们前面说,容器适配器是在普通容器的基础上定义的。stack标准的定义格式为:


stack<Type,Container=deque<T>> 变量名


其中Type数据类型Container底层数据类型,也叫容器类型。默认为deque,还可以为vector和 list。举几个例子:


stack<int> values; //底层容器默认为deque。

stack<int, list<int>> values; //底层容器为list:

list<int> values{ 1, 2, 3 };
stack<int, std::list<int>> my_stack1(values); //使用基础容器初始化适配器
stack<int, std::list<int>> my_stack=my_stack1; //使用其他适配器来初始化


当然在我们使用中,我们没有特殊需求,使用默认deque底层容器即可。

stack常用操作如下:


函数

功能

size()

返回栈元素个数。

empty()

判断栈是否为空。

push()

入栈。

pop()

出栈。

top()

获取栈顶元素。


举个例子:


#include <iostream>
#include <stack>
using namespace std;
int main()
{
//构建 stack 容器适配器
stack<int> stk;

//判断栈空
if(stk.empty()) cout<<"当前栈为空栈"<<endl;

//入栈
for(int i = 0;i<5;i++) stk.push(i);

cout << "栈元素个数为: " << stk.size() << endl;

//出栈与输出
while (!stk.empty())
{
cout << stk.top() << endl;
stk.pop();
}
return 0;
}


3 queue容器适配器

queue容器适配器实现的功能,就是我们前面的队列的功能。


首先需要的就是头文件和命名空间:


#include <queue>
using namespace std;


最简单的定义queue的方法如下:


// 创建一个不包含任何元素的 stack 适配器,并采用默认的 deque 基础容器
std::queue<int> values;


我们前面说,容器适配器是在普通容器的基础上定义的。queue标准的定义格式为:


queue<Type,Container=deque<T>> 变量名


其中Type数据类型Container底层数据类型,也叫容器类型。默认为deque,还可以为 list。举几个例子:


queue<int> values; //底层容器默认为deque。

queue<int, list<int>> values; //底层容器为list:

list<int> values{ 1, 2, 3 };
queue<int> my_queue1(values); //使用基础容器初始化适配器
queue<int> my_queue(my_queue1); //使用其他适配器来初始化


当然在我们使用中,我们没有特殊需求,使用默认deque底层容器即可。

queue常用操作如下:


函数

功能

size()

返回栈元素个数。

empty()

判断栈是否为空。

push()

入队。

pop()

出队。

front()

获取队头元素。

back()

获取队尾元素。


举个例子:


#include <iostream>
#include <queue>
#include <deque>
using namespace std;
int main()
{
//使用deque构建 queue 容器适配器
deque<int> values{ 1,2,3 };
queue<int> q(values);//{1,2,3}

q.push(4); //入队元素4
cout<<"新队尾元素:"<<q.back()<<endl;
cout << "队列元素个数:" << q.size() << endl;


while (!q.empty())
{
cout << q.front() << endl;
q.pop(); //访问后出队
}

return 0;
}


执行结果如下:


信息学集训 | 08 栈与队列实战_#include_03


3 栈练习

了解了STL库,我们来看一下栈的相关练习吧!先从栈开始!

1 STL实现IO操作

现在有一串机器人指令(长度不超过100),指令只有字母I和字母O(均为大写),有一个空盒子用来存放物品;如果机器人接收到指令I,就会将对应的物体放到指定的盒子中,如果接收到指令O,就会将盒子中的物体取出来。


如果执行取出指令时盒子中没有物体,机器人的显示屏上就会出现一行字“错误”,如果正确执行完指令并且盒子中没有物体,就会输出“正确”;如果正确执行完指令,盒子中还有物体,就会显示“小呆瓜,指令不对称”。


【示例输入-1】
IIIIOOOO
【示例输出-1】
正确


【示例输入-2】
IOIOIOIOI
【示例输出-2】
小呆瓜,指令不对称


【示例输入-3】
IOIOOIOIOI
【示例输出-3】
错误


示例代码如下:


#include <iostream>
#include <stack>
using namespace std;
int main()
{

stack<char> stk;
char c[100];


cin>>c;

for(int i=0;c[i];i++){
if(c[i] == 'I')
stk.push('I');
else if(c[i] == 'O') {
if(stk.empty()){
cout<<"错误"<<endl;
return 0;
}
stk.pop();
}
}

if(stk.empty()) cout<<"正确"<<endl;
else cout<<"小呆瓜,指令不对称"<<endl;

return 0;
}


2 计算器

小红刚刚学完四则运算,知道括号可以改变运算符优先级。帮助小红实现一个计算器,能够做如下运算:


加法:+
减法:-
乘法:*
整除:/
括号:(),用于改变运算符优先级


参与运算的数字没有负数。


示例代码如下:


/*
1*12+1+2*20+(5)*(2+3)/5

1+2*3

1.分两部分存,一部分存数字,另一部分存字符(运算符+括号)
2.计算的时候,考虑运算符优先级:
如果栈中的优先级高,先算栈里的,
如果优先级相同,从左往右,左边先入栈,也先计算栈中的
如果栈中的优先级低 ,就让新的运算符入栈。
3.当判断到最后的时候, 如果存在前面比后面的运算符优先级低,算了后面的,最后还会有前面低的没有计算,在后面要单独计算
4.计算完后输出栈顶,并出栈让栈变空。


*/
#include <iostream>
#include <stack>
using namespace std;

stack<int> si;
stack<char> sc;
char ch[50];
int n;
int temp,flag;

int pre(char c){ //定义优先级
if(c == '(') return 0;
if(c == '+' || c == '-') return 1;
if(c == '*' || c == '/') return 2;
}

void calc(){ //计算
int x, y,ans = 1,i;
char ch;

x = si.top();
si.pop();
y = si.top();
si.pop();
ch = sc.top();
sc.pop();
switch(ch){
case '+': si.push(y+x); break;
case '-': si.push(y-x); break;
case '*': si.push(y*x); break;
case '/': si.push(y/x); break;
default: break;
}
}

int main()
{
cin>>ch; //输入要计算的式子
for(int i=0;ch[i];i++){
if(ch[i]>='0'&&ch[i]<='9'){ //数字
temp = temp*10+ch[i]-'0';
flag = 1;
}
else{ //说明不是数字,是运算符
if(flag){ //要把前面记录的数字入栈
si.push(temp);
temp = 0;
flag = 0;
}
if(ch[i] == '(')sc.push(ch[i]); //左括号要入栈
else if(ch[i] == ')'){ //右括号就要一直运算到左括号为止,然后左括号出栈
while(sc.top()!='(') calc(); //一直计算到左括号为止
sc.pop(); //删除掉左括号
}
else{
//要考虑运算符优先级
while(!sc.empty() && pre(sc.top())>=pre(ch[i])) calc();
sc.push(ch[i]); //如果空栈,运算符直接入栈
}
}
}
if(flag){
si.push(temp);
temp = 0;
flag = 0;
}
while(!sc.empty()) calc();
cout<<si.top()<<endl;
si.pop();

return 0;
}


执行示例如下:


信息学集训 | 08 栈与队列实战_#include_04


4 队列练习

本节课的作业,就是复习上面的所有知识,并完成下面两道题目!

1 STL库实现扑克牌游戏

我们有一副扑克牌,将其中的1-K,每个数字取出一张,按照从小到大的顺序排序,小的放在上面,大的放在下面,做如下操作:


1、将最上面的牌放在最底下;
2、将最上面的牌扔掉;


一直重复上述两个操作,问最后一张扔掉的牌是什么?


#include <iostream>
#include <queue>
using namespace std;

queue<int> card;
int temp;

int main()
{

for(int i=0;i<13;i++){
card.push(i+1);
}
while(!card.empty()){
//先放后面
temp = card.front();
card.pop();
card.push(temp);

//再扔掉
temp = card.front();
card.pop();
}
if(temp == 11) cout<<'J'<<endl;
else if(temp == 12) cout<<'Q'<<endl;
else if(temp == 13) cout<<'K'<<endl;
else cout<<temp<<endl;

return 0;
}


5 作业

本次课的作业是复习上面的题目,特别是第二题。



AI与区块链技术

信息学集训 | 08 栈与队列实战_c++_05

长按二维码关注

举报

相关推荐

0 条评论