C++技术点积累(8)——STL之算法汇总:
一、算法概述
1)算法部分主要由头文件<algorithm>,<numeric>和<functional>组成。
<algorithm>是所有STL头文件中最大的一个,其中常用到的功能范围涉及到比较、交换、查找、遍历操作、复制、修改、反转、排序、合并等等。<numeric>体积很小,只包括几个在序列上面进行简单数学运算的模板函数,包括加法和乘法在序列上的一些操作。
<functional>中则定义了一些模板类,用以声明函数对象(仿函数)。
STL提供了大量实现算法的模版函数,只要我们熟悉了STL之后,许多代码可以被大大的化简,只需要通过调用一两个算法模板,就可以完成所需要的功能,从而大大地提升效率。
#include <algorithm>,#include <numeric>,#include <functional>
2)STL中算法分类
操作对象
直接改变容器的内容
将原容器的内容复制一份,修改其副本,然后传回该副本
功能:
a.非可变序列算法 指不直接修改其所操作的容器内容的算法
计数算法 count、count_if
搜索算法 search、find、find_if、find_first_of、…
比较算法 equal、mismatch、lexicographical_compare
b.可变序列算法 指可以修改它们所操作的容器内容的算法
删除算法 remove、remove_if、remove_copy、…
修改算法 for_each、transform
排序算法 sort、stable_sort、partial_sort、
c.排序算法 包括对序列进行排序和合并的算法、搜索算法以及有序序列上的集合操作
d.数值算法 对容器内容进行数值计算
二、函数对象、谓词、函数适配器
从概念上说,函数对象是用作函数的对象;但从实现上说,函数对象是实现了operator()的类的对象。虽然函数和函数指针也可以归为函数对象,但实现了operator()的类的对象才能保存状态(即类的成员属性的值)(见下面main02()函数),才能用于STL算法。
接受一个参数的函数,叫做一元函数,如果一元函数返回布尔值,则该函数成为一元谓词;接受两个参数的函数为二元函数,如果返回一个布尔值,则该函数称为二元谓词。
重载函数调用操作符的类,其对象常称为函数对象(function object),即它们是行为类似函数的对象。一个类对象,表现出一个函数的特征,就是通过“对象名+(参数列表)”的方式使用一个类对象,如果没有上下文,完全可以把它看作一个函数对待。
这是通过重载类的operator()来实现的。
“在标准库中,函数对象被广泛地使用以获得弹性”,标准库中的很多算法都可以使用函数对象或者函数来作为自定的回调行为;
一元函数对象:函数参数1个;
二元函数对象:函数参数2个;
一元谓词 函数参数1个,函数返回值是bool类型,可以作为一个判断式
谓词可以是一个仿函数,也可以是一个回调函数。
二元谓词 函数参数2个,函数返回值是bool类型
#include<iostream>
#include<string>
#include<vector>
#include<list>
#include<set>
#include<functional>
#include<algorithm>
using namespace std;
//1 一元函数对象
template <typename T>
class ShowElemt
{
public:
ShowElemt()
{
n = 0;
}
void operator()(T& t)
{
n++;
print();
cout << "成员函数()" << t << endl;
}
void print()
{
cout << n <<endl;
}
private:
int n;
};
//2 函数模板 ====函数
template <class T>
void FuncShowElemt(T &t)
{
cout << "函数模板<>:" << t << endl;
}
//3 普通函数
void FuncShowElemt2(int &t)
{
cout << "普通函数:" << t << endl;
}
//4 直接调用 函数对象
void main01()
{
int a = 10;
ShowElemt<int> show1;//函数对象
show1(a);
//show1.operator()(a);
FuncShowElemt<int>(a);
FuncShowElemt2(a);
}
//5 函数对象 做 函数参数,以及for_each()函数
void main02()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
for_each(v1.begin(), v1.end(), ShowElemt<int>());//匿名函数对象,匿名仿函数
cout << endl;
for_each(v1.begin(), v1.end(), FuncShowElemt2);
cout << endl;
//函数对象 做 函数参数
ShowElemt<int> show1;
/*调试跟踪 调出 for_each函数原型:
template<class _InIt,class _Fn1>
inline _Fn1 for_each(_InIt _First, _InIt _Last, _Fn1 _Func)//_Fn1 _Func可以看出这是个元素,而不是一个引用
{ // perform function for each element
_DEBUG_RANGE(_First, _Last);
_DEBUG_POINTER(_Func);
return (_For_each(_Unchecked(_First), _Unchecked(_Last), _Func));
}
*/
//结论:for_each算法的 函数对象的传递 是元素值传递 ,不是引用传递
for_each(v1.begin(), v1.end(), show1);//使用函数对象
//从for_each函数原型中的_Fn1 _Func可以看出这是个元素,而不是一个引用
//所以在for_each中对show1的操作
show1.print(); //打印的n仍为0,没有把上面的n信息变化的状态记录下来
//还要注意一下函数返回值
cout << endl;
cout << "通过for_each算法的返回值看调用的次数:" << endl;
show1 = for_each(v1.begin(), v1.end(), show1);
show1.print(); //打印3
//要点: 分清楚 stl算法返回的值是迭代器 还是 谓词(函数对象) 是stl算法入门的重要点
}
//6 一元谓词,以及find_if()函数
template <typename T>
class isDiv
{
public:
isDiv(const T &divisor)
{
this->divisor = divisor;
}
bool operator()(T &t)
{
return (t % divisor == 0);
}
private:
T divisor;
};
void main03()
{
vector<int> v2;
for (int i = 10; i < 33; i++)
{
v2.push_back(i);
}
int a = 4;
isDiv<int> myDiv(a);
vector<int>::iterator it;
/*
template<class _InIt,class _Pr>
inline _InIt find_if(_InIt _First, _InIt _Last, _Pr _Pred)
{ // find first satisfying _Pred
_DEBUG_RANGE(_First, _Last);
_DEBUG_POINTER(_Pred);
return (_Rechecked(_First,_Find_if(_Unchecked(_First), _Unchecked(_Last), _Pred)));
}
//find_if返回值是一个迭代器
//要点: 分清楚 stl算法返回的值是迭代器 还是 谓词(函数对象) 是stl算法入门的重要点
*/
it = find_if(v2.begin(), v2.end(), isDiv<int>(a));
if (it == v2.end())
{
cout << "容器中没有被 4 整除的元素" << endl;
}
else
cout << "容器中第一个被 4 整除的的元素的是:" << *it << endl;
}
//7 二元函数对象,以及transform()函数
template <class T>
class Add
{
public:
T operator()(T t1, T t2)
{
return t1 + t2;
}
};
void main04()
{
vector<int> v1, v2;
vector<int> v3;
v1.push_back(1);
v1.push_back(3);
v1.push_back(4);
v2.push_back(2);
v2.push_back(4);
v2.push_back(6);
v3.resize(10); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
/*
template<class _InIt1,class _InIt2,class _OutIt,class _Fn2>
inline _OutIt transform(_InIt1 _First1, _InIt1 _Last1,_InIt2 _First2, _OutIt _Dest, _Fn2 _Func)
{ // transform [_First1, _Last1) and [_First2, ...) with _Func
_DEBUG_RANGE(_First1, _Last1);
_DEBUG_POINTER(_Dest);
_DEBUG_POINTER(_Func);
if (_First1 != _Last1)
return (_Transform2(_Unchecked(_First1), _Unchecked(_Last1),_First2, _Dest, _Func,_Is_checked(_Dest)));
return (_Dest);
}
//transform 把运算结果的迭代器的开始位置 返出来
*/
//v1+v2,存到v3,因为下面的transform接收了Add
transform(v1.begin(), v1.end(), v2.