C++与STL入门C  与STL入门 - 图11

从C到C++:happy:

头文件

stdio.h变成了cstdio

string.h变成了cstring

math.h变成了cmath

ctype.h变成了cctype

总结就是在前面添加一个c,去掉.h


说明:

ctype.h或cctype中的函数。实际上没什么用,我们都能用条件判断语句来实现。

单字节 宽字节 描述
isalnum iswalnum 是否为字母数字
isalpha iswalpha 是否为字母
islower iswlower 是否为小写字母
isupper iswupper 是否为大写字母
isdigit iswdigit 是否为数字
isxdigit iswxdigit 是否为16进制数字
iscntrl iswcntrl 是否为控制字符

C++框架

  1. #include <iostream>
  2. #include <algorithm>
  3. //algorithm头文件提供了一些常用的算法
  4. using namespace std; //将std里的名字导入默认空间
  5. const int maxn = 10; //C语言不允许用const变量(或者说变量)来定义数组长度,C++则可以用静态变量了。
  6. int A[maxn]; //c++里定义数组长度时可以放const的常量。而不一定需要define了。
  7. int main(){
  8. int a,b;
  9. while(cin >> a >> b){
  10. cout << min(a,b) << "\n";
  11. }
  12. return 0;
  13. }

说明:

  1. iosteam 提供了输入输出流。

  2. algorithm,中文是“算法”,提供了常用算法,例如代码中的min。常用函数如下:

    1. sort
    2. swap
    3. max
    4. min
  3. 如果不引入std命名空间,程序就会变成这样

    1. ```cpp

      include

      include

int main(){ int a,b; while(std::cin >> a >> b){ std::cout << std::min(a,b) << “\n”; } return 0; }

  1. 4.
  2. C语言不允许用const变量(或者说变量)来定义数组长度,C++则可以用静态变量了。
  3. 5.
  4. cin>>a是从标注输入中读取a,返回值是一个读取了a的新流。
  5. 6.
  6. cin很方便,但是速度慢,不如scanf
  7. 7.
  8. 多个bool类型,用truefalse表示真和假。
  9. <a name="3b61c966"></a>
  10. ### 引用
  11. ```c
  12. #include <iostream>
  13. #include <algorithm>
  14. using namespace std;
  15. void swap2(int &a,int &b){
  16. int t;
  17. t = a;
  18. a = b;
  19. b = t;
  20. }
  21. int main(){
  22. int a = 3,b = 4;
  23. swap2(a,b);
  24. cout << a << " " << b << "\n";
  25. return 0;
  26. }

说明:

  1. 在参数前加一个&,就表示这个参数是引用传递的。而不是普通的值传递。

  2. 这样的话,在函数中修改形参的值,也会修改到实参的值。

  3. algorithm中有了swap函数,功能非常强大,不仅同时支持int、double等内置类型,还支持用户自己编写的结构体类型。

字符串

此部分内容需要另外详细学习。

C++中有一个string类型,用来替代C语言中的字符数组(当然,字符数组的形式也是可以使用)。

特点:

  1. 头文件string
  2. string类型可以直接用“+”进行字符串连接,而不需要使用strcat函数。

例题:

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <string>
  4. #include <sstream>
  5. using namespace std;
  6. int main(){
  7. string line;
  8. /*
  9. C 有 fgets(),gets() 函数,gcc编译器扩展定义了getline()函数。
  10. 用于读取一行字符,包括换行符和字符串终止符。
  11. */
  12. //从输入流读取一行放进line中
  13. getline(cin,line);
  14. cout << line;
  15. return 0;
  16. }

getline的参数是 一个输入流和一个string类型的字符串,返回值是获取到的字符串长度。

注意:C++中不能使用gets来读一行字符串了!

例题2:

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <string>
  4. #include <sstream>
  5. using namespace std;
  6. int main(){
  7. string line;
  8. while(getline(cin,line)){
  9. int sum = 0,x;
  10. //获取流
  11. stringstream ss(line);
  12. while(ss >> x)sum+=x;//这里把它当成scanf来理解
  13. cout << sum << "\n";
  14. }
  15. return 0;
  16. }

这里有面向对象的特性。

虽然string和sstream很方便,但string很慢,sstream更慢。

结构体

此部分内容需要另外详细学习。

  1. #include <iostream>
  2. using namespace std;
  3. struct Point{
  4. int x,y;
  5. //构造函数,参数默认值为0;Point()相当于Point(0,0).
  6. Point(int x=0,int y=0){
  7. this->x = x;
  8. this->y = y;
  9. }
  10. };
  11. //operator为重载操作符
  12. //为结构体定义加法
  13. Point operator + (const Point &A,const Point &B){
  14. return Point(A.x+B.x, A.y+B.y);
  15. }
  16. //定义这个结构体的流输出方式。
  17. ostream &operator << (ostream &out,const Point &p){
  18. out << "(" << p.x << "," << p.y << ")";
  19. return out;
  20. }
  21. int main(){
  22. //测试结构体
  23. Point one,two(1,2);
  24. one.x = 3;
  25. one = one + two;
  26. cout << one << "\n";
  27. return 0;
  28. }

模板

前面加个template <typename T>

之后函数内的类型都用T来替换

这样做使得函数支持不同的类型。

例子:

普通swap

  1. void swap(int &a, int &b) {
  2. int temp = a;
  3. a = b;
  4. b = temp;
  5. }

模板swap

  1. template<typename T>
  2. void swap(T &t1, T &t2) {
  3. T temp;
  4. temp = t1;
  5. t1 = t2;
  6. t2 = temp;
  7. }

书上例题

  1. #include <iostream>
  2. using namespace std;
  3. template <typename T>
  4. struct Point{
  5. T x,y;
  6. //初始化列表方法
  7. Point(T x=0,T y=0):x(x),y(y){
  8. }
  9. };
  10. template <typename T>
  11. Point<T> operator + (const Point<T> &A,const Point<T> &B){
  12. return Point<T>(A.x+B.x, A.y+B.y);
  13. }
  14. template <typename T>
  15. ostream &operator << (ostream &out,const Point<T> &p){
  16. out << "(" << p.x << "," << p.y << ")";
  17. return out;
  18. }
  19. int main(){
  20. //测试结构体
  21. Point<int> a(1,2),b(3,4);
  22. Point<double> c(1.1,2.2),d(3.3,4.4);
  23. cout << a+b << " " << c+d << "\n";
  24. return 0;
  25. }

