C++梁哥笔记day24 | 我的日常分享

C++梁哥笔记day24

算法

一、函数对象

重载函数调用操作符的类,其对象常称为函数对象(function object),即它们是行为类似函数的对象,也叫仿函数(functor),其实就是重载()运算符,使得类对象可以像函数那样调用。

注意:

  1. 函数对象(仿函数)是一个类,不是一个函数
  2. 函数对象(仿函数)重载了()运算符,使得它可以像函数一样调用。

分类:

假定某个类有一个重载的operator(),而且重载的operator()要求获取一个参数,我们就将这个类称为“一元仿函数(unary functor)”;类似地,如果重载的operator()要求获取两个参数,就将这个类称为“二元仿函数(binary functor)”

函数对象的作用主要是什么?

STL提供的算法往往都有两个版本,其中一个版本表现出最常用的某种运算,另一版本则允许用户通过template参数的形式来指定所要采取的策略。

总结:

  1. 函数对象通常不定义构造函数和析构函数,所以在构造和析构时不会发生任何问题,避免了函数调用的运行时问题。
  2. 函数对象超出了普通函数的概念,函数对象可以有自己的状态(因为类可以用自己的成员)
  3. 函数对象可内联编译,性能好。用函数指针几乎不可能
  4. 模板函数对象使函数对象具有通用性,这也是它的优势之一。

二、谓词

谓词是指普通函数或重载的operator()返回值是bool类型的函数对象(仿函数)。如果operator接受一个参数,那么叫做一元谓词;如果接受两个参数,那么叫做二元谓词,谓词可作为一个判断式。

即返回值类型是bool类型的普通函数或仿函数,就叫谓词。

案例:一元谓词

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
36
37
38
39
40
41
42
43
//普通函数作为一元谓词
bool greaterThan20(int val){
return val > 20;
}
//函数对象作为一元谓词
class greaterThan20A{
public:
bool operator ()(int val){
return val > 20;
}
};

void test05(){
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);

for_each(v.begin(),v.end(),[](int val){
cout<<val<<" ";
});
cout<<endl;
//需求找出第一个大于20的数
vector<int>::iterator ret;
//普通函数作为一元谓词
ret = find_if(v.begin(),v.end(),greaterThan20);
if(ret == v.end()){
cout<<"没有查到"<<endl;
}else{
cout<<"第一个大于20的数为:"<<*ret<<endl;
}
cout<<"****************"<<endl;
//函数对象作为一元谓词
/*greaterThan20A ob;
ret = find_if(v.begin(),v.end(),ob);*/
ret = find_if(v.begin(),v.end(),greaterThan20A());//使用匿名对象
if(ret == v.end()){
cout<<"没有查到"<<endl;
}else{
cout<<"第一个大于20的数为:"<<*ret<<endl;
}
}

运行结果:

image-20210128152710924

案例:二元谓词

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
36
37
38
39
40
41
42
43
44
45
46
//普通函数作为二元谓词
bool myGreater1(int v1,int v2){
return v1 > v2;
}
//函数对象作为二元谓词
class myGreater2{
public:
bool operator ()(int v1,int v2){
return v1 > v2;
}
};
void test06(){
vector<int> v;
v.push_back(10);
v.push_back(30);
v.push_back(15);
v.push_back(40);

for_each(v.begin(),v.end(),[](int val){
cout<<val<<" ";
});
cout<<endl;

//默认从小到大排序
sort(v.begin(),v.end());
cout<<"默认从小到大:";
for_each(v.begin(),v.end(),[](int val){
cout<<val<<" ";
});
cout<<endl;
//更改排序规则
//普通函数作为二元谓词
sort(v.begin(),v.end(),myGreater1);
cout<<"普通函数作为二元谓词 从大到小:";
for_each(v.begin(),v.end(),[](int val){
cout<<val<<" ";
});
cout<<endl;
//函数对象作为二元谓词
sort(v.begin(),v.end(),myGreater2());
cout<<"函数对象作为二元谓词 从大到小:";
for_each(v.begin(),v.end(),[](int val){
cout<<val<<" ";
});
cout<<endl;
}

运行结果:

image-20210128153511763

三、内建函数对象

STL内建了一些函数对象。

分为:算法类函数对象,关系运算类函数对象,逻辑运算类函数对象。这些函数对象所产生的对象,用法和一般函数完全相同,当然我们还可以产生无名的临时对象(匿名对象)来履行函数功能。

使用内建函数对象,需要引入头文件#include<functional>

3.1 算法类函数对象

6个算法类函数对象,除了negate是一元运算,其它的都是二元运算。

