Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Algorithms

C++ 标准库中的算法 (Algorithms) 是一组强大的函数模板,用于对容器或其他范围([first, last))内的元素进行搜索、排序、计数、修改等操作。自 C++20 以来,Ranges (约束算法) 的引入极大地简化了算法的使用,并增强了其通用性。C++17 则引入了执行策略,允许算法进行并行或乱序执行以提升性能。

C++20 约束算法 (Constrained Algorithms / Ranges)

Ranges 是 C++20 引入的一项革命性特性,它极大地简化了 STL 算法的使用,并增强了它们的组合性。

核心概念与优势

特性优势和示例
单一 Range 参数您不再需要手动传递 begin()end() 迭代器。只需将整个容器(或 Range 视图)作为一个参数传递给算法即可。
Projections (投影)简化复杂对象操作。 算法可以直接操作容器内元素的某个成员变量或计算结果,而无需使用复杂的 Lambda 表达式。例如,对一个 vector<Person> 按照 Person::age 成员进行排序。
返回类型增强提供所有有用的信息。 大多数 std::ranges:: 算法不再只返回一个迭代器,而是返回一个包含所有相关结果的结构体(如 std::ranges::sort_result)。例如,ranges::copy 会返回输入范围和输出范围的结束迭代器,方便后续操作。
更强的约束它们是“约束算法”,这意味着它们使用 C++20 的 Concepts 确保传入的迭代器和 Range 满足算法所需的最低要求,从而提供更清晰的编译错误

示例:

std::vector<int> v{7, 1, 4, 0, -1};

// 经典算法:需要显式传入begin()和end()
std::sort(v.begin(), v.end());

// C++20 Ranges 算法:更简洁
std::ranges::sort(v);

C++17/C++20 执行策略 (Execution Policies)

执行策略 是 C++17 引入的功能,它允许程序员通过向算法传递一个策略对象,来指示算法如何执行——是串行、并行还是乱序执行,从而在多核 CPU 上实现性能优化

核心策略类型

策略类型全局对象C++ 版本描述
sequenced_policyseqC++17序列化执行:算法按顺序执行,不会使用并行或乱序操作。这是所有算法的默认行为。
parallel_policyparC++17并行执行:算法可以在不同的线程中并发执行。它保证了并发性,但不保证指令的顺序。
parallel_unsequenced_policypar_unseqC++17并行且乱序执行:结合了并行和乱序执行,允许并发执行,同时允许编译器进行矢量化 (vectorization) 优化。
unsequenced_policyunseqC++20乱序执行不保证并行,但允许编译器对单个线程中的操作进行乱序处理(矢量化),以提高性能。

头文件: 所有执行策略都定义在 <execution> 头文件中,并位于 std::execution 命名空间下(尽管全局对象如 std::parstd 命名空间)。

示例:

#include <algorithm>
#include <execution>
#include <vector>

std::vector<int> data = ...;

// 使用并行策略对数据进行排序,以利用多核CPU
std::sort(std::execution::par, data.begin(), data.end());

// C++20 Ranges 版本也支持策略(如果算法有重载)
std::ranges::sort(std::execution::par, data);

并行算法的技术限制

在使用执行策略时,有一个关键的安全限制需要注意:

对于大多数并行算法(除了 std::for_eachstd::for_each_n):

  • 如果 Range 中的元素类型 \( T \) 满足 Trivial Copy Construction (std::is_trivially_copy_constructible_v<T> == true) 和 Trivial Destruction (std::is_trivially_destructible_v<T> == true),库可以对元素进行任意复制,以便在不同的并行线程之间分发数据。

这意味着: 如果你的自定义对象具有非平凡(复杂)的构造函数、析构函数或资源管理,使用并行策略时需要额外小心,以确保你的操作是线程安全的。对于基本类型(如 int, float)和简单的 C 结构体,这通常不是问题。

一、非修改序列操作 (Non-modifying sequence operations)

