10.2 初识泛型算法
在C++标准库中,泛型算法是用于操作容器元素的一组通用函数。这些算法独立于特定的容器类型,只要容器提供了适当的迭代器,它们就可以作用于任何容器。泛型算法使得代码更加简洁和易于维护。
10.2.1 使用算法的基本步骤
使用泛型算法的一般步骤如下:
- 选择合适的算法:根据具体需求选择一个合适的算法。
- 提供迭代器范围:算法通常需要两个迭代器参数,表示操作的范围。
- 提供额外的参数:有些算法需要额外的参数,如函数对象或初始值。
示例代码
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// 使用for_each算法打印每个元素
std::for_each(vec.begin(), vec.end(), [](int n) {
std::cout << n << " ";
});
std::cout << std::endl;
// 使用find算法查找某个元素
auto it = std::find(vec.begin(), vec.end(), 3);
if (it != vec.end()) {
std::cout << "Found: " << *it << std::endl;
} else {
std::cout << "Not found" << std::endl;
}
return 0;
}
10.2.2 非修改算法
非修改算法不改变容器中的元素值,只对元素进行访问操作。常见的非修改算法有:
for_each:对范围内的每个元素执行给定的操作。find:在范围内查找第一个等于给定值的元素。count:计算范围内等于给定值的元素个数。accumulate:对范围内的元素进行累加。
示例代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// 使用count算法计算3的个数
int count = std::count(vec.begin(), vec.end(), 3);
std::cout << "Count of 3: " << count << std::endl;
// 使用accumulate算法计算所有元素的和
int sum = std::accumulate(vec.begin(), vec.end(), 0);
std::cout << "Sum of elements: " << sum << std::endl;
return 0;
}
10.2.3 修改算法
修改算法会改变容器中的元素值。常见的修改算法有:
copy:将范围内的元素复制到另一个范围。transform:对范围内的每个元素应用给定的操作,并将结果保存到另一个范围。replace:将范围内等于某个值的元素替换为另一个值。fill:将范围内的所有元素设置为给定值。
示例代码
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
std::vector<int> vec2(5);
// 使用copy算法复制元素
std::copy(vec.begin(), vec.end(), vec2.begin());
std::for_each(vec2.begin(), vec2.end(), [](int n) {
std::cout << n << " ";
});
std::cout << std::endl;
// 使用replace算法将所有3替换为99
std::replace(vec2.begin(), vec2.end(), 3, 99);
std::for_each(vec2.begin(), vec2.end(), [](int n) {
std::cout << n << " ";
});
std::cout << std::endl;
return 0;
}
10.2.4 复杂算法
复杂算法包括排序、搜索和集合操作等。常见的复杂算法有:
sort:对范围内的元素进行排序。unique:移除范围内相邻的重复元素。reverse:反转范围内元素的顺序。binary_search:在有序范围内进行二分查找。
示例代码
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {5, 2, 3, 1, 4};
// 使用sort算法排序
std::sort(vec.begin(), vec.end());
std::for_each(vec.begin(), vec.end(), [](int n) {
std::cout << n << " ";
});
std::cout << std::endl;
// 使用binary_search算法查找元素
bool found = std::binary_search(vec.begin(), vec.end(), 3);
std::cout << "3 found: " << std::boolalpha << found << std::endl;
return 0;
}
重点与难点分析
重点:
- 理解非修改算法和修改算法的区别:掌握不同算法的用途和使用场景。
- 熟悉常用算法的用法:了解常用算法的参数和返回值,能够在实际编程中灵活使用。
难点:
- 自定义操作的实现:很多算法需要通过函数对象(如lambda表达式)来自定义操作,理解这些函数对象的使用是一个难点。
- 迭代器失效问题:在使用修改算法时,要特别注意迭代器失效的问题,尤其是在对容器进行修改的算法中。
练习题解析
- 练习10.4:编写一个程序,使用
count算法计算一个vector中某个元素出现的次数。
-
- 示例代码:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 3, 3, 5};
int count = std::count(vec.begin(), vec.end(), 3);
std::cout << "Count of 3: " << count << std::endl;
return 0;
}
- 练习10.5:编写一个程序,使用
replace算法将一个vector中的某个元素替换为另一个值。
-
- 示例代码:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
std::replace(vec.begin(), vec.end(), 3, 99);
std::for_each(vec.begin(), vec.end(), [](int n) {
std::cout << n << " ";
});
std::cout << std::endl;
return 0;
}
- 练习10.6:编写一个程序,使用
sort算法对一个vector进行排序,并使用binary_search算法在排序后的vector中查找某个元素。
-
- 示例代码:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> vec = {5, 2, 3, 1, 4};
std::sort(vec.begin(), vec.end());
std::for_each(vec.begin(), vec.end(), [](int n) {
std::cout << n << " ";
});
std::cout << std::endl;
bool found = std::binary_search(vec.begin(), vec.end(), 3);
std::cout << "3 found: " << std::boolalpha << found << std::endl;
return 0;
}
总结与提高
本节总结:
- 了解了泛型算法的基本概念和用途,掌握了非修改算法和修改算法的区别。
- 学会了如何选择和使用合适的算法,理解了常用算法的参数和返回值。
- 掌握了一些常用的复杂算法的使用方法,并通过示例代码进行了实践。
提高建议:
- 多练习算法操作:通过编写更多涉及泛型算法的程序,熟悉各种操作方法,提高对泛型算法的使用能力。
- 深入理解算法:通过阅读标准库文档和相关书籍,深入理解泛型算法的实现原理和使用场景,提高编写高效代码的能力。
- 关注性能优化:在实际项目中,根据具体需求选择最合适的算法和迭代器类型,优化程序性能。
10.3 定制操作
在泛型算法中,许多算法可以通过函数对象(function object)或lambda表达式定制操作行为。这些定制操作使得算法更为灵活和强大。本节将介绍如何使用函数对象和lambda表达式来定制算法操作。
10.3.1 使用函数对象
函数对象是一个定义了operator()的类或结构体的实例。它的作用类似于一个函数,可以在算法中被调用。函数对象比普通函数具有更多的灵活性,因为它们可以在对象中保存状态。
示例代码
#include <iostream>
#include <vector>
#include <algorithm>
struct Print {
void operator()(int n) const {
std::cout << n << " ";
}
};
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// 使用函数对象Print打印元素
std::for_each(vec.begin(), vec.end(), Print());
std::cout << std::endl;
return 0;
}
在这个示例中,Print是一个函数对象,它实现了operator(),可以像函数一样被调用。

最低0.47元/天 解锁文章
495

被折叠的 条评论
为什么被折叠?



