竞赛中常用的库函数
1. 大小写转换
1.1 islower/isupper(判断一个字符(char类型)是否为大写或小写)
1.2 tolower/toupper(将字符的大小写形式转换)
1.3 ASCII码
操作:
小写转大写:'c' - 'a' + 'A' = 'C'
大写转小写:'C' - 'A' + 'a' = 'c'
数字(字符)转数字:'6' - '0' = 6
2. 二分查找
2.1 二分查找的前提(序列有序)
2.2 binary_search函数
函数返回一个bool值,true表示要查找的元素在序列中,false表示要查找的元素不在序列中
如果需要找到元素的位置,那么就可以用lower_bound和upper_bound函数
示例:
#include <bits/stdc++.h>
int main(void){
vector<int> numbers = {1, 2, 3, 4, 5};
int target = 5;
bool found = binary_search(numbers.begin(), numbers.end(), target);//found = true
//参数意义:起始地址, 结束地址, 目标
}
2.3 lower_bound和upper_bound(重点)
前提:数组必须是非降序的(也就是说后一个元素必须大于等于前一个元素)
可用于找到一个元素所在的左闭右开区间(大于和大于等于的区别)
其中:
lower_bound返回指定区间内第一个大于等于指定元素的地址
upper_bound返回指定区间内第一个大于指定元素的地址
示例:
vector<int> v = {3, 2, 5, 5,6, 8};
sort(v.begin(), v.end());//2, 3, 5, 5, 6, 8
cout << upper_bound(v.begin(), v.end(), 5) - v.begin() << '\n';//返回第一个大于5的下标-->4
cout << lower_bound(v.begin(), v.end(), 5) - v.begin() << '\n';//返回第一个大于等于5的下标-->2
//上述用法说明了upper_bound和lower_bound函数常用来返回指定元素所在的左闭右开区间的下标
3. 排序(sort)
3.1 sort的速度
sort使用的是类快速排序的实现方法,时间复杂度非常优秀,为O(n logn)
3.2 用法
sort函数有三个地址:
sort(起始地址, 结束地址的下一位(, 比较函数))
示例:
int a[1000];
sort(a, a + 1000);//此时默认最后一个参数为“<”,也就是说最终排序出来的是升序序列
3.3 自定义比较函数
假如现在想排列出一个降序序列,可以自定义一个返回值为bool类型的函数
示例:
#include <bits/stdc++.h>
using namespace std;
bool cmp(const int &u, const int &v){
return u > v;
}
int main(void){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
vector<int> v = {5, 1, 3, 9, 11};
sort(v.begin(), v.end(), cmp);
for(auto i : v){
cout << i << ' ' << '\n';
}
}
结构体可以将小于号重载后再进行比较:
struct Node{
int u, v;
bool operator < (const Node &m)const{
return u == m.u ? v < m.v : u < m.u;
}
}
4. 例题–排序
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 1;
int a[N];
int main()
{
int n; cin >> n;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
sort(a + 1, a + n + 1);
for(int i = 1; i <= n; i++) cout << a[i] << " \n"[i == n];
for(int i = n; i >= 1; i--) cout << a[i] << " \n"[i == 1];
return 0;
}
几点经验:
- 开大数组最好以全局变量的形式声明
- cout << a[i] << " \n"[i == n];—>活用字符串下标的机制,达成最后一个字符后打出回车,其余都是空格的效果
5. 全排列
5.1 next_permutation(返回当前序列的下一个排列)
此函数将序列按照字典序重新排列
若当前序列存在下一个排列,则将当前序列修改为下一个排列,并返回true
若当前序列不存在下一个排列(即当前序列已经是最后一个排列),则将序列修改为第一个序列,并返回false
示例:
#include <bits/stdc++.h>
using namespace std;
int main(void){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
vector<int> nums = {1, 2, 3};
cout << "Initial permutation: ";
for(auto num : nums){
cout << num << ' ';
}
cout << '\n';
while(next_permutaion(nums.begin(), nums.end())){
cout << "Next permutation";
for(auto num : nums){
cout << num << ' ';
}
cout << '\n';
}
return 0;
}
5.2 prev_permutation(返回当前序列的上一个排列)
与next_permutation顺序相反
6. 最值查找
6.1 min max
可传入两个值或者一个列表
min(3, 5);
min({1, 2, 3, 4, 5});
6.2 min_element/ max_element
min_element(st, ed)返回地址[st, ed)中最小的那个值的地址(迭代器),传入参数为两个地址或者迭代器
max_element同理
min_element(v.begin(), v.end());
6.3 nth_element
nth_element(st, k, ed)
进行部分排序,没有返回值
效果:第K个元素的前面的元素都要比这个元素小,后面的元素都要比这个元素大(除了第k个元素,其余元素的位置可能是任意的)
示例:
vector<int> v = {5, 1, 7, 3, 10, 18 ,9};
nth_element(v.begin(), v.begin() + 3, v.end());
7. 其他常用库函数
7.1 memset()
常用来初始化数组
int a[5];
memset(a, 0, sizeof(a));
//三个参数的意义为:指向想要修改的内存的指针,想要修改的值,修改多大的空间
需要注意的是,memset对于非字符类型的数组会存在未定义行为(除了字符其余数据类型都至少为2个字节)
比如int:
//还是上面的例子
int a[5];
memset(a, 1, sizeof(a));
//此处memset实际上把数组的五个元素都设置为了 00000001 00000001 00000001 00000001(int占4个字节)
7.2 swap()
接受两个值的引用,并将它们交换
7.3 reverse()
用于反转容器中的元素顺序
传入起始地址(迭代器)和结束地址(迭代器)(左闭右开)
7.4 unique()
用于去除容器中相邻重复元素的函数
传入起始地址(迭代器)和结束地址(迭代器)(左闭右开)
如果需要去除所有重复的元素,那么需要先对容器进行排序
返回值为第一个重复元素的地址
//注意:调用unique()函数后,重复的元素只是被放在了后面,并没有被删除
vector<int> v = {1, 1, 2, 2, 3};
auto it = unique(v.begin(), v.end());//此时v中元素为1, 2, 3, 1, 2
//此处it的值为指向末尾1的指针
v.erase(it, v.end());//利用unique的返回值,将重复的元素去除