标准库
- STL 内的所有基于比较的容器,都需要定义一个满足严格弱序的比较符号
<
,要求如下:- 满足传递性:$a<b,b<c\rightarrow a<c$
- 不能同时满足 $a<b$ 且 $b<a$
- 如果
!(a<b)&&!(b<a)
那么a=b
- 除开
array
之外的所有STL
容器,定义时都会初始化,即初始化为空或者(全为0
,对于bitset
)。 - 基于比较的容器,需要定义友元类型的大小比较符号,因为实现的时候使用了右值做比较。
priority_queue
默认是大根堆,跑得很快,1 秒 $3\times 10^6$
如果需要实现可以删除的优先队列,必须保证每次删除时对应元素存在。
构造方式 priority_queue<type,container,cmp>
,container
是实现 priority_queue
的容器,一般用 vector
,cmp
是比较算子(因此它是一个类名)。
通过 greater<typename>
可以用重载的 >
构造出一个大于算子。
map
本质上是 set<pair<T1,T2>>
,见 set
。
set
insert
可以插入一段区间,支持数组,同类型的 set vector
容器。
开 O2,$1$ 秒 $1.5\times 10^6$ 次操作。
如果接下来的一段插入有序,开 O2,$1$ 秒 $4.5\times 10^6$ 次,所以尽量排序后再插入。
erase
开 O2,$1$ 秒 $1.5\times 10^6$ 次操作。
如果接下来的一段插入有序,开 O2,$1$ 秒 $4.5\times 10^6$ 次。
迭代器操作
开 O2,$1$ 秒 $10^7$。
遍历
1 | for(auto v:st) |
以上两种均为 $O(n\log n)$
开 O2,$1$ 秒 $10^7$ 次操作。
unordered_set
基础操作相比 set
快 $2$ 倍。
有序操作和 set
效率差不多。
unoedered_map
本质上是 unordered_set<pair<T1,T2>>
。
vector
比较理想的一个容器。
push_back()
一个 vector
做 push_back
需要 log
次 new
,而 new
操作一般只能接受 $2\times 10^7$ 次,所以尽量避免用太多 vector
,如果能事先确定容器的 capacity
,最好用 resize
iterator insert(iterator,val)
将 val
插入 iterator
位置,原 iterator
位置往后移动。
复杂度 $O(n)$,但是跑得飞快。
iterator erase(iterator)
删除 iterator
位置的值,返回原容器(删除前)下一个位置的 iterator
。
iterator
这东西的迭代器本质上是个指针,但是不能和指针做强转,所以 iterator
在被 erase
后会指向错误的数据,不同于 set
这一类基于 RBT
的容器。
内存过程
调用析构函数之前,vector
的内存不可能被释放,push_back
或者 resize
如果导致了内存改变,会开辟一块新的内存并将原有数据全部拷贝过去,保证内存地址的连续,同时原有迭代器全部失效。
vector 所有的 $O(n)$ 操作都很快,如果题目性质决定了很可能 $O(n^2)$ 卡不满,那么 vector
可以得到很高的分数。
拓展库
一个 C++
拓展库,STL
升级版,C++11
特性。
gp_hash_table
Introduction
如名称,哈希表,比 unordered_map
快 3~4
倍,用法完全一样,你值得拥有。
效率 1S 能做 4e7 次基本操作
在 ext/pb_ds/assoc_container.hpp
中。
如果对非标准结构,例如类和 pair
,容器等,需要自己写哈希方法,哈希方式为一个类,重载了 ()
运算,该重载必须被声明为常函数,且参数必须为常值引用。
1 |
|
Tests:
这里只放关键部分,其余实现见代码
测试环境为 Windows10,CPU 型号为 Inter I7-9750H,内存 16GB, 2667Mhz
命中率对速度影响:
1 | gen_data(1e6,4e7,8,7);//hit rate:87.5% |
插入和查询次数调整:
1 | gen_data(2e6,4e7,4,1); //插入 2e6 |
插入很多,查询很少。
1 | gen_data(2e7,4e6,50,1); |
其实插入操作比较慢是正常的,内存占用大了之后自然就慢了。
另外,手写哈希表探测法还有救,拉链法直接抬走(你写代码的时候考虑过 CPU cache 的感受吗?)。
插入较少且全部在查询前面 cc_hash_table
的效率优于 gp_hash_table
。
Tree
虽然有这个东西,但是还是应该学习如何写平衡树。
效率还可以,开 O2 和以前手写差不多,估计现在手写的会快一些,不过问题不大。