C++ find()和find_if()的用法(非常详细)
在 C++ 程序中,很多时候我们并不需要处理容器中的所有数据,更多的是希望从容器保存的大量数据中找到符合某个条件的数据,然后将它们挑选出来进行处理。
这里有“大量数据”,也有“条件”,很自然地会想到使用 for 循环来遍历大量数据,再配合 if 条件判断从中挑选符合条件的数据。这样的方式虽然可行,但代码书写比较烦琐,显得不够“优雅”。
在 STL 算法中,有专门用于从容器中挑选数据的 find() 算法。有了它,我们无须费尽周折地使用 for 语句和 if 语句来查找符合条件的数据,只需设置 find() 算法查找的范围和条件即可。
find() 算法可以从一个容器的某个范围中查找具有某个特定数值的数据元素,它的函数原型如下:
需要注意的是,find() 算法会利用“==”运算符将目标内容与容器中的数据元素进行相等比较,以确定某个元素是否符合条件。因此,这里的数据类型必须能用“==”运算符进行相等比较:
在执行过程中,find() 算法会将目标内容与容器中查找范围内的数据元素逐个进行相等比较,如果 find() 算法找到了与之相等的数据元素,它将返回指向该数据元素的迭代器;如果没有找到,则返回的迭代器将指向这个容器的末尾位置。据此,我们可以判断查找是否成功。
例如,商店的商品信息保存在一个 vector 容器中,我们可以从中查找某个商品,以此确定这个商品是否正在销售:
使用 find() 算法可以从容器中找到等于某个特定值的数据元素,然而,更多时候,我们希望能够从容器中找到符合某种条件的数据。
例如,在一个保存学生成绩的 vector 容器中,我们希望找到及格的分数,即大于 60 的数据。当需要根据某个特定条件来查找数据时,find() 算法就显得力不从心,这时它的孪生兄弟—find_if() 算法就登场了。
find_if() 算法的原型如下:
当然,它们也有着不同之处,与 find() 算法使用某个特定值作为查找目标的第三个参数不同,find_if() 算法的第三个参数是一个规则函数(其形式可以是一个普通函数,也可以是函数对象或 Lambda 表达式),其返回值为 bool 类型,并且拥有一个与容器中数据类型相同的参数。
find_if() 算法利用这个规则函数来表达某个特定的查找规则。在执行 find_if() 算法时,它会将查找范围内的数据逐个传递给这个规则函数。规则函数根据一定条件对数据进行判断:
如果在整个查找范围内都未找到符合条件的数据,则返回指向查找范围末尾的迭代器。
例如,我们想要找出 vector 容器中所有及格的分数:
在这里,我们以 vecScores 容器的开始位置作为 find_if() 算法的查找起点。当该算法在容器中找到符合条件的数据元素时,程序会输出当前找到的元素,同时迭代器进行自增操作,指向当前元素的下一个位置,并以此作为下一次循环中 find_if() 算法的开始位置。这样的操作会不断循环,直到 find_if() 算法在 vecScores 容器中再也找不到符合条件的元素,返回指向容器末尾的迭代器为止。
查找是 C++ 程序中最常见的一类操作,因此,除 find() 算法和 find_if() 算法外,STL 中还有多个查找算法的变种。例如:
此外,与查找算法在容器中查找单个符合条件的数据不同,STL 还提供了搜索算法,例如 search() 算法和 search_n() 算法,它们可以用于在容器中搜索与目标串相匹配的子串数据。
总之,STL 中与查找相关的算法种类繁多,各有特点,适用于不同的场景,我们需要熟悉各种查找算法,并根据需要选择合适的查找算法来针对性地解决问题。
使用查找算法不仅是为了找到某个符合条件的数据,更多时候,我们希望找到这些数据并对其进行处理。例如删除容器中符合某个条件的数据,或者用其他的数据替换那些符合条件的数据等。
对容器中符合某个条件的数据进行处理非常普遍。为此,STL 提供了相应的算法来帮助我们快速、高效地完成这些常见的数据处理任务。
例如,STL 中的 remove() 算法可以删除(remove)容器中所有符合某个条件的数据,而 replace() 算法则可以使用其他数据替换(replace)容器中符合某个条件的数据。利用这些算法,我们可以直接对容器中符合条件的数据进行日常处理,而不必先用 find() 算法找到这些数据再进行处理。
下面来看一个实际例子。考试结束后,所有考试成绩都保存在 vecScores 容器中。在公布成绩之前,为了让考试成绩好看一点,我们需要对 vecScores 容器中那些“惨不忍睹”的成绩数据进行一定的处理。首先,需要删除其中的缺考成绩(如果学生缺考,则成绩记为 -1),然后将所有不及格的成绩替换为及格成绩。使用 STL 中的 remove() 算法和 replace_if() 算法,可以很轻松地完成对数据的一番“装饰打扮”:
为此,STL 提供了多个算法来判断容器中是否存在符合某个条件的数据。例如:
有了这些算法的帮助,我们可以更加方便地对容器中的数据进行判断。例如,我们可以使用 any_of() 算法来判断保存学生成绩的容器中是否存在不及格的分数:
这里有“大量数据”,也有“条件”,很自然地会想到使用 for 循环来遍历大量数据,再配合 if 条件判断从中挑选符合条件的数据。这样的方式虽然可行,但代码书写比较烦琐,显得不够“优雅”。
在 STL 算法中,有专门用于从容器中挑选数据的 find() 算法。有了它,我们无须费尽周折地使用 for 语句和 if 语句来查找符合条件的数据,只需设置 find() 算法查找的范围和条件即可。
find() 算法可以从一个容器的某个范围中查找具有某个特定数值的数据元素,它的函数原型如下:
template <class InputIterator, class T> InputIterator find ( InputIterator first, InputIterator last, const T& value );其中,find() 算法的前两个参数都是某个容器的迭代器,用于指定查找的起始位置和终止位置;第三个参数是要查找的内容,它的数据类型跟容器中的数据类型相同。
需要注意的是,find() 算法会利用“==”运算符将目标内容与容器中的数据元素进行相等比较,以确定某个元素是否符合条件。因此,这里的数据类型必须能用“==”运算符进行相等比较:
- 对于基本数据类型而言,它们原生就可以用这个运算符进行相等比较;
- 如果是自定义的数据类型,则需要重载“==”运算符以使该自定义的数据类型支持相等比较。
在执行过程中,find() 算法会将目标内容与容器中查找范围内的数据元素逐个进行相等比较,如果 find() 算法找到了与之相等的数据元素,它将返回指向该数据元素的迭代器;如果没有找到,则返回的迭代器将指向这个容器的末尾位置。据此,我们可以判断查找是否成功。
例如,商店的商品信息保存在一个 vector 容器中,我们可以从中查找某个商品,以此确定这个商品是否正在销售:
// 创建保存商品信息的 vector 容器 vector<string> vecGoods; // 老板进货,向容器中添加商品 vecGoods.push_back("Eraser"); vecGoods.push_back("Book"); vecGoods.push_back("Pen"); // ... // “老板,你这里卖铅笔吗?”,定义要购买的商品 string strBuy = "Pencil"; // “稍等,让我找找看!”,在 vecGoods 中找 Pencil auto it = find( vecGoods.begin(), vecGoods.end(), strBuy ); // 如果 find() 函数返回的迭代器没有指向容器的末尾 // 那就是找到想要的东西了 if( it != vecGoods.end() ) { cout<<"恭喜,本店提供"<<strBuy<<endl; } else // 如果迭代器指向容器末尾位置,则是没找到 { cout<<"抱歉,本店不提供"<<strBuy<<endl; }在这里,我们使用 find() 算法在容器的整个范围内查找 Pencil 数据,然后根据算法的不同返回值判断这个数据是否在这个容器中。如果在容器中,则可以通过迭代器进一步访问这个数据。
使用 find() 算法可以从容器中找到等于某个特定值的数据元素,然而,更多时候,我们希望能够从容器中找到符合某种条件的数据。
例如,在一个保存学生成绩的 vector 容器中,我们希望找到及格的分数,即大于 60 的数据。当需要根据某个特定条件来查找数据时,find() 算法就显得力不从心,这时它的孪生兄弟—find_if() 算法就登场了。
find_if() 算法的原型如下:
template <class InputIterator, class Predicate> InputIterator find_if ( InputIterator first, InputIterator last, Predicate pred );既然 find_if() 算法与 find() 算法是“孪生兄弟”,自然有着相似之处:都可以接受三个参数,前两个参数用于指定查找的范围。
当然,它们也有着不同之处,与 find() 算法使用某个特定值作为查找目标的第三个参数不同,find_if() 算法的第三个参数是一个规则函数(其形式可以是一个普通函数,也可以是函数对象或 Lambda 表达式),其返回值为 bool 类型,并且拥有一个与容器中数据类型相同的参数。
find_if() 算法利用这个规则函数来表达某个特定的查找规则。在执行 find_if() 算法时,它会将查找范围内的数据逐个传递给这个规则函数。规则函数根据一定条件对数据进行判断:
- 如果符合条件,规则函数返回 true,表示这个数据就是我们要找的数据,此时 find_if() 算法会返回指向这个数据的迭代器,表示找到了一个符合条件的数据;
- 如果数据不符合规则函数中的条件,规则函数返回 false,表示当前数据不符合查找条件,find_if() 算法会将下一个数据传递给规则函数,开始同样的判断过程。这个过程会不断重复,直到找到符合条件的数据并返回指向这个数据的迭代器。
如果在整个查找范围内都未找到符合条件的数据,则返回指向查找范围末尾的迭代器。
例如,我们想要找出 vector 容器中所有及格的分数:
// 使用函数表达查找规则:查找及格分数 // 如果分数大于或等于 60,就返回 true,表示及格,否则返回 false bool ispass( int n ) { // 判断传递的数据是否符合条件 // 如果符合条件,就返回 true,否则返回 false return n >= 60 ? true : false; } // 定义保存成绩的容器 vector<int> vecScores = {97,56,82,81,59,60}; // 定义查找的起始位置 auto it = vecScores.begin(); // 利用循环逐个查找容器中符合条件的数据 do { // 在容器中查找符合条件的数据元素 // 其中 ispass 表达了查找规则 it = find_if(it, vecScores.end(), ispass ); if ( vecScores.end() != it ) // 判断是否找到符合条件的数据 { // 输出查找到的符合条件的数据 cout<<"找到及格分数:"<<*it<<endl; // 将迭代器指向下一个位置,从新的位置开始下一次查找 ++it; } else { // 如果没有找到,就退出循环 break; } } while( true );在这段代码中,我们将 find_if() 算法与 do…while 循环结合,同时用 ispass() 函数来表达查找规则,构造了一个从 vecScores 容器中查找所有及格分数的循环。
在这里,我们以 vecScores 容器的开始位置作为 find_if() 算法的查找起点。当该算法在容器中找到符合条件的数据元素时,程序会输出当前找到的元素,同时迭代器进行自增操作,指向当前元素的下一个位置,并以此作为下一次循环中 find_if() 算法的开始位置。这样的操作会不断循环,直到 find_if() 算法在 vecScores 容器中再也找不到符合条件的元素,返回指向容器末尾的迭代器为止。
查找是 C++ 程序中最常见的一类操作,因此,除 find() 算法和 find_if() 算法外,STL 中还有多个查找算法的变种。例如:
- find_end() 算法能够从容器的末尾位置开始向前查找符合条件的数据;
- find_first_of()算法会查找容器中某个元素首次出现的位置;
- adjacent_find()算法则会查找容器中重复出现的数据。
此外,与查找算法在容器中查找单个符合条件的数据不同,STL 还提供了搜索算法,例如 search() 算法和 search_n() 算法,它们可以用于在容器中搜索与目标串相匹配的子串数据。
总之,STL 中与查找相关的算法种类繁多,各有特点,适用于不同的场景,我们需要熟悉各种查找算法,并根据需要选择合适的查找算法来针对性地解决问题。
使用查找算法不仅是为了找到某个符合条件的数据,更多时候,我们希望找到这些数据并对其进行处理。例如删除容器中符合某个条件的数据,或者用其他的数据替换那些符合条件的数据等。
对容器中符合某个条件的数据进行处理非常普遍。为此,STL 提供了相应的算法来帮助我们快速、高效地完成这些常见的数据处理任务。
例如,STL 中的 remove() 算法可以删除(remove)容器中所有符合某个条件的数据,而 replace() 算法则可以使用其他数据替换(replace)容器中符合某个条件的数据。利用这些算法,我们可以直接对容器中符合条件的数据进行日常处理,而不必先用 find() 算法找到这些数据再进行处理。
下面来看一个实际例子。考试结束后,所有考试成绩都保存在 vecScores 容器中。在公布成绩之前,为了让考试成绩好看一点,我们需要对 vecScores 容器中那些“惨不忍睹”的成绩数据进行一定的处理。首先,需要删除其中的缺考成绩(如果学生缺考,则成绩记为 -1),然后将所有不及格的成绩替换为及格成绩。使用 STL 中的 remove() 算法和 replace_if() 算法,可以很轻松地完成对数据的一番“装饰打扮”:
#include <vector> // 为了使用 vector 容器 #include <algorithm> // 为了使用 remove() 和 replace_if() 算法 #include <iostream> using namespace std; // 判断分数是否不及格(小于 60)的函数 bool isfail(int nScore) { return nScore < 60 ? true : false; } int main() { // 待处理的成绩数据 vector<int> vecScores = {54, -1, 92, 49, 5, 67}; // 从容器中删除缺考成绩-1 // remove() 算法实际上是将待删除的数据移动到容器末尾来实现删除 // 因此,这里要用 itend 保存 remove() 算法返回的 vecScores 容器新的末尾位置 // 这样,从 vecScores.begin() 到 itend 界定了容器中有效数据的范围 // 而在 itend 之后, vecScores.end() 之前的是被删除的无效数据 auto itend = remove(vecScores.begin(), vecScores.end(), -1); // 需要删除的元素 replace_if(vecScores.begin(), // 开始位置 itend, // vecScores 新的结束位置,也可以使用 end() 得到此位置 isfail, // 判断数据是否符合替换条件 60); // 替换后的数据 // 输出处理后的数据 for(auto it = vecScores.begin(); it != itend; ++it) { cout<<*it<<endl; } return 0; }
C++判断容器中是否存在符合目标数据
使用 find_if() 算法可以从容器中找到符合某个条件的数据,然而有的时候,我们只是想知道容器中是否存在这样的数据,而并不想访问这些数据。为此,STL 提供了多个算法来判断容器中是否存在符合某个条件的数据。例如:
- all_of() 算法可以判断容器中的所有数据是否都符合某个条件;
- any_of() 算法可以判断容器中是否存在至少一个数据符合条件;
- none_of() 算法可以判断容器中的所有数据是否都不符合条件。
有了这些算法的帮助,我们可以更加方便地对容器中的数据进行判断。例如,我们可以使用 any_of() 算法来判断保存学生成绩的容器中是否存在不及格的分数:
// 用 any_of 算法判断处理后的成绩是否还有不及格的分数 bool bFail = any_of(vecScores.begin(), itend, isfail); // 用 isfail() 函数判断分数是否及格 if(bFail) { cout<<"还有不及格的分数"<<endl; } else { cout<<"所有分数都及格"<<endl; }