1
2
3
4
5
6
template<class T> T plus<T>;//加法仿函数
template<class T> T minus<T>;//减法仿函数
template<class T> T multiplies<T>;//乘法仿函数
template<class T> T divides<T>;//除法仿函数
template<class T> T modulus<T>;//取模仿函数
template<class T> T nagete<T>;//取反仿函数

案例:

1
2
3
4
5
void test07(){
plus<int> p;
cout<<p(12,15)<<endl;
cout<<plus<int>()(123,23)<<endl;//匿名对象
}

运行结果:

image-20210128155939702

3.2 关系运算类函数对象

6个关系运算类函数对象,每一种都是二元运算,也是二元谓词(返回值为bool类型)。

1
2
3
4
5
6
template<class T> bool equal_to<T>;//等于=
template<class T> bool not_equal_to<T>;//不等于!=
template<class T> bool greater<T>;//大于>
template<class T> bool greater_equal<T>;//大于或等于>=
template<class T> bool less<T>;//小于<
template<class T> bool less_equal<T>;//小于或等于<=

案例:

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
void test08(){
vector<int> v;
v.push_back(10);
v.push_back(30);
v.push_back(15);
v.push_back(40);

for_each(v.begin(),v.end(),[](int val){
cout<<val<<" ";
});
cout<<endl;

//默认从小到大排序
sort(v.begin(),v.end());
cout<<"默认从小到大:";
for_each(v.begin(),v.end(),[](int val){
cout<<val<<" ";
});
cout<<endl;
//更改排序规则
//内建函数对象作为二元谓词
sort(v.begin(),v.end(),greater<int>());
cout<<"内建函数对象作为二元谓词 从大到小:";
for_each(v.begin(),v.end(),[](int val){
cout<<val<<" ";
});
cout<<endl;
}

运行结果:

image-20210128160230499

3.3 逻辑运算类函数对象

not为一元谓词,其余为二元谓词。

1
2
3
template<class T> bool logical_and<T>;//逻辑与&&
template<class T> bool logical_or<T>;//逻辑或||
template<class T> bool logical_not<T>;//逻辑非!

四、函数对象适配器

拓展函数的参数接口(假如函数有一个参数,在拓展一个接口,就可以传递两个参数)

如果我们想使用绑定适配器,需要我们自己的函数对象继承binary_function或者unary_function,根据我们函数对象是一元函数对象还是二元函数对象。

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
//void myPrintVectorInt(int val,int tmp){
// cout<<val+tmp<<" ";
//}
//第二步:公有继承binary_function或una
//第三步:参数的萃取 <int,int,void> 分别是第一个形参第二个形参和返回值
//第四步:对operator()进行const修饰
class myPrintVectorInt:public binary_function<int,int,void>{
public:
void operator ()(int val){
cout<<val<<" ";
}
void operator ()(int val,int temp)const{
cout<<val+temp<<" ";
}
};

void test09(){
vector<int> v;
v.push_back(10);
v.push_back(30);
v.push_back(15);
v.push_back(40);

//需求:for_each打印数据时,将每个元素加上100
//第一步:使用bind2nd或者bind1st绑定参数 要对类进行绑定即仿函数(函数对象)
for_each(v.begin(),v.end(),bind2nd(myPrintVectorInt(),100));
cout<<endl;
for_each(v.begin(),v.end(),myPrintVectorInt());
cout<<endl;
}

运行结果:

202101282108

总结:

  • 第一步:使用bind2nd或者bind1st绑定参数 要对类进行绑定即仿函数(函数对象)
  • 第二步:公有继承binary_function或unary_function
  • 第三步:参数的萃取 <int,int,void> 分别是第一个形参第二个形参和返回值
  • 第四步:对operator()进行const修饰

bind2nd与bind1st的区别:绑定的参数位置不同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class myPrintVectorInt:public binary_function<int,int,void>{
public:
void operator ()(int val){
cout<<val<<" ";
}
void operator ()(int val,int temp)const{
cout<<"val:"<<val<<" "<<"temp:"<<temp<<","<<endl;
}
};
void test10(){
vector<int> v;
v.push_back(10);
v.push_back(30);
v.push_back(15);
v.push_back(40);

for_each(v.begin(),v.end(),bind2nd(myPrintVectorInt(),100));
cout<<endl;
for_each(v.begin(),v.end(),bind1st(myPrintVectorInt(),100));
cout<<endl;
}

运行结果:

20210128211528

总结:

  1. bind2nd:将外界数据绑定到第二个参数
  2. bind1st:将外界数据绑定到第一个参数

五、一元取反适配器