主要头文件:

  • <algorithm>:包含绝大多数经典的非修改序列操作。
  • <ranges>:包含所有 ranges:: 版本的算法。

批处理操作 (Batch operations)

批处理操作 (Batch operations)C++ 版本头文件描述 (功能)
for_each / ranges::for_each(C++20)<algorithm> / <ranges>对范围内的元素应用一个一元函数对象
for_each_n / ranges::for_each_n(C++17) / (C++20)<algorithm> / <ranges>对序列的前 \( N \) 个元素应用一个函数对象

搜索操作 (Search operations)

搜索操作 (Search operations)C++ 版本头文件描述 (功能)
all_of / ranges::all_of(C++11) / (C++20)<algorithm> / <ranges>检查一个谓词对范围内的所有元素是否为真
any_of / ranges::any_of(C++11) / (C++20)<algorithm> / <ranges>检查一个谓词对范围内的任一元素是否为真
none_of / ranges::none_of(C++11) / (C++20)<algorithm> / <ranges>检查一个谓词对范围内的所有元素是否为假
ranges::contains(C++23)<ranges>检查范围是否包含给定元素
ranges::contains_subrange(C++23)<ranges>检查范围是否包含给定子范围
find / ranges::find(C++20)<algorithm> / <ranges>查找满足特定条件的第一个元素
find_if / ranges::find_if(C++11) / (C++20)<algorithm> / <ranges>查找满足谓词第一个元素
find_if_not / ranges::find_if_not(C++11) / (C++20)<algorithm> / <ranges>查找不满足谓词第一个元素
ranges::find_last(C++23)<ranges>查找满足特定条件的最后一个元素
ranges::find_last_if(C++23)<ranges>查找满足谓词最后一个元素
ranges::find_last_if_not(C++23)<ranges>查找不满足谓词最后一个元素
find_end / ranges::find_end(C++20)<algorithm> / <ranges>查找某一范围内的最后一个序列
find_first_of / ranges::find_first_of(C++20)<algorithm> / <ranges>搜索一组元素中的任意一个
adjacent_find / ranges::adjacent_find(C++20)<algorithm> / <ranges>查找第一个两个相邻且相等的项(或满足给定谓词)
count / ranges::count(C++20)<algorithm> / <ranges>返回满足特定条件的元素的数量
count_if / ranges::count_if(C++20)<algorithm> / <ranges>返回满足谓词的元素的数量
mismatch / ranges::mismatch(C++20)<algorithm> / <ranges>查找两个范围第一次不同的位置
equal / ranges::equal(C++20)<algorithm> / <ranges>确定两组元素是否相同
search / ranges::search(C++20)<algorithm> / <ranges>搜索一个元素范围的第一次出现
search_n / ranges::search_n(C++20)<algorithm> / <ranges>搜索一个元素在范围中连续出现 \( N \) 次的第一次出现
ranges::starts_with(C++23)<ranges>检查一个范围是否以另一个范围开始
ranges::ends_with(C++23)<ranges>检查一个范围是否以另一个范围结束

折叠操作 (Fold operations)

折叠操作 (Fold operations)C++ 版本头文件描述 (功能)
ranges::fold_left(C++23)<ranges>对一系列元素进行左折叠
ranges::fold_left_first(C++23)<ranges>使用第一个元素作为初始值对一系列元素进行左折叠
ranges::fold_right(C++23)<ranges>对一系列元素进行右折叠
ranges::fold_right_last(C++23)<ranges>使用最后一个元素作为初始值对一系列元素进行右折叠
ranges::fold_left_with_iter(C++23)<ranges>对一系列元素进行左折叠,并返回 (迭代器, 值) 对
ranges::fold_left_first_with_iter(C++23)<ranges>使用第一个元素作为初始值对一系列元素进行左折叠,并返回 (迭代器, 可选值) 对

二、修改序列操作 (Modifying sequence operations)

