一些好用的函数
前言:本篇主要简单记录一些平时遇到的好用的或者性能很特别的函数,持续更新中
1.关于全排列
(1)next_permutation()
头文件:
#include <algorithm>
函数原型:
bool next_permutation(iterator begin,iterator end)
用来求全排列的下一项(按字典序)
例:对于以下代码:
char ch[5]={'a','d','b','e','c'};
next_permutation(ch,ch+5);
cout << ch[0] << ch[1] << ch[2] << ch[3] << ch[4] << endl;
输出结果为:
adcbe
需要注意的是,这个函数返回值类型是
b
o
o
l
bool
bool,当数组已经完全倒序时,函数会返回
f
a
l
s
e
false
false,根据这一原理,我们可以利用一个do-while
循环,对一个按字典序排列好的数组进行全排列的遍历:
char ch[3]={'a','b','c'};
do{
cout << ch[0] << ch[1] << ch[2] << endl;
}while(next_permutation(ch,ch+3));
注意这里一定要用do-while
,否则不会有原先的排列顺序
输出结果为:
abc
acb
bac
bca
cab
cba
(2)prev_permutation()
头文件:
#include <algorithm>
函数原型:
bool prev_permutation(iterator begin,iterator end)
用来求全排列的上一项(按字典序)
与上面的next_permutation()
类似,对于以下代码:
char ch[5]={'a','d','b','e','c'};
prev_permutation(ch,ch+5);
cout << ch[0] << ch[1] << ch[2] << ch[3] << ch[4] << endl;
输出结果为:
adbce
同理,当数组已经完全正序时,函数会返回
f
a
l
s
e
false
false,我们同样可以利用一个do-while
循环,对一个完全倒序的数组进行全排列的遍历:
char ch[3]={'c','b','a'};
do{
cout << ch[0] << ch[1] << ch[2] << endl;
}while(prev_permutation(ch,ch+3));
输出结果为:
cba
cab
bca
bac
acb
abc
2.关于二分查找
(1)lower_bound()
头文件:
#include <algorithm>
函数原型:
iterator lower_bound(iterator begin,iterator end,const T& val);
用来求单调不减序列 [ b e g i n , e n d ) [begin,end) [begin,end)中第一个数值 ≥ v a l \geq val ≥val的迭代器,其中传入的参数 b e g i n begin begin和 e n d end end分别为序列开头和末尾的迭代器,特别地,如果序列中所有数值都 < v a l < val <val,则会返回数组中下标为 e n d end end的迭代器
例:对于以下代码
int a[5]={1,3,5,7,9};
cout << (lower_bound(a,a+5,1)-a) << " ";
cout << (lower_bound(a,a+5,2)-a) << " ";
cout << (lower_bound(a,a+3,8)-a) << " ";
cout << (lower_bound(a,a+5,10)-a) << endl;
输出结果为:
0 1 3 5
需要注意的是,这个函数内部实现利用了二分查找的原理,时间复杂度为 O ( log n ) O(\log n) O(logn),所以并不需要手写二分查找函数
(2)upper_bound()
头文件:
#include <algorithm>
函数原型:
iterator upper_bound(iterator begin,iterator end,const T& val);
用来求单调不减序列 [ b e g i n , e n d ) [begin,end) [begin,end)中第一个数值 > v a l > val >val的迭代器,其中传入的参数 b e g i n begin begin和 e n d end end分别为序列开头和末尾的迭代器,特别地,如果序列中所有数值都 ≤ v a l \leq val ≤val,则会返回数组中下标为 e n d end end的迭代器
例:对于以下代码
int a[5]={1,3,5,7,9};
cout << (upper_bound(a,a+5,1)-a) << " ";
cout << (upper_bound(a,a+5,2)-a) << " ";
cout << (upper_bound(a,a+3,8)-a) << " ";
cout << (upper_bound(a,a+5,10)-a) << endl;
输出结果为:
1 1 3 5
同理,这个函数内部实现也利用二分查找,时间复杂度 O ( log n ) O(\log n) O(logn)
(3)equal_range()
头文件:
#include <algorithm>
函数原型:
pair<iterator,iterator> equal_range(iterator begin,iterator end,const T& val)
主要作用是在单调不减区间 [ b e g i n , e n d ) [begin,end) [begin,end)内,寻找 v a l val val,返回值是一个 p a i r pair pair, f i r s t first first指向 v a l val val的起始位置, s e c o n d second second指向 v a l val val的末尾之后的位置,特别地,如果序列内不含 v a l val val,会返回一个 f i r s t first first和 s e c o n d second second都指向 v a l val val应插入的位置
其实这个函数就是lower_bound()
和upper_bound()
的相加,
p
a
i
r
pair
pair的第一维就相当于lowerbound(begin,end,val)
的值,第二维相当于upperbound(begin,end,val)
的值
例:对于以下代码
int a[10]={1,2,2,3,3,3,5,5,7,7};
auto bounds=equal_range(a,a+10,3);
cout << bounds.first-a << " " << bounds.second-a << endl;
bounds=equal_range(a,a+10,4);
cout << bounds.first-a << " " << bounds.second-a << endl;
输出结果为:
3 6
6 6
同理,利用二分,时间复杂度 O ( n ) O(n) O(n)
(4)binary_search()
头文件:
#include <algorithm>
函数原型:
bool binary_search(iterator start,iterator end,const T& val)
用来判断单调不减序列 [ b e g i n , e n d ) [begin,end) [begin,end)中,是否存在 v a l val val,若存在,返回 t r u e true true;若不存在,返回 f a l s e false false
例:对于以下代码:
int a[5]={1,3,5,7,9};
if(binary_search(a,a+5,5)) cout << "Yes" << endl;
else cout << "No" << endl;
if(binary_search(a,a+5,6)) cout << "Yes" << endl;
else cout << "No" << endl;
输出结果为:
Yes
No
这个函数的内部实现同样利用了二分查找,时间复杂度 O ( log n ) O(\log n) O(logn)
3.关于排序
(1)partial_sort()
头文件:
#include <algorithm>
函数原型:
void partial_sort(iterator begin,iterator middle,iterator end)
函数名称直译为“部分排序”,这个函数可以在无序的 [ b e g i n , e n d ) [begin,end) [begin,end)区间内找到 ( m i d d l e − b e g i n ) (middle-begin) (middle−begin)个最小的元素,并将它们置于数组的最前面,其余元素置于数组最后,但是要注意,其余元素的相对位置可能会改变,主要原因是内部实现利用大根堆进行比较
与sort()
函数一样,我们也可以自定义比较函数,传参时写在
e
n
d
end
end之后,就能以自定义排序规则进行部分排序
例:对于以下代码
//自定义升序规则
bool cmp(int a,int b){return a>b;}
//以下在main函数中
int a[10];
srand(time(0));
for(int i=0;i<10;i++)
a[i]=rand()%100;
cout << "随机赋值后,a数组如下:" << endl;
for(int i=0;i<10;i++)
cout << a[i] << "\t";
partial_sort(a,a+5,a+10);
cout << endl;
cout << "局部排序后,a数组如下:" << endl;
for(int i=0;i<10;i++)
cout << a[i] << "\t";
partial_sort(a,a+10,a+10);
cout << endl;
cout << "全部排序后,a数组如下:" << endl;
for(int i=0;i<10;i++)
cout << a[i] << "\t";
partial_sort(a,a+5,a+10,cmp);
cout << endl;
cout << "自定义局部排序后,a数组如下:" << endl;
for(int i=0;i<10;i++)
cout << a[i] << "\t";
partial_sort(a,a+10,a+10,cmp);
cout << endl;
cout << "自定义全部排序后,a数组如下:" << endl;
for(int i=0;i<10;i++)
cout << a[i] << "\t";
一组输出结果为:
随机赋值后,a数组如下:
49 16 36 52 17 83 60 51 14 27
局部排序后,a数组如下:
14 16 17 27 36 83 60 52 51 49
全部排序后,a数组如下:
14 16 17 27 36 49 51 52 60 83
自定义局部排序后,a数组如下:
83 60 52 51 49 14 16 17 27 36
自定义全部排序后,a数组如下:
83 60 52 51 49 36 27 17 16 14
值得一提的是,这个函数的时间复杂度为 O ( n log m ) O(n\log m) O(nlogm),其中 n n n表示整个区间长度, m m m表示从 b e g i n begin begin到 m i d d l e middle middle的长度,对于算法竞赛可能较慢,建议谨慎使用
(2)nth_element()
头文件:
#include <algorithm>
函数原型:
void nth_element(iterator begin,iterator middle,iterator end)
这个函数可以在无序的 [ b e g i n , e n d ) [begin,end) [begin,end)区间内找到第 ( m i d d l e − b e g i n + 1 ) (middle-begin+1) (middle−begin+1)小的数,并将其置于正确的下标为 ( m i d d l e − b e g i n ) (middle-begin) (middle−begin)的位置,且所有小于等于这个数的都在其左边,大于等于这个数的都在其右边 (但不保证整个序列单调不下降)
例:对于以下代码:
int a[10];
srand(time(0));
for(int i=0;i<10;i++)
a[i]=rand()%100;
cout << "随机赋值后,a数组如下:" << endl;
for(int i=0;i<10;i++)
cout << a[i] << "\t";
nth_element(a,a+4,a+10);
cout << endl;
cout << "找到下标为4的元素后,a数组如下:" << endl;
for(int i=0;i<10;i++)
cout << a[i] << "\t";
一组输出结果为:
随机赋值后,a数组如下:
61 83 9 62 41 13 92 51 95 42
找到下标为4的元素后,a数组如下:
42 13 9 41 51 61 92 83 95 62
值得一提的是,这个函数的时间复杂度为
O
(
n
)
O(n)
O(n),其中
n
n
n为序列长度,在无序序列中求第
k
k
k小(大)等类似题目中是比较优秀的时间复杂度,与上面的partial_sort()
函数相比,nth_element()
函数可能可以处理更多的实际问题
(3)stable_sort()
头文件:
#include <algorithm>
函数原型:
void stable_sort(iterator begin,iterator end)
与sort()
函数类似,这个函数也是对
[
b
e
g
i
n
,
e
n
d
)
[begin,end)
[begin,end)区间内的序列进行排序(默认升序)
但是这两个函数的不同点是:sort()
函数的内部实现主要依靠快速排序的思想,还外加了插入排序和堆排序,而stable_sort()
函数的内部实现依靠归并排序的思想,它能保证排序后相同元素的相对位置不改变,是一种稳定排序,但是相比而言,sort()
函数比stable_sort()
函数时间复杂度要低一些
同理,我们也可以自定义比较函数,与sort()
函数一样传入参数,使其按照自定义的排序规则排序
(4)is_sorted()
C++11的新函数,算法竞赛勿用
头文件:
#include <algorithm>
函数原型:
bool is_sorted(iterator begin,iterator end)
这个函数会判断 [ b e g i n , e n d ) [begin,end) [begin,end)区间内序列是否有序(默认判断是否升序),若已有序返回 t r u e true true,反之返回 f a l s e false false,特别地,如果序列内只有 1 1 1个元素,也会返回 t r u e true true
同理,我们也可以自定义比较函数传入参数,使其按照自定义的排序规则判断是否有序
内部实现是 f o r for for循环暴力判断相邻两个是否有序,时间复杂度 O ( n ) O(n) O(n)
4.关于序列操作
(1)fill()
头文件:
#include <algorithm>
函数原型:
void fill(iterator begin,iterator end,const T& val)
主要作用是将序列内 [ b e g i n , e n d ) [begin,end) [begin,end)区间内的值全部赋为 v a l val val
(其实就是省个for循环而已)
例:对于以下代码
int a[10];
srand(time(0));
for(int i=0;i<10;i++)
a[i]=rand()%100;
cout << "随机赋值后,a数组如下:" << endl;
for(int i=0;i<10;i++)
cout << a[i] << "\t";
fill(a,a+5,111);
cout << endl;
cout << "填充前五个元素为111后,a数组如下:" << endl;
for(int i=0;i<10;i++)
cout << a[i] << "\t";
一组输出结果为:
随机赋值后,a数组如下:
29 54 83 2 68 79 71 6 81 55
填充前五个元素为111后,a数组如下:
111 111 111 111 111 79 71 6 81 55
(2)fill_n()
头文件:
#include <algorithm>
函数原型:
void fill_n(iterator begin,const T& count,const T& val)
主要作用是将序列内从 b e g i n begin begin开始,长度为 c o u n t count count的区间内的值全部赋为 v a l val val
(其实也还是省个for循环而已)
例:对于以下代码
int a[10];
srand(time(0));
for(int i=0;i<10;i++)
a[i]=rand()%100;
cout << "随机赋值后,a数组如下:" << endl;
for(int i=0;i<10;i++)
cout << a[i] << "\t";
fill_n(a,5,111);
cout << endl;
cout << "填充前五个元素为111后,a数组如下:" << endl;
for(int i=0;i<10;i++)
cout << a[i] << "\t";
一组输出结果为:
随机赋值后,a数组如下:
38 37 14 27 10 57 61 3 85 29
填充前五个元素为111后,a数组如下:
111 111 111 111 111 57 61 3 85 29
(3)unique()
头文件:
#include <algorithm>
函数原型:
iterator unique(iterator begin,iterator end)
主要作用是把一个有序数组去重,并返回首个不重复的元素的迭代器