not1()

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
//第二步:公有继承unary_function
//第三步:参数萃取<int,bool>
class myGreaterThan30:public unary_function<int,bool>{
public:
//第四步:加const修饰
//一元谓词
bool operator ()(int val)const{
return val>30;
}
};

void test11(){
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(50);

//找到第一个大于30的数
vector<int>::iterator ret;
ret = find_if(v.begin(),v.end(),myGreaterThan30());
cout<<"*ret="<<*ret<<endl;

//使用取反适配器找到第一个小于30的数
//第一步:使用not1修饰
ret = find_if(v.begin(),v.end(),not1(myGreaterThan30()));
cout<<"*ret="<<*ret<<endl;
}

运行结果:

20210128213326

六、二元取反适配器

not2

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
36
37
38
39
40
41
42
43
44
45
46
47
48
//第二步:公有继承binary_function
//第三步:参数萃取<int,int,bool>
class myGreaterInt:public binary_function<int,int,bool>{
public:
//第三步:加const修饰
bool operator ()(int v1,int v2) const{
return v1>v2;
}
};

void test12(){
vector<int> v;
v.push_back(4);
v.push_back(9);
v.push_back(1);
v.push_back(12);
v.push_back(32);
//原始顺序
cout<<"原始顺序:";
for_each(v.begin(),v.end(),[](int val){
cout<<val<<" ";
});
cout<<endl;
//默认从小到大排序
sort(v.begin(),v.end());
cout<<"默认从小到大:";
for_each(v.begin(),v.end(),[](int val){
cout<<val<<" ";
});
cout<<endl;

//指定排序规则从大到小
sort(v.begin(),v.end(),myGreaterInt());
cout<<"指定排序规则从大到小:";
for_each(v.begin(),v.end(),[](int val){
cout<<val<<" ";
});
cout<<endl;

//使用not2将指定排序规则从大到小改成从小到大
//第一步:加not2修饰
sort(v.begin(),v.end(),not2(myGreaterInt()));
cout<<"not2将指定排序规则从大到小改成从小到大:";
for_each(v.begin(),v.end(),[](int val){
cout<<val<<" ";
});
cout<<endl;
}

运行结果:

20210128214821

七、成员函数适配器

利用mem_fun_ref 将Person内部成员函数适配

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
class Person{
public:
string name;
int age;
public:
Person(string name,int age){
this->name = name;
this->age = age;
}
void myPrint(){
cout<<"name="<<this->name<<" "<<"age="<<this->age<<endl;
}
};
void test13(){
vector<Person> v;
v.push_back(Person("小花",18));
v.push_back(Person("小明",17));
v.push_back(Person("lucy",23));
v.push_back(Person("bob",22));

//使用普通函数遍历
for_each(v.begin(),v.end(),myPrintPerson);
cout<<"**************"<<endl;
//使用成员函数遍历
for_each(v.begin(),v.end(),mem_fun_ref(&Person::myPrint));
//mem_fun_ref(&Person::myPrint)固定写法
}

运行结果:

20210128223217

20210128223547

八、普通函数作为适配器(函数指针作为适配器)

ptr_fun()

1
2
3
4
5
6
7
8
9
10
void test14(){
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);

for_each(v.begin(),v.end(),bind2nd(ptr_fun(myPrintInt),100));
cout<<endl;
}

20210129101327

总结:与函数对象适配器类似,只是普通函数不是一个类,需要加个ptr_fun()

九、算法概述

算法只要是由头文件组成,是所有STL头文件中最大的一个,其中常用的功能涉及到比较、交换、查找、遍历、复制、修改、反转、排序、合并等,体积很小,只包括在几个序列容器上进行的简单运算的模板函数,定义了一些模板类,用以声明函数对象。

9.1 常用遍历算法

9.1.1 for_each 遍历算法

1
2
3
4
5
6
7
8
/*
遍历算法 遍历容器元素
@param beg 开始迭代器
@param end 结束迭代器
@param _callback 函数回调或者函数对象
@return 函数对象
*/
for_each(iterator beg,iterator end,_callback);

9.1.2 transform 算法

将指定容器区间元素搬运到另一容器中

注意:transform 不会给目标容器分配内存,所以需要我们提前分配好内存

系统虽然可能会调用push_back,但是我们最好还是预先分配一下内存resize();

