第九册第01课时 常见STL讲解
一、常见STL讲解
标准模板库
C++的标准模板库中封装了很多常用的容器,包含了诸多常用的数据结构与基本算法。算法竞赛常用的STL:算法函数(alogorithm)、栈(stack)、队列(queue)、优先队列(priority_queue)、双端队列(deque)、向量(vector)、集合(set)、多重集合(multiset)、映射(map)、无序映射(unordered_map)、数对(pair)等。
每种数据结构都有自己的实现细节,STL 库把一切细节都隐藏起来,使用统一的接口(命令)格式(头文件),供简单方便使用。
学习 STL 部分内容时,需要大家动手实践尝试!
stack与queue
stack(栈)是STL实现的栈容器,queue(队列)是STL实现的队列容器。栈和队列容器可以实现基本的栈与队列功能。以下所有操作时间复杂度O(1)。
stack操作:
stackst; | 常用函数功能 |
---|---|
st.push(x) | 将x入栈 |
st.top() | 获取栈顶元素 |
st.pop() | 弹出栈顶元素 |
st.empty() | 判断栈空,空为true,否则为false |
st.size() | 返回stack内元素个数 |
queue操作:
queueq; | 常用函数功能 |
---|---|
q.push(x) | 将x入队 |
q.front() | 返回队头 |
q.back() | 返回队尾 |
q.pop() | 队首元素出队 |
q.empty() | 判断队空,空为true,否则为false |
q.size() | 返回queue内元素个数 |
deque
头文件:
deque是STL的双端队列容器,可以在队头和队尾入队或出队
单调队列算法是通过双端队列实现的
双端队列操作:
dequeq; | 常用函数功能 |
---|---|
q.back() | 返回队尾元素 |
q.front() | 返回队首元素 |
q.push_back(x) | 从队尾将x入队 |
q.push_front(x) | 从队首将x入队 |
q.pop_back() | 队尾元素出队 |
q.pop_front() | 队首元素出队 |
q.size() | 返回deque内元素个数 |
q.empty() | 判断队空,空为true,否则为false |
练习题A 括号匹配
题目描述
假设表达式中允许包含圆括号和方括号两种括号,其嵌套的顺序随意,如()或[([][])]等为正确的匹配,[(])或(或(()))均为错误的匹配
本题的任务是检验一个给定的表达式中的括号是否匹配正确
输入一个只包含圆括号和方括号的字符串,判断字符串中的括号是否匹配,匹配就输出“OK”,不匹配就输出“Wrong”。
输入描述
一行字符,只含有圆括号和方括号,个数小于 255。
输出描述
匹配就输出一行文本“OK“,不匹配就输出一行文本”Wrong”
输入样例
[(])
输出样例
Wrong
练习题B 周末舞会
题目描述
假设在周末舞会上,男士们和女士们进入舞厅时,各自排成一队。跳舞开始时,依次从男队和女队的队头上各出一人配成舞伴。规定每个舞曲能有一对跳舞者。若两队初始人数不相同,则较长的那一队中未配对者等待下一轮舞曲。现要求写一个程序,模拟上述舞伴配对问题。(0<m,n,k<1000)
输入描述
第一行男士人数m和女士人数n;
第二行舞曲的数目k。
输出描述
共k行,每行两个数,表示配对舞伴的序号,男士在前,女士在后。
输入样例
2 4
6
输出样例
1 1
2 2
1 3
2 4
1 1
2 2
vector
vector(向量)本质就是一个动态大小的数组,可以存放任意类型的对象。
vector<int> a(10); //定义了 10 个整型元素的向量(尖括号中为元素类型名,它可以是任何合法的数据类型),但没有给出初值,其值是不确定的 O(n)
vector<int> a(10,1); //定义了 10 个整型元素的向量,且元素的初值都为 1 O(n)
vector<int> a(b); //用 b 向量来创建 a 向量,整体复制性赋值 O(n)
vector<int> a(b.begin(),b.begin+3); //a 的初值为向量 b 的前三个值 O(n)
int b[7]={1,2,3,4,5,9,8};vector<int> a(b,b+7); //从数组中获得初值 O(n)
v[i]; // 以下标方式访问元素 O(1)
v.push_back(x); //往尾部插入元素 x (常用) O(1)
v.insert(it, x); //向迭代器 it 指向的位置插入 x,原位置后移 O(n)
v.size();//返回表长,注意是 unsigned 类型 O(1)
v.front(); //取首元素的值 v.back(); // 取尾元素的值 O(1)
v.empty(); //表为空返回真,否则返回假 O(1)
v.clear(); //清空表 O(1)
v.pop_back(); // 删除表尾元素 O(1)
v.erase(it); //删除迭代器 it 指向的元素 O(1)
vector迭代器
要访问顺序容器和关联容器中的元素,需要通过“迭代器(iterator)”进行。迭代器是一个变量,相当于容器和操纵容器的算法之间的中介。迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素。从这一点上看,迭代器和指针类似。接下来看一个通过迭代器遍历vector容器的例子:
vector<int>::iterator it;
for(it=v.begin();it!=v.end();it++){
cout<<*it<<endl;
}
迭代器的定义:
容器类型::iterator 迭代器名称
容器类型取决于迭代器指向什么容器,如vector,vector
迭代器的增减:
迭代器可以使用++或–指向下一个元素或上一个元素
vector的迭代器也可以是+或-一个数值,如it+3代表迭代器变换为后面的第三个元素
迭代器取值:
迭代器变量it存储的并不是容器中元素的值,而是类似于指针存储一个地址类型,所以要取it所指向的元素的值,需要用运算符*
map映射
头文件
常用的map方法:
map<int,int>m;
m.find(x):find是查找是否存在。
m.count(x):count是查找键为x的数量。(等同于查找x是否存在)
m.empty():判断是否为空
m.size():容器内元素数量
m.insert(x):x必须为对的形式。可以为pair的形式,通过make_pair创建。
m.erase(x):将键值为x的元素删除。
m.clear():清空map
map的底层是红黑树,所以查找、插入、删除、访问的时间复杂度都是log级
map迭代器
迭代器定义:
map<int,int>::iterator it;
map<char,int>::iterator it;
map<string,int>::iterator it;
map迭代器特性:
map是有序的,根据第一关键字自动排序,如map内只有两个元素:
mp[14]=5
mp[1000]=1
则遍历map时会先遍历到mp[14],再遍历到mp[1000]
map的迭代器it取值不用*,而用->first取第一关键字,->second取第二关键字
map的迭代器只能++,–,不能±数值
unordered_map
头文件<unordered_map>
unordered_map是和map类似的STL容器,用来完成映射的操作。
不同的是,unordered_map底层实现是哈希表,不是自有序的,是无序的,增删改查的速度也比map要快一些。
定义方法:
unordered_map<int,int>ump;
unordered_map<char,int>ump;
unordered_map<string,int>ump;
增加元素方法(直接访问下标赋值):
ump[1000000009] = 56
ump[‘t’]=123
ump[“asdfas”]=565
方法:
常用方法与map相同
ump.find(x):find是查找是否存在。
ump.count(x):count是查找键为x的数量。(等同于查找x是否存在)
ump.empty():判断是否为空
ump.size():容器内元素数量
ump.insert(x):x必须为对的形式。可以为pair的形式,通过make_pair创建。
ump.erase(x):将键值为x的元素删除。
ump.clear():清空unordered_map
迭代器:
与map相同,只是遍历的unordered_map是无序的
multimap
multimap容器保存的是有序的键/值对,顺序按第一关键字,第一关键字相同时,越早插入的元素越靠前。此容器可以保存重复的元素。由于能保存重复的元素,所以multimap中的元素不能通过下标访问。只能通过迭代器。
如multimap中含有键值对(1,6)(1,5)(2,3)(1,5),遍历后输出顺序为:
1 6
1 5
1 5
2 3
方法:
由于键值可以重复,所以有些方法不适用
mmp.insert(x):x必须为对的形式。可以为pair的形式,通过make_pair创建。
mmp.erase(x):若x为键值,所有键值为x的元素都被删除,若x为迭代器,删除迭代器指向的元素
mmp.count(x):返回有多少个键值为x的元素
mmp.clear():清空multimap
练习题C 查字典
题目描述
全国英语四级考试就这样如期到来了,可是小Y依然没有做好充分的准备。为了能够大学毕业,可怜的小Y决定作弊。(太胆大妄为了,不怕被学校开除!!)
小Y费尽心机,在考试的时候夹带了一本字典进考场。但是现在的问题是,考试的时候可能有很多单词要查,小Y能不能来得及呢。
输入描述
第一行一个整数N,表示字典中一共有多少单词(N<=10000)。
接下来每两行表示一个单词,其中: 第一行是一个长度<=100的字符串,表示这个单词,全部小写字母,单词不会重复。
第二行是一个整数,表示这个单词在字典中的页码。
接下来一行是一个整数M,表示要查的单词数(M<=10000)。
接下来M行,每行一个字符串,表示要查的单词,保证在字典中存在。
输出描述
M行,每行一个整数,表示第i个单词在字典中的页数。
输入样例
2
scan
10
word
15
2
scan
word
输出样例
10
15
练习题D 分类
题目描述
仓库里面堆积着很多的货物。小可放假归来,发现仓库里多了许多的货物。
小可作为仓库管理员,需要对新多出来的货物进行分类整理。但是这段时间多出来的货物太多了,你能帮小可整理一下吗?
输入描述
第一行一个正整数T(1≤T≤10),代表有T组数据。
对于每组数据,第一行有一个正整数n,代表有n条入库记录。
接下来有n行入库记录。
每个入库记录由字符串a,b和一个正整数m(1≤m≤100)组成,a是货物名,b是货物来源,m是数量。这两个字符串的长度都不长于80。
输出描述
请输出合并整理后格式正确的货物统计表。格式为:
把货物按照来源地分类,来源地按照字典序排序输出。每个来源地都有下级分类。
每个来源地下级分类中,每一行输出三个空格,一个|,四个-,用来保持缩进。子分类中,将来源地是此来源地的货物按照字典序排序,每个货物后面有一个括号,里面写货物的总数量。具体格式参考样例
输入样例
1
5
pencil shandong 3
eraser guangdong 1
water sichuan 1
eraser guangdong 3
water guangdong 1
输出样例
guangdong
|----eraser(4)
|----water(1)
shandong
|----pencil(3)
sichuan
|----water(1)
练习题E 信息筛选
题目描述
小可在服务器上维护了一个字典库,里面存放了很多单词信息。
但是使用的时间长了,难免会出现很多的冗余信息。于是小可打算着手去整理这个字典库。
小可认为,如果一个字典中的单词,能够通过重新排列字母变成另一个字典中的单词,那么这个单词就是冗余的。比如ride和dire都在字典中,那么这两个都是冗余的。
当然,我们进行比较的时候,忽略字母大小写。也就是说如果字典中有ride和DiRe,这两个单词依旧都是冗余的。
输入描述
若干行单词,都是由大写字母或者小写字母构成。长度不超过80。以#结束输入。
输出描述
按字典序顺序输出非冗余的单词。
输入样例
ssy
DiRe
My
would
ride
SHOULD
Yss
#
输出样例
My
SHOULD
would
练习题F 玩具箱
题目描述
小可获得了一个玩具箱,他买了许多玩具放在玩具箱里。有一天,达达来找小可玩,发现玩具箱里的玩具没有分类,十分杂乱,所以就替小可给玩具箱分类。每一个玩具有一个种类值
c
i
c_i
ci,有一个价格值
p
i
p_i
pi。最开始,达达将玩具箱置空,接下来达达要操作n次。达达一共有三种操作:
1 c p表示将种类值为c,价格值为p的玩具塞入玩具箱。
2 c表示将种类值为c的玩具全部删除,数据保证删除时玩具箱中一定存在。
3 c表示计算种类值为c的玩具的数量。
输入描述
第一行输入n,表示操作的次数。
接下来n行每行一个操作,格式如题面所示。
输出描述
对应每个3操作,输出对应的值
n次操作后,按照顺序输出玩具箱中的所有玩具的种类值和价格值,每行对应一个玩具。顺序定义:优先种类值从小到大,若种类值相同,则按照玩具加入玩具箱的先后输出。
数据范围
1<=n<=100000,1<= c i c_i ci, p i p_i pi<= 1 0 9 10^9 109
输入样例
8
1 2 3
1 2 2
1 3 5
1 3 4
3 2
3 3
1 1 6
2 2
3 2
输出样例
2
2
0
1 6
3 5
3 4
set
功能:元素去重,可以按照从小到大的顺序进行排序。
集合是由一些不重复的数据组成的。比如{1,2,3}就是一个有1,2,3三个元素的集合。C++中的集合可以支持高效的插入、删除、查询操作。
这三个时间复杂度都为O(logn)
头文件:
注意:必须添加using namespace std;
构造一个set的语句为:sets;
这样就定义了一个类型为T,名为s的集合。
插入操作:insert(),如果集合中已经存在了某个元素,再次插入不会产生任何效果,集合中不会出现重复元素。
删除操作:erase(),删除集合中一个元素,集合中不存在这个元素,不进行任何操作。查找元素:count(),如果集合中存在要查找的元素,返回1,否则返回0。
遍历元素:使用迭代器。迭代器类似于指针,可以遍历集合中的内容。
清空:clear();
set迭代器
迭代器定义:
set<int>::iterator it;
set<char>::iterator it;
set<string>::iterator it;
set迭代器特性:
set是有序的,insert后自动排序
set的迭代器it取值需要使用*
set的迭代器只能++,–,不能±数值
unordered_set
头文件<unordered_set>
unordered_set是和set类似的STL容器,无序且去重。
unordered_set底层实现是哈希表,增删查均摊复杂度O(1)
定义方法:
unordered_set<int>use;
unordered_set<char>use;
unordered_set<string>use;
方法:
use.find(x):find是查找x是否存在。
use.count(x):返回元素x的数量。(有为1,没有为0)
use.size():容器内元素数量
use.insert(x):插入元素x
use.erase(x):将值为x的元素删除。
use.clear():清空unordered_set
迭代器定义:
unordered_set<T>::iterator it;
multiset
multiset是和set类似的STL容器,有序且不去重。
定义方法:
multiset<int>mse;
multiset<char>mse;
multiset<string>mse;
方法:
mse.find(x):返回查到的第一个元素值为x的迭代器
mse.count(x):返回元素x的数量。
mse.size():容器内元素数量
mse.insert(x):插入元素x
mse.erase(x):将值为x的元素全部删除。
mse.clear():清空multiset
mse.lower_bound(x):返回第一个值等于大于x的元素的迭代器
mse.upper_bound(x):返回第一个值大于x的元素的迭代器
迭代器定义:
multiset<T>::iterator it;
练习题G 集合的并
题目描述
现在有两个集合,计算两个集合的并,即{A}+{B}。注:{A}+{B}中不允许出现重复元素,但是{A}与{B}之间可能存在相同元素。
输入描述
多组测试数据。每组数据包含三行。
第一行为两个正整数n,m。分别表示两个集合的元素数量。
第二行为第一个集合元素、第三行为第二个集合元素。
输出描述
输出两个集合并后的元素。
输入样例
1 2
1
2 3
1 2
1
1 2
输出样例
1 2 3
1 2
练习题H 修补道路
题目描述
小可家门口的路历经沧桑,已经不复平坦,变得坑坑洼洼的。
于是小可拜托达达找了一个施工队来修补道路。施工队把这个道路分成了 n段,然后测量了这n段各自的高度。
施工队的人对小可说:“我们现在要通过两种方式按段修复这条道路:
- 对有些路段填充一些材料,增加x高度。
- 对有些路段削减掉x的高度。
当然有的路段我们可能什么都不做。”
x是一个固定值,小可并不知道具体是多少。但是小可想知道通过这种方式,有没有可能让这个道路重新变得平坦。
输入描述
第一行一个正整数n(1≤n≤
1
0
5
10^5
105)
第二行有n个整数
a
i
a_i
ai(0≤
a
i
a_i
ai≤
1
0
9
10^9
109),代表每段路的高度。
输出描述
如果可能重新变得平坦,输出"YES"。否则输出"NO"。平坦即为所有路段高度都相同。
输入样例
5
1 3 3 2 1
输出样例
YES
练习题I 包裹与数
题目描述
有一个用于装整数的包裹,一开始包裹里没有数,接下来有 n 次操作,每一次操作包含三种类型。
1 x将一个整数x放入包裹
2 x从包裹中取出一个值为x的数(保证包裹中有值为x的数)
3 询问包裹中所有数中最接近的一对数(即差的绝对值最小的两个数,并输出这对数的差的绝对值)
对于每次查询操作,输出对应的结果。如果查询时包裹中整数个数小于2个,输出2147483647
输入描述
输入的第一行包含一个整数n
加下来n行,每一行可能是1 x、2 x、3这三种操作中的一种
输出描述
对于每一步查询操作,输出一行,为其对应的结果,即包裹中的所有数中最接近的一对数的差的绝对值。如果查询时包裹中整数个数小于2个,输出2147483647
数据范围
1<=n<= 1 0 5 10^5 105,1<=x<= 1 0 9 10^9 109
输入样例
9
1 2
1 5
3
1 7
3
2 5
3
2 7
3
输出样例
3
2
5
2147483647
pair
pair是一种模板类型,其中包含两个数据值,两个数据的类型可以不同。
例如:
pair<int,int>t; //其中t包含了两个整数类型。
pair<int,string>a;//表示a中第一个元素是int型,第二个元素是string类型。
#### 初始化:
pair<string,string>a(“James”,”Joy”);//直接初始化
如果有三个属性的话,其实也是可以用pair的,极端的写法 pair<int,pair<int,int> >写法极端。
(后边的两个>之间要有空格,否则就会是>>位移运算符 )
用法
- 对于pair类,由于它只有两个元素,分别名为first和second,因此直接使用普通的点操作符即可访问其成员。
pair<string,string>a(“Lily”,”Poly”);
cout<<a.first<<" "<<a.second; //a.first返回Lily,a.second返回Poly
- 生成新的pair对象,可以使用make_pair对已存在的两个数据构造一个新的pair类型。
pair<int,string>newone=make_pair(1,”abcd”);
- pair比较数据大小优先按照first比较,其次按照second比较,都默认升序。