在实际的算法竞赛中,选手很少亲自编写模板。但是模板能更好地理解STL。

C++11说明

在dev-c中使用c11的方法。

第一步:点击工具-编译选项

C  与STL入门 - 图22

第二步:打钩并输入命令:-std=c++11

C  与STL入门 - 图23

然后就可以愉快地使用了!

STL初步C  与STL入门 - 图24

排序和检索

排序C  与STL入门 - 图25

1.sort函数包含在头文件为#include<algorithm> 的c++标准库中,调用标准库里的排序方法可以实现对数据的排序,但是sort函数是如何实现的,我们不用考虑!

2.sort函数的模板有三个参数:

  1. void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);

(1)第一个参数first:是要排序的数组的起始地址。

(2)第二个参数last:是结束的地址(最后一个数据的后一个数据的地址)

(3)第三个参数comp是排序的方法:可以是从升序也可是降序。如果第三个参数不写,则默认的排序方法是从小到大排序。

注意:comp 可以是 C++ STL 标准库提供的排序规则(比如 std::greater),也可以是自定义的排序规则。


升序排序

  1. #include <cstdio>
  2. #include <algorithm>//sort函数在其中
  3. using namespace std;
  4. const int maxn = 10000;
  5. int main(){
  6. int n,a[maxn];
  7. scanf("%d",&n);
  8. for(int i=0;i<n;i++)scanf("%d",&a[i]);
  9. sort(a,a+n);
  10. for(int i=0;i<n;i++)printf("%d ",a[i]);
  11. return 0;
  12. }

降序排序

  1. #include <cstdio>
  2. #include <algorithm>//sort函数在其中
  3. #include <functional>//greater在其中
  4. using namespace std;
  5. const int maxn = 10000;
  6. int main(){
  7. int n,a[maxn];
  8. scanf("%d",&n);
  9. for(int i=0;i<n;i++)scanf("%d",&a[i]);
  10. sort(a,a+n,greater<int>());
  11. for(int i=0;i<n;i++)printf("%d ",a[i]);
  12. return 0;
  13. }

说明:

  1. 注意greater在functional里面,它重载了 () 运算符。
  2. greater是一个模板类,所以要输入一个类型。
  3. 第二个参数有2写法,第一种如上述代码。
  4. 与greater对应的就是less,但是sort函数默认就是升序排序,所以没有使用的必要。

查找C  与STL入门 - 图26

lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

书上例题

  1. #include <cstdio>
  2. #include <algorithm>
  3. using namespace std;
  4. const int maxn = 1000;
  5. int main(){
  6. int n,q,kase,x;//n个数,q个问题
  7. int a[maxn];
  8. while(scanf("%d%d",&n,&q)==2){
  9. printf("CASE# %d:\n",++kase);
  10. //输入n个数
  11. for(int i=0;i<n;i++)scanf("%d",&a[i]);
  12. sort(a,a+n);//排序
  13. //处理问题
  14. while(q--){
  15. scanf("%d",&x);//输入x
  16. int p = lower_bound(a,a+n,x)-a;//寻找x
  17. if(a[p] == x)printf("%d found at %d\n",x,p+1);
  18. else printf("%d not found\n",x);
  19. }
  20. }
  21. return 0;
  22. }

还有一个unique函数可以删除有序数组中的重复元素,后面的例题将展现其用法。

容器

认识容器

简单的理解容器,它就是一些模板类的集合,但和普通模板类不同的是,容器中封装的是组织数据的方法(也就是数据结构)。STL 提供有 3 类标准容器,分别是序列容器、排序容器和哈希容器,其中后两类容器有时也统称为关联容器。它们各自的含义如下表所示:

容器种类 功能
序列容器 主要包括 vector 向量容器、list 列表容器以及 deque 双端队列容器。之所以被称为序列容器,是因为元素在容器中的位置同元素的值无关,即容器不是排序的。将元素插入容器时,指定在什么位置,元素就会位于什么位置。
排序容器 包括 set 集合容器、multiset多重集合容器、map映射容器以及 multimap 多重映射容器。排序容器中的元素默认是由小到大排序好的,即便是插入元素,元素也会插入到适当位置。所以关联容器在查找时具有非常好的性能。
哈希容器 C++ 11 新加入 4 种关联式容器,分别是 unordered_set 哈希集合、unordered_multiset 哈希多重集合、unordered_map 哈希映射以及 unordered_multimap 哈希多重映射。和排序容器不同,哈希容器中的元素是未排序的,元素的位置由哈希函数确定。

迭代器(iterator)

遍历容器,常用迭代器来实现。

迭代器和 C++指针非常类似,它可以是需要的任意类型,通过迭代器可以指向容器中的某个元素。

常用的迭代器按功能强弱分为以下五种,重点是后三种。

  1. 输入迭代器
  2. 输出迭代器
  3. 前向迭代器
  4. 双向迭代器
  5. 随机访问迭代器

输入迭代器和输出迭代器比较特殊,它们不是把数组或容器当做操作对象,而是把输入流/输出流作为操作对象。

  1. 前向迭代器(forward iterator)
    假设 p 是一个前向迭代器,则 p 支持 p,p,*p 操作,还可以被复制或赋值,可以用 == 和 != 运算符进行比较。此外,两个正向迭代器可以互相赋值。

  2. 双向迭代器(bidirectional iterator)
    双向迭代器具有正向迭代器的全部功能,除此之外,假设 p 是一个双向迭代器,则还可以进行 —p 或者 p— 操作(即一次向后移动一个位置)。

  3. 随机访问迭代器(random access iterator)
    随机访问迭代器具有双向迭代器的全部功能。除此之外,假设 p 是一个随机访问迭代器,i 是一个整型变量或常量,则 p 还支持以下操作:

  • p+=i:使得 p 往后移动 i 个元素。
  • p-=i:使得 p 往前移动 i 个元素。
  • p+i:返回 p 后面第 i 个元素的迭代器。
  • p-i:返回 p 前面第 i 个元素的迭代器。
  • p[i]:返回 p 后面第 i 个元素的引用。