主要头文件:

  • <algorithm>:包含绝大多数经典的修改序列操作。
  • <ranges>:包含所有 ranges:: 版本的算法。
  • <utility>:包含 std::swapstd::iter_swap

复制操作 (Copy operations)

复制操作 (Copy operations)C++ 版本头文件描述 (功能)
copy / ranges::copy(C++11) / (C++20)<algorithm> / <ranges>将一个元素范围复制到一个新位置
copy_if / ranges::copy_if(C++11) / (C++20)<algorithm> / <ranges>有条件地复制一个元素范围到新位置
copy_n / ranges::copy_n(C++11) / (C++20)<algorithm> / <ranges>指定数量的元素复制到一个新位置
copy_backward / ranges::copy_backward(C++20)<algorithm> / <ranges>向后顺序复制一个元素范围
move / ranges::move(C++11) / (C++20)<algorithm> / <ranges>将一个元素范围移动到一个新位置
move_backward / ranges::move_backward(C++11) / (C++20)<algorithm> / <ranges>向后顺序移动一个元素范围

交换操作 (Swap operations)

交换操作 (Swap operations)C++ 版本头文件描述 (功能)
swap(C++11)<utility>交换两个对象的值
swap_ranges / ranges::swap_ranges(C++20)<algorithm> / <ranges>交换两个元素范围
iter_swap<utility>交换两个迭代器所指向的元素

变换操作 (Transformation operations)

变换操作 (Transformation operations)C++ 版本头文件描述 (功能)
transform / ranges::transform(C++20)<algorithm> / <ranges>对一个元素范围应用一个函数,并将结果存储在目标范围中
replace / ranges::replace(C++20)<algorithm> / <ranges>将所有满足特定条件的值替换为另一个值
replace_if / ranges::replace_if(C++20)<algorithm> / <ranges>将所有满足谓词的值替换为另一个值
replace_copy / ranges::replace_copy(C++20)<algorithm> / <ranges>复制一个范围,并将满足特定条件的元素替换为另一个值
replace_copy_if / ranges::replace_copy_if(C++20)<algorithm> / <ranges>复制一个范围,并将满足谓词的元素替换为另一个值

生成操作 (Generation operations)

生成操作 (Generation operations)C++ 版本头文件描述 (功能)
fill / ranges::fill(C++20)<algorithm> / <ranges>将给定值赋值给范围内的每个元素
fill_n / ranges::fill_n(C++20)<algorithm> / <ranges>将给定值赋值给范围内的 \( N \) 个元素
generate / ranges::generate(C++20)<algorithm> / <ranges>将连续函数调用的结果赋值给范围内的每个元素
generate_n / ranges::generate_n(C++20)<algorithm> / <ranges>将连续 \( N \) 次函数调用的结果赋值给范围内的 \( N \) 个元素

移除操作 (Removing operations)

这里的“移除”通常是逻辑移除,通过将未被移除的元素移动到范围的前部来实现,并返回新的逻辑尾部。

移除操作 (Removing operations)C++ 版本头文件描述 (功能)
remove / ranges::remove(C++20)<algorithm> / <ranges>移除满足特定条件的元素(逻辑移除)
remove_if / ranges::remove_if(C++20)<algorithm> / <ranges>移除满足谓词的元素(逻辑移除)
remove_copy / ranges::remove_copy(C++20)<algorithm> / <ranges>复制一个范围,省略满足特定条件的元素
remove_copy_if / ranges::remove_copy_if(C++20)<algorithm> / <ranges>复制一个范围,省略满足谓词的元素
unique / ranges::unique(C++20)<algorithm> / <ranges>移除范围内的连续重复元素(逻辑移除)
unique_copy / ranges::unique_copy(C++20)<algorithm> / <ranges>创建一个不含连续重复元素的范围副本

顺序改变操作 (Order-changing operations)

