C++ STL常见容器
目录
1、string字符串支持常见的比较操作符(>,>=,<,<=,==,!=),甚至支持string与C-string的比较(如 str<”hello”)。
1.array
1.1 介绍
1.1.1 头文件
#include <array>
1.1.2 定义
array<int,5> myarray = {1,2,3,4,5};
array<int,5> otherarray = myarray;
int b[5];
//编译报错,error: no viable conversion from 'int [5]' to 'array<int, 5>
array<int,5> otherarray2 = b;
int c=5;
//编译报错,error: non-type template argument is not a constant expression
array<int,5> otherarray3;
int d[c];//普通数组是可以支持用变量初始化大小,所以std::array有点鸡肋呀
1.1.3 初始化
std::array<double, 10> values {};
std::array<double, 10> values {0.5,1.0,1.5,,2.0};
1.1.4 访问
可通过下标运算符[]对元素进行操作,还可以通过at/front/back
进行操作
for (int i = 0; i < 5; i++)
{
cout << setw(10) << << myarray.at(i) << endl;
}
1.1.5 遍历
可以通过正向和反向迭代器对元素进行遍历
for (auto it = myarray.begin(); it != myarray.end();++it)
{
cout << *it << endl;
}
1.2 方法函数
成员函数 | 功能 |
---|---|
begin() | 返回指向容器中第一个元素的随机访问迭代器。 |
end() | 返回指向容器最后一个元素之后一个位置的随机访问迭代器,通常和 begin() 结合使用。 |
rbegin() | 返回指向最后一个元素的随机访问迭代器。 |
rend() | 返回指向第一个元素之前一个位置的随机访问迭代器。 |
crbegin() | 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
crend() | 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。 |
size() | 返回容器中当前元素的数量,其值始终等于初始化 array 类的第二个模板参数 N。 |
max_size() | 返回容器可容纳元素的最大数量,其值始终等于初始化 array 类的第二个模板参数 N。 |
empty() | 判断容器是否为空,和通过 size()==0 的判断条件功能相同,但其效率可能更快。 |
at(n) | 返回容器中 n 位置处元素的引用,该函数自动检查 n 是否在有效的范围内,如果不是则抛出 out_of_range 异常。 |
front() | 返回容器中第一个元素的直接引用,该函数不适用于空的 array 容器。 |
back() | 返回容器中最后一个元素的直接应用,该函数同样不适用于空的 array 容器。 |
data() | 返回一个指向容器首个元素的指针。利用该指针,可实现复制容器中所有元素等类似功能。 |
fill(val) | 将 val 这个值赋值给容器中的每个元素。 |
array1.swap(array2) | 交换 array1 和 array2 容器中的所有元素,但前提是它们具有相同的长度和类型。 |
2.vector(动态数组)
2.1 介绍
vector为可变长数组(动态数组),定义的vector数组可以随时添加数值和删除元素。
2.1.1 头文件
#include <vector>
2.1.2 定义:
//初始化
//方式一:初始化一维可变长数组
vector<int>num; //定义了一个名为num的存int数据的一维数组
vector<double>num;//定义了一个名为num的存double数据的一维数组
vector<node>num;//node是结构体类型
//方式二:初始化二维可变长数组
vector<int>num[5];//定义可变长二维数组
//注意:行是不可变的(只有5行),而列可变可以在指定行添加元素
//第一维固定长度为5,第二维长度可以改变
//方式三:初始化二维均可变长数组
vector<vectot<int> >num;//定义一个行和列均可变的二维数组
2.1.3 初始化:
//1.附加长度
vector<int >v(n);//定义一个长度为n的数组,动态定义
//2.附加长度和初始值
vector<int>v(n,0);//所有的元素均为0
2.1.4 访问:
//方式一:单个访问,假设num数组中已经有了5个元素
cout<<num[4]<<"\n"; //输出第五个数据
//一二维可变数组和普通数组的访问方法一样
//方式二:遍历
for(int i=0;i<num.size();i++)
cout<<num[i]<<" ";//下标范围在[0,num.size()),前开后闭
//方式三:智能指针
for(auto i : num)
cout<<i<<" ";
2.2 方法函数
知道了如何定义初始化可变数组,下面就需要知道如何添加,删除,修改数据。
相关方法函数如下:c指定为数组名称
代码 | 含义 |
---|---|
c.front() | 返回第一个数据 |
c.back() | 返回最后一个数据 |
c.push_back(element) | 在尾部加一个数据 O(1) |
c.pop_back() | 删除最后一个数据 O(1) |
c.size() | 返回实际数据个数(unsigned类型) O(1) |
c.clear() | 清除元素个数 O(N),N为元素个数 |
c.resize(n,v) | 改变数组大小为n,n个空间数值赋为v,如果没有默认赋值为0 |
c.insert(it,x) | 向任意迭代器it插入一个元素x O(N), 例:c.insert(c.begin()+2,-1) 将-1插入c[2]的位置 |
c.erase(first,last) | 删除[first,last)的所有元素 |
c.begin() | 返回首元素的迭代器(通俗来说就是地址) |
c.end() | 返回最后一个元素后一个位置的迭代器(地址) |
c.empty() | 判断是否为空,为空返回真,反之返回假 |
emplace() | 在指定的位置直接生成一个元素。 |
emplace_back() | 在序列尾部生成一个元素。 |
注意: end()返回的是最后一个元素的后一个位置的地址,不是最后一个元素的地址,所有容器均是如此。以上 O(n),O(1)说的是时间复杂度。
2.3 注意点
2.3.1 排序
使用sort排序要: sort(c.begin(),c.end());
对所有元素进行排序,如果要对指定区间进行排序,可以对sort()里面的参数进行加减改动。
2.3.2 访问
数组访问:上面有简单的访问演示,下面进行扩充并复习。数组访问主要有下标法和迭代器法。
下标法: 和普通数组一样;注意:一维数组的下标是从0到v.size()-1,访问之外的数可能会出错
迭代器法: 类似指针一样的访问 ,首先需要声明迭代器变量,和声明指针变量一样,可以根据代码进行理解(附有注释)。
代码如下:
vector<int>vi; //定义一个vi数组
vector<int>::iterator it = vi.begin();//声明一个迭代器指向vi的初始位置
vector数组访问相关代码:
(1)下标访问:
//添加元素
for(int i=0;i<5;i++)
vi.push_back(i);
//下标访问
for(int i=0;i<5;i++)
cout<<vi[i]<<" ";
cout<<"\n";
(2)迭代器访问:
//迭代器访问
vector<int>::iterator it;
//相当于声明了一个迭代器类型的变量it
//通俗来说就是声明了一个指针变量
//方式一:
vector<int>::iterator it=vi.begin();
for(int i=0;i<5;i++)
cout<<*(it+i)<<" ";
cout<<"\n";
//方式二:
vector<int>::iterator it;
for(it=vi.begin(); it != vi.end();it++)
cout<<*it<<" ";
//vi.end()指向尾元素地址的下一个地址
(3)智能指针:
只能遍历完数组,如果要指定的内容进行遍历,需要另选方法。
auto 能够自动识别类型。
vector<int>v;
v.push_back(12);
v.push_back(241);
for( auto i : v)
cout<<i<<" "; // 12 241
综上:
vi[i] 和 *(vi.begin() + i) 等价
说明:只有vector和string的stl容器支持*(it+i)的元素访问。关于string下面有讲述,可以往后看哦
3.stack(栈)
3.1 介绍
栈为数据结构的一种,是STL中实现的一个先进后出,后进先出的容器。
就像火车进入没有出口的隧道一样,隧道是stack栈容器,火车车厢是入栈元素,火车头先进去,火车尾最后进隧道,当火车倒出来时,火车尾最先出来,火车头最后出来,所有的元素满足先进后出的规则。
3.1.1 头文件:
//头文件需要添加
#include<stack>
3.1.2 定义:
//定义
stack<int>sta;
stack<string>sta;
stack<node>sta;//node是结构体类型
3.2 方法函数
代码 | 含义 |
---|---|
push() | 压栈,增加元素 O(1) |
pop() | 移除栈顶元素 O(1) |
top() | 取得栈顶元素(但不删除)O(1) |
empty | 检测栈内是否为空,空为真 O(1) |
size() | 返回stack内元素的个数 O(1) |
3.3 注意点
3.3.1 栈遍历
栈只能对栈顶元素进行操作,如果想要进行遍历,只能将栈中元素一个个取出来存在数组中
3.3.2 模拟栈
可以通过一个数组对栈进行模拟,一个存放下标的变量top模拟指向栈顶的指针。
特点: 比stack更快,如果能模拟就模拟,会减少时间。(注:我就做过这样的题,stack超时了,但模拟轻松通过)
下面是简单的代码描述:
int sta[100];
int top=0;//初始化top
for(int i=0;i<=5;i++)
{
//入栈
sta[top++]=i;
}
int top_element = sta[--top];
//入栈操作示意
// 0 1 2 3 4 5
// top
//出栈后示意
// 0 1 2 3 4
// top
注意:
top始终指向栈顶元素的下一个位置(除初始位置外),数组中的元素其实还存在。看完上述代码,我i们可以发现这种方法可以很方便地对栈的元素进行遍历。如果想要在栈中存放不同的数据类型,可以考虑使用vector数组进行模拟栈。
4.queue(队列)
4.1 介绍
队列是一种先进先出的数据结构。
比喻性的描述可为 一条两端通透的隧道,火车车厢先进就先出,后进就后出。
4.1.1 头文件:
//头文件
#include<queue>
4.1.2 定义:
//定义初始化
queue<int>q;
4.2 方法函数
代码 | 含义 |
---|---|
front() | 返回队首元素 O(1) |
back() | 返回队尾元素 O(1) |
push() | 尾部添加一个元素副本 进队O(1) |
pop() | 删除第一个元素 出队 O(1) |
size() | 返回队列中元素个数,返回值类型unsigned int O(1) |
empty() | 判断是否为空,队列为空,返回true O(1) |
队列还有另外两种双端队列,优先队列,下面做简单阐述,如果要详细了解大家可以找相关的博客学习,很抱歉不能够提供更多信息。
5.deque(双端队列)
5.1 介绍
首尾都可插入和删除的队列为双端队列。
5.1.1 头文件:
//添加头文件
#include<deque>
5.1.2 定义:
//初始化定义
deque<int>dq;
5.2 方法函数
代码 | 含义 |
---|---|
back()/front() | 访问(不删除)后/前端元素 |
push_back(x)/push_front(x) | 把x压入后/前端 |
pop_back() /pop_front() | 删除后/前端元素 |
erase(iterator it) | 删除双端队列中的某一个元素 |
erase(iterator first,iterator last) | 删除双端队列中[first,last)中的元素 |
empty() | 判断deque是否空 |
size() | 返回deque的元素数量 |
clear() | 清空deque |
5.3 注意点
deque可以进行排序,只能进行从小到大的排序.
sort(d.begin(),d.end())//从小到大
6.priority_queue(优先级队列)
6.1 介绍
优先队列是在正常队列的基础上加了优先级,保证每次的队首元素都是优先级最大的。每次操作队列中的元素都是按优先级排序的。
(你可以用它来排序,但是sort一般就可以排序,他的用处一般是在每次对序列进行增 删 改 的操作时,优先队列还能按优先级排序)
(比如一个优先队列 6 4 2 1 0 加入一个值5,优先队列就是6 5 4 2 1 0)
它的底层是通过堆来实现的。
6.1.1 头文件:
//头文件
#include<queue>
6.1.2 定义:
//初始化定义
priority_queue<int>pq;
6.2 函数方法
代码 | 含义 |
---|---|
top() | 访问队首元素 |
push() | 入队 |
pop() | 堆顶(队首)元素出队 |
size() | 队列元素个数 |
empty() | 是否为空 |
注意没有clear()! | 不提供该方法 |
优先队列只能通过top()访问队首元素(优先级最高的元素)
6.3 设置优先级
6.3.1 基本数据类型的优先级
priority_queue<int, vector<int>, less<int> >pq;
//最后两个>之间要有空格
解释:
- 第二个参数:
- vector< int > 是用来承载底层数据结构堆的容器,若优先队列中存放的是double型数据,就要填vector< double >。总之存的是什么类型的数据,就相应的填写对应类型。
- 第三个参数:
- less< int > 表示数字大的优先级大,降序队列;
- greater< int >表示数字小的优先级大,升序队列;
- int代表的是数据类型,也要填对应的类型
自定义排序:
struct cmp1
{
bool operator()(int x,int y)
{
return x>y;//小的优先级高 ,从小到大排(数字小的优先级大)
}
};
struct cmp2
{
bool operator()(const int x,const int y)
{
return a[x]>a[y];
}
};
priority_queue<int,vector<int>,cmp1>pq1;
priority_queue<int,vector<int>,cmp2>pq2;
6.3.2 结构体(或自定义)优先级设置
优先级设置可以定义在结构体内进行小于号重载,也可以定义在结构体外,下面演示定义在外面的简单写法,详情大家可以搜索相关博客进行了解。
//要排序的结构体(存储在优先队列里面的)
struct node
{
int x,y;
};
版本一:
//定义的比较结构体
//注意:cmp是个结构体
struct cmp
{//自定义堆的排序规则
bool operator()(const node& a,const node& b)
{
return a.x>b.x;//小的优先级高 ,从小到大排(数字小的优先级大)
}
};
//初始化定义,
priority_queue<node,vector<node>,cmp >pq;
版本二:不用写关的参数,直接在node里面写
因为是在node里面自定义的规则,所以是node里面的值自动排了,并不需要cmp结构体了。
struct node
{
int x,y;
friend bool operator<(node a,node b)
{//为两个结构体参数,结构体调用一定要写上friend
return a.x>b.x;//按x从小到大排
}
};
或者:
struct node
{
bool operator < (const node& a) const
{
//直接传入一个参数,不必要写friend
return x > a.x;//优先级大的为x小的值,升序排列
}
};
优先队列的定义:
priority_queue<node>pq4;
注意:
优先对列自定义排序规则和sort()函数定义cmp函数很相似,但是最后返回的情况是相反的。即相同的符号,最后定义的排列顺序是完全相反的。
所以只需要记住sort的排序规则和优先队列的排序规则是相反的就可以了。
6.4 存储特殊类型的优先级
6.4.1 存储pair类型
排序规则:默认先对pair的first进行降序排序,然后再对second降序排序
对first先排序,大的排在前面,如果first元素相同,再对second元素排序,保持大的在前面。
#include <bits/stdc++.h>
using namespace std;
int main()
{
priority_queue<pair<int, int> > q;
q.push({7, 8});
q.push({7, 9});
q.push(make_pair(8, 7));
while (!q.empty()) {
cout << q.top().first << " " << q.top().second << endl;
q.pop();
}
return 0;
}
运行结果:
8 7
7 9
7 8
7.map(映射)
7.1. map介绍
映射类似于函数的对应关系,每个x对应一个y,而map是每个键对应一个值。会python的朋友学习后就会知道这和python的字典非常类似。
比如说:学习 对应 看书,学习 是键,看书 是值。
学习->看书
玩耍 对应 打游戏,玩耍 是键,打游戏 是值。
玩耍->打游戏
7.1.1 头文件:
//头文件
#include<map>
7.1.2 定义:
//初始化定义
map<string,string>mp;
map<string,int>mp;
map<int,node>mp;//node是结构体类型
map特性:map会按照键的顺序从小到大自动排序
7.2. 函数方法
代码 | 含义 |
---|---|
mp.find(key) | 返回键为key的映射的迭代器 O(logN) 注意:用find函数来定位数据出现位置,它返回的一个迭代器,当数据出现时,它返回数据所在位置的迭代器,如果map中没有要查找的数据,它返回的迭代器等于end函数返回的迭代器, |
mp.erase(it) | 删除迭代器对应的键和值O(1) |
mp.erase(key) | 根据映射的键删除键和值 O(logN) |
mp.erase(first,last) | 删除左闭右开区间迭代器对应的键和值 O(last-first) |
mp.size() | 返回映射的对数 O(1) |
mp.clear() | 清空map中的所有元素 O(N) |
mp.insert() | 插入元素,插入时要构造键值对 |
mp.empty() | 如果map为空,返回true,否则返回false |
mp.begin() | 返回指向map第一个元素的迭代器(地址) |
mp.end() | 返回指向map尾部的迭代器(最后一个元素的下一个地址) |
mp.rbegin() | 返回指向map最后一个元素的迭代器(地址) |
mp.rend() | 返回指向map第一个元素的迭代器(地址) |
注意:
mp.begin()和mp.end()用法:用于正向遍历map
map<int,int>mp;
mp[1] = 2;
mp[2] = 3;
mp[3] = 4;
auto it = mp.begin();
while(it!=mp.end())
{
cout<<it->first << " "<<it->second<<"\n";
it ++;
}
运行结果:
1 2
2 3
3 4
mp.rbegin()和mp.rend():用于逆向遍历map
map<int,int>mp;
mp[1] = 2;
mp[2] = 3;
mp[3] = 4;
auto it = mp.rbegin();
while(it!=mp.rend())
{
cout<<it->first << " "<<it->second<<"\n";
it ++;
}
运行结果:
3 4
2 3
1 2
7.3. 添加元素
//先声明
map<string,string>mp;
方式一:
mp["学习"] = "看书";
mp["玩耍"] = "打游戏";
方式二:插入元素构造键值对
mp.insert(make_pair("vegetable","蔬菜"));
方式三:
mp.insert(pair<string,string>("fruit","水果"));
方式四:
mp.insert({"hahaha","wawawa"});
7.4. 访问元素
7.4.1 下标访问:(大部分情况用于访问单个元素)
mp["菜哇菜"] = "强哇强";
cout<<mp["菜哇菜"]<<"\n";//只是简写的一个例子,程序并不完整
7.4.2 遍历访问:
方式一:迭代器访问
map<string,string>::iterator it;
for(it=mp.begin();it!=mp.end();it++)
{
// 键 值
// it是结构体指针访问所以要用 -> 访问
cout<<it->first<<" "<<it->second<<"\n";
//*it是结构体变量 访问要用 . 访问
//cout<<(*it).first<<" "<<(*it).second;
}
方式二:智能指针访问
for(auto i:mp)
cout<<i.first<<" "<<i.second<<endl;//键,值
方式三:对指定单个元素访问
map<char,int>::iterator it = mp.find('a');
cout<<it -> first <<" "<< it->second<<"\n";
方式四:C++17特性才具有的特性
for(auto [x,y] : mp)
cout<<x<<" "<<y<<"\n";
//x,y对应键和值
还有两种映射:
multimap:
键可以重复,即一个键对应多个值,如要了解,可以自行搜索。
unordered_map:
不排序,只映射,速度更快,方法基本一样
8.set(集合)
8.1 介绍
set容器中的元素不会重复,当插入集合中已有的元素时,并不会插入进去,而且set容器里的元素自动从小到大排序。
即:set里面的元素不重复且有序。
8.1.1 头文件:
//头文件
#include<set>
8.1.2 定义:
//初始化定义
set<int>se;
8.2 函数方法
代码 | 含义 |
---|---|
s.begin() | 返回set容器的第一个元素 |
s.end() | 返回set容器的最后一个元素 |
s.rbegin() | 返回逆序迭代器,指向容器元素最后一个位置 |
s.rend() | 返回逆序迭代器,指向容器第一个元素前面的位置 |
s.clear() | 删除set容器中的所有的元素,返回unsigned int类型O(N) |
s.empty() | 判断set容器是否为空 |
s.insert() | 插入一个元素 |
s.size() | 返回当前set容器中的元素个数O(1) |
erase(iterator) | 删除定位器iterator指向的值 |
erase(first,second) | 删除定位器first和second之间的值 |
erase(key_value) | 删除键值key_value的值 |
查找 | |
s.find(元素) | 查找set中的某一元素,有则返回该元素对应的迭代器,无则返回结束迭代器 |
s.lower_bound(k) | 返回大于等于k的第一个元素的迭代器 |
s.upper_bound(k) | 返回大于k的第一个元素的迭代器 |
8.3 访问
a.迭代器访问
for(set<int>::iterator it=s.begin();it!=s.end();it++)
cout<<*it<<" ";
b.简便的方法
for(auto i : s)
cout<<i<<endl;
c.访问最后一个元素
//第一种
cout<<*s.rbegin()<<endl;
//第二种
set<int>::iterator iter = s.end();
iter--;
cout<<(*iter)<<endl; //打印2;
//第三种
cout<<*(--s.end())<<endl;
8.4 重载<运算符
//重载<运算符
struct cmp {
bool operator () (const int& u, const int& v) const
{
// return + 返回条件
}
};
set<int, cmp> se;
8.5 其它的set
multiset:元素可以出现多次,且有序
unordered_set :元素无序且只能出现一次
unordered_multiset : 元素无序可以出现多次
若需了解,读者可自行查阅相关资料。
9.pair
9.1 介绍
pair只含有两个元素,可以看作是只有两个元素的结构体。
应用:
- 代替二元结构体
- 作为map键值对进行插入,代码如下:
map<string,int>mp;
mp.insert(pair<string,int>("xingmaqi",1));
//头文件
#include<utility>
//1.初始化定义
pair<string,int>p("wangyaqi",1);//带初始值的
pair<string,int>p;//不带初始值的
//2.赋值
p = {"wang",18};
9.2 访问
//定义结构体数组
pair<int,int>p[20];
for(int i=0;i<20;i++)
{
//和结构体类似,first代表第一个元素,second代表第二个元素
cout<<p[i].first<<" "<<p[i].second;
}
10.string字符串
10.1 介绍
可以把string理解为一个字符串类型,像int一样可以定义。
10.1.1.初始化定义:
//头文件
#include<string>
//1.
string str1; //生成空字符串
//2.
string str2("123456789"); //生成"1234456789"的复制品
//3.
string str3("12345", 0, 3);//结果为"123" ,从0位置开始,长度为3
//4.
string str4("123456", 5); //结果为"12345" ,长度为5
//5.
string str5(5, '2'); //结果为"22222" ,构造5个字符'2'连接而成的字符串
//6.
string str6(str2, 2); //结果为"3456789",截取第三个元素(2对应第三位)到最后
10.1.2.简单使用
string访问单个字符:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s = "xing ma qi!!!";
for(int i=0;i<s.size();i++)
{
cout<<s[i]<<" ";
}
return 0;
}
/*
x i n g m a q i ! ! !
*/
string数组:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string s[10];
for(int i=1;i<10;i++)
{
s[i] = "loading... " ;
cout<<s[i]<<i<<"\n";
}
return 0;
}
/*
loading... 1
loading... 2
loading... 3
loading... 4
loading... 5
loading... 6
loading... 7
loading... 8
loading... 9
*/
10.2 string 特性
1、string字符串支持常见的比较操作符(>,>=,<,<=,==,!=),甚至支持string与C-string的比较(如 str<”hello”)。
在使用>,>=,<,<=这些操作符的时候是根据“当前字符特性”将字符按 字典顺序 进行逐一得 比较。字典排序靠前的字符小, 比较的顺序是从前向后比较,遇到不相等的字符就按这个位置上的两个字符的比较结果确定两个字符串的大小(前面减后面) 同时,string (“aaaa”) <string(aaaaa)。
2、另一个功能强大的比较函数是成员函数compare()。
他支持多参数处理,支持用索引值和长度定位子串来进行比较。 他返回一个整数来表示比较结果,返回值意义如下:0:相等 1:大于 -1:小于 (A的ASCII码是65,a的ASCII码是97)
3、string字符串可以拼接,通过"+"运算符进行拼接。
string s1;
string s2;
s1 = "123";
s2 = "456";
string s = s1 + s2;
cout<<s; //123456
10.3 读入详解
10.3.1 读入字符串,遇空格,回车结束
string s;
cin>>s;
10.3.2 读入一行字符串(包括空格),遇回车结束
string s;
getline(cin,s);
注意!!! getline(cin,s)会获取前一个输入的换行符,需要在前面添加读取换行符的语句。如:getchar() 或 cin.get()
错误读取:
int n;
string s;
cin>>n;
getline(cin,s);//此时读取相当于读取了前一个回车字符
正确读取:
int n;
string s;
cin>>n;
getchar();//cin.get()
getline(cin,s);//可正确读入下一行的输入
10.3.3 cin>>与cin.getline()混用
cin输入完后,回车,cin遇到回车结束输入,但回车还在输入流中,cin并不会清除,导致getline()读取回车,结束。 (即上述注意点)
需要在cin后面加cin.ignore();主动删除输入流中的换行符.
10.3.4 cin和cout解锁
代码:
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
为什么要进行cin和cout的解锁呢,原因是:
在一些题目中,读入的数据量很大,往往超过了1e5(105)的数据量,而cin和cout的读入输出的速度很慢,远不如scanf和printf的速度,具体原因可以搜索相关的博客进行了解。
所以对cin和cout进行解锁使cin和cout的速度几乎接近scanf和printf,避免超时。
注意!!!:cin cout解锁使用时,不能与 scanf 、getchar, printf,cin.getline( ) 混用,一定要注意,会出错。
10.4 函数方法
(1) 获取字符串长度
代码 | 含义 |
---|---|
s.size()和length() | 返回string对象的字符个数,他们执行效果相同。 |
s.max_size() | 返回string对象最多包含的字符数,超出会抛出length_error异常 |
s.capacity() | 重新分配内存之前,string对象能包含的最大字符数 |
(2) 插入
代码 | 含义 |
---|---|
s.push_back() | 在末尾插入 例:s.push_back(‘a’),末尾插入一个字符a |
s.insert(pos,element) | 在pos位置插入element 例:s.insert(s.begin(),‘1’),在第一个位置插入1字符 |
s.append(str) | 在s字符串结尾添加str字符串 例:s.append(“abc”),在s字符串末尾添加字符串“abc” |
(3) 删除
代码 | 含义 |
---|---|
erase(iterator p) | 删除字符串中p所指的字符 |
erase(iterator first, iterator last) | 删除字符串中迭代器区间[first,last)上所有字符 |
erase(pos, len) | 删除字符串中从索引位置pos开始的len个字符 |
clear() | 删除字符串中所有字符 例:s.clear(),实质是把字符串空间首字符设置为了“\0” |
(4)字符替换
代码 | 含义 |
---|---|
s.replace(pos,n,str) | 把当前字符串从索引pos开始的n个字符替换为str |
s.replace(pos,n,n1,c) | 把当前字符串从索引pos开始的n个字符替换为n1个字符c |
s.replace(it1,it2,str) | 把当前字符串[it1,it2)区间替换为str it1 ,it2为迭代器哦 |
(5) 大小写转换
法一:
代码 | 含义 |
---|---|
tolower(s[i]) | 转换为小写 |
toupper(s[i]) | 转换为大写 |
法二:
通过stl的transform算法配合tolower 和toupper 实现。
有4个参数,前2个指定要转换的容器的起止范围,第3个参数是结果存放容器的起始位置,第4个参数是一元运算。
string s;
transform(s.begin(),s.end(),s.begin(),::tolower);//转换小写
transform(s.begin(),s.end(),s.begin(),::toupper);//转换大写
(6) 分割
代码 | 含义 |
---|---|
s.substr(pos,n) | 截取从pos索引开始的n个字符 |
(7) 查找
代码 | 含义 |
---|---|
s.find (str, pos) | 在当前字符串的pos索引位置(默认为0)开始,查找子串str,返回找到的位置索引,-1表示查找不到子串 |
s.find (c, pos) | 在当前字符串的pos索引位置(默认为0)开始,查找字符c,返回找到的位置索引,-1表示查找不到字符 |
s.rfind (str, pos) | 在当前字符串的pos索引位置开始,反向查找子串s,返回找到的位置索引,-1表示查找不到子串 |
s.rfind (c,pos) | 在当前字符串的pos索引位置开始,反向查找字符c,返回找到的位置索引,-1表示查找不到字符 |
s.find_first_of (str, pos) | 在当前字符串的pos索引位置(默认为0)开始,查找子串s的字符,返回找到的位置索引,-1表示查找不到字符 |
s.find_first_not_of (str,pos) | 在当前字符串的pos索引位置(默认为0)开始,查找第一个不位于子串s的字符,返回找到的位置索引,-1表示查找不到字符 |
s.find_last_of(str, pos) | 在当前字符串的pos索引位置开始,查找最后一个位于子串s的字符,返回找到的位置索引,-1表示查找不到字符 |
s.find_last_not_of ( str, pos) | 在当前字符串的pos索引位置开始,查找最后一个不位于子串s的字符,返回找到的位置索引,-1表示查找不到子串 |
还是不会用? 下面上代码,进行实操
#include <string>
#include <iostream>
using namespace std;
int main()
{
string s("dog bird chicken bird cat");
//字符串查找-----找到后返回首字母在字符串中的下标
// 1. 查找一个字符串
cout << s.find("chicken") << endl; // 结果是:9
// 2. 从下标为6开始找字符'i',返回找到的第一个i的下标
cout << s.find('i', 6) << endl; // 结果是:11
// 3. 从字符串的末尾开始查找字符串,返回的还是首字母在字符串中的下标
cout << s.rfind("chicken") << endl; // 结果是:9
// 4. 从字符串的末尾开始查找字符
cout << s.rfind('i') << endl; // 结果是:18因为是从末尾开始查找,所以返回第一次找到的字符
// 5. 在该字符串中查找第一个属于字符串s的字符
cout << s.find_first_of("13br98") << endl; // 结果是:4---b
// 6. 在该字符串中查找第一个不属于字符串s的字符------先匹配dog,然后bird匹配不到,所以打印4
cout << s.find_first_not_of("hello dog 2006") << endl; // 结果是:4
cout << s.find_first_not_of("dog bird 2006") << endl; // 结果是:9
// 7. 在该字符串最后中查找第一个属于字符串s的字符
cout << s.find_last_of("13r98") << endl; // 结果是:19
// 8. 在该字符串最后中查找第一个不属于字符串s的字符------先匹配t--a---c,然后空格匹配不到,所以打印21
cout << s.find_last_not_of("teac") << endl; // 结果是:21
}
(8) 排序
sort(s.begin(),s.end()); //按ASCII码排序
11.list(链表)
0、介绍
// 头文件
#include<list>
// 声明一个int型的list:
list<int> a;
1、list的构造函数
list<int>a{1,2,3}
list<int>a(n) //声明一个n个元素的列表,每个元素都是0
list<int>a(n, m) //声明一个n个元素的列表,每个元素都是m
list<int>a(first, last) //声明一个列表,其元素的初始值来源于由区间所指定的序列中的元素,first和last是迭代器
2、 begin()和end()
通过调用list容器的成员函数begin()得到一个指向容器起始位置的iterator,可以调用list容器的end()函数来得到list末端下一位置
3、 push_back()和push_front()
使用list的成员函数push_back和push_front插入一个元素到list中。其中push_back()是从list的末端插入,而push_front()是从list的头部插入。
4、 empty()
判断list是否为空
5、resize()
调用resize(n)将list的长度改为只容纳n个元素,超出的元素将被删除。如果n比list原来的长度长,那么默认超出的部分元素置为0。也可以用resize(n, m)的方式将超出的部分赋值为m。
例子:
list<int>b{1, 2, 3, 4};
b.resize(2);
// list中输出元素:1,2
list<int>b{1, 2, 3, 4};
b.resize(6);
// list中输出元素:1,2,3,4,0,0
list<int>b{1, 2, 3, 4};
b.resize(6,9);
//list中输出元素:1,2,3,4,9,9
6、clear()
清空list中的所有元素
7、front()和back()
通过front()可以获得list容器中的头部元素,通过back()可以获得list容器的最后一个元素。注意:当list元素为空时,这时候调用front()和back()不会报错。因此在编写程序时,最好先调用empty()函数判断list是否为空,再调用front()和back()函数。
8、pop_back()和pop_front()
使用pop_back()可以删掉尾部第一个元素,pop_front()可以删掉头部第一个元素。注意:list必须不为空,如果当list为空的时候调用pop_back()和pop_front()会使程序崩掉。
9、assign()
有两种使用情况:
(1)a.assign(n, val):将a中的所有元素替换成n个val元素
例如:
list<int>b{1,2,3,4,5};
b.assign(5,10);
// b中的元素变为10, 10, 10, 10, 10
(2)a.assign(b.begin(), b.end())
list<int>a{6,7,8,9};
list<int>b{1,2,3,4,5};
b.assign(a.begin(),a.end());
// b中的元素变为6,7,8,9
10、swap()
交换两个链表。a.swap(b)和swap(a, b),都可以完成a链表和b链表的交换。
例子:
list<int>a{6,7,8,9};
list<int>b{1,2,3,4,5};
swap(a, b); //或a.swap(b)
// a中元素变为1,2,3,4,5
// b中元素变为6,7,8,9
11、reverse()
可以实现list的逆置
例子:
list<int>b{1,2,3,4,5};
reverse(b.begin(),b.end());
// b中元素变为5,4,3,2,1
12、merge()
a.merge(b) 调用结束后b变为空,a中元素包含原来a和b的元素。
例子:
list<int>a{6,7,8,9};
list<int>b{2, 1, 3, 6, 5};
a.merge(b,greater<int>());
// a中元素变为:6,7,8,9,2,1,3,6,5
list<int>a{6,7,8,9};
list<int>b{2, 1, 3, 6, 5};
a.merge(b);
// a中元素变为:2,1,3,6,5,6,7,8,9
13、insert()
在指定位置插入一个或多个元素
a.insert(a.begin(),100); //在a的开始位置(即头部)插入100
a.insert(a.begin(),2, 100); //在a的开始位置插入2个100
a.insert(a.begin(),b.begin(), b.end());//在a的开始位置插入b从开始到结束的所有位置的元素
14、erase()
删除一个元素或一个区域的元素
a.erase(a.begin()); //将a的第一个元素删除
a.erase(a.begin(),a.end()); //将a的从begin()到end()之间的元素删除。
15、remove()函数
从list中删除元素
list<int>a{6,7,8,9,7,10};
a.remove(7);
// 删除了a中所有值为7的元素,此时a中元素为6,8,9,10
16、remove_if()函数
括号中可以传入
(1)回调函数
回调函数的原型为boolisRemove(T &obj1);
函数名任意,如果obj1需要被移除则返回1,否则返回0
使用方法:list.remove_if(isRemove)
这种方法最简单,但是无法向回调函数中传递参数,每一个条件就要有一个回调函数,因此不推荐使用.
(2)创建用于比较的类,传入类名及初始化参数
用于比较的类必须重载bool operator()(T &obj1)方法,如果obj1需要被移除则返回1,否则返回0.
用于比较的类还应当包含必要的构造函数,用于传递参数。
使用方法:list.remove_if(classname(args))
例1:
bool is_odd(constint& value){
return (value==4);
}
int main(){
list<int> a{6,7,4,9,7,10};
a.remove_if(is_odd);
list<int>::iterator it = a.begin();
while(it != a.end()){
cout<<*it<< " ";
it++;
}
return 0;
}
输出:
6 7 9 7 10
例2:
class single_digit{
public:
bool operator()(const int& value){
return (value<10);
}
};
int main(){
list<int> a{6,7,4,9,7,10};
a.remove_if(single_digit());
list<int>::iterator it = a.begin();
while(it != a.end()){
cout<<*it<<" ";
it++;
}
return 0;
}
输出:
10
11. tuple
0. 介绍
c++中的tuple是一个允许存放多种不同的数据类型的容器,是针对pair的泛型,和pair一样在std 的namespace中,在使用的时候,需要引用头文件,同时注意namespace;
// 头文件
#include<tuple>
1. 可用函数
和tuple相关的一共有四个函数,下面分别对其进行介绍
(1)make_tuple
创建并初始化tuple
auto tup = std::make_tuple("liu","yi","jiang","is",6,1,9);
该代码创建的tuple 对应类型如下,
tuple<const char*, char*, char*, char*, int, int, int>
不难看出tuple中并不要求元素的类型相同,同时可以支持多个元素。
需要注意的是,tuple变量中的元素数量,在创建完成后就是固定不变的,不能增加或减少。
(2)tie
分而取之,获取tuple中的单个元素
auto tup = std::make_tuple('l',6,2.33);
char a;
int b;
double c;
std:tie(a,b,c) = tup;
std::cout<<"a="<<a<<" "<<"b="<<b<<" "<<"c="<<c<<endl;
程序的输出结果为a=l b=6 c=2.33
如果不想取某一位的值,可以使用ingore代替:
auto tup = std::make_tuple('l',6,2.33);
char a;
double c;
std:tie(a,ingore,c) = tup;
std::cout<<"a="<<a<<" "<<"c="<<c<<endl;
程序的输出结果为a=l c=2.33
(3)forward_as_tuple
用于接收右值引用数据生成tuple
auto tup = std::forward_as_tuple(1,"csdn")
上述代码创建了一个tuple<int &&, char (&)[6]>类型的元组。
可以看出,tuple中的参数全部为右值引用。而前面讨论的tie函数就只能接受左
(4)tuple_cat
用于连接tuple
std::tuple<float, string> tup1(3.14, "pi");
std::tuple<int, char> tup2(10, 'a');
auto tup3 = tuple_cat(tup1, tup2);
2. tuple中对元素的操作
(1)get < i >
获取第i个元素的值
std::tuple<float, string> tup(666, "emmmm");
cout << get<0>(tup);
// 输入第一个元素666.
(2)tuple_element
获取tuple中特定元素数据结构
std::tuple_element<0, decltype(tup1)>::type
(3)size
获取tuple中的元素个数
std::tuple<float, string> tup(666, "emmmm");
cout << tuple_size<decltype(tup1)>::value;
// 输出结果为2
12.bitset
12.1 介绍
bitset 在 bitset 头文件中,它类似数组,并且每一个元素只能是0或1,每个元素只用1bit空间
//头文件
#include<bitset>
12.2 初始化定义
初始化方法:
代码 | 含义 |
---|---|
bitset < n >a; | a有n位,每位都为0 |
bitset < n >a(b); | a是unsigned long型u的一个副本 |
bitset < n >a(s); | a是string对象s中含有的位串的副本 |
bitset < n >a(s,pos,n); | a是s中从位置pos开始的n个位的副本 |
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
bitset<4> bitset1; //无参构造,长度为4,默认每一位为0
bitset<9> bitset2(12); //长度为9,二进制保存,前面用0补充
string s = "100101";
bitset<10> bitset3(s); //长度为10,前面用0补充
char s2[] = "10101";
bitset<13> bitset4(s2); //长度为13,前面用0补充
cout << bitset1 << endl; //0000
cout << bitset2 << endl; //000001100
cout << bitset3 << endl; //0000100101
cout << bitset4 << endl; //0000000010101
return 0;
}
12.3 特性
bitset可以进行位操作:
bitset<4> foo (string("1001"));
bitset<4> bar (string("0011"));
cout << (foo^=bar) << endl;// 1010 (foo对bar按位异或后赋值给foo)
cout << (foo&=bar) << endl;// 0010 (按位与后赋值给foo)
cout << (foo|=bar) << endl;// 0011 (按位或后赋值给foo)
cout << (foo<<=2) << endl;// 1100 (左移2位,低位补0,有自身赋值)
cout << (foo>>=1) << endl;// 0110 (右移1位,高位补0,有自身赋值)
cout << (~bar) << endl;// 1100 (按位取反)
cout << (bar<<1) << endl;// 0110 (左移,不赋值)
cout << (bar>>1) << endl;// 0001 (右移,不赋值)
cout << (foo==bar) << endl;// false (0110==0011为false)
cout << (foo!=bar) << endl;// true (0110!=0011为true)
cout << (foo&bar) << endl;// 0010 (按位与,不赋值)
cout << (foo|bar) << endl;// 0111 (按位或,不赋值)
cout << (foo^bar) << endl;// 0101 (按位异或,不赋值)
访问:
//可以通过 [ ] 访问元素(类似数组),注意最低位下标为0,如下:
bitset<4> foo ("1011");
cout << foo[0] << endl; //1
cout << foo[1] << endl; //1
cout << foo[2] << endl; //0
12.4 方法函数
代码 | 含义 |
---|---|
b.any() | b中是否存在置为1的二进制位,有 返回true |
b.none() | b中是否没有1,没有 返回true |
b.count() | b中为1的个数 |
b.size() | b中二进制位的个数 |
b.test(pos) | 测试b在pos位置是否为1,是 返回true |
b[pos] | 返回b在pos处的二进制位 |
b.set() | 把b中所有位都置为1 |
b.set(pos) | 把b中pos位置置为1 |
b.reset() | 把b中所有位都置为0 |
b.reset(pos) | 把b中pos位置置为0 |
b.flip() | 把b中所有二进制位取反 |
b.flip(pos) | 把b中pos位置取反 |
b.to_ulong() | 用b中同样的二进制位返回一个unsigned long值 |
更多推荐
所有评论(0)