以下是不同容器指定的迭代器类型

容器 对应的迭代器类型
array 随机访问迭代器
vector 随机访问迭代器
deque 随机访问迭代器
list 双向迭代器
set / multiset 双向迭代器
map / multimap 双向迭代器
forward_list 前向迭代器
unordered_map / unordered_multimap 前向迭代器
unordered_set / unordered_multiset 前向迭代器
stack 不支持迭代器
queue 不支持迭代器

注意,容器适配器 stack 和 queue 没有迭代器,它们包含有一些成员函数,可以用来对元素进行访问。

C  与STL入门 - 图27注意栈和队列没有迭代器,他们使用一些成员函数,实现遍历。

迭代器的四种定义方式:

迭代器定义方式 具体格式
正向迭代器 容器类名::iterator 迭代器名;
常量正向迭代器 容器类名::const_iterator 迭代器名;
反向迭代器 容器类名::reverse_iterator 迭代器名;
常量反向迭代器 容器类名::const_reverse_iterator 迭代器名;

代码示例:

  1. //遍历 vector 容器。
  2. #include <iostream>
  3. //需要引入 vector 头文件
  4. #include <vector>
  5. using namespace std;
  6. int main()
  7. {
  8. vector<int> v{1,2,3,4,5,6,7,8,9,10}; //v被初始化成有10个元素
  9. cout << "第一种遍历方法:" << endl;
  10. //size返回元素个数
  11. for (int i = 0; i < v.size(); ++i)
  12. cout << v[i] <<" "; //像普通数组一样使用vector容器
  13. //创建一个正向迭代器,当然,vector也支持其他 3 种定义迭代器的方式
  14. cout << endl << "第二种遍历方法:" << endl;
  15. vector<int>::iterator i;
  16. //用 != 比较两个迭代器
  17. for (i = v.begin(); i != v.end(); ++i)
  18. cout << *i << " ";
  19. cout << endl << "第三种遍历方法:" << endl;
  20. for (i = v.begin(); i < v.end(); ++i) //用 < 比较两个迭代器
  21. cout << *i << " ";
  22. cout << endl << "第四种遍历方法:" << endl;
  23. i = v.begin();
  24. while (i < v.end()) { //间隔一个输出
  25. cout << *i << " ";
  26. i += 2; // 随机访问迭代器支持 "+= 整数" 的操作
  27. }
  28. }

不定长数组:vector C  与STL入门 - 图28

简单说明

vector 容器是 STL 中最常用的容器之一,vector 实现的是一个动态数组,即可以进行元素的插入和删除,在此过程中,vector 会动态调整所占用的内存空间,整个过程无需人工干预。

vector 常被称为向量容器,因为该容器擅长在尾部插入或删除元素,在常量时间内就可以完成,时间复杂度为O(1);而对于在容器头部或者中部插入或删除元素,则花费时间要长一些(移动元素需要耗费时间),时间复杂度为线性阶O(n)

vector 容器以类模板 vector( T 表示存储元素的类型)的形式定义在 头文件中

创建方式

下列代码语句展示了创建的操作。(以下代码默认引入了std命名空间)

  1. vector<double> values; //创建
  2. vector<double> values(20); //创建并指定元素个数
  3. values.reserve(20); //改变容量
  4. vector<int> primes {2, 3, 5, 7, 11, 13, 17, 19}; //初始化的同时设置默认值
  5. vector<double> values(20, 1.0); //创建、指定元素个数、指定默认值,20个都为1.0。不指定默认值,默认值为0
  6. std::vector<char>value1(5, 'c');
  7. std::vector<char>value2(value1); //也可以通过复制另外一个vector的方式创建
  8. //复制另一个数组或vector的部分,数组直接放收尾地址,vector通过begin或end函数放地址
  9. int array[]={1,2,3};
  10. std::vector<int>values(array, array+2);//values 将保存{1,2}
  11. std::vector<int>value1{1,2,3,4,5};
  12. std::vector<int>value2(begin(value1),begin(value1)+3);//value2保存{1,2,3}

如果指定了大小,就可以直接用a[i]来访问,否则必须push_back了数据之后才能访问。

成员函数

函数成员 函数功能
begin() 返回指向容器中第一个元素的迭代器。
end() 返回指向容器最后一个元素所在位置后一个位置的迭代器,通常和 begin() 结合使用。
rbegin() 返回指向最后一个元素的迭代器。
rend() 返回指向第一个元素所在位置前一个位置的迭代器。
cbegin() 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
cend() 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crbegin() 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crend() 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
size() 返回实际元素个数。
max_size() 返回元素个数的最大值。这通常是一个很大的值,一般是 232-1,所以我们很少会用到这个函数。
resize() 改变实际元素的个数。
capacity() 返回当前容量。
empty() 判断容器中是否有元素,若无元素,则返回 true;反之,返回 false。
reserve() 增加容器的容量。
shrink _to_fit() 将内存减少到等于当前元素实际所使用的大小。
operator[ ] 重载了 [ ] 运算符,可以向访问数组中元素那样,通过下标即可访问甚至修改 vector 容器中的元素。
at() 使用经过边界检查的索引访问元素。
front() 返回第一个元素的引用。
back() 返回最后一个元素的引用。
data() 返回指向容器中第一个元素的指针。
assign() 用新元素替换原有内容。
push_back() 在序列的尾部添加一个元素。
pop_back() 移出序列尾部的元素。
insert() 在指定的位置插入一个或多个元素。
erase() 移出一个元素或一段元素。
clear() 移出所有的元素,容器大小变为 0。
swap() 交换两个容器的所有元素。
emplace() 在指定的位置直接生成一个元素。
emplace_back() 在序列尾部生成一个元素。

如果忘记了某个函数,也不要惊慌,打开api手册,搜索Vectors,就能找到所有的成员函数了。