顺序改变操作 (Order-changing operations)C++ 版本头文件描述 (功能)
reverse / ranges::reverse(C++20)<algorithm> / <ranges>反转范围内的元素顺序
reverse_copy / ranges::reverse_copy(C++20)<algorithm> / <ranges>创建一个反转后的范围副本
rotate / ranges::rotate(C++20)<algorithm> / <ranges>旋转范围内的元素顺序
rotate_copy / ranges::rotate_copy(C++20)<algorithm> / <ranges>复制旋转一个元素范围
shift_left / ranges::shift_left(C++20) / (C++23)<algorithm> / <ranges>左移范围内的元素
shift_right / ranges::shift_right(C++20) / (C++23)<algorithm> / <ranges>右移范围内的元素
shuffle / ranges::shuffle(C++11) / (C++20)<algorithm> / <ranges>随机重新排序范围内的元素

采样操作 (Sampling operations)

采样操作 (Sampling operations)C++ 版本头文件描述 (功能)
sample / ranges::sample(C++17) / (C++20)<algorithm> / <ranges>从序列中选择 \( N \) 个随机元素

主要头文件:

  • <algorithm>:包含绝大多数经典的排序及相关操作。
  • <ranges>:包含所有 ranges:: 版本的算法。

分区操作 (Partitioning operations)

分区操作 (Partitioning operations)C++ 版本头文件描述 (功能)
is_partitioned / ranges::is_partitioned(C++11) / (C++20)<algorithm> / <ranges>确定范围是否被给定谓词分区
partition / ranges::partition(C++20)<algorithm> / <ranges>将一个元素范围划分为两组
partition_copy / ranges::partition_copy(C++11) / (C++20)<algorithm> / <ranges>复制一个范围,将元素划分为两组
stable_partition / ranges::stable_partition(C++20)<algorithm> / <ranges>将元素划分为两组,同时保持它们的相对顺序
partition_point / ranges::partition_point(C++11) / (C++20)<algorithm> / <ranges>定位一个已分区范围的分区点

排序操作 (Sorting operations)

排序操作 (Sorting operations)C++ 版本头文件描述 (功能)
sort / ranges::sort(C++20)<algorithm> / <ranges>将一个范围排序为升序
stable_sort / ranges::stable_sort(C++20)<algorithm> / <ranges>排序一个元素范围,同时保持相等元素间的相对顺序
partial_sort / ranges::partial_sort(C++20)<algorithm> / <ranges>排序一个范围的前 \( N \) 个元素
partial_sort_copy / ranges::partial_sort_copy(C++20)<algorithm> / <ranges>复制部分排序一个元素范围
is_sorted / ranges::is_sorted(C++11) / (C++20)<algorithm> / <ranges>检查一个范围是否已排序为升序
is_sorted_until / ranges::is_sorted_until(C++11) / (C++20)<algorithm> / <ranges>查找最大已排序子范围
nth_element / ranges::nth_element(C++20)<algorithm> / <ranges>部分排序给定范围,确保它被给定元素分区

二分搜索操作 (Binary search operations)

这些操作要求输入范围必须是已排序的。

二分搜索操作 (Binary search operations)C++ 版本头文件描述 (功能)
lower_bound / ranges::lower_bound(C++20)<algorithm> / <ranges>返回第一个不小于给定值的元素的迭代器
upper_bound / ranges::upper_bound(C++20)<algorithm> / <ranges>返回第一个大于给定值的元素的迭代器
equal_range / ranges::equal_range(C++20)<algorithm> / <ranges>返回匹配特定键的元素的范围
binary_search / ranges::binary_search(C++20)<algorithm> / <ranges>确定一个元素是否存在于一个部分有序的范围中

集合操作 (Set operations)

这些操作要求输入范围必须是已排序的。

集合操作 (Set operations)C++ 版本头文件描述 (功能)
includes / ranges::includes(C++20)<algorithm> / <ranges>如果一个序列是另一个序列的子序列则返回 true
set_union / ranges::set_union(C++20)<algorithm> / <ranges>计算两个集合的并集
set_intersection / ranges::set_intersection(C++20)<algorithm> / <ranges>计算两个集合的交集
set_difference / ranges::set_difference(C++20)<algorithm> / <ranges>计算两个集合的差集
set_symmetric_difference / ranges::set_symmetric_difference(C++20)<algorithm> / <ranges>计算两个集合的对称差集

