(Flu给没空调队(Air_conditioning = nullptr)的STL讲义)
我们当中,有人不会STL.
— Flu
函数类
排序,去重,查找
STL的区间一直都是 $[l,r)$ .1
2
3
4
5
6
7
8
9
10
11
12sort(dat+l,dat+r+1,cmp); //这是闭区间[l,r]的写法
int m=unique(dat+l,dat+r+1)-dat-l; //现在的m是数列还剩多少个元素
//这是去重的写法,和排序一样
//注意1 返回的是一个指针,表示还剩多少个元素,返回的是 r) 的指针,也就是末端元素
//注意2 要建立在排好序的基础上
int* pos=upper_bound(dat+l,dat+1+r,num); //在整个序列中查找第一个大于num的元素
//注意1 返回的是一个指针,表示该元素的位置
//注意2 要建立在排好序的基础上
//lower_bound同理,但是注意lower_bound是返回第一个 大于等于的元素
其他不常用的函数
1 | next_permutation(a+1,a+1+n);//自动求下一个排列 |
随机数
挖坑.
三角函数
挖坑.
杂碎函数
STL重载了很多变量类型的函数,比如1
2
3constexpr const long long &std::max<long long>(const long long &__a, const long long &__b)
constexpr const int &std::max<int>(const int &__a, const int &__b)
//也就是说不按上述类型的函数比较max会出锅,比如 max(0,(long long)1) 不能过编译
容器类
容器类也是左开右闭的区间,体现为 [vc.begin(),vc.end()) ,千万别对vc.end()取元素值(事实上取这个值没意义,一般这种小RE不算RE的)
如果你想用迭代器方式访问最后一个元素可以用 vc.rbegin() 逆迭代器.逆迭代器是 (rend(),rbegin()] 的顺序,掉了个方向.仍然使用 ++ 自增运算符进行遍历.
bitset
不会用,挖坑.
array
只是一个普通的数组,声明时长度已经定死了,没有任何特点,当成普通数组用就可以了.1
vector<array<int,114>>vc;//声明一个二维数组,vc[514][113]的意思是先找vc的第515个元素,得到的是一个array,然后在array里面找第114个元素.
由于array过于平淡,在这里不介绍任何成员函数.
唯一需要介绍的是array的比较函数:以 < 为例:从下标0开始遍历两个array,如果满足 a<b 或 a>b 就return 1或0,否则继续遍历.(和pair一样,先比第一个,再比第二个…)
array作为普通数组的占位符,可以简单理解为pair的上位替代.1
2
3
4
5
6map<int,int[10]>mp;
map<int,array<int,10>>mmp;
int main(){
mp[0][0]=114;
return 0;
}
Flu也很惊讶,上面的代码居然能过编译…
vector
1 | // 定义 |
set
注意set是去重的.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35// 定义
set<int>st;
//默认是小元素在前(也就是less<int>),你可以用greater<int>让大元素在前,例如set<int,greater<int>> sst;
// 添加元素
st.insert(114514);
// 遍历
for(auto i:st){
//一般情况下不建议在这里修改set的值,毕竟set很复杂,搞炸了怎么办
i=0;
}
// 查找
st.find(114);//返回找到的指针
//如果没找到就返回 st.end()
/* Postscript:
STL容器类的指针都是 set<int>::iterator 的模样出现的,因为很长所以建议用 auto i=st.find(xxx)
我们比一个更复杂的例子:
set<pair<int,int>>st;
set<pair<int,int>>::iterator it=st.find({114,514});
cout<<(*it).first;
容器指针就是这么使用的,需要注意的是这个指针指向的元素是const类型的,自己不要私自直接改容器元素
*/
st.upper_bound(114);//set同时也维护了两个bound函数,用法不再赘述
// 删除(传参传指针或元素值都可以)
st.erase(st.begin());
st.erase(114514);
// 清空
st.clear();
需要注意的是,删除元素的时候树的形态会变,原来的指针部分会变成野指针.
举例1: st.erase(it); 这个时候it会变成野指针
举例2: 珂朵莉树对于set有一个小trick,见下面的板子:
1 | struct Chtholly{ |
multiset
大致用法和set一样,只不过multiset允许多个元素.
值得注意的是,multiset删除元素也是完全删除,例如
1 | int main(){ |
map
1 | map<int,map<int,int>>mp;//map是能嵌套各种奇怪容器的,这不罕见,毕竟map的本质是一个映射,只要你能搞清楚映射关系,map就能维护(暴论) |
有一个小点是,map会对所有直接访问的元素建立新表,也就是说如果你用 if(mp[0]==0)xxx; 而不是 if(mp.find(0)==mp.end()) 会让map对0建立索引,直接的影响就是 mp.size() 对不上,umap也有这个问题.
unordered_map
因为map本质是红黑树,它只需要一个比较运算符就能开出来,而umap本质是哈希表,你的自定义类需要写一个哈希方法才能过编译.1
2
3
4
5
6
7
8
9
10
11
12
13struct node{
float aaa;
double bbb;
long long ccc;
node(const float& _aaa,const double& _bbb,const long long& _ccc):aaa(_aaa),bbb(_bbb),ccc(_ccc){}
};
struct nodeHash{
int operator()(const node& io)const{
int res=io.aaa+io.bbb+io.ccc;
return res;
}
};
unordered_map<node,int,nodeHash>mp;
上面的代码使用简单的加一起的哈希.(这里只是教你们怎么过编译,程序常数问题暂且不考虑,一般我们用val的3次方或者5次方取模表示哈希,当然如果你会写FNV哈希那更好了)
其他用法和map差不多,不过这个在遍历的时候是按照加入的时间戳进行遍历的,先遍历最晚加进来的,和元素大小没有关系.
priority_queue
不能for遍历,也没有几个方法,不容易写错.
1 | priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>>pq; |
估计绝大多数人接触pq是在Dijkstra这里…
因为 greater<int> 的本质是对 int 调用内置的 > 比较方法,所以我们完全可以这么写:
1 | struct node{ |
queue,stack,deque
这几个数据结构没什么方法,也不能for遍历,直接用VsCode自带的补全就可以了.
后记
“方法” 和 “函数” 其实是一回事.