用法代码

  1. #include <iostream>
  2. #include <vector>
  3. using namespace std;
  4. int main()
  5. {
  6. //初始化一个空vector容量
  7. vector<char>value;
  8. //向value容器中的尾部依次添加 S、T、L 字符
  9. value.push_back('S');
  10. value.push_back('T');
  11. value.push_back('L');
  12. //调用 size() 成员函数容器中的元素个数
  13. printf("元素个数为:%d\n", value.size());
  14. //使用迭代器遍历容器
  15. for (auto i = value.begin(); i < value.end(); i++) {
  16. cout << *i << " ";
  17. }
  18. cout << endl;
  19. //向容器开头插入字符
  20. value.insert(value.begin(), 'C');
  21. cout << "首个元素为:" << value.at(0) << endl;
  22. return 0;
  23. }

输出结果为:

  1. 元素个数为:3
  2. S T L
  3. 首个元素为:C

书上例题:放木块

输入n,得到编号为0n-1的位置。现对这些木块进行操作,操作分为四种。

1、move a onto b:把木块a、b上方的木块放回各自的原位,再把a放到b上;

2、move a over b:把a上方的木块放回各自的原位,再把a放到b所在的木块的堆的上面;

3、pile a onto b:把b上方的木块放回各自的原位,再把a连同a上的木块整体移到b上;

4、pile a over b:把a连同a上方木块移到b所在的木块的堆的上面。

当输入quit时,结束操作并输出0~n-1的位置上的木块情况

代码

  1. #include <cstdio>
  2. #include <algorithm>
  3. #include <string>
  4. #include <vector>
  5. #include <iostream>
  6. using namespace std;
  7. const int maxn = 30;
  8. int n;
  9. vector<int> pile[maxn];//每个pile是一个vector
  10. //找木块a所在的pile和height,以引用的形式返回
  11. void find_block(int a,int &p,int &h){
  12. for(p=0;p<n;p++){
  13. for(h=0;h<pile[p].size();h++){
  14. if(pile[p][h]==a)return;
  15. }
  16. }
  17. }
  18. //把第p堆高度为h的木块上方的所有木块移回原位
  19. void clear_above(int p,int h){
  20. for(int i=h+1;i<pile[p].size();i++){
  21. int b = pile[p][i];
  22. pile[b].push_back(b);//把木块放回原位
  23. //pile[p].pop_back();//p堆删除该元素。
  24. }
  25. pile[p].resize(h+1);//pile只应保留下标0~h的元素
  26. //因为高度从0开始,所以要保留h+1的长度。
  27. }
  28. //把第p堆高度为h及其上方的木块整体移动到p2堆的顶部
  29. void pile_onto(int p,int h,int p2){
  30. for(int i=h;i<pile[p].size();i++){
  31. pile[p2].push_back(pile[p][i]);
  32. //pile[p].pop_back();//p堆删除该元素。
  33. }
  34. pile[p].resize(h);//p堆保留h个.
  35. }
  36. void print(){
  37. for(int i=0;i<n;i++){
  38. printf("%d:",i);
  39. for(int j=0;j<pile[i].size();j++)printf(" %d",pile[i][j]);
  40. printf("\n");
  41. }
  42. }
  43. int main(){
  44. int a,b;
  45. //n个木块
  46. cin >> n;
  47. string s1,s2;
  48. for(int i=0;i<n;i++){
  49. pile[i].push_back(i);//每堆木块放一个木块
  50. }
  51. //输入指令,例如move 9 onto 1
  52. while(1){
  53. cin >> s1;
  54. if(s1=="quit")break;
  55. cin >> a >> s2 >> b;
  56. int pa,pb,ha,hb;
  57. find_block(a,pa,ha);//找到木块a所在堆pa和木块a的高度ha
  58. find_block(b,pb,hb);//找到木块b所在堆pb和木块b的高度hb
  59. if(pa == pb)continue;//自己对自己操作,不能做到,跳到下个指令。
  60. if(s2 == "onto")clear_above(pb,hb);//如果s2指令是onto,将b上方的木块归位
  61. if(s2 == "move")clear_above(pa,ha);//如果s1指令是move,将a上方的木块归位
  62. pile_onto(pa,ha,pb);//将a上方的木块移到b的上面。
  63. }
  64. print();
  65. return 0;
  66. }

键值对:pair C  与STL入门 - 图29

简单说明

  1. 头文件是,但一般不需要添加,因为iostream里面包含了。
  2. pair就想只包含两个成员变量的结构体变量。而且这两个变量类型是泛型的。
  3. pair的第一个元素是first,第二个元素是second,做算法时,为了方便,会通过define给他重新命名,例如x和y。
  4. 使用sort对pair进行排序,默认首先关注first,按照升序排序,其次再关注second。

使用方式

  1. #include <iostream>
  2. #include <utility> // pair
  3. #include <string> // string
  4. using namespace std;
  5. int main() {
  6. // 调用构造函数 1,也就是默认构造函数
  7. pair <string, double> pair1;
  8. // 调用第 2 种构造函数
  9. pair <string, string> pair2("STL教程","http://c.biancheng.net/stl/");
  10. // 调用拷贝构造函数
  11. pair <string, string> pair3(pair2);
  12. //调用移动构造函数
  13. pair <string, string> pair4(make_pair("C++教程", "http://c.biancheng.net/cplus/"));
  14. // 调用第 5 种构造函数
  15. pair <string, string> pair5(string("Python教程"), string("http://c.biancheng.net/python/"));
  16. cout << "pair1: " << pair1.first << " " << pair1.second << endl;
  17. cout << "pair2: "<< pair2.first << " " << pair2.second << endl;
  18. cout << "pair3: " << pair3.first << " " << pair3.second << endl;
  19. cout << "pair4: " << pair4.first << " " << pair4.second << endl;
  20. cout << "pair5: " << pair5.first << " " << pair5.second << endl;
  21. return 0;
  22. }

程序输出结果为:

pair1: 0
pair2: STL教程 http://c.biancheng.net/stl/
pair3: STL教程 http://c.biancheng.net/stl/
pair4: C++教程 http://c.biancheng.net/cplus/
pair5: Python教程 http://c.biancheng.net/python/

算法实战:全球变暖

https://www.acwing.com/problem/content/1235/