合并操作 (Merge operations)

合并操作 (Merge operations)C++ 版本头文件描述 (功能)
merge / ranges::merge(C++20)<algorithm> / <ranges>合并两个已排序的范围
inplace_merge / ranges::inplace_merge(C++20)<algorithm> / <ranges>原地合并两个有序范围

堆操作 (Heap operations)

这些操作用于维护最大堆 (max heap) 的属性。

堆操作 (Heap operations)C++ 版本头文件描述 (功能)
push_heap / ranges::push_heap(C++20)<algorithm> / <ranges>最大堆中添加一个元素
pop_heap / ranges::pop_heap(C++20)<algorithm> / <ranges>最大堆中移除最大的元素
make_heap / ranges::make_heap(C++20)<algorithm> / <ranges>将一个元素范围创建最大堆
sort_heap / ranges::sort_heap(C++20)<algorithm> / <ranges>将一个最大堆转换为一个升序排序的元素范围
is_heap / ranges::is_heap(C++11) / (C++20)<algorithm> / <ranges>检查给定范围是否为最大堆
is_heap_until / ranges::is_heap_until(C++11) / (C++20)<algorithm> / <ranges>查找作为最大堆最大子范围

最小/最大操作 (Minimum/maximum operations)

最小/最大操作 (Minimum/maximum operations)C++ 版本头文件描述 (功能)
max / ranges::max(C++20)<algorithm> / <ranges>返回给定值中的较大者
max_element / ranges::max_element(C++20)<algorithm> / <ranges>返回范围中的最大元素
min / ranges::min(C++20)<algorithm> / <ranges>返回给定值中的较小者
min_element / ranges::min_element(C++20)<algorithm> / <ranges>返回范围中的最小元素
minmax / ranges::minmax(C++11) / (C++20)<algorithm> / <ranges>返回两个元素中的较小者较大者
minmax_element / ranges::minmax_element(C++11) / (C++20)<algorithm> / <ranges>返回范围中的最小最大元素
clamp / ranges::clamp(C++17) / (C++20)<algorithm> / <ranges>将一个值钳制在一对边界值之间

字典序比较操作 (Lexicographical comparison operations)

字典序比较操作 (Lexicographical comparison operations)C++ 版本头文件描述 (功能)
lexicographical_compare / ranges::lexicographical_compare(C++20)<algorithm> / <ranges>如果一个范围字典序上小于另一个,则返回 true
lexicographical_compare_three_way(C++20)<algorithm>使用三路比较比较两个范围

排列操作 (Permutation operations)

排列操作 (Permutation operations)C++ 版本头文件描述 (功能)
next_permutation / ranges::next_permutation(C++20)<algorithm> / <ranges>生成范围元素的下一个更大的字典序排列
prev_permutation / ranges::prev_permutation(C++20)<algorithm> / <ranges>生成范围元素的下一个更小的字典序排列
is_permutation / ranges::is_permutation(C++11) / (C++20)<algorithm> / <ranges>确定一个序列是否是另一个序列的排列

四、数值操作 (Numeric operations)

主要头文件:

  • <numeric>:包含所有传统的数值算法和 C++17 的并行数值算法。
  • <ranges>:包含 ranges::iota
