C++ 标准模板库 (STL, Standard Template Library):包含一些常用数据结构与算法的模板的 C++ 软件库。其包含四个组件——算法 (Algorithms)、容器 (Containers)、仿函数 (Functors)、迭代器 (Iterators).
示例:
sort(a.begin(), a.end())
priority_queue pque
greater()
vector::iterator it = a.begin()
STL 作为一个封装良好,性能合格的 C++ 标准库,在算法竞赛中运用极其常见。灵活且正确使用 STL 可以节省非常多解题时间,这一点不仅是由于可以直接调用,还是因为它封装良好,可以让代码的可读性变高,解题思路更清晰,调试过程 往往 更顺利。
不过 STL 毕竟使用了很多复杂的结构来实现丰富的功能,它的效率往往是比不上自己手搓针对特定题目的数据结构与算法的。因此,STL 的使用相当于使用更长的运行时间换取更高的编程效率。因此,在实际比赛中要权衡 STL 的利弊,不过这一点就得靠经验了。
接下来,我会分享在算法竞赛中常用的 STL 容器和算法,对于函数和迭代器,就不着重展开讲了。
打勾的是本次将会详细讲解的,加粗的是算法竞赛中有必要学习的。
顺序容器
关联容器
无序关联容器
容器适配器
字符串
对与元组
#include
连续的顺序的储存结构(和数组一样的类别),但是有长度可变的特性。
vector<类型> arr(长度, [初值])
时间复杂度:O(n)
常用的一维和二维数组构造示例,高维也是一样的(就是会有点长).
vector arr; // 构造int数组
vector arr(100); // 构造初始长100的int数组
vector arr(100, 1); // 构造初始长100的int数组,初值为1
vector> mat(5,vector(6));//构造五行六列的二维数组
vector> mat(100, vector ()); // 构造初始100行,不指定列数的二维数组
vector> mat(100, vector (666, -1)) // 构造初始100行,初始666列的二维数组,初值为-1
构造二维数组的奇葩写法,千万别用:
vector arr[100]; // 正确,构造初始100行,不指定列数的二维数组,可用于链式前向星存图
vector arr[100](100, 1); // 语法错误!
vector arr(100, 1)[100]; // 语法错误!
vector arr[100] {
{100, 1}, 这里省略98个 ,{100, 1}}; // 正确但奇葩,使用列表初始化
.push_back(元素)
:在 vector 尾接一个元素,数组长度 +1..pop_back()
:删除 vector 尾部的一个元素,数组长度 −1时间复杂度:均摊 O(1)
// init: arr = []
arr.push_back(1);
// after: arr = [1]
arr.push_back(2);
// after: arr = [1, 2]
arr.pop_back();
// after: arr = [1]
arr.pop_back();
// after: arr = []
和一般数组一样的作用
时间复杂度:O(1)
.size()
获取当前 vector 的长度
时间复杂度:O(1)
for (int i = 0; i < arr.size(); i++)
cout << a[i] << endl;
.clear()
清空 vector
时间复杂度:O(n)
.empty()
如果是空返回 true
反之返回 false
.
时间复杂度:O(1)
resize改长时默认多的位置补0,改短时多的尾部位置被删去
.resize(新长度, [默认值])
eg:arr.resize(5,3) //新长度为5,多的尾部位置初值为3
修改 vector 的长度
时间复杂度:O(n)
一般情况 vector
可以替换掉普通数组,除非该题卡常。
有些情况普通数组没法解决:n×m 的矩阵,1≤n,m≤106 且 n×m≤106
int mat[1000010][1000010]
,浪费内存,会导致 MLE。vector> mat(n + 10, vector (m + 10))
,完美解决该问题。另外,vector
的数据储存在堆空间中,不会爆栈。
如果长度已经确定,那么应当直接在构造函数指定长度,而不是一个一个 .push_back()
. 因为 vector
额外内存耗尽后的重分配是有时间开销的,直接指定长度就不会出现重分配了。
在构造时指定数组长度有利于避免多次重分配,节省时间。
// 优化前: 522ms
vector a;
for (int i = 0; i < 1e8; i++)
a.push_back(i);
// 优化后: 259ms
vector a(1e8);
for (int i = 0; i < a.size(); i++)
a[i] = i;
size_t类型范围与编译器位数有关
vector 获取长度的方法 .size()
返回值类型为 size_t
,通常 OJ 平台使用的是 32 位编译器(有些平台例如 cf 可选 64 位),那么该类型范围为 [0,232).
vector a(65536);
long long a = a.size() * a.size(); // 直接溢出变成0了
v.push_back(1) | 存入1 |
v.size() | 动态数组v中的元素个数 |
vector遍历 | for(int i=0;i |
auto it=find(v.begin(),v.end(),x); | 查找第一次出现元素x的迭代器 |
auto it=find(v.begin(),v.end(),x)-v.begin(); | 查找第一次出现元素x的下标,此时it不是指针而是下标,是个int |
v.earse(it); | 删除指针迭代器it的空间 常用于先find找到元素再删除 |
#include
通过二次封装双端队列 (deque) 容器,实现先进后出的栈数据结构。
作用 | 用法 | 示例 |
---|---|---|
构造 | stack<类型> stk |
stack |
进栈 | .push(元素) |
stk.push(1); |
出栈 | .pop() |
stk.pop(); |
取栈顶 | .top() |
int a = stk.top(); |
查看大小 / 清空 / 判空 | .size()/.empty()/无清空 | 略 |
如果不卡常的话,就可以直接用它而不需要手写栈了。
另外,vector 也可以当栈用,vector 的 .back()
取尾部元素,就相当于取栈顶,.push_back()
相当于进栈,.pop_back()
相当于出栈。