C++ 标准库 <algorithm> 和 <numeric> 提供了 100+ 个算法,配合迭代器和范围(C++20),覆盖了绝大多数数据处理需求。本文按功能分类系统梳理,附带典型用法与复杂度。
1. 非修改序列操作
只读遍历,不改变元素值。
| 算法 | 作用 | 复杂度 | 典型场景 |
|---|---|---|---|
all_of / any_of / none_of |
全真/任真/全假判断 | O(n) | 校验容器是否满足条件 |
for_each |
对每个元素执行操作 | O(n) | 仅有副作用的遍历(C++20 推荐 ranges::for_each) |
find / find_if / find_if_not |
查找首个匹配元素 | O(n) | 线性查找 |
find_end |
查找子序列最后一次出现 | O(n·m) | 子串/子序列搜索 |
find_first_of |
查找集合中任一元素首次出现 | O(n·m) | 多模式首次匹配 |
adjacent_find |
查找相邻重复元素 | O(n) | 去重前检测 |
count / count_if |
统计匹配元素个数 | O(n) | 频次统计 |
mismatch |
找到两序列首个不匹配位置 | O(min(n,m)) | 差异对比 |
equal |
判断两序列逐元素相等 | O(n) | 容器相等性检查 |
is_permutation |
判断是否为排列关系 | O(n²) 平均 O(n) | 乱序相等性 |
search / search_n |
子序列/连续 n 次值查找 | O(n·m) | 模式匹配 |
C++20 所有算法在
std::ranges命名空间有对应版本,支持投影与范围直接传参,如ranges::find(v, 42)。
2. 修改序列操作
改变元素值或位置。
| 算法 | 作用 | 复杂度 | 备注 |
|---|---|---|---|
copy / copy_if / copy_n / copy_backward |
拷贝元素 | O(n) | 目标空间需预留 |
move / move_backward |
移动元素 | O(n) | C++11,利用移动语义 |
fill / fill_n |
赋固定值 | O(n) | 初始化/重置 |
transform |
一元/二元映射 | O(n) | out = op(in) 或 op(in1, in2) |
generate / generate_n |
用生成器填充 | O(n) | 随机数、序列生成 |
remove / remove_if / remove_copy / remove_copy_if |
移除满足条件元素 | O(n) | 不改变容器大小 ,返回新逻辑末尾,需配合 erase |
replace / replace_if / replace_copy / replace_copy_if |
替换满足条件元素 | O(n) | 就地或拷贝替换 |
swap / swap_ranges / iter_swap |
交换元素/范围 | O(1)/O(n) | 交换两对象或两范围对应位置 |
reverse / reverse_copy |
反转序列 | O(n) | 双向迭代器 |
rotate / rotate_copy |
左旋(首元素移至中间) | O(n) | 循环移位 |
shuffle |
随机打乱 | O(n) | 需 UniformRandomBitGenerator |
unique / unique_copy |
移除相邻重复 | O(n) | 需先排序才能全局去重 |
partition / stable_partition / partition_copy |
按谓词分区 | O(n) / O(n log n) | stable_ 保持相对序 |
is_partitioned |
判断是否已分区 | O(n) | 配合 partition_point 定位分界 |
3. 排序与相关操作
| 算法 | 作用 | 复杂度 | 稳定性 | 适用场景 |
|---|---|---|---|---|
sort |
全排序 | O(n log n) | ❌ | 通用排序 |
stable_sort |
稳定全排序 | O(n log n) / O(n log² n) | ✅ | 需保持等值相对序 |
partial_sort |
前 k 小排序 | O(n log k) | ❌ | Top-K |
partial_sort_copy |
拷贝并部分排序 | O(n log k) | ❌ | 只需前 k 个有序拷贝 |
nth_element |
第 n 小元素归位 | O(n) 平均 | ❌ | 中位数、分位数 |
is_sorted / is_sorted_until |
判断/定位有序区间 | O(n) | - | 预检 |
sort_heap |
堆转有序序列 | O(n log n) | ❌ | 堆排序收尾 |
比较器 :默认
std::less<>(升序),自定义传可调用对象,C++20 支持投影std::ranges::sort(v, {}, &Member)。
4. 二分查找(要求有序区间)
| 算法 | 作用 | 返回值 | 复杂度 |
|---|---|---|---|
binary_search |
判断是否存在 | bool |
O(log n) |
lower_bound |
首个 ≥ val 的位置 | 迭代器 | O(log n) |
upper_bound |
首个 > val 的位置 | 迭代器 | O(log n) |
equal_range |
等值范围 [first, last) |
pair<iter, iter> |
O(log n) |
典型用法:
cpp
auto [first, last] = std::equal_range(v.begin(), v.end(), key);
if (first != last) { /* 存在 */ }
auto idx = std::distance(v.begin(), std::lower_bound(v.begin(), v.end(), key)); // 插入位置/排名
5. 归并操作(要求输入有序)
| 算法 | 作用 | 复杂度 |
|---|---|---|
merge |
归并两有序范围到输出 | O(n+m) |
inplace_merge |
就地归并相邻两有序子范围 | O(n log n) / O(n) 额外内存 |
includes |
判断一个有序范围是否包含另一个 | O(n+m) |
set_union / set_intersection / set_difference / set_symmetric_difference |
集合并/交/差/对称差 | O(n+m) |
输出迭代器需预留足够空间;
set_*算法对重复元素按最大/最小计数处理。
6. 堆操作(随机访问迭代器)
将区间视为最大堆(默认 less,顶端最大)。
| 算法 | 作用 | 复杂度 |
|---|---|---|
make_heap |
建堆 | O(n) |
push_heap |
插入元素(假设新元素已在末尾) | O(log n) |
pop_heap |
取出堆顶(移至末尾) | O(log n) |
sort_heap |
堆转有序序列 | O(n log n) |
is_heap / is_heap_until |
判断/定位堆区间 | O(n) |
优先队列封装 :std::priority_queue 内部即用这组算法。
7. 极值操作
| 算法 | 作用 | 复杂度 | C++ 版本 |
|---|---|---|---|
min / max |
两值极值 | O(1) | C++98 |
minmax |
同时返回极值对 | O(1) | C++11 |
min_element / max_element |
区间极值位置 | O(n) | C++98 |
minmax_element |
同时返回极值位置 | O(1.5n) | C++11 |
clamp |
限制在区间 [lo, hi] |
O(1) | C++17 |
minmax_element单次遍历完成,比分别调用快 ~25%。
8. 字典序比较
| 算法 | 作用 | 复杂度 |
|---|---|---|
lexicographical_compare |
字典序 < 判断 |
O(min(n,m)) |
lexicographical_compare_three_way |
三路比较 (C++20) | O(min(n,m)) |
用于容器/字符串的 < 重载实现。
9. 排列生成
| 算法 | 作用 | 复杂度 | 备注 |
|---|---|---|---|
next_permutation |
生成下一字典序排列 | O(n) | 返回 false 表示已到末尾 |
prev_permutation |
生成上一字典序排列 | O(n) | 配合 sort 可全排列枚举 |
is_permutation |
判断排列关系 | 见 1. | 已列在非修改操作 |
全排列模板:
cpp
std::sort(v.begin(), v.end());
do {
process(v);
} while (std::next_permutation(v.begin(), v.end()));
10. 数值算法 <numeric>
| 算法 | 作用 | 复杂度 | C++ 版本 |
|---|---|---|---|
accumulate |
左折叠求和/累积 | O(n) | C++98 |
reduce |
并行可折叠求和 | O(n) | C++17 |
transform_reduce |
映射后折叠 | O(n) | C++17 |
inner_product |
内积/广义折叠 | O(n) | C++98 |
transform_inclusive_scan / transform_exclusive_scan |
变换前缀和 | O(n) | C++17 |
inclusive_scan / exclusive_scan |
前缀和(含/不含当前) | O(n) | C++17 |
partial_sum |
就地前缀和 | O(n) | C++98 |
adjacent_difference |
相邻差分 | O(n) | C++98 |
iota |
填充递增序列 | O(n) | C++11 |
gcd / lcm |
最大公约数/最小公倍数 | O(log min(a,b)) | C++17 |
midpoint / lerp |
中点/线性插值 | O(1) | C++20 |
并行版本 :C++17 起在
<execution>头下提供std::execution::par策略,如std::reduce(std::execution::par, v.begin(), v.end())自动并行化。
11. C++20 Ranges 算法增强
#include <algorithm> → std::ranges 命名空间,主要改进:
- 直接接受范围 :
ranges::sort(v)无需begin/end。 - 投影 :
ranges::sort(employees, {}, &Employee::salary)。 - 组合视图 :
v | views::filter(pred) | views::transform(op)惰性管道,配合ranges::to<vector>()落地。 - 借用迭代器 :
borrowed_iterator_t避免悬垂。 - 新算法 :
ranges::starts_with、ends_with、contains、find_last、fold_left、fold_right等。
示例:Top-K 去重并按得分降序
cpp
auto topk = data
| views::filter([](auto& e){ return e.valid; })
| views::transform(&Entry::score)
| ranges::to<std::vector>();
ranges::sort(topk, std::greater{});
topk.erase(ranges::unique(topk).begin(), topk.end());
if (topk.size() > K) topk.resize(K);
12. 实战模式速查
| 需求 | 推荐组合 |
|---|---|
| 去重 | sort → erase(unique(...), end) |
| Top-K | nth_element(begin, begin+K, end) → sort(begin, begin+K) |
| 中位数 | nth_element(begin, begin+n/2, end) |
| 计数排序(小整数域) | counting_sort 手写或 transform_reduce 直方图 |
| 多键排序 | sort + 自定义比较器或 tie:[](a,b){ return tie(a.x,a.y) < tie(b.x,b.y); } |
| 流式处理/内存受限 | views 管道 + ranges::fold_left / 输出迭代器 |
| 并行加速 | <execution> + reduce/transform_reduce/sort(数据量大、操作无副作用) |
13. 常见坑与最佳实践
| 坑 | 说明 | 规避 |
|---|---|---|
remove 不删除 |
只覆盖,返回新逻辑末尾 | v.erase(remove(...), v.end()) |
unique 需有序 |
只能去相邻重复 | 先 sort 再 unique |
| 比较器严格弱序 | 必须满足不可反、传递性 | 别写 a <= b,用 a < b |
| 迭代器失效 | vector 扩容/擦除会失效 |
用索引或 list/稳定容器 |
binary_search 只返回 bool |
需位置用 lower_bound |
配合 equal_range 取范围 |
accumulate 初值类型决定推导 |
0 导致整数溢出/截断 |
显式 0.0 或 T{} |
| 并行算法数据竞争 | lambda 捕获引用/副作用 | 保证纯函数、线程局部存储再归约 |
C++20 ranges 需 -std=c++20 且库支持 |
GCC≥10, Clang≥13, MSVC≥19.28 | 检查 __cpp_lib_ranges |
14. 复杂度速查表(核心算法)
| 算法 | 最好 | 平均 | 最坏 | 空间 |
|---|---|---|---|---|
sort |
O(n log n) | O(n log n) | O(n log n) | O(log n) |
stable_sort |
O(n log n) | O(n log n) | O(n log² n) | O(n) |
nth_element |
O(n) | O(n) | O(n²) | O(log n) |
merge |
O(n+m) | O(n+m) | O(n+m) | O(n+m) 输出 |
inplace_merge |
O(n) | O(n log n) | O(n log n) | O(n) / O(1) |
make_heap |
O(n) | O(n) | O(n) | O(1) |
binary_search |
O(1) | O(log n) | O(log n) | O(1) |
set_union/intersection |
O(n+m) | O(n+m) | O(n+m) | O(n+m) 输出 |
15. 扩展阅读
- C++ 参考 :https://en.cppreference.com/w/cpp/algorithm
- 《Effective STL》 Item 43--51 算法选用准则
- 《C++20 标准库》 Rainer Grimm,Ranges 章节
- Abseil / Boost.Range / Range-v3 补充算法与视图
一句话总结:熟练掌握「排序/分区/二分/归并/堆/数值」六大核心族群,配合 C++20 Ranges 与并行执行策略,即可覆盖 95% 以上工程场景的算法需求。