数值操作 (Numeric operations)C++ 版本头文件描述 (功能)
iota / ranges::iota(C++11) / (C++23)<numeric> / <ranges>递增的起始值填充一个范围
accumulate<numeric>对一个元素范围求和折叠
inner_product<numeric>计算两个元素范围的内积
adjacent_difference<numeric>计算一个范围中相邻元素之间的差值
partial_sum<numeric>计算一个元素范围的部分和
reduce(C++17)<numeric>类似于 std::accumulate,但无序
exclusive_scan(C++17)<numeric>类似于 std::partial_sum,但不包括第 \( N \) 个输入元素在第 \( N \) 个和中
inclusive_scan(C++17)<numeric>类似于 std::partial_sum包括第 \( N \) 个输入元素在第 \( N \) 个和中
transform_reduce(C++17)<numeric>应用一个可调用对象,然后无序归约
transform_exclusive_scan(C++17)<numeric>应用一个可调用对象,然后计算独占扫描
transform_inclusive_scan(C++17)<numeric>应用一个可调用对象,然后计算包含扫描

五、未初始化内存操作 (Operations on uninitialized memory)

主要头文件:

  • <memory>:包含所有未初始化内存操作。
未初始化内存操作 (Operations on uninitialized memory)C++ 版本头文件描述 (功能)
uninitialized_copy / ranges::uninitialized_copy(C++20)<memory>将一系列对象复制到一块未初始化内存区域
uninitialized_copy_n / ranges::uninitialized_copy_n(C++11) / (C++20)<memory>指定数量的对象复制到一块未初始化内存区域
uninitialized_fill / ranges::uninitialized_fill(C++20)<memory>将一个对象复制赋值给一块未初始化内存区域
uninitialized_fill_n / ranges::uninitialized_fill_n(C++20)<memory>将一个对象复制赋值指定数量未初始化内存区域
uninitialized_move / ranges::uninitialized_move(C++17) / (C++20)<memory>将一系列对象移动到一块未初始化内存区域
uninitialized_move_n / ranges::uninitialized_move_n(C++17) / (C++20)<memory>指定数量的对象移动到一块未初始化内存区域
uninitialized_default_construct / ranges::uninitialized_default_construct(C++17) / (C++20)<memory>在一块未初始化内存区域中默认构造对象
uninitialized_default_construct_n / ranges::uninitialized_default_construct_n(C++17) / (C++20)<memory>在一块未初始化内存区域中默认构造 \( N \) 个对象
uninitialized_value_construct / ranges::uninitialized_value_construct(C++17) / (C++20)<memory>在一块未初始化内存区域中值构造对象
uninitialized_value_construct_n / ranges::uninitialized_value_construct_n(C++17) / (C++20)<memory>在一块未初始化内存区域中值构造 \( N \) 个对象
destroy / ranges::destroy(C++17) / (C++20)<memory>销毁一系列对象
destroy_n / ranges::destroy_n(C++17) / (C++20)<memory>销毁范围内的 \( N \) 个对象
destroy_at / ranges::destroy_at(C++17) / (C++20)<memory>销毁给定地址处的对象
construct_at / ranges::construct_at(C++20)<memory>在给定地址处创建一个对象

六、其他算法和工具

随机数生成 (Random number generation)

主要头文件:

  • <ranges>
随机数生成 (Random number generation)C++ 版本头文件描述 (功能)
ranges::generate_random(C++26)<ranges>均匀随机位生成器填充一个随机数范围

C 库函数 (C library functions)

主要头文件:

  • <cstdlib> (或 <stdlib.h>)
C 库函数 (C library functions)C++ 版本头文件描述 (功能)
qsort<cstdlib>排序一个类型未指定的元素范围
bsearch<cstdlib>搜索一个类型未指定的数组中的元素

附注:执行策略 (Execution policies)

主要头文件:

  • <execution>
执行策略类型 (Execution policy types)宏/类/对象C++ 版本头文件描述 (功能)
sequenced_policy (seq)类 / 全局对象(C++17)<execution>序列化执行(无并行)
parallel_policy (par)类 / 全局对象(C++17)<execution>并行执行
parallel_unsequenced_policy (par\_unseq)类 / 全局对象(C++17)<execution>并行乱序执行
unsequenced_policy (unseq)类 / 全局对象(C++20)<execution>乱序执行(允许编译器向量化)
is_execution_policy类模板(C++17)<execution>测试一个类是否表示一个执行策略