1
2
3
4
5
6
7
8
/*
@param beg1 源容器开始迭代器
@param end1 源容器结束迭代器
@param beg3 目标容器开始迭代器
@param _callback 函数回调或者函数对象
@return 返回目标容器迭代器
*/
transform(iterator beg1,iterator end1,iterator beg2,_callback);

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void test15(){
vector<int> v1;
v1.push_back(10);
v1.push_back(20);
v1.push_back(30);
v1.push_back(40);
vector<int> v2;
//预留空间
v2.resize(v1.size());
//使用普通函数
transform(v1.begin(),v1.end(),v2.begin(),myTransInt);
//使用函数对象
transform(v1.begin(),v1.end(),v2.begin(),myTransInt1());
cout<<"v2:";
for_each(v2.begin(),v2.end(),[](int val){
cout<<val<<" ";
});
cout<<endl;
}

运行结果:

20210129105555

9.2 常用查找算法

9.2.1 find() 查找元素

1
2
3
4
5
6
7
8
/*
find算法 查找元素
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param value 查找的元素
@return 返回查找元素的位置(迭代器)
*/
find(iterator beg,iterator end,value);

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void test16(){
vector<int> v1;
v1.push_back(10);
v1.push_back(20);
v1.push_back(30);
v1.push_back(40);

vector<int>::iterator ret;
ret = find(v1.begin(),v1.end(),30);
if(ret != v1.end()){
cout<<"查找到元素:"<<*ret<<endl;
}else{
cout<<"没有查到"<<endl;
}
}

运行结果:

image-20210129162538309

案例:

如果是自定义数据,需要重载==

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
class Person{
public:
string name;
int age;
public:
Person(string name,int age){
this->name = name;
this->age = age;
}
bool operator ==(const Person &ob){
return (this->age == ob.age && this->name == ob.name);
}
};
void test17(){
vector<Person> v1;
v1.push_back(Person("lucy",18));
v1.push_back(Person("bob",21));
v1.push_back(Person("mick",19));
v1.push_back(Person("lily",23));

vector<Person>::iterator ret;
ret = find(v1.begin(),v1.end(),Person("mick",19));
if(ret != v1.end()){
cout<<"查找到元素:"<<(*ret).name<<" "<<(*ret).age<<endl;
}else{
cout<<"没有查到"<<endl;
}
}

运行结果:

image-20210129163401764

9.2.2 find_if条件查找

1
2
3
4
5
6
7
/*
find_if算法 条件查找
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param _callback 回调函数或者谓词(返回bool类型的函数对象)
*/
find_if(iterator beg,iterator end,_callback);

案例:

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
class myGreaterThan25A{
public:
bool operator ()(int &val){
return val > 25;
}
};

void test18(){
vector<int> v1;
v1.push_back(10);
v1.push_back(20);
v1.push_back(30);
v1.push_back(40);

vector<int>::iterator ret;
//使用普通函数
ret = find_if(v1.begin(),v1.end(),myGreaterThan25);
//使用一元谓词
ret = find_if(v1.begin(),v1.end(),myGreaterThan25A());
if(ret != v1.end()){
cout<<"查找到大于25元素:"<<*ret<<endl;
}else{
cout<<"没有查到"<<endl;
}
}

运行结果:

image-20210129164007927

9.2.3 adjacent_find 算法 查找相邻重复元素

1
2
3
4
5
6
7
8
/*
adjacent_find 算法 查找相邻重复元素
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param _callback 回调函数或者谓词(返回bool类型的函数对象)
@param 返回相邻重复元素的第一个位置的迭代器
*/
adjacent_find(iterator beg,iterator end,_callback);

案例:

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
bool fun1(int v1,int v2){
return v1==v2;
}
void test19(){
vector<int> v1;
v1.push_back(10);
v1.push_back(20);
v1.push_back(20);
v1.push_back(40);
v1.push_back(50);
v1.push_back(50);
v1.push_back(70);

for_each(v1.begin(),v1.end(),[](int &val){
cout<<val<<" ";
});
cout<<endl;

vector<int>::iterator ret;
//对于普通数据时不需要回调函数的
ret = adjacent_find(v1.begin(),v1.end());
//也可以写回调函数或者二元谓词
ret = adjacent_find(v1.begin(),v1.end(),fun1);
if(ret != v1.end()){
cout<<"找到第一个相邻重复元素:"<<*ret<<endl;
}else{
cout<<"没有相邻重复元素"<<endl;
}
}

运行结果:

image-20210129164635519

对于普通数据时不需要回调函数的,也可以写,对于自定义数据需要回调函数,也可以重载== 内存比较???!不是内存比较,如果不传第三个参数默认调用函数对象模板中的equal_to<>()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// FUNCTION TEMPLATE adjacent_find
template <class _FwdIt, class _Pr>
_NODISCARD _FwdIt adjacent_find(const _FwdIt _First, _FwdIt _Last, _Pr _Pred) {
// find first satisfying _Pred with successor
_Adl_verify_range(_First, _Last);
auto _UFirst = _Get_unwrapped(_First);
auto _ULast = _Get_unwrapped(_Last);
if (_UFirst != _ULast) {
for (auto _UNext = _UFirst; ++_UNext != _ULast; _UFirst = _UNext) {
if (_Pred(*_UFirst, *_UNext)) {
_ULast = _UFirst;
break;
}
}
}

_Seek_wrapped(_Last, _ULast);
return _Last;
}