代码:

  1. /*
  2. 思路:
  3. 1. 扫描初始有几个岛屿,并判断该岛屿是否可淹没
  4. 2. 淹没前岛数- 淹没后岛数 = res
  5. */
  6. #include <cstdio>
  7. #include <cstring>
  8. #include <iostream>
  9. #include <algorithm>
  10. #include <queue>
  11. #define x first
  12. #define y second
  13. using namespace std;
  14. const int N = 1002;
  15. typedef pair<int, int> PII;
  16. int n;
  17. char a[N][N]; //初始地图
  18. bool st[N][N]; //判重数组
  19. const int dir[4][2] = {
  20. {-1,0},{0,1},{1,0},{0,-1},
  21. };
  22. bool check(int x,int y){ //判断是否是可淹没陆地
  23. for (int i = 0; i < 4; i++) {
  24. int tx = x + dir[i][0];
  25. int ty = y + dir[i][1];
  26. if (tx < 0 || ty < 0 || tx >= n || ty >= n) continue;
  27. if(a[tx][ty] == '.') return true; //周围发现海了,可淹没
  28. }
  29. return false; //周围没发现海,不可淹没
  30. }
  31. //每进一次bfs都是一个新岛屿,返回之后是否会被淹没
  32. bool bfs(PII start) {
  33. bool merge = true;
  34. queue<PII> que;
  35. st[start.x][start.y] = true;
  36. que.push(start);
  37. while (!que.empty()) {
  38. PII cur = que.front();
  39. que.pop();
  40. //如果发现了一块不可淹没的陆地,那么它就是不可淹没岛屿
  41. if(!check(cur.x,cur.y)) {
  42. merge = false;
  43. }
  44. for (int i = 0; i < 4; i++) {
  45. int tx = cur.x + dir[i][0];
  46. int ty = cur.y + dir[i][1];
  47. if (tx < 0 || ty < 0 || tx >= n || ty >= n) continue;
  48. if (a[tx][ty] == '.') continue;
  49. if (st[tx][ty]) continue;
  50. st[tx][ty] = true;
  51. que.push({ tx,ty });
  52. }
  53. }
  54. return merge;
  55. }
  56. int main() {
  57. cin >> n;
  58. for (int i = 0; i < n; i++) scanf("%s", a[i]);
  59. int qnum = 0, hnum = 0; //淹没前的岛屿数量,淹没后岛屿数量
  60. for (int i = 0; i < n; i++) {
  61. for (int j = 0; j < n; j++) {
  62. if (a[i][j] == '#' && st[i][j]==false) {
  63. //找到一个新岛屿
  64. qnum ++;
  65. //如果不会被淹没,那么hnum+1
  66. if(!bfs({ i, j })) hnum ++;
  67. }
  68. }
  69. }
  70. cout << qnum - hnum << endl;
  71. return 0;
  72. }

集合:set C  与STL入门 - 图30

简单说明

  1. 每个元素必须各不相同,相同的会自动合并。
  2. 自动排序(升序排序)

成员函数

成员方法 功能
begin() 返回指向容器中第一个(注意,是已排好序的第一个)元素的双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
end() 返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
rbegin() 返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。
rend() 返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。
cbegin() 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。
cend() 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。
crbegin() 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。
crend() 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。
find(val) 在 set 容器中查找值为 val 的元素,如果成功找到,则返回指向该元素的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
lower_bound(val) 返回一个指向当前 set 容器中第一个大于或等于 val 的元素的双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
upper_bound(val) 返回一个指向当前 set 容器中第一个大于 val 的元素的迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
equal_range(val) 该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的值为 val 的元素(set 容器中各个元素是唯一的,因此该范围最多包含一个元素)。
empty() 若容器为空,则返回 true;否则 false。
size() 返回当前 set 容器中存有元素的个数。
max_size() 返回 set 容器所能容纳元素的最大个数,不同的操作系统,其返回值亦不相同。
insert() 向 set 容器中插入元素。
erase() 删除 set 容器中存储的元素。
swap() 交换 2 个 set 容器中存储的所有元素。这意味着,操作的 2 个 set 容器的类型必须相同。
clear() 清空 set 容器中所有的元素,即令 set 容器的 size() 为 0。
emplace() 在当前 set 容器中的指定位置直接构造新元素。其效果和 insert() 一样,但效率更高。
emplace_hint() 在本质上和 emplace() 在 set 容器中构造新元素的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示新元素生成位置的迭代器,并作为该方法的第一个参数。
count(val) 在当前 set 容器中,查找值为 val 的元素的个数,并返回。注意,由于 set 容器中各元素的值是唯一的,因此该函数的返回值最大为 1。

使用方法

  1. #include <iostream>
  2. #include <set>
  3. #include <string>
  4. using namespace std;
  5. int main()
  6. {
  7. //创建空set容器
  8. std::set<std::string> myset;
  9. //空set容器不存储任何元素
  10. cout << "1、myset size = " << myset.size() << endl;
  11. //向myset容器中插入新元素
  12. myset.insert("http://c.biancheng.net/java/");
  13. myset.insert("http://c.biancheng.net/stl/");
  14. myset.insert("http://c.biancheng.net/python/");
  15. cout << "2、myset size = " << myset.size() << endl;
  16. //利用双向迭代器,遍历myset
  17. for (auto iter = myset.begin(); iter != myset.end(); ++iter) {
  18. cout << *iter << endl;
  19. }
  20. return 0;
  21. }

程序执行结果为:

1、myset size = 0
2、myset size = 3
http://c.biancheng.net/java/
http://c.biancheng.net/python/
http://c.biancheng.net/stl/

书上例题:安迪的词典

安迪的第一个字典

输入一个文本,找出所有不同的单词(连续的字母序列),按字典序从小到大输出。单词不区分大小写。

代码

#include <cstdio>
#include <iostream>
#include <set>
#include <string>
#include <sstream>
using namespace std;

set<string> dict;//string集合

int main(){
    string s,buf;
    //到空格就断 
    while(cin >> s){
        for(int i=0;i<s.length();i++){
            if(s[i]>='A'&&s[i]<='Z')s[i] = s[i]-'A'+'a';
            else if(s[i]>='a'&&s[i]<='z');
            else s[i] = ' ';
        }
        stringstream ss(s);
        while(ss >> buf)dict.insert(buf);
    }
    for(set<string>::iterator it = dict.begin(); it!= dict.end(); it++){
        cout << *it << "\n";
    }
    return 0;
}

算法实战:日期问题

