STL用法
序列式容器
-
向量 (
vector
) 后端可高效增加元素的顺序表。 -
数组 (
array
) C++11 ,定长的顺序表,C 风格数组的简单包装。 -
双端队列 (
deque
) 双端都可高效增加元素的顺序表。 -
列表 (
list
) 可以沿双向遍历的链表。 -
单向列表 (
forward_list
) 只能沿一个方向遍历的链表。
关联式容器
-
集合 (
set
) 用以有序地存储 互异 元素的容器。其实现是由节点组成的红黑树,每个节点都包含着一个元素,节点之间以某种比较元素大小的谓词进行排列。 -
多重集合 (
multiset
) 用以有序地存储元素的容器。允许存在相等的元素。 -
映射 (map) 由 {键,值} 对组成的集合,以某种比较键大小关系的谓词进行排列。
-
多重映射 (
multimap
) 由 {键,值} 对组成的多重集合,亦即允许键有相等情况的映射。
无序(关联式)容器
-
无序(多重)集合 (
unordered_set
/unordered_multiset
) C++11 ,与set
/multiset
的区别在于元素无序,只关心「元素是否存在」,使用哈希实现。 -
无序(多重)映射 (
unordered_map
/unordered_multimap
) C++11 ,与map
/multimap
的区别在于键 (key) 无序,只关心 "键与值的对应关系",使用哈希实现。
共有函数
=:有赋值运算符以及复制构造函数。
begin()
:返回指向开头元素的迭代器。
end()
:返回指向末尾的 下一个元素的迭代器 。end() 不指向某个元素,但它是末尾元素的后继。
size()
:返回容器内的元素个数。
max_size()
:返回容器 理论上 能存储的最大元素个数。依容器类型和所存储变量的类型而变。
empty()
:返回容器是否为空。
swap()
:交换两个容器。
clear()
:清空容器。
迭代器:
类似于指针,有两种字符可以使用 例如 *it
和自增 ++
-
begin()
/cbegin()
返回指向首元素的迭代器,其中
*begin = front
。 -
end()
/cend()
返回指向数组尾端占位符的迭代器,注意是没有元素的。
-
rbegin()
/crbegin()
返回指向逆向数组的首元素的逆向迭代器,可以理解为正向容器的末元素。
-
rend()
/crend()
返回指向逆向数组末元素后一位置的迭代器,对应容器首的前一个位置,没有元素。
很多 STL 函数 都使用迭代器作为参数。
可以使用 std::advance(it, n)
将迭代器 it
向后移动 n
步;若 n
为负数,则对应向前移动。迭代器必须满足双向迭代器,否则行为未定义。
在 C++11 以后可以使用 std::next(it)
获得向前迭代器 it
的后继(此时迭代器 it
不变),
std::next(it, n)
获得向前迭代器 it
的第 n
个后继。
在 C++11 以后可以使用 std::prev(it)
获得双向迭代器 it
的前驱(此时迭代器 it
不变),std::prev(it, n)
获得双向迭代器 it
的第 n
个前驱。
vector
很泛用,优势在于动态开空间.
经典用法例如;邻接表存图等
vector<int>e[MAXN]
void push(int x,int y){
e[x].push_back(y);
e[y].push_back(x);
}
for (int i = 0 ; i < data.size(); i++)
cout << data[i] << endl; //vector
for(auto y:v[x])//c++11以后
for(auto it=x.begin();it!=x.end();++it)
此两种遍历方式适合几乎所有STL
注意
insert()
支持在某个迭代器位置插入元素、可以插入多个。 复杂度与 pos
距离末尾长度成线性而非常数的
erase()
删除某个迭代器或者区间的元素,返回最后被删除的迭代器。复杂度与 insert()
一致。
priority_queue
优先队列,默认是大跟堆.
如果是pair类型默认的比较函数只比较第一个元素.
push(val)
: 将元素 val
插入优先队列。
pop()
: 移除队列顶部的元素。
top()
: 返回队列顶部的元素,即优先级最高的元素。
size()
: 返回优先队列中的元素数量。
empty()
: 检查优先队列是否为空。
set/multiset
set 是关联容器,含有键值类型对象的已排序集,搜索、移除和插入拥有对数复杂度。set 内部通常采用 红黑树 实现。平衡二叉树 的特性使得 set
非常适合处理需要同时兼顾查找、插入与删除的情况。
和数学中的集合相似,set
中不会出现值相同的元素。如果需要有相同元素的集合,需要使用multiset
。multiset
的使用方法与 set
的使用方法基本相同。
insert(x)
当容器中没有等价元素的时候,将元素 x 插入到 set
中。
insert(first,last)
插入迭代器在 $[first,last)$ 范围内的所有元素。
erase(x)
删除值为 x 的所有元素,返回删除元素的个数。
erase(pos)
删除迭代器为 pos 的元素,要求迭代器必须合法。
erase(first,last)
删除迭代器在 $[first,last)$ 范围内的所有元素。
clear()
清空 set
。
count(x)
返回 set
内键为 x 的元素数量。
find(x)
在 set
内存在键为 x 的元素时会返回该元素的迭代器,否则返回 end()
。
lower_bound(x)
返回指向首个不小于给定键的元素的迭代器。如果不存在这样的元素,返回end()。
upper_bound(x)
返回指向首个大于给定键的元素的迭代器。如果不存在这样的元素,返回end()
。
empty()
返回容器是否为空。
size()
返回容器内元素个数。
map
map 是有序键值对容器,它的元素的键是唯一的。搜索、移除和插入操作拥有对数复杂度。map 通常实现为 红黑树 。
map<Key, T> yourMap;
Key
是键的类型,T
是值的类型,下面是使用 map
的实例:
map<string, int> M;
插入与删除操作
可以直接通过下标访问来进行查询或插入操作。例如 mp["Alan"]=100
。
通过向 map
中插入一个类型为 pair<Key, T>
的值可以达到插入元素的目的,例如mp.insert(pair<string,int>("Alan",100));
;
erase(key)
函数会删除键为 key
的 所有 元素。返回值为删除元素的数量。
erase(pos)
: 删除迭代器为 pos
的元素,要求迭代器必须合法。
erase(first,last)
: 删除迭代器在 $[first,last)$ 范围内的所有元素。
clear()
函数会清空整个容器。
map
中不会存在键相同的元素,multimap
中允许多个元素拥有同一键。multimap
的使用方法与 ```map`` 的使用方法不太一样.
使用样例
map
用于存储复杂状态
在搜索中,我们有时需要存储一些较为复杂的状态(如坐标,无法离散化的数值,字符串等)以及与之
有关的答案(如到达此状态的最小步数)。map
可以用来实现此功能。其中的键是状态,而值是与之相
关的答案。
bitset
size()
:返回 bitset
的位数。
count()
:返回 bitset
中值为 1 的位的数量。
any()
:返回 bitset
中是否有。
set()
:将所有位都设置为 1 。
set(pos)
:将第 pos
位设置为 1 。
reset()
:将所有位都设置为 0 。
reset(pos)
:将第 pos
位设置为 0 。
flip()
:对所有位取反。
flip(pos)
:对第 pos
位取反。
位运算操作符:
&
:按位与。
|
:按位或。
^
:按位异或。
<<
:左移。
>>
:右移。
比较常见的用法就是优化各种暴力,使其复杂度 ,从而通过某些题目.
例如
-
给你n个集合( $n\le10000$ )每个集合最多 $10000$ 个数,每个数最大为 $10000$ ,最多$2^5$次查询,询问是否存在$x,y$是否在同一个集合中.
-
有 $n$ 个数, $x_i$ 可以取值 $l_i-r_i$ ,问 $sum({x_i}^2)$ 可能的值有多少。
考虑设 $f[i][j]$ ,前 $i$ 个数,和为 $j$ 的数字是否存在,转移方程如下:
$$
f[i][j]=f[i-1][j-x*x]
$$
直接暴力复杂度是 $10^10$ , 但是可以通过bitset优化.
- $n$ 个数,背包容量 $m$ ,问装满背包时候,背包里面异或值最大可能是多少( $n,m,v_i\le1024$ )?
f[i][j][k] = f[i - 1][j][k] 不选i
f[i][j][k] |= f[i - 1][j ^ w][k - v] 选i
需要注意的是bitset
大概能通过 $10^5$ 的 $n^2$ 再往上会因为空间问题挂掉,不过可以使用根号分治之类的手段降低空间复杂度.