template <class _FwdIt>
_NODISCARD _FwdIt adjacent_find(const _FwdIt _First, const _FwdIt _Last) { // find first matching successor
return _STD adjacent_find(_First, _Last, equal_to<>());
}

案例:自定义数据使用回调函数

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
class Person {
public:
string name;
int age;
public:
Person(string name, int age) {
this->name = name;
this->age = age;
}
};
void test20() {
vector<Person> v1;
v1.push_back(Person("lucy", 18));
v1.push_back(Person("bob", 21));
v1.push_back(Person("bob", 21));
v1.push_back(Person("mick", 19));
v1.push_back(Person("lily", 23));

for_each(v1.begin(), v1.end(), [](Person& ob) {
cout << (ob).name << " " << (ob).age << endl;
});
cout << endl;

vector<Person>::iterator ret;

//对于自定义数据 必须写回调函数或者二元谓词或者重载==
ret = adjacent_find(v1.begin(), v1.end(), fun2);
if (ret != v1.end()) {
cout << "找到第一个相邻重复元素:" << (*ret).name << " " << (*ret).age << endl;
}
else {
cout << "没有相邻重复元素" << endl;
}
}

运行结果:

image-20210129171408338

案例:自定义数据使用二元谓词

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
class Person {
public:
string name;
int age;
public:
Person(string name, int age) {
this->name = name;
this->age = age;
}
};
void test21() {
vector<Person> v1;
v1.push_back(Person("lucy", 18));
v1.push_back(Person("bob", 21));
v1.push_back(Person("bob", 21));
v1.push_back(Person("mick", 19));
v1.push_back(Person("lily", 23));

for_each(v1.begin(), v1.end(), [](Person& ob) {
cout << (ob).name << " " << (ob).age << endl;
});
cout << endl;

vector<Person>::iterator ret;

//对于自定义数据 必须写回调函数或者二元谓词或者重载==
ret = adjacent_find(v1.begin(), v1.end(), fun2a());
if (ret != v1.end()) {
cout << "找到第一个相邻重复元素:" << (*ret).name << " " << (*ret).age << endl;
}
else {
cout << "没有相邻重复元素" << endl;
}
}

运行结果:

image-20210129171420350

案例:自定义数据重载==

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
36
37
class Person {
public:
string name;
int age;
public:
Person(string name, int age) {
this->name = name;
this->age = age;
}
bool operator ==(const Person &ob){
return (this->age == ob.age && this->name == ob.name);
}
};
void test22() {
vector<Person> v1;
v1.push_back(Person("lucy", 18));
v1.push_back(Person("bob", 21));
v1.push_back(Person("bob", 21));
v1.push_back(Person("mick", 19));
v1.push_back(Person("lily", 23));

for_each(v1.begin(), v1.end(), [](Person& ob) {
cout << (ob).name << " " << (ob).age << endl;
});
cout << endl;

vector<Person>::iterator ret;

//对于自定义数据 必须写回调函数或者二元谓词或者重载==
ret = adjacent_find(v1.begin(), v1.end());
if (ret != v1.end()) {
cout << "找到第一个相邻重复元素:" << (*ret).name << " " << (*ret).age << endl;
}
else {
cout << "没有相邻重复元素" << endl;
}
}

9.2.4 binary_search 算法 二分查找法(前提:容器必须有序)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
binary_search 算法 二分查找法
注意:容器必须有序
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param value 查找的元素
@return 返回true 或 false
*/
binary_search(iterator beg,iterator end,value);

template <class _FwdIt, class _Ty>
_NODISCARD bool binary_search(_FwdIt _First, _FwdIt _Last, const _Ty& _Val) {
// test if _Val equivalent to some element, using operator<
return _STD binary_search(_First, _Last, _Val, less<>());
}

案例:

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
void test23(){
vector<int> v1;
srand(time(NULL));
for(int i=0;i<50;i++){
int randomNum = rand()%100;
v1.push_back(randomNum);
}

sort(v1.begin(), v1.end());
int n=1;
for_each(v1.begin(), v1.end(), [&](int& val) {
cout << val << "\t";
if(n%10 == 0)
cout<<"\n";
n++;
});
cout << endl;

bool b=binary_search(v1.begin(),v1.end(),58);
if(b==true){
cout<<"查询成功"<<endl;
}else{
cout<<"数据未查到"<<endl;
}
}

