非变异算法不直接改变其操作的数据结构的元素,其查找数据结构中的元素,检查序列元素是否满足某函数式,计算序列元素满足某条件的个数等。下表是按功能划分的非变异算法
1.循环 for_each 遍历容器元素,对每个元素执行相同的函数操作
2.查询 find 在单迭代器序列中找出某个值第一次出现的位置
find_if 在单迭代器序列中找出符合某谓词的第一个元素
find_first_of 在双迭代器序列中找出子序列中某元素第一次出现的位置
adjacent_find 在单选代器序列中找出第一次相邻值相等元素的位置
find_end 在双迭代器序列中找出一子序列最后一次出现的位置
Search 在双迭代器序列中找出一个子序列第一次出现的位置
search_n 在单选代器序列中找出一个值连续 n次出现的位置
3.计数 count 在序列中统计某个值出现的次数
count_if 在序列中统计与某谓词(表达式)匹配的次数
4.比较 equal 两个序列中的对应元素都相同时为真
Mismatch 找出两个序列相异的第一个元豪
8.1 循环
8.1.1 主要函数
for_each():遍历容器元素,对每个元素执行相同的函数操作
Template<clnss Inlt,class Fun>
Fun For_each(InIt first, InIt last, Fun f);
InIt:可以是数组或输入迭代器 first:表示数组的起始元素的指针或输入迭代器的起始迭代指针 f:可以是全局函数或者是一元函数
该模板需数的含义是[first,last)之间的每个元案,作为函数f的参数传入并执行。
例:打印向量中每个整型元素的立方
使用 vector 容器存储一系列整数,并使用 for_each 算法对容器中的每个元素应用自定义的函数。在本例中,函数 PrintCube 计算每个元素的立方并将结果打印到控制台上
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
void PrintCube(int n)
{
cout << n * n * n << " ";
}
int main()
{
const int VECTOR_SIZE = 8;
typedef vector<int> IntVector;
typedef IntVector::iterator IntVectorIt;
IntVector Numbers(VECTOR_SIZE);//创建容器并规定大小
IntVectorIt start, end, it;//定义了 IntVectorIt 类型的迭代器变量
for (int i = 0; i < VECTOR_SIZE; i++)//整数依次存储到 Numbers 容器中
Numbers[i] = i + 1;
start = Numbers.begin();//赋值
end = Numbers.end();
cout << "Numbers{";
for (it = start; it != end; it++)//for 循环打印出 Numbers 容器中的所有元素
cout << *it << " ";
cout << "}\n" << endl;
// for_each 算法,对 Numbers 容器中的每个元素应用 PrintCube 函数
for_each(start, end, PrintCube);
cout << "\n";
return 0;
}
(1)for_each 函数各参数的含义。start,end 表示向量的起始迭代指针、结束迭代指针,不是具体的值,比如for_each(start[0],end,PrintCube)是错误的,因为 start[0]表示的是向量中第一个元素的值 1。但 for_each(&start[0],end,PrintCube)是正确的,因为&start[0]表示的是第一个元素的地址。
(2) PrintCube函数必须有且只有一个参数,且参数类型与向量的模板类型一致。
例 求整数向量的和、最大值、最小值。
PrintInfo P = for_each(A, A + N, PrintInfo());
PS:使用 for_each 算法,对数组 A 中的每个元素应用 PrintInfo 类的对象。通过这个操作,我们将数组中的元素依次传递给 PrintInfo 类的函数调用运算符,并在其中进行总和、最大值和最小值的计算。将 for_each 的结果赋值给 PrintInfo 类的对象 P
(1)必须重载PrintInfo的operator()运算符,本例中即 void operator() (int x)在此函数内完成了求和、最大值、最小值的功能。
(2)主程序中 for_each(A,A+N,PrintInfo<int>())
使用 for_each 算法,对数组 A 中的每个元素应用 PrintInfo 类的对象。通过这个操作,我们将数组中的元素依次传递给 PrintInfo 类的函数调用运算符,并在其中进行总和、最大值和最小值的计算。将 for_each 的结果赋值给 PrintInfo 类的对象 P。
运行时,序先执行 PrintInfo<int>(),即调用了 PrintInfo 类中不含参数的构造函数,然后数组中的每一项依次调用PrintInfo类中的 void operator()(int x)函数完成相应功能。
(3)对主程序中 PrintInfo<int> P=for_each(A,A十N,PrintInfo<int>())等号左边PrintInfo<intP的理解。可从for_each 原型看出:
template<class InIt,class Fun>
Fun for_each(InIt first, InIt last,Fun f);
返回值可以是函数对象,因此本例中等号左侧定义了 PrintInfo&P引用对象。当然只有当[first,last)中所有迭代值都执行了f 函数才最终返回函数对象。
例:下面采用模板传参技术,用STL提供的一元函数模板类,让上例只能求整型向量的和最大值最小值
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
//两个模板参数 T 和 _outPara
template <class T, class _outPara>
class PrintInfo : public unary_function<T, _outPara>//定义模板类 PrintInfo
{
private:
T nSum;
T nMax;
T nMin;
int count;//已处理的元素数量
public:
PrintInfo() : count(0), nSum(0), nMax(0), nMin(0) {}
T GetSum() { return nSum; }
T GetMax() { return nMax; }
T GetMin() { return nMin; }
//类模板定义的 _outPara 类型
_outPara operator()(T x)//函数调用运算符的重载,接受一个参数 x
{
if (count == 0)//是第一个元素
{
nMax = x;
nMin = x;
}
else
{
if (nMax < x)
nMax = x;
if (nMin > x)
nMin = x;
}
nSum += x;
count++;
}
};
int main()
{
float A[] = {1.5, 4.2, 2.6, 8.9, 5.7, 7.1};
const int N = sizeof(A) / sizeof(float);//计算数组的大小
//将数组中的元素依次传递给 PrintInfo 类的函数调用运算符
PrintInfo<float, void> P = for_each(A, A + N, PrintInfo<float, void>());
cout << "总和是: " << P.GetSum() << endl;
cout << "最大值: " << P.GetMax() << endl;
cout << "最小值: " << P.GetMin() << endl;
return 0;
}
8.2 查询
8.2.1主要函数
查询操作是应用最广的操作,STL 主要提供了以下查询函数
find():在单迭代器序列中找出某个值第一次出现的位置。
find_if():在单迭代器序列中找出符合某谓词的第一个元素。
find_first_of():在双迭代器序列中找出一子序列中某元素第一次出现的位置
adjacent_find();在单迭代器序列中找出第一次相邻值相等元素的位置。
find_end():在双迭代器序列中找出一子序列最后一次出现的位置。
search():在双迭代器序列中找出一子序列第一次出现的位置。
search_n():在单选代器序列中找出一个值连续 n次出现的位置。
各个函数原型如下所示。
find:
原型:
template<class InIt, class T>
InIt find(InIt first, InIt last, const T& val);
参数说明:
InIt:输入选代器,first 表示起始元素的迭代器指针,last 表示结束元素的迭代器指针。
T:模板类型参数。
该函数是查询[first,last)间迭代器对应的元素值是否有等于 val的,若有则返回其迭代器指针;若无则返回 last。可知查询元素的个数范围是 N∈[0,last-first),由于要判定* (first+N)==val,因此板丁对应的类必须重运符“operator==”.
find_if
原型:
template<class InIt, class Pred>InIt find_if(Init first, InIt last, Pred pr);
参数说明:
InIt: 输人选代器,first 表示起始元的选代器指针,last 表示结束元的选代器指针。
Pred:普通全局函数或一元函数对象,返回值是 bool类型。该函数是查询[first,last)间迭代器对应的元素 *(first十i),若 pr( * (first+i))返回true,则返回此时的迭代器指针,表明满足条件的元素已找到;若没有找到则返回 last。
find_first_of
原型:
template<class FwdIt1, class FwdIt2>
FwdItl find first of(FwdIt1 first1, FwdItl lastl,FwdIt2 first2, FwdIt2 last2);
template<class FwdIt1,class FwdIt2, class Pred>
FwdItl find first_of(FwdItl first1, FwdIt1 lastl,FwdIt2 first2, FwdIt2 last2,Pred pr);
参数说明:
FwdIt1,FwdIt2;前向迭代器,first 表示起始元素的选代器指针,last 表示结束元素的迭代器指针。
Pred;二元全局函数或函数对象。
第一个原型含义是:若第一个前向迭代器[first1,last1)间第 N个元素与第二个前向代器[first2,last2)间某元素相等,且N最小,则返回 first1+N。表明第一个前向选代器wdIt1中有元素与第二个前向迭代器 FwdIt2 中的元素相等,否则返回 last1。
第二个原型与第一个类似,只不过要定义预判定函 pr( * (firstl+N),* (first2+M))
adjacent_find
原型:
template<class FwdIt>
FwdIt adjacent_find(FwdIt first,FwdIt last);
template<class FwdIt,class Pred>
FwdIt adjacent_find(FwdIt first,FwdIt last, Pred pr);
参数说明:
FwdIt:前向选代器,first 表示起始元的选代器指针,last 表示结束元素的选代器指针。
Pred:二元全局函数或函数对象。
第一个原型含义是,若前向代器 Fwdlt 中存在第N个元素,有*(first+N),且N最小,则表明有两个相邻元素是相等的,返回(first+N)否返回last。
第二个原型与第一个类似,只不过要定义预判定函数 pr( * (frst+N),*(first+N+1))。
find_end
原型:
template<class FwdIt1, class FwdIt2>
FwdItI find_end(FwdItl first1, FwdItl lastl,Fwdit2 first2, Fwdit2 last2),
template<class FwdIt1, class FwdIt2, class Pred>
FwdIti find_end(Ewdit1 first1, EwdIt1 last1,wdit2 first2, Fwdit2 last2, Pred pe
参数说明:
FwdIt1,FwdIt2:前向迭代器,first 表示起始元的迭代器指针,last 表示结束元的迭代器指针。
Pred:二元全局函数或函数对象。
第一个原型含义是:若前向迭代器 FwdItl 从第 N 个元开始:* (firstl+N)* (first2+0), * (firstl+N+1) = * (first2+ 1),..., * [firstl十 (last2- first2-1)]*[first2+(last2-first2-1)],且 N 最大,则返回(firstl+N),否则返回 last1。即返回FwdIt1 元素中最后一次完全与 FwdIt2 序列元素匹配的开始位置。
第二个原型与第一个类似,只不过要定义预判定函数 pr( * (first1+N+M),* (first2+N+M))。
search
原型:
template<class FwdIt1,class FwdIt2>
FwdIt1 search(FwdIt1 first1, FwdIt1 last1,FwdIt2 first2,FwdIt2 last2);
template<class FwdItl, class FwdIt2, class Pred >
FwdIt1 search(FwdIt1 first1, FwdIt1 last1,FwdIt2 first2, FwdIt2 last2,Pred pr);
参数说明:
FwdIt1,FwdIt2:前向迭代器,first 表示起始元的迭代器指针,last 表示结束元素的迭代器指针。
Pred;二元全局函数或函数对象。
第一个原型含义是:若前向迭代器 FwdIt1从第 N个元素开始:* (first1十N)=* (first2+0), * (firstl+N+1)= * (first2+ 1),..., * [first] 十 (last2 - first2-1)]* [first2+(last2-first2-1)],且N最小,则返回(firstl十N),否则返回 last2。即返回在FwdIt1 元素中首次完全与 FwdIt2 序列元素匹配的开始位置。
第二个原型与第一个类似,只不过要定义预判定函数 pr( * (first1+N+M),* (irst2+M))
Search_n
template<class Fwdit, class Dist, class T>
FwdIt search_n(Fwdit first, Fwdit last,Dist n, const T& val);
template<class Fwdit, class Dist, class T, class pred>
spwdrt seareh n(FwdIt firat, fwdit last,Dist n, const Ts val, Pred pr);
参数说明:。FwdIt:前向选代器,first 表示起始元素的选代器指针,last 表示结束元素的迭代器指针。count:要搜索的连续出现次数,value:要搜索的特定值。n: 整型变量,表示大小。 val:待比较的值。 Pred:二元全局函数或函数对象。
第一个原型含义是:在前向迭代器 FwdIt 中,从第 N 个元开始连续的n个元素满足;* (first+N)=val,* (first+1)= val,...,* (first+N+n)-val,且 N最小,则返回*(first+N),否则返回 last。
第二个原型与第一个类似,只不过要定义预判定函数 pr( * (first1+N+M),val)。
8.2.2示例分析
例: 7个查询函数的简单应用。
#include <algorithm>//STL算法库的头文件
#include <iostream>
using namespace std;
bool mygreater(int m) {//自定义的谓词函数
return m > 4;//查找大于4的元素。该函数接受一个整数参数m
}
bool pr(int* ptr) {
return (*ptr) % 2 == 0;//是否为偶数
}
int main() {
int a[] = {1, 2, 2, 2, 3, 4, 4, 5, 6, 7, 1, 2, 2, 31};
int nSize = sizeof(a) / sizeof(int);//计算数组a的大小,即元素个数
cout << "原始数组:" << endl;
for (int i = 0; i < nSize; i++) {
cout << a[i] << "\t";
}
cout << endl << endl;
//a 结尾的指针 a + nSize
int* pl = find(a, a + nSize, 3);//find算法在数组a中查找首次出现的值为3的元素
if (pl != a + nSize) {//指针 pl 相对于数组 a 的偏移量
cout << "(find) 首次等于 3 的位置:" << pl - a << "\t值:" << *pl << endl;
}
int* p2 = find_if(a, a + nSize, mygreater);
if (p2 != a + nSize) {
cout << "(find_if) 首次大于 4 的位置:" << p2 - a << "\t值:" << *p2 << endl;
}
int b[] = {10, 12, 61};
int nSize2 = sizeof(b) / sizeof(int);
int* p3 = find_first_of(a, a + nSize, b, b + nSize2);
if (p3 != a + nSize) {//数组a中查找首次出现的与数组b中任意元素匹配
cout << "(find_first_of) 首次在 a 数组中发现 b 数组 [10, 12, 61] 中元素位置:" << p3 - a << "\t值:" << *p3 << endl;
}
int* p4 = adjacent_find(a, a + nSize);
if (p4 != a + nSize) {//在数组a中查找首次出现的相邻元素相同的位置
cout << "(adjacent_find) 首次相邻元素相同位置:" << p4 - a << "\t值:" << *p4 << endl;
}
int c[] = {2, 31};
int nSize3 = sizeof(c) / sizeof(int);
int* p5 = find_end(a, a + nSize, c, c + nSize3);
if (p5 != a + nSize) {//在数组a中查找最后一次出现的与数组c完全匹配的子序列的位置
cout << "最后一次匹配 c 数组 [2, 31] 位置:" << p5 - a << endl;//12
}
int* p6 = search(a, a + nSize, c, c + nSize3);
if (p6 != a + nSize) {//使用search算法在数组a中查找首次出现的与数组c完全匹配的子序列的位置
cout << "首次匹配 c 数组 [2, 31] 位置:" << p6 - a << endl;//3
}
int* p7 = search_n(a, a + nSize, 3, 2);
if (p7 != a + nSize) {//在数组a中查找首次出现的连续3个值为2的元素的位置
cout << "首次出现 3 个 2 的位置:" << p7 - a << endl;//1
}
return 0;
}
find_if 和 find 是C++ STL算法库中的两个函数,用于在容器或者数组中查找元素。它们的区别如下:
1.功能不同:
find 函数用于在范围内查找第一个与指定值相等的元素,并返回指向该元素的迭代器(或指针)。
find_if 函数用于在范围内查找第一个满足指定条件(通过谓词函数)的元素,并返回指向该元素的迭代器(或指针)。
2.查找条件不同:
find 函数通过比较相等运算符(==)来确定元素是否与指定值相等。
find_if 函数通过调用用户自定义的谓词函数(predicate function)来判断元素是否满足特定条件。
3.使用方式不同:
find 函数的调用形式为 find(first, last, value),其中 first 和 last 是表示范围的迭代器(或指针),value 是要查找的特定值。
find_if 函数的调用形式为 find_if(first, last, predicate),其中 first 和 last 是表示范围的迭代(或指针),predicate 是一个谓词函数,用于判断元素是否满足条件。
例 :根据学号询学生信息,且已知学号是关键字
#include <algorithm>
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Student {
public:
int NO;
string strName;
Student(int NO, string strName) {
this->NO = NO;//初始化
this->strName = strName;
}
bool operator==(int NO) {//重载了 operator== 运算符,用于判断学生对象的学号是否与给定的学号相等
return (this->NO == NO);
}
};
int main() {
vector<Student> v;
Student s1(101, "张三");
Student s2(102, "李四");
v.push_back(s1);
v.push_back(s2);
vector<Student>::iterator begin, end, it_find;//声明了三个迭代器it_find存储查找结果的迭代器
begin = v.begin();
end = v.end();
int nFindNO = 102;//表示要查询的学号
it_find = find(begin, end, nFindNO);//find 函数在向量中查找学号为 nFindNO 的学生
cout << "查询学号为" << nFindNO << "的信息:" << endl;
if (it_find != end) {
cout << "学号:" << it_find->NO << "\t" << "姓名:" << it_find->strName << endl;
} else {
cout << "无该学号学生!" << endl;
}
return 0;
}
理解 Student类必须重载运算符“==”。当执行 find(begin,end,nFindNO)语句时,[begin,end)间的每一个迭代指针表示的 Student 对象都要与 nFindNO 比较,判断是否相等,因此必须重载 Student类中的“==”运算符。由于 nFindNO 是整型,因此重载的“=="运算符参数必须是整型数。
8.3 习题
1. 下面哪个algorithm定义的函数支持对数据传入自定义的处理算法( )
A. for B. find_if C. count D. swap
2. 下面哪个不属于algorithm算法定义的函数(D)
A. for_each B. find_if C. count D. less<>//属于函数对象functional
3.下面哪个函数实现遍历容器元素,对每个元素执行相同的函数操作()
A. for B. for_each C. count D. swap