在C++开发中,标准模板库(STL)提供了丰富的算法,这些算法主要定义在<algorithm>和<numeric>头文件中。作为C++开发者,熟练掌握这些算法能极大提升开发效率和代码质量。STL算法主要分为以下几类:
这些算法通过迭代器与容器交互,具有高度的通用性。理解它们的特性和适用场景,是写出高效C++代码的关键。
查找算法是日常开发中最常用的算法之一,STL提供了多种查找方式:
cpp复制vector<int> nums = {1, 3, 5, 7, 9};
// 基础查找
auto it = find(nums.begin(), nums.end(), 5);
if (it != nums.end()) {
cout << "Found: " << *it << endl;
}
// 条件查找
auto it2 = find_if(nums.begin(), nums.end(), [](int x) {
return x > 6;
});
// 子序列查找
vector<int> sub = {3, 5};
auto it3 = find_end(nums.begin(), nums.end(), sub.begin(), sub.end());
注意:
find_end查找的是子序列最后一次出现的位置,而search查找的是第一次出现的位置。
计数算法用于统计满足特定条件的元素数量:
cpp复制vector<int> vec = {1, 2, 3, 2, 4, 2};
// 统计特定值出现次数
int cnt = count(vec.begin(), vec.end(), 2); // 结果为3
// 统计满足条件的元素数量
int even_cnt = count_if(vec.begin(), vec.end(), [](int x) {
return x % 2 == 0;
}); // 偶数个数,结果为4
for_each是最常用的遍历算法,可以对容器中每个元素执行操作:
cpp复制vector<int> vec = {1, 2, 3, 4, 5};
for_each(vec.begin(), vec.end(), [](int& x) {
x *= 2; // 将每个元素乘以2
});
比较算法用于判断或查找序列间的差异:
cpp复制vector<int> a = {1, 2, 3};
vector<int> b = {1, 2, 4};
// 判断是否相等
bool is_equal = equal(a.begin(), a.end(), b.begin());
// 查找第一个不匹配的元素
auto mis = mismatch(a.begin(), a.end(), b.begin());
if (mis.first != a.end()) {
cout << "First mismatch: " << *mis.first << " vs " << *mis.second << endl;
}
C++11引入的条件检查算法可以简化逻辑判断:
cpp复制vector<int> vec = {2, 4, 6, 8};
// 检查所有元素是否满足条件
bool all_even = all_of(vec.begin(), vec.end(), [](int x) {
return x % 2 == 0;
});
// 检查是否存在满足条件的元素
bool any_odd = any_of(vec.begin(), vec.end(), [](int x) {
return x % 2 != 0;
});
// 检查是否没有元素满足条件
bool none_negative = none_of(vec.begin(), vec.end(), [](int x) {
return x < 0;
});
复制算法需要注意目标容器必须有足够空间:
cpp复制vector<int> src = {1, 2, 3, 4, 5};
vector<int> dest(5); // 必须预先分配空间
// 基本复制
copy(src.begin(), src.end(), dest.begin());
// 条件复制
vector<int> evens;
copy_if(src.begin(), src.end(), back_inserter(evens), [](int x) {
return x % 2 == 0;
});
提示:使用
back_inserter可以自动处理目标容器空间不足的问题,但会带来一定的性能开销。
transform算法可以对元素进行转换:
cpp复制vector<int> nums = {1, 2, 3};
vector<int> squares(3);
// 单序列转换
transform(nums.begin(), nums.end(), squares.begin(), [](int x) {
return x * x;
});
// 双序列转换
vector<int> a = {1, 2, 3};
vector<int> b = {4, 5, 6};
vector<int> sum(3);
transform(a.begin(), a.end(), b.begin(), sum.begin(), plus<int>());
替换算法有多种变体:
cpp复制vector<int> nums = {1, 2, 3, 2, 5};
// 简单替换
replace(nums.begin(), nums.end(), 2, 20);
// 条件替换
replace_if(nums.begin(), nums.end(), [](int x) {
return x > 10;
}, 0);
// 复制时替换
vector<int> res;
replace_copy(nums.begin(), nums.end(), back_inserter(res), 3, 300);
删除算法需要特别注意其实际行为:
cpp复制vector<int> nums = {1, 2, 3, 2, 4};
// remove只是把要删除的元素移到末尾
auto new_end = remove(nums.begin(), nums.end(), 2);
// 需要配合erase才能真正删除
nums.erase(new_end, nums.end());
// 常用的一行式删除
nums.erase(remove_if(nums.begin(), nums.end(), [](int x) {
return x % 2 == 0;
}), nums.end());
unique算法用于去除连续重复元素:
cpp复制vector<int> vec = {1, 1, 2, 2, 3, 3, 3, 4, 5};
auto last = unique(vec.begin(), vec.end());
vec.erase(last, vec.end());
注意:
unique只去除连续的重复元素,如果需要去除所有重复元素,应先排序。
cpp复制// 反转元素
reverse(vec.begin(), vec.end());
// 旋转元素
rotate(vec.begin(), vec.begin() + 2, vec.end());
// 随机打乱
random_device rd;
mt19937 g(rd());
shuffle(vec.begin(), vec.end(), g);
cpp复制vector<int> vec = {5, 3, 1, 4, 2};
// 快速排序(不稳定)
sort(vec.begin(), vec.end());
// 稳定排序
vector<pair<int, int>> pairs = {{1,2}, {2,1}, {1,1}};
stable_sort(pairs.begin(), pairs.end(), [](const auto& a, const auto& b) {
return a.first < b.first;
});
// 部分排序
partial_sort(vec.begin(), vec.begin() + 3, vec.end());
nth_element用于快速找到第n小的元素:
cpp复制vector<int> vec = {5, 3, 1, 4, 2, 6};
nth_element(vec.begin(), vec.begin() + 2, vec.end());
// vec[2]是第三小的元素,左边<=它,右边>=它
二分查找要求容器必须已排序:
cpp复制vector<int> sorted = {1, 3, 3, 5, 7};
bool exists = binary_search(sorted.begin(), sorted.end(), 3);
auto lb = lower_bound(sorted.begin(), sorted.end(), 3); // 第一个>=3的
auto ub = upper_bound(sorted.begin(), sorted.end(), 3); // 第一个>3的
merge可以合并两个已排序的序列:
cpp复制vector<int> a = {1, 3, 5};
vector<int> b = {2, 4, 6};
vector<int> merged(a.size() + b.size());
merge(a.begin(), a.end(), b.begin(), b.end(), merged.begin());
STL提供了一套堆操作算法:
cpp复制vector<int> vec = {4, 1, 3, 2, 5};
// 建堆
make_heap(vec.begin(), vec.end());
// 添加元素
vec.push_back(6);
push_heap(vec.begin(), vec.end());
// 弹出堆顶
pop_heap(vec.begin(), vec.end());
int max_val = vec.back();
vec.pop_back();
// 堆排序
sort_heap(vec.begin(), vec.end());
数值算法定义在<numeric>头文件中:
cpp复制vector<int> vec = {1, 2, 3, 4, 5};
// 累加
int sum = accumulate(vec.begin(), vec.end(), 0);
// 内积
vector<int> a = {1, 2, 3};
vector<int> b = {4, 5, 6};
int dot = inner_product(a.begin(), a.end(), b.begin(), 0);
// 填充递增序列
vector<int> seq(5);
iota(seq.begin(), seq.end(), 10); // 10,11,12,13,14
// 部分和
vector<int> src = {1, 2, 3, 4, 5};
vector<int> dst(src.size());
partial_sum(src.begin(), src.end(), dst.begin());
// 相邻差
adjacent_difference(src.begin(), src.end(), dst.begin());
find、find_iflower_bound、upper_boundtransform、replace、remove+erasesort或stable_sort<numeric>中的算法back_inserter会带来额外的内存分配开销remove需要配合erase使用back_inserter导致性能问题sample算法用于采样shift_left和shift_right算法ranges库提供了更简洁的算法调用方式cpp复制// C++20 ranges示例
#include <ranges>
#include <algorithm>
vector<int> vec = {1, 2, 3, 4, 5};
auto even = vec | views::filter([](int x) { return x % 2 == 0; });
掌握STL算法需要不断实践和总结。建议在实际项目中多尝试使用这些算法,积累经验。对于性能关键的场景,应该进行基准测试,而不是仅凭理论分析。