运行结果:

image-20210129173307302

9.2.5 count 算法 统计元素出现次数

1
2
3
4
5
6
7
8
/*
count 算法 统计元素出现次数
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param value 元素
@return int 返回元素个数
*/
count(iterator beg,iterator end,value);

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void test24(){
vector<int> v;
v.push_back(10);
v.push_back(11);
v.push_back(12);
v.push_back(12);
v.push_back(13);
v.push_back(13);
v.push_back(13);
v.push_back(14);

int ct=0;
ct = count(v.begin(),v.end(),13);
cout<<"13出现的次数:"<<ct<<endl;
}

运行结果:

image-20210129202341115

9.2.6 count_if 算法 条件统计元素出现次数

1
2
3
4
5
6
7
8
/*
count_if 算法 条件统计元素出现次数
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param _callback 回调函数或者一元谓词
@return int 返回元素个数
*/
count_if(iterator beg,iterator end,_callback);

案例:

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
36
37
bool myGreaterThan12(int &val){
return val > 12;
}
class myGreaterThan13{
public:
bool operator ()(int &val){
return val > 13;
}
};

void test25(){
vector<int> v;
v.push_back(10);
v.push_back(11);
v.push_back(12);
v.push_back(12);
v.push_back(13);
v.push_back(13);
v.push_back(13);
v.push_back(14);
v.push_back(8);
v.push_back(19);
v.push_back(68);

int ct=0;
//使用普通函数作为回调函数
ct = count_if(v.begin(),v.end(),myGreaterThan12);
cout<<"大于12元素出现的次数:"<<ct<<endl;
cout<<"************"<<endl;
//使用函数对象(仿函数或一元谓词)
ct = count_if(v.begin(),v.end(),myGreaterThan13());
cout<<"大于13元素出现的次数:"<<ct<<endl;
cout<<"************"<<endl;
//使用内建函数
ct = count_if(v.begin(),v.end(),bind2nd(greater<int>(),14));
cout<<"大于14元素出现的次数:"<<ct<<endl;
}

运行结果:

image-20210129203555267

9.3 常用排序算法

9.3.1 merge 算法

1
2
3
4
5
6
7
8
9
10
11
/*
merge 算法 容器元素合并,并存储到另一个容器中
注意:两个容器必须是有序的
@param beg1 容器1开始迭代器
@param end1 容器1结束迭代器
@param beg2 容器2开始迭代器
@param end2 容器2结束迭代器
@param dest 目标容器开始迭代器
*/
//两个容器必须有序 其实无序也可以但是最后合并出来的结果也是乱的
merge(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);

v1和v2无序 合并出的v3如图:

image-20210129204853891

案例:

要预先设置目标容器的大小

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
36
37
38
void test26(){
vector<int> v1;
srand(time(NULL));
cout<<"v1:";
for(int i=0;i<6;i++){
int randomNum = rand()%100;
v1.push_back(randomNum);
}
for_each(v1.begin(), v1.end(), [&](int& val) {
cout << val << " ";
});
cout << endl;
sort(v1.begin(),v1.end());

vector<int> v2;
cout<<"v2:";
for(int i=0;i<6;i++){
int randomNum = rand()%100;
v2.push_back(randomNum);
}
for_each(v2.begin(), v2.end(), [&](int& val) {
cout << val << " ";
});
cout << endl;
sort(v2.begin(),v2.end());

vector<int> v3;
//预先设置空间
//v3.reserve(v1.size()+v2.size());
v3.resize(v1.size()+v2.size());
merge(v1.begin(),v1.end(),v2.begin(),v2.end(),v3.begin());
//cout<<"v3:"<<v3.capacity()<<" "<<v3.size();
cout<<"v3:";
for_each(v3.begin(), v3.end(), [&](int& val) {
cout << val << " ";
});
cout << endl;
}

运行结果:

image-20210129205629539

既然无序的合并也是无序的,要保证两个容器全部有序合并出来才是有序的,其实我觉得可以无序合并的结果最后做一个排序,即把无序的v3进行排序就行了阿。

注意:预先设置容器大小时,不能使用reserve要用resize,使用reserve,只会实现分配容量,大小还是0,相当于没有迭代器,这时候进行合并时,函数操作的是一个个的迭代器,这时候会出现问题,而既没有使用reserve和resize会出现段错误。

9.3.2 sort 算法 排序算法

