STL 学习笔记
part 0:前言(必读)
本文章是在学习 STL 算法和容器后写的,如果有缺少的容器或函数,请评论区指正。
本文为 c++ 环境,编译选项为 -std=c++14 -O2。
火车头:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
return 0;
}
配套题单为:877299
本文目前共约13k字。
part 1:容器
STL 库拥有很多好用的容器,通用的定义方法是:
容器类型 < 数据类型 > 名称;
其中数据类型可以是 int、long long 之类的普通类型,也可以是其他的 STL,比如:
vector < pair < pair < int,int >,int > > a;
通用的迭代器(必读)
容器类型 < 数据类型 >::iterator it;//定义
//对于普通类型
for(it = a.begin();it != a.end();it++){
cout << *it << ' ';
}
//也可以
for(auto x : a){
cout << x << ' ';
}
//对于map
for(it = mp.begin();it != mp.end();it++){
cout << it->first << ' ' << it->second >> '\n';
}
//也可以
for(auto x : mp){
cout << it.first << ' ' << it.second >> '\n';
}
序列式容器
vector
简介
vector 是动态数组,支持随机访问,尾部插入效率高。
基本定义
vector < int > a;//定义一个空的vector
vector < int > a(arr);//用数组初始化
vector < int > a(n);//长度为n,初始值为0
vector < int > a(n,x);//长度为n,初始值为x
常用操作
- 下标访问:
a[i] - 尾部插入:
a.push_back(x) - 尾部删除:
a.pop_back() - 获取大小:
a.size() - 容量查询:
a.capacity() - 清空容器:
a.clear() - 判断为空:
a.empty() - 更改大小:
a.resize(x)迭代器
vector < int >::iterator it;//定义一个给vector的迭代器相关函数:
a.begin():返回头部迭代器a.end():返回尾部迭代器插入与删除
a.insert(it,x):在迭代器it 位置插入值x 。a.erase(it):删除迭代器it 位置的元素a.erase(l,r):删除[l,r) 区间元素题目
暂无,请贡献一些。
deque
简介
deque基本和vector一样,支持双端操作。常用操作
deque拥有vector的全部函数,还有两个函数:- 头部插入:
a.push_front(x) - 头部删除:
a.pop_front()题目
P1886 滑动窗口 /【模板】单调队列
不会的话可以看下题解或我的代码。
::::info[我的代码]
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int a[1000005];
deque < int > q;
int n,m;
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
cin >> n >> m;
for(int i = 1;i <= n;i++) cin >> a[i];
for(int i = 1;i <= n;i++){
while(!q.empty() && a[q.back()] > a[i]) q.pop_back();
q.push_back(i);
if(i >= m){
while(!q.empty() && q.front() <= i - m) q.pop_front();
cout << a[q.front()] << ' ';
}
}
cout << '\n';
while(!q.empty()) q.pop_front();
for(int i = 1;i <= n;i++){
while(!q.empty() && a[q.back()] < a[i]) q.pop_back();
q.push_back(i);
if(i >= m){
while(!q.empty() && q.front() <= i - m) q.pop_front();
cout << a[q.front()] << ' ';
}
}
return 0;
}
::::
list
简介
list 是一个双向链表容器,支持任意位置的高效插入和删除操作,但不支持随机访问。
基本定义
list < int > a;//定义一个空的list
list < int > a(arr);//用数组初始化
list < int > a(n);// 长度为n,初始值为0
list < int > a(n,x);//长度为n,初始值为x
list < int > a(begin,end);// 用迭代器范围初始化
常用操作
- 头部插入:
a.push_front(x) - 尾部插入:
a.push_back(x) - 头部删除:
a.pop_front() - 尾部删除:
a.pop_back() - 获取大小:
a.size() - 清空容器:
a.clear() - 判断为空:
a.empty() - 反转链表:
a.reverse() - 排序链表:
a.sort()(注意不是全局的sort) - 移除指定值:
a.remove(x)迭代器
list < int >::iterator it;// 定义一个正向迭代器 list < int >::reverse_iterator rit;// 定义一个反向迭代器相关函数:
a.begin():返回头部迭代器a.end():返回尾部迭代器a.rbegin():返回反向头部迭代器a.rend():返回反向尾部迭代器 所以还可以这样:auto it = a.begin(); auto rit = a.rbegin();插入与删除
a.insert(it,x):在迭代器it 位置插入值x a.erase(it):删除迭代器it 位置的元素a.erase(l,r):删除[l,r) 区间元素a.splice(pos,b):将链表b 的所有元素插入到pos 位置题目
B3656 【模板】双端队列 1
关联式容器
set
简介
set是一个有序的关联容器,其内部元素会按照一定的规则自动排序(默认升序),且不允许存在重复元素。它基于红黑树实现,支持高效的插入、删除和查找操作,时间复杂度通常为O(\log_2 n) 。基本定义
set < int > s;//定义一个空的set,默认按升序排序 set < int,greater < int > > s;//定义一个按降序排序的set int arr[] = {1,2,3}; set < int > s(arr);//用数组初始化 set < int > s(begin,end);//用迭代器范围初始化 set < int > s2(s);//拷贝初始化,用s初始化s2常用操作
s.insert(x):插入值x ,若已存在则插入失败s.erase(x):删除值为x 的元素,返回删除的元素个数s.size():返回容器中元素的个数s.clear():清空容器中所有元素s.empty():若容器为空返回true,否则返回falses.find(x):查找值为x 的元素,返回其迭代器;若不存在,返回s.end()s.count(x):返回值为x 的元素个数(由于set中无重复元素,结果只能是0 或1 )迭代器
set < int >::iterator it;//定义一个正向迭代器 set < int >::reverse_iterator rit; // 定义一个反向迭代器相关函数:
s.begin():返回指向第一个元素的正向迭代器s.end():返回指向最后一个元素之后位置的正向迭代器s.rbegin():返回指向最后一个元素的反向迭代器s.rend():返回指向第一个元素之前位置的反向迭代器 所以还可以这样:auto it = s.begin(); auto rit = s.rbegin();插入与删除
s.insert(it,x):在迭代器it 指示的位置附近插入x (实际位置由排序规则决定),返回新插入元素的迭代器s.insert(l,r):插入迭代器范围[l,r) 内的元素s.erase(it):删除迭代器it 指向的元素,返回下一个元素的迭代器s.erase(l,r):删除迭代器范围[l,r) 内的元素,返回r 的迭代器题目
P11227 [CSP-J 2024] 扑克牌
map
简介
map是一种有序的关联容器,其内部存储的是键值对(key-value pair),会根据键(key )的大小按照特定规则自动排序(默认升序),且键具有唯一性(不允许重复)。它基于红黑树实现,支持高效的插入、删除和查找操作,核心操作的时间复杂度通常为O(\log_2 n) 。基本定义
map < int,string > m1;//定义空map,key为int型,value为string型,默认按key升序排序 map <string,int,greater < string > > m2;//定义按key降序排序的map(key为string型,value为int型) int a[] = {0,1,2,3}; string b[] = {" ","a","b","c"}; map <int,string > m3;//手动通过数组初始化map(键值对一一对应) for(int i = 1;i <= 3;i++) m3[b[i]] = a[i]; map < int,string > m4(m3.begin(),m3.end());//用迭代器范围初始化(拷贝m3的所有元素) map < int,string > m5(m3);//拷贝初始化,用m3的元素初始化m5常用操作
m.insert({key,val}):插入键值对(key,val) ,若key 已存在则插入失败m.erase(key):删除键为key 的键值对,返回删除的元素个数(0 或1 ,因key 唯一)m.size():返回容器中键值对的个数m.clear():清空容器中所有键值对m.empty():若容器为空返回true,否则返回falsem.find(key):查找键为key 的键值对,返回其迭代器;若不存在,返回m.end()m.count(key):返回键为key 的元素个数(因key 唯一,结果只能是0 或1 )m[key]:访问键为key 对应的value ;若key 不存在,会自动创建该键值对(value 为默认值)迭代器
map < int,string >::iterator it;//定义正向迭代器 map < int,string >::reverse_iterator rit;//定义反向迭代器相关函数:
m.begin():返回指向第一个键值对的正向迭代器m.end():返回指向最后一个键值对之后位置的正向迭代器m.rbegin():返回指向最后一个键值对的反向迭代器m.rend():返回指向第一个键值对之前位置的反向迭代器 所以还可以这样:auto it = m.begin();//定义正向迭代器 auto rit = m.rbegin();//定义反向迭代器插入与删除
m.insert(pos,{key,val}):在迭代器pos 指示的位置附近插入键值对(key,val) (实际位置由排序规则决定),返回新插入元素的迭代器m.insert(l,r):插入迭代器范围[l,r) 内的所有键值对m.erase(pos):删除迭代器pos 指向的键值对,返回指向下一个元素的迭代器(注意:不能删除m.end()指向的位置)m.erase(l,r):删除迭代器范围[l, r) 内的所有键值对,返回迭代器r 题目
P1102 A-B 数对
无序关联式容器
multiset
简介
multiset和set类似,只是允许重复元素,在此不过多介绍。unordered_map
简介
unordered_map和map类似,只是没有自动排序功能,可以当作哈希表使用,在此不过多介绍。unordered_set
简介
不常用,请自行前往OI-wiki了解
multimap
简介
不常用,请自行前往OI-wiki了解
unordered_multiset
简介
不常用,请自行前往OI-wiki了解
unordered_multiset
简介
不常用,请自行前往OI-wiki了解
容器适配器
stack
简介
stack是一种 后进先出(LIFO,Last In First Out) 的容器适配器,只能在容器的一端(栈顶)进行插入和删除操作。它默认基于deque实现,也可以指定底层容器(如vector或list)。基本定义
stack < int > s1; //定义空栈,元素类型为int,默认底层容器是deque //用 vector作为底层容器 stack < int,vector < int > > s2; //用list作为底层容器 stack < int,list < int > > s3;常用操作
push(val):将元素val 压入栈顶pop():弹出栈顶元素(不返回值,若需要取值请先top())top():返回栈顶元素的引用size():返回栈中元素个数empty():判断栈是否为空,空返回true,否则返回false示例代码
#include <bits/stdc++.h> using namespace std;
int main(){ stack < int > s; s.push(10); s.push(20); s.push(30); cout << "栈顶元素: " << s.top() << '\n'; cout << "栈大小: " << s.size() << '\n'; s.pop(); cout << "栈顶元素: " << s.top() << '\n'; while(!s.empty()){//遍历 cout << s.top() << ' '; s.pop(); } return 0; }
#### **底层容器要求**
`stack` 是容器适配器,底层容器需要支持以下操作:
`push_back()`
`pop_back()`
`back()`
默认使用 `deque`,不过可以指定底层容器为 `vector` 或 `list`:
```cpp
stack < int,vector < int > > s;//底层容器为vector
stack < int,list < int > > s;//底层容器为list
题目
B3614 【模板】栈
queue
简介
queue 是一种 先进先出(FIFO, First In First Out) 的容器适配器,只能在队尾插入元素,在队头删除元素。它默认基于 deque 实现,也可以指定底层容器(如 list)。
基本定义
queue < int > q1;//定义空队列,元素类型为int,默认底层容器是deque
//用list作为底层容器
queue < int,list < int > > q2;
常用操作
push(val):将元素 val 插入队尾pop():删除队头元素(不返回值,若需要取值请先front())front():返回队头元素back():返回队尾元素size():返回队列中元素个数empty():判断队列是否为空,空返回true,否则返回false示例代码#include <bits/stdc++.h> using namespace std;
int main() { queue < int > q; q.push(10); q.push(20); q.push(30); cout << "队头元素: " << q.front() << '\n'; cout << "队尾元素: " << q.back() << '\n'; cout << "队列大小: " << q.size() << '\n'; q.pop(); cout << "队头元素: " << q.front() << '\n'; while(!q.empty()){ cout << q.front() << ' '; q.pop(); } return 0; }
#### **底层容器要求**
`queue` 是容器适配器,底层容器需要支持以下操作:
`push_back()`
`pop_front()`
`front()`
`back()`
默认使用 `deque`,也可以指定 `list`:
```cpp
queue < int,list < int > > q;//底层容器为list
题目
B3616 【模板】队列
priority_queue
简介
priority_queue 是优先队列,每次出队的元素都是当前队列中优先级最高的元素(默认最大值优先)。它的底层默认使用堆实现,并且是一种容器适配器,可以基于 vector 或 deque 作为底层容器。
基本定义
//默认:最大堆(降序)
priority_queue < int > pq1;
//自定义比较器,最小堆(升序)
priority_queue < int,vector < int >, greater < int > > pq2;
//使用list不能作为底层容器(需要随机访问迭代器)
常用操作
-push(val):将元素
pop():移除队头(优先级最高)的元素(不返回值)top():返回队头元素size():返回队列中元素个数empty():判断队列是否为空,空返回true,否则返回false示例代码
#include <bits/stdc++.h> using namespace std;
int main(){ priority_queue < int > pq;//默认大顶堆 pq.push(30); pq.push(10); pq.push(50); pq.push(20); cout << "队头元素: " << pq.top() << '\n'; pq.pop(); cout << "队头元素: " << pq.top() << '\n'; cout << "队列大小: " << pq.size() << '\n'; while(!pq.empty()){ cout << pq.top() << ' '; pq.pop(); } return 0; }
#### 自定义比较器
如果存储的是自定义类型,可以自己写比较器:
```cpp
struct Person{
string name;
int age;
};
bool cmp(Person a,Person b){
return a.age < b.age;
}
priority_queue < Person,vector < Person >,cmp > pq;
底层容器要求
priority_queue 底层容器需要支持随机访问迭代器,并且支持以下操作:
push_back()
pop_back()
随机访问(如 vector、deque)
默认使用 vector 作为底层容器。
part 2:算法
find:顺序查找。find(v.begin(),v.end(),x),其中x 为需要查找的值。reverse:翻转数组、字符串。reverse(v.begin(),v.end())或reverse(a + x,a + y)。unique:去除容器中相邻的重复元素。返回值为指向去重后容器结尾的迭代器或指针,原容器大小不变。与sort结合使用可以实现完整容器去重。sort:排序。sort(v.begin(),v.end(),cmp)或sort(a + x,a + y,cmp),其中y 是排序的数组最后一个元素的后一位,cmp 为自定义的比较函数。stable_sort:稳定排序,用法同 sort()。nth_element:按指定范围进行分类,即找出序列中第n 大的元素,使其左边均为小于它的数,右边均为大于它的数。nth_element(v.begin(),v.begin() + n,v.end(),cmp)或nth_element(a + x,a + y + n,a + y,cmp)。binary_search:二分查找。binary_search(v.begin(),v.end(),x),其中x 为需要查找的值。lower_bound:在一个有序序列中进行二分查找,返回指向第一个 大于等于x 的元素的位置的迭代器。如果不存在这样的元素,则返回尾迭代器。lower_bound(v.begin(),v.end(),x)。upper_bound:在一个有序序列中进行二分查找,返回指向第一个 大于x 的元素的位置的迭代器。如果不存在这样的元素,则返回尾迭代器。upper_bound(v.begin(),v.end(),x)。 ::::warning[lower_bound和upper_bound的时间复杂度] 在一般的数组里,这两个函数的时间复杂度均为O(\log_2 n) ,但在set等关联式容器中,直接调用lower_bound(s.begin(),s.end(),x)的时间复杂度是O(n) 的。
set 等关联式容器中已经封装了 lower_bound 等函数(像 s.lower_bound(x) 这样),这样调用的时间复杂度是
next_permutation:将当前排列更改为 全排列中的下一个排列。如果当前排列已经是 全排列中的最后一个排列(元素完全从大到小排列),函数返回false并将排列更改为 全排列中的第一个排列(元素完全从小到大排列);否则,函数返回truenext_permutation(v.begin(),v.end())或next_permutation(v + x,v + y)。prev_permutation:将当前排列更改为 全排列中的上一个排列。用法同next_permutation。part 3:string
简介
string则是一个简单的类,使用简单,在OI中被广泛使用。并且相比于其他 STL 容器,string的常数较低。
string 能动态分配空间,这使得我们可以直接使用 cin 来输入,但其速度较慢。
string 的加法运算符可以直接拼接两个字符串或一个字符串和一个字符。string 重载了比较运算符,按字典序比较,所以我们可以直接调用 sort 对字符串进行排序。
声明
string 的定义方法十分简单,为 string s。
转 char 数组
string 转 char 数组的方式很简单,只要用一个函数 s.c_str() 就可以解决。
printf("%s",s);//编译错误
printf("%s",s.c_str());//能够正确输出
长度
很多函数都可以返回 string 的长度,如下:
printf("%zu",s.size());//我用这个
printf("%zu",s.length());//有人用这个
printf("%zu",strlen(s.c_str()));//绝对的整活,还慢
::::info[复杂度]
第三种写法是 size_t 或 unsigned long 类型。因此,这些返回值不支持直接与负数比较或运算,需要时要进行强制类型转换。
::::
查找
find(s,pos) 函数可以用来查找字符串中 string::npos(一般是 size_t/unsigned long)。
示例:
string s = "IAKIOI",s1 = "OI",s2 = "I";
int pos = 4;
s.find("I");//返回0
s.find("a");//返回string::npos
s.find(s1)//返回4
s.find(s2,pos);//返回5
截取
substr(pos,len) 函数的参数返回从
示例:
string s = "IAKIOI", t = "OI";
s.substr(3,3);//返回IOI
t.substr(1,3);//返回I
插入/删除
insert(pos,cnt,s1) 和 insert(pos,s2) 是插入函数。它们分别表示在
erase(pos,cnt) 函数将字符串
示例:
string s = "IAKIOI",t = " IOI";
char ch = '!';
s.erase(3);//s = "IAK"
s.insert(3,t);//s = "IAKIOI"
s.insert(7,3,ch);//s = "IAKIOI!!!"
替换
replace(pos,cnt,s1) 和 replace(l,r,s2) 是替换函数。它们分别表示从
示例:
string s = "IOI";
s.replace(1,2,"");//s = "I"
s.replace(s.begin(),s.begin() + 1,"NOI");//s = "NOI"
例题
P5734 【深基6.例6】文字处理软件