STL(标准模板库)的算法组件是C++泛型编程的核心之一,它们以函数模板形式提供,通过迭代器解耦了容器与操作逻辑。在实际工程中,合理运用这些算法不仅能缩短代码量,还能借助标准库经过充分测试的实现来降低缺陷率。常见算法集中在排序、查找、遍历与变换几类,下面通过具体用法逐一说明。

排序类算法的常见用法
排序是开发中最频繁遇到的需求之一。STL提供了sort、stable_sort与partial_sort等接口。其中sort采用内省排序,平均时间复杂度为O(n log n),但不保证相等元素的原有顺序;若需要稳定排序,应使用stable_sort。
对于自定义类型或特殊顺序,可以传入函数对象或lambda表达式作为比较器。下面的例子演示了对整数向量做降序排列,以及对结构体按成员排序:
#include <iostream>
#include <vector>
#include <algorithm>
struct Student {
int id;
int score;
};
int main() {
std::vector<int> nums = {5, 2, 9, 1, 5, 6};
// 降序排序
std::sort(nums.begin(), nums.end(), [](int a, int b) {
return a > b;
});
for (int v : nums) {
std::cout << v << " ";
}
std::cout << std::endl;
std::vector<Student> stus = {{1, 80}, {2, 95}, {3, 80}};
// 按分数降序,分数相同按id升序
std::stable_sort(stus.begin(), stus.end(), [](const Student& a, const Student& b) {
if (a.score != b.score) return a.score > b.score;
return a.id < b.id;
});
return 0;
}
使用排序算法时要注意,被排序的区间必须是随机访问迭代器(如vector、deque),list容器应使用其成员函数sort。另外,比较器必须满足严格弱序关系,否则会导致未定义行为。
当只需要前k个最大元素而无需全排时,partial_sort会更高效。它在内部将区间前k个元素排好序,其余元素不保证顺序,适合排行榜截取等场景。
查找类算法的使用方式
STL的查找算法包括find、find_if、binary_search、lower_bound与upper_bound。线性查找find适用于未排序区间,时间复杂度O(n);若数据已排序,应使用二分查找系列以获得O(log n)性能。
以下代码展示了在未排序向量中查找偶数,以及在已排序向量中用lower_bound定位首个不小于目标值的元素:
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6};
// 查找第一个偶数
auto it = std::find_if(v.begin(), v.end(), [](int x) {
return x % 2 == 0;
});
if (it != v.end()) {
std::cout << "first even: " << *it << std::endl;
}
std::vector<int> sorted = {1, 2, 2, 3, 4, 5};
// 已排序区间的二分查找
auto pos = std::lower_bound(sorted.begin(), sorted.end(), 2);
std::cout << "lower_bound of 2 at index: " << (pos - sorted.begin()) << std::endl;
return 0;
}
binary_search仅返回是否存在,而lower_bound与upper_bound能给出插入位置,二者结合可得到等于某值的所有元素区间。对于频繁查询的场景,先排序再使用二分算法比反复线性查找有明显性能优势。
需要注意,二分类算法要求区间已按对应比较规则排序,且比较规则须与排序时一致,否则结果不可预期。若使用自定义比较器,排序和查找都应传入相同逻辑。
遍历与变换算法的实践
遍历类算法以for_each为代表,它将区间每个元素传给指定函数。配合lambda可替代手写循环,使意图更清晰。变换类如transform则把一个区间映射为另一个区间,常用于数据规整。
下面示例用for_each打印元素,并用transform将整数向量每个元素平方后写入新向量:
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
std::vector<int> a = {1, 2, 3, 4};
std::for_each(a.begin(), a.end(), [](int x) {
std::cout << x * x << " ";
});
std::cout << std::endl;
std::vector<int> b(a.size());
std::transform(a.begin(), a.end(), b.begin(), [](int x) {
return x * x;
});
return 0;
}
for_each的返回值是传入的函数对象,若该函数对象有内部状态,可借此收集遍历过程中的统计信息。而transform要求输出区间有足够空间,通常先用resize扩容或直接使用插入迭代器。
在C++17之后,许多算法增加了执行策略参数(如std::execution::par),可在不改变调用形式的情况下启用并行化,对大区间的for_each、sort等提升吞吐量,但需注意线程安全与数据竞争问题。
其他易忽略的实用算法
除了上述三类,STL还提供count、accumulate(在<numeric>中)、unique、reverse等。比如unique配合erase可去除有序区间相邻重复项,accumulate能做求和或自定义折叠。
示例展示如何删除向量中相邻重复值并统计总和:
#include <vector>
#include <algorithm>
#include <numeric>
#include <iostream>
int main() {
std::vector<int> v = {1, 1, 2, 2, 3, 3, 3};
// 先排序保证相同元素相邻
std::sort(v.begin(), v.end());
auto last = std::unique(v.begin(), v.end());
v.erase(last, v.end());
int sum = std::accumulate(v.begin(), v.end(), 0);
std::cout << "sum after unique: " << sum << std::endl;
return 0;
}
这些算法大多语义明确、副作用可控,比手写的临时循环更容易评审与维护。建议开发中遇到容器数据处理时,先查阅标准库是否已提供对应算法,再决定是否自行实现。
总体而言,STL算法通过迭代器抽象屏蔽了容器差异,在排序、查找、遍历等场景都能提供高效且安全的实现。掌握它们的参数约定与复杂度特征,是写出简洁C++代码的重要一步。
C++_STLSTL_algorithms排序查找修改时间:2026-07-31 23:30:46