1
2
3
4
5
6
7
/*
sort算法 容器元素排序
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param _callback 回调函数或者谓词
*/
sort(iterator beg,iterator end,_callback);

案例:

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
void test27(){
vector<int> v;
for(int i=0;i<6;i++){
int randomNum = rand()%100;
v.push_back(randomNum);
}
cout<<"排序前:";
for_each(v.begin(), v.end(), [&](int& val) {
cout << val << " ";
});
cout << endl;
sort(v.begin(),v.end());
cout<<"排序后:";
for_each(v.begin(), v.end(), [&](int& val) {
cout << val << " ";
});
cout << endl;
// 使用内建函数作为回调函数 从大到小
sort(v.begin(),v.end(),greater<int>());
cout<<"排序后:";
for_each(v.begin(), v.end(), [&](int& val) {
cout << val << " ";
});
cout << endl;
}

运行结果:

image-20210129210227203

9.3.3 random_shuffle算法

1
2
3
4
5
6
/*
random_shuffle算法 对指定范围内的元素随机调整顺序
@param beg 容器开始迭代器
@param end 容器结束迭代器
*/
random_shuffle(iterator beg,iterator end);

案例:

需要设置种子

1
2
3
4
5
6
7
8
9
10
11
12
void test28(){
vector<int> v;
for(int i=0;i<10;i++){
v.push_back(i);
}
srand(time(NULL));
random_shuffle(v.begin()+5,v.end());
for_each(v.begin(),v.end(),[](int &val){
cout<<val<<" ";
});
cout<<"\n";
}

运行结果:

image-20210129210656742

9.3.4 reverse算法

1
2
3
4
5
6
/*
reverse算法 反转指定范围的元素
@param beg 容器开始迭代器
@param end 容器结束迭代器
*/
reverse(iterator beg,iterator end);

案例:

1
2
3
4
5
6
7
8
9
10
11
12
void test29(){
vector<int> v;
for(int i=0;i<10;i++){
v.push_back(i);
}
srand(time(NULL));
reverse(v.begin()+5,v.end());
for_each(v.begin(),v.end(),[](int &val){
cout<<val<<" ";
});
cout<<"\n";
}

运行结果:

image-20210129210735826

9.4 常用拷贝和替换算法

9.4.1 copy算法

1
2
3
4
5
6
7
/*
copy算法 将容器内指定范围的元素拷贝到另一个容器中
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param dest 目标起始迭代器
*/
copy(iterator beg,iterator end,iterator dest);

要事先设置空间

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void test30(){
vector<int> v1;
for(int i=0;i<10;i++){
v1.push_back(i);
}
cout<<"v1:";
for_each(v1.begin(),v1.end(),[](int &val){
cout<<val<<" ";
});
cout<<"\n";

vector<int> v2;
v2.resize(v1.size());
copy(v1.begin(),v1.end(),v2.begin());
cout<<"v2:";
for_each(v2.begin(),v2.end(),[](int &val){
cout<<val<<" ";
});
cout<<"\n";
}

运行结果:

image-20210129211040164

copy高级用法:

用copy输出

1
2
3
4
5
6
7
8
void test31(){
vector<int> v1;
for(int i=0;i<10;i++){
v1.push_back(i);
}
copy(v1.begin(),v1.end(),ostream_iterator<int>(cout,"~~"));//包含头文件#include<iterator>
cout<<endl;
}

运行结果:

image-20210129212532403

9.4.2 replace算法 将容器内指定范围的旧元素修改为新元素

1
2
3
4
5
6
7
8
/*
replace算法 将容器内指定范围的旧元素修改为新元素
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param oldvalue 旧元素
@param newvalue 新元素
*/
replace(iterator beg,iterator end,oldvalue,newvalue);

9.4.3 replace_if算法 将容器内指定范围满足条件的元素替换为新元素

1
2
3
4
5
6
7
8
/*
replace_if算法 将容器内指定范围满足条件的元素替换为新元素
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param _callback 函数回调或者谓词
@param newvalue 新元素
*/
replace_if(iterator beg,iterator end,_callback,newvalue);

9.4.4 swap算法

swap(container c1,container c2);

1
2
3
4
template <class _Ty, class _Alloc>
void swap(vector<_Ty, _Alloc>& _Left, vector<_Ty, _Alloc>& _Right) noexcept /* strengthened */ {
_Left.swap(_Right);
}
1
2
3
4
5
6
7
public:
void swap(vector& _Right) noexcept /* strengthened */ {
if (this != _STD addressof(_Right)) {
_Pocs(_Getal(), _Right._Getal());
_Mypair._Myval2._Swap_val(_Right._Mypair._Myval2);
}
}
1
2
3
4
5
6
7
8
9
10
template <class _Alloc>
void _Pocs(_Alloc& _Left, _Alloc& _Right) noexcept {
if constexpr (allocator_traits<_Alloc>::propagate_on_container_swap::value) {
_Swap_adl(_Left, _Right);
} else {
_STL_ASSERT(_Left == _Right, "containers incompatible for swap");
(void) _Left; // TRANSITION, VSO-486357
(void) _Right; // TRANSITION, VSO-486357
}
}