https://www.acwing.com/problem/content/1231/

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <set>
using namespace std;

const int table[13] = {
    0,31,28,31,30,31,30,31,31,30,31,30,31,
};

set<int> st;

bool check(int year,int mon,int day){
    if(year < 1960 || year > 2059) return false;//特判 

    if(mon < 1 || mon > 12) return false;
    int leap = 0;
    if(mon == 2){
        if(year%4==0&&year%100!=0 || year%400==0) leap++;
    }
    if(day < 1 || day > table[mon] + leap) return false;

    return true;
}

int main() {
    int n1,n2,n3;
    scanf("%d/%d/%d",&n1,&n2,&n3);
    int cnt = 0;
    //年月日
    int year = n1, mon = n2, day = n3;
    if(check(1900+year,mon,day)) st.insert((1900+year)*10000 + mon*100 + day);
    if(check(2000+year,mon,day)) st.insert((2000+year)*10000 + mon*100 + day);
    //月日年
    mon = n1, day = n2, year = n3;
    if(check(1900+year,mon,day)) st.insert((1900+year)*10000 + mon*100 + day);
    if(check(2000+year,mon,day)) st.insert((2000+year)*10000 + mon*100 + day);
    //日月年
    day = n1, mon = n2, year = n3;
    if(check(1900+year,mon,day)) st.insert((1900+year)*10000 + mon*100 + day);
    if(check(2000+year,mon,day)) st.insert((2000+year)*10000 + mon*100 + day);

    for(set<int>::iterator it = st.begin(); it != st.end(); it++) {
        int num = *it;
        printf("%d-%02d-%02d\n",num/10000,num/100%100,num%100);
    }

    return 0;
}

映射:map C  与STL入门 - 图31

简单说明

map 容器存储的都是 pair 对象,也就是用 pair 类模板创建的键值对。

成员函数

成员方法 功能
begin() 返回指向容器中第一个(注意,是已排好序的第一个)键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
end() 返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
rbegin() 返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。
rend() 返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。
cbegin() 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。
cend() 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。
crbegin() 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。
crend() 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的键值对。
find(key) 在 map 容器中查找键为 key 的键值对,如果成功找到,则返回指向该键值对的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
lower_bound(key) 返回一个指向当前 map 容器中第一个大于或等于 key 的键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
upper_bound(key) 返回一个指向当前 map 容器中第一个大于 key 的键值对的迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
equal_range(key) 该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的键为 key 的键值对(map 容器键值对唯一,因此该范围最多包含一个键值对)。
empty() 若容器为空,则返回 true;否则 false。
size() 返回当前 map 容器中存有键值对的个数。
max_size() 返回 map 容器所能容纳键值对的最大个数,不同的操作系统,其返回值亦不相同。
operator[] map容器重载了 [] 运算符,只要知道 map 容器中某个键值对的键的值,就可以向获取数组中元素那样,通过键直接获取对应的值。
at(key) 找到 map 容器中 key 键对应的值,如果找不到,该函数会引发 out_of_range 异常。
insert() 向 map 容器中插入键值对。
erase() 删除 map 容器指定位置、指定键(key)值或者指定区域内的键值对。后续章节还会对该方法做重点讲解。
swap() 交换 2 个 map 容器中存储的键值对,这意味着,操作的 2 个键值对的类型必须相同。
clear() 清空 map 容器中所有的键值对,即使 map 容器的 size() 为 0。
emplace() 在当前 map 容器中的指定位置处构造新键值对。其效果和插入键值对一样,但效率更高。
emplace_hint() 在本质上和 emplace() 在 map 容器中构造新键值对的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示键值对生成位置的迭代器,并作为该方法的第一个参数。
count(key) 在当前 map 容器中,查找键为 key 的键值对的个数并返回。注意,由于 map 容器中各键值对的键的值是唯一的,因此该函数的返回值最大为 1。

使用方法

#include <iostream>
#include <map>      // map
#include <string>       // string
using namespace std;

int main() {
    //创建空 map 容器,默认根据个键值对中键的值,对键值对做降序排序
    std::map<std::string, std::string, std::greater<std::string>>myMap;
    //调用 emplace() 方法,直接向 myMap 容器中指定位置构造新键值对
    myMap.emplace("C语言教程","http://c.biancheng.net/c/");
    myMap.emplace("Python教程", "http://c.biancheng.net/python/");
    myMap.emplace("STL教程", "http://c.biancheng.net/stl/");
    //输出当前 myMap 容器存储键值对的个数
    cout << "myMap size==" << myMap.size() << endl;
    //判断当前 myMap 容器是否为空
    if (!myMap.empty()) {
        //借助 myMap 容器迭代器,将该容器的键值对逐个输出
        for (auto i = myMap.begin(); i != myMap.end(); ++i) {
            cout << i->first << " " << i->second << endl;
        }
    }  
    return 0;
}

程序执行结果为:

myMap size==3
STL教程 http://c.biancheng.net/stl/
Python教程 http://c.biancheng.net/python/
C语言教程 http://c.biancheng.net/c/

遍历map

for(map<string,int>::iterator it = cnt.begin(); it!=cnt.end() ; it++){
        cout << it->first << ":" << it->second << "\n";
    }

输入一些单词,找出所有满足如下条件的单词:该单词不能通过字母重排,得到输入文 本中的另外一个单词。在判断是否满足条件时,字母不分大小写,但在输出时应保留输入中 的大小写,按字典序进行排列(所有大写字母在所有小写字母的前面)。

代码

/*
ladder came tape soon leader acme RIDE lone Dreis peat
ScAlE orb eye Rides dealer NotE derail LaCeS drIed
noel dire Disk mace Rob dries
#
*/

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;

//key为string存单词,value 为int存次数 
map<string,int> cnt;
vector<string> words;

//标准化:将字符串全部化为小写,再按ascii排序 
string repr(const string &s){
    string ans = s;
    for(int i=0;i<ans.length();i++){
        if(ans[i]>='A' && ans[i]<='Z')
            ans[i] = ans[i]-'A'+'a';
    }
    sort(ans.begin(),ans.end());
    return ans;
}

