介绍一些 STL 的基本常识和使用。
迭代器
迭代器是 STL 容器中类似指针的东西。在很多时候善于利用迭代器可以让 STL 更加强大。
对于一个容器 s ,可以通过 s.begin() 和 s.end() 得到内存区间。
不同容器的迭代器在这种容器的命名空间中,如定义一个迭代器 set <int> :: iterator it = s.begin() 。
获得这个迭代器 it 对应的值,使用 *it ,如一个 set 中最小的元素为 *s.begin() ,最大的元素为 *--s.end() 。
双向容器支持 it++ ,it-- ,单向容器只支持 it++ 。
例如在 vector <int> 中,需要遍历所有的元素。
因为它支持随机访问元素,所以可以这样写:
1 | for (int i = 0; i < v.size(); i++) |
因为它支持单向迭代,所以可以这样写:
1 | for (vector <int> :: iterator i = v.begin(); i != v.end(); i++) |
注意迭代器不支持 < > 运算,所以使用 != 。
大多数容器也可以这样写:
1 | for (int i : v) |
STL 算法
大多数容器都可以用的。
sort(s.begin(), s.end())reverse(s.begin(), s.end())unique(s.begin(), s.end())erase(s.begin(), s.end())
lower_bound 与 upper_bound
用法 lower_bound(s.begin(), s.end(), v)
前者返回第一个值大于等于 v 的迭代器,后者返回第一个大于 v 的迭代器,通过这两个函数可以实现后继查询。
通过 --lower_bound(s.begin(), s.end(), v) 和 --upper_bound(s.begin(), s.end(), v) 可以实现前驱查询。
在有序的容器下复杂度为 $O(\log n)$ ,一般在排好序的 vector 中使用,因为 vector 的迭代器支持 + - 运算。
注意,set 中需要使用 s.lower_bound(v) 和 s.upper_bound(v) 才能保证复杂度为 $O(\log n)$ ,否则会退化到 $O(n)$ 。
离散化的实现
1 | vector <int> ws; |
list
不想写链表?使用 list !
头文件:
#include <list>。左边插入
s.push_front(),删除s.pop_front()。右边插入
s.push_back(),删除s.pop_back()。定位到这个元素的迭代器后
s.erase(it)。注意,删除后不能继续迭代,因为当前迭代器被删除了,就不能正确地找到下一个迭代器了。正确的写法是这样:1
2
3
4
5for (list<pair<int, int>>::iterator i = s.begin(); i != s.end();)
if ((*i).first == 3)
i = s.erase(i);
else
i++;因为
s.erase()会返回下一个删除的迭代器。当然也支持区间删除,这意味着你还是需要找到两个迭代器。
list 并不能替代链表的所有功能,但在简单的应用上很方便。
priority_queue
优先队列不是线性的。
本质是堆,操作有一个 $\log$ 。
配合结构体使用会很强大。
注意默认是大根堆,需要重载运算符 > 。
1 | struct Node { |
或者这样写:
1 | struct Node { |
还可以写 lambda 函数自定义 Compare
1 | typedef pair<int, int> PII; |
在其他的 STL 中,如 set 中,不能写 lambda 函数。
必须写在一个类里面,因此,可以这样写:
1 | struct cmp { |
string
几个常见的函数:
s.find(t)。s.substr(st, len)。s.insert(st, t)。s.erase(st, len)。
还支持 s += t s + t 。
但是注意复杂度和字符串长度线性相关,并且输入输出必须用 cin cout ,所以比较慢。
一般尽量用 char ,遇到字符串的题,做不来,可以用 string 的函数打暴力。
bitset
相当于 vector<bool> 但是强大得多;相当于用于状压的 int ,但是没有 $32$ 位的限制。
定义: bitset<N> f ,位数为 $N$ 。
一般用于优化暴力,比如
1 | for (int i = 0; i < n; i++) |
可以写作
1 | f[k] &= f[k - 1] |
复杂度可以从 $O(n)$ 优化到 $O(\frac n \omega)$ 。
支持所有位运算,但是不能和 int 计算,支持 cin cout 。
unordered_map 和 unordered_set
这是两个坑,千万不要用。遇到大值域问题,优先考虑离散化,实在不行另可多一个 $\log$ 用 map 或 set ,因为这两个坑最坏情况下单次操作为 $O(n)$ 。
deque
可以完全用 list 代替了。
stack 和 queue
推荐手写,如果操作中不能确定需要多大空间,就用 STL。