层层套 我tm快烦死了,应该就是交换了两个容器的指向

9.5 常用算数生成算法

9.5.1 accumulate算法 计算容器元素累积总和

1
2
3
4
5
6
7
8
/*
accumulate 算法 计算容器元素累积总和
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param value 累加值
*/
//value是计算完后再累加的额外值
accumulate(iterator beg,iterator end,value);

案例:

运行结果:

9.5.2 fill算法 向容器中添加元素

1
2
3
4
5
6
7
/*
fill 算法 向容器中添加(填充)元素
@param beg 容器开始迭代器
@param end 容器结束迭代器
@param value 填充元素
*/
fill(iterator beg,iterator end,value);

案例:

运行结果:

9.6 集合算法

image-20210129214854597

9.6.1 set_intersection算法 求两个set集合的交集

1
2
3
4
5
6
7
8
9
10
11
/*
set_intersection 算法 求两个set集合的交集
@param beg1 容器1开始迭代器
@param end1 容器1结束迭代器
@param beg2 容器2开始迭代器
@param end2 容器2结束迭代器
@param dest 目标容器开始迭代器
@return 目标容器的最后一个元素的迭代器地址
*/
//注意:两个集合必须是有序序列
set_intersection(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);

案例:

运行结果:

9.6.2 set_union 求两个set集合的并集

1
2
3
4
5
6
7
8
9
10
11
/*
set_union 算法 求两个set集合的并集
@param beg1 容器1开始迭代器
@param end1 容器1结束迭代器
@param beg2 容器2开始迭代器
@param end2 容器2结束迭代器
@param dest 目标容器开始迭代器
@return 目标容器的最后一个元素的迭代器地址
*/
//注意:两个集合必须是有序序列
set_union(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);

案例:

运行结果:

9.6.3 set_difference 求两个set集合的差集

1
2
3
4
5
6
7
8
9
10
11
/*
set_difference 算法 求两个set集合的差集
@param beg1 容器1开始迭代器
@param end1 容器1结束迭代器
@param beg2 容器2开始迭代器
@param end2 容器2结束迭代器
@param dest 目标容器开始迭代器
@return 目标容器的最后一个元素的迭代器地址
*/
//注意:两个集合必须是有序序列
set_difference(iterator beg1,iterator end1,iterator beg2,iterator end2,iterator dest);

案例:

运行结果:

十、STL综合案例

演讲比赛

比赛规则:

某市举行一场演讲比赛( speech_ contest ),共有24个人参加。比赛共三轮,前两轮为淘汰赛,笫三轮为决赛。

比赛方式:分组比赛,每组6个人;选于每次要随机分组,进行比赛;

第-轮分为4个小组,每组6个人。比如编号为: 100-123.整体进行抽签

(draw)后顺序演讲。当小组演讲完后,淘汰组内排名最后的三个选手,然后继续下一个小组的比赛。

第二轮分为2个小组,每组6人。比赛完毕,淘汰组内排名最后的三个选于,然后继续下一-个小组的比赛。

第三轮只剩下1组6个人,本轮为决赛,选出前三名。比赛评分: 10 个评委打分,去除最低、最高分,求平均分每个选手演讲完由10个评委分别打分。该选手的最终得分是去掉一一个最高分和-一个最低分,求得剩下的8个成绩的平均分。选手的名次按得分降序排列。

用STL编程,求解这个问题1) 请打印出所有选于的名字与参赛号,并以参赛号的升序排列。2)打印每–轮比赛后,小组比赛成绩和小组晋级名单

需求分析: 1) 产生选手( ABCDEFGHIJKLMNOPQRSTUVWX )姓名、得分;选手编号

第1轮选手抽签选手比赛查看比赛结果

第2轮选手抽签选手比赛查看比赛结果

第3轮选手抽签选手比赛查看比赛结果

实现思路:需要把选于信息、选于得分信息、选于比赛抽签信息、选手的晋级信息保存在容器中,需要涉及到各个容器的选型。选 手可以设计一个类Speaker(姓名和得分)所有 选手的编号可以单独放在一个vector容器中,做抽签用所有选手编号和选手信息,可以放在容器内:map<int,