int main(){
    int n = 0;
    string s;
    while(cin >> s){
        if(s[0] == '#')break;
        //将单词添加进单词vector 
        words.push_back(s);
        string r = repr(s);//标准化该单词 
        //inc该标准化单词的次数 
        if(!cnt.count(r))cnt[r] = 0;
        cnt[r]++;
    }
    vector<string> ans;
    for(int i=0;i<words.size();i++){
        //如果该单词的标准化单词的个数为1
        //说明不能通过单词重排,得到另一个单词
        if(cnt[repr(words[i])] == 1){
            //在结果vector中添加 
            ans.push_back(words[i]);
        }    
    }
    //对结果vector排序 
    sort(ans.begin(),ans.end());
    //输出
    for(int i=0;i<ans.size();i++){
        cout << ans[i] << "\n";
    }
    return 0;
}

算法实战:十六进制转八进制

资源限制

时间限制:1.0s 内存限制:512.0MB

问题描述
  给定n个十六进制正整数,输出它们对应的八进制数。

输入格式
  输入的第一行为一个正整数n (1<=n<=10)。
  接下来n行,每行一个由0F组成的字符串,表示要转换的十六进制正整数,每个十六进制数长度不超过100000。

输出格式
  输出n行,每行为输入对应的八进制正整数。

【注意
  输入的十六进制数不会有前导0,比如012A。
  输出的八进制数也不能有前导0。

样例输入
  2
  39
  123ABC

样例输出
  71
  4435274

提示
  先将十六进制数转换成某进制数,再由某进制数转换成八进制。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
using namespace std;

const int maxn = 100010;
string hs,bs,os;//存十六进制、二进制、八进制 

map<char,string> table;//16到2的映射 
map<string,char> table2;//2到8的映射 

//初始化映射关系 
void init() {
    table['0'] = "0000";
    table['1'] = "0001";
    table['2'] = "0010";
    table['3'] = "0011";
    table['4'] = "0100";
    table['5'] = "0101";
    table['6'] = "0110";
    table['7'] = "0111";
    table['8'] = "1000";
    table['9'] = "1001";
    table['A'] = "1010";
    table['B'] = "1011";
    table['C'] = "1100";
    table['D'] = "1101";
    table['E'] = "1110";
    table['F'] = "1111";

    table2["000"] = '0';
    table2["001"] = '1';
    table2["010"] = '2';
    table2["011"] = '3';
    table2["100"] = '4';
    table2["101"] = '5';
    table2["110"] = '6';
    table2["111"] = '7';
}

//删除开头多余的0
void deleteZero(string& str) {
    while(str[0] == '0') {
        str.erase(0,1);
    }
}

void BinToOct() {
    //当二进制串不是3的倍数,给它补足
    while (bs.size() % 3 != 0) {
        bs = "0" + bs;
    }
    //初始化os
    os = ""; 
    //每3个二进制位对应一个八进制串 
    for (int i = 0; i < bs.size(); i+=3) {
        string str = bs.substr(i, 3);
        os += table2[str];
    }
    //删除开头多余的0 
    deleteZero(os); 
}

void HexToBin() {
    //初始化bs
    bs = ""; 
    //遍历十六进制串,每个十六进制字符转换成二进制串 
    for (int i = 0; i < hs.size(); i++) {
        bs += table[hs[i]];
    }
    //删除开头多余的0 
    deleteZero(bs); 
}

int main() {
    init();
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> hs;
        HexToBin();
        BinToOct();
        cout << os << endl; 
    }
    return 0;
}

栈:stack C  与STL入门 - 图32

成员函数

成员函数 功能
empty() 当 stack 栈中没有元素时,该成员函数返回 true;反之,返回 false。
size() 返回 stack 栈中存储元素的个数。
top() 返回一个栈顶元素的引用,类型为 T&。如果栈为空,程序会报错。
push(const T& val) 先复制 val,再将 val 副本压入栈顶。这是通过调用底层容器的 push_back() 函数完成的。
push(T&& obj) 以移动元素的方式将其压入栈顶。这是通过调用底层容器的有右值引用参数的 push_back() 函数完成的。
pop() 弹出栈顶元素。
emplace(arg…) arg… 可以是一个参数,也可以是多个参数,但它们都只用于构造一个对象,并在栈顶直接生成该对象,作为新的栈顶元素。
swap(stack & other_stack) 将两个 stack 适配器中的元素进行互换,需要注意的是,进行互换的 2 个 stack 适配器中存储的元素类型以及底层采用的基础容器类型,都必须相同。

使用方法

#include <iostream>
#include <stack>
#include <list>
using namespace std;
int main()
{
    //构建 stack 容器适配器
    list<int> values{ 1, 2, 3 };
    stack<int, list<int>> my_stack(values);
    //查看 my_stack 存储元素的个数
    cout << "size of my_stack: " << my_stack.size() << endl;
    //将 my_stack 中存储的元素依次弹栈,直到其为空
    while (!my_stack.empty())
    {  
        cout << my_stack.top() << endl;
        //将栈顶元素弹栈
        my_stack.pop();
    }
    return 0;
}

书上例题:集合栈计算机

有一个专门为了集合运算而设计的“集合栈”计算机。该机器有一个初始为空的栈,并且支持以下操作:
PUSH:空集“{ }“入栈
DUP:把当前栈顶元素复制一份后再入栈
UNION:出栈两个集合,然后把二者的并集入栈。
INTERSECT:出栈两个集合,然后把二者的交集入栈。
*ADD:出栈两个集合,然后把先出栈的集合加入到后出栈的集合中,把结果入栈。
每次操作之后,输出栈顶集合的大小(即元素个数)。例如:栈顶元素是A={ { },{{ }} },下一个元素是B={ { },{{{ }}} },则:
UNION操作得到:{ { },{{ }},{{{ }}} },输出3.
INTERSECT操作得到:{ { } },输出1.
ADD操作得到:{{ }, {{{ }}}, {{ },{{ }}} }.,输出3

分析
本题的集合并非简单的整数或字符串集合,而是集合的集合。很难直接表示,所以为方便起见,为每个不同的集合分配一个唯一的ID
,则每个集合表示为所包含元素(集合)的ID集合。这样就可以用STL中的set<int>来表示,而整个栈则是一个stack<int>

  1. 在进行操作时,使用map将每种集合与对应ID相关联,既可完成查找ID,也可判定是否出现新集合。
  2. 使用vector作为存储每种集合的cache,当map中没有相应的ID时,向vector加入一个set<int>元素,并将下标作为ID进行唯一标识。
  3. 使用vector将Set存储:可以用ID查询到对应的set。这样通过map和vector实现set到ID的双射。
  4. 最后 ,输出栈顶集合的大小(元素个数)。

代码

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <set>
#include <stack>
#include <map>
#include <vector>
#include <iterator>
using namespace std;

typedef set<int> Set; 
map<Set,int> id_cache;//集合对应id 
vector<Set> set_cache;//集合库,根据id取集合,id刚好是vector的下标 

int find_id(Set x){
    //如果找到了x的id,那么返回该集合的id 
    if(id_cache.count(x)) return id_cache[x];
    //否则添加进集合库,给该集合对应一个id 
    set_cache.push_back(x);//添加进新集合
    //新id为集合库的长度-1,即id为该集合从0开始的下标。 
    id_cache[x] = set_cache.size() - 1;
    return id_cache[x]; //返回该集合的id 
}

int main(){
    stack<int> s;//做一个栈,栈中存的是集合的id 
    int n;
    cin >> n;//输入指令个数
    for(int i = 0; i < n; i++){
        string op;
        cin >> op;//输入指令 
        if(op[0] == 'P')s.push(find_id(Set()));//一个空的集合入栈。 
        else if(op[0] == 'D')s.push(s.top());//取栈顶元素再入栈
        else{
            //出栈两个集合 
            Set x1 = set_cache[s.top()];s.pop();
            Set x2 = set_cache[s.top()];s.pop();
            Set x;
            //两个集合的并集放入x 
            if(op[0] == 'U')set_union(x1.begin(),x1.end(),x2.begin(),x2.end(),inserter(x,x.begin()));
            //两个集合的交集放入x 
            if(op[0] == 'I')set_intersection(x1.begin(),x1.end(),x2.begin(),x2.end(),inserter(x,x.begin()));
            if(op[0] == 'A'){
                x = x2;//存一下x2 
                x.insert(find_id(x1));//x添加x1 
            }
            //将x入栈
            s.push(find_id(x));
        } 
        cout << set_cache[s.top()].size() << endl;
    }
    return 0;
}

队列:queue C  与STL入门 - 图33

成员函数

成员函数 功能
empty() 如果 queue 中没有元素的话,返回 true。
size() 返回 queue 中元素的个数。
front() 返回 queue 中第一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
back() 返回 queue 中最后一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
push(const T& obj) 在 queue 的尾部添加一个元素的副本。这是通过调用底层容器的成员函数 push_back() 来完成的。
emplace() 在 queue 的尾部直接添加一个元素。
push(T&& obj) 以移动的方式在 queue 的尾部添加元素。这是通过调用底层容器的具有右值引用参数的成员函数 push_back() 来完成的。
pop() 删除 queue 中的第一个元素。
swap(queue &other_queue) 将两个 queue 容器适配器中的元素进行互换,需要注意的是,进行互换的 2 个 queue 容器适配器中存储的元素类型以及底层采用的基础容器类型,都必须相同。

说明:

  1. 最重要的函数如下:

    1. 增加:push
    2. 删除:pop
    3. 查询:front
    4. 查询长度:size

注意:和 stack 一样,queue 也没有迭代器,因此访问元素的唯一方式是遍历容器,通过不断移除访问过的元素,去访问下一个元素。

使用方法

#include <iostream>
#include <queue>
#include <list>
using namespace std;
int main()
{
    //构建 queue 容器适配器
    std::deque<int> values{ 1,2,3 };
    std::queue<int> my_queue(values);//{1,2,3}
    //查看 my_queue 存储元素的个数
    cout << "size of my_queue: " << my_queue.size() << endl;
    //访问 my_queue 中的元素
    while (!my_queue.empty())
    {
        cout << my_queue.front() << endl;
        //访问过的元素出队列
        my_queue.pop();
    }
    return 0;
}

算法实战:献给阿尔吉侬的花束

https://www.acwing.com/problem/content/1103/

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>

#define x first
#define y second

using namespace std;

typedef pair<int,int> PII;

const int N = 210;

int R,C;    //地图行数列数
char map[N][N];
int dist[N][N]; //从起点到(i,j)的长度

int bfs(PII start,PII end) {
    //定义队列
    queue<PII> que;
    //长度初始为-1,表示没有遍历到
    memset(dist,-1,sizeof dist);//每个字节全为-1,即二进位全为1 

    dist[start.x][start.y] = 0;
    //start入列
    que.push(start);

    while(que.size()){
        //取头 
        PII t = que.front(); 
        que.pop();

        //遍历四个方向,上右下左 
        const int dir[4][2] = {
            {-1,0},{0,1},{1,0},{0,-1},
        };
        for(int i = 0; i < 4; i++) {
            int tx = t.x + dir[i][0];
            int ty = t.y + dir[i][1];
            if(tx < 0 || ty < 0 || tx >= R || ty >= C) continue;    //超地图边界,跳出 
            if(map[tx][ty] == '#') continue;    //墙跳出
            if(dist[tx][ty] != -1) continue;    //有长度了,说明遍历过了,跳出

            //计算长度,如果找到目标,直接返回长度 
            dist[tx][ty] = dist[t.x][t.y] + 1;
            if(end.x == tx && end.y == ty) return dist[tx][ty];

            //添加队列
            que.push({tx,ty});
        }
    }
    //如果没找到,返回-1 
    return -1;
}

int main() {
    int T;  //T组数据 
    cin >> T;

    while(T--) { 
        scanf("%d%d",&R,&C);
        for(int i = 0; i < R; i++) scanf("%s",map[i]);
        //开始找起点和终点
        PII start,end;
        for(int i = 0; i < R; i++) {
            for(int j = 0; j < C; j++) {
                if(map[i][j] == 'S') start = {i,j};
                else if(map[i][j] == 'E') end = {i,j};
            }
        }
        //从起点宽搜终点,获得最小步数 
        int step = bfs(start,end);
        //输出最小步数
        if(step == -1) printf("oop!\n");
        else printf("%d\n",step);
    }

    return 0;
}