C++ STL 常用算法详解 引言 C++标准模板库(STL)提供了一套强大的通用算法,这些算法独立于具体的容器,通过迭代器与容器交互。熟练掌握这些算法可以大大提高代码的简洁性、可读性和开发效率。本文旨在整理最常用的STL算法,作为日常开发的快速参考手册。
C++20 重要更新 :
引入范围库(Ranges) ,提供新的算法重载(位于 std::ranges 命名空间),支持直接操作范围(如 rng)而非迭代器对,并支持投影(projection)功能。
新增算法:shift_left、shift_right。
许多现有算法在 C++20 中成为 constexpr,允许在编译时计算。
包含头文件 大多数通用算法定义在 <algorithm> 头文件中,部分数值算法在 <numeric> 中,还有一些涉及函数对象的在 <functional> 中。C++20 范围算法也在 <algorithm> 中(通过 std::ranges 命名空间提供),范围视图(views)则在 <ranges> 头文件中。
1 2 3 4 5 #include <algorithm> #include <numeric> #include <functional> #include <ranges>
算法分类
非修改式序列操作 :不改变容器内容,如查找、计数等。
修改式序列操作 :会修改容器元素(但不改变容器大小),如复制、替换、变换等。
排序和相关操作 :排序、二分查找、集合操作等。
数值算法 :求和、内积、部分和等。
C++20 新特性 :范围算法、shift_left/right、constexpr 支持。
非修改式序列操作 for_each
功能 :对序列中的每个元素应用指定函数。
原型 :for_each(InputIt first, InputIt last, UnaryFunction f)
返回值 :返回 f(在C++11后返回移动构造后的 f)。
C++20 :新增 std::ranges::for_each 范围版本,并支持投影。另外 for_each_n(C++17 已引入)在 C++20 中也有范围版本。
示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <iostream> #include <vector> #include <algorithm> void print (int n) { std::cout << n << ' ' ; }int main () { std::vector<int > v = {1 , 2 , 3 , 4 }; std::for_each(v.begin (), v.end (), print); std::for_each(v.begin (), v.end (), [](int &n){ n++; }); std::ranges::for_each(v, print); }
find / find_if
功能 :在范围内查找第一个等于某值(或满足谓词)的元素。
原型 :
find(InputIt first, InputIt last, const T& value)
find_if(InputIt first, InputIt last, UnaryPredicate p)
返回值 :指向第一个匹配元素的迭代器,若未找到则返回 last。
C++20 :std::ranges::find, std::ranges::find_if 支持范围和投影。
示例 :
1 2 3 4 5 6 7 std::vector<int > v = {1 ,2 ,3 ,4 ,5 };auto it = std::find (v.begin (), v.end (), 3 ); auto it2 = std::find_if (v.begin (), v.end (), [](int x){ return x > 3 ; }); auto it3 = std::ranges::find (v, 3 );auto it4 = std::ranges::find_if (v, [](int x){ return x > 3 ; });
count / count_if
功能 :返回范围内等于某值(或满足谓词)的元素个数。
原型 :
count(InputIt first, InputIt last, const T& value)
count_if(InputIt first, InputIt last, UnaryPredicate p)
返回值 :满足条件的元素数量(difference_type)。
C++20 :std::ranges::count, std::ranges::count_if。
示例 :
1 2 3 4 5 6 std::vector<int > v = {1 ,2 ,3 ,4 ,3 ,2 ,1 };int cnt = std::count (v.begin (), v.end (), 3 ); int cnt_if = std::count_if (v.begin (), v.end (), [](int x){ return x % 2 == 0 ; }); auto cnt2 = std::ranges::count (v, 3 );
search
功能 :在范围 [first1, last1) 中查找第一个子序列 [first2, last2) 出现的位置。
原型 :search(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, ForwardIt2 last2)
返回值 :指向子序列起始位置的迭代器,若未找到则返回 last1。
C++20 :std::ranges::search。
示例 :
1 2 3 4 5 6 std::vector<int > v = {1 ,2 ,3 ,4 ,5 ,3 ,4 ,6 }; std::vector<int > sub = {3 ,4 };auto it = std::search (v.begin (), v.end (), sub.begin (), sub.end ()); auto it2 = std::ranges::search (v, sub).begin ();
equal
功能 :比较两个序列是否相等(元素一一对应相同)。
原型 :equal(InputIt1 first1, InputIt1 last1, InputIt2 first2)(假设第二个序列长度至少与第一个相等)
返回值 :若两个范围相等返回 true,否则 false。
C++20 :std::ranges::equal 支持范围和投影,并且可指定结束迭代器。
示例 :
1 2 3 4 5 6 std::vector<int > a = {1 ,2 ,3 }; std::vector<int > b = {1 ,2 ,3 };bool eq = std::equal (a.begin (), a.end (), b.begin ()); bool eq2 = std::ranges::equal (a, b);
修改式序列操作 copy / copy_if
功能 :将范围 [first, last) 中的元素复制到从 d_first 开始的另一范围。
原型 :
copy(InputIt first, InputIt last, OutputIt d_first)
copy_if(InputIt first, InputIt last, OutputIt d_first, UnaryPredicate pred) (C++11)
返回值 :指向目标范围最后被复制元素的下一个位置的迭代器。
C++20 :std::ranges::copy, std::ranges::copy_if,返回更详细的结果对象(包含输入迭代器和输出迭代器)。
示例 :
1 2 3 4 5 6 7 8 9 std::vector<int > src = {1 ,2 ,3 ,4 ,5 };std::vector<int > dest (src.size()) ; std::copy (src.begin (), src.end (), dest.begin ()); std::vector<int > even; std::copy_if (src.begin (), src.end (), std::back_inserter (even), [](int x){ return x%2 ==0 ; }); auto result = std::ranges::copy (src, dest.begin ());
fill
功能 :将指定值赋给范围 [first, last) 中的每个元素。
原型 :fill(ForwardIt first, ForwardIt last, const T& value)
返回值 :无(void)。
C++20 :std::ranges::fill。
示例 :
1 2 3 4 std::vector<int > v (5 ) ; std::fill (v.begin (), v.end (), 42 ); std::ranges::fill (v, 42 );
功能 :对序列中的每个元素应用函数,并将结果写入目标范围(可原地修改)。
原型 :
一元形式:transform(InputIt first1, InputIt last1, OutputIt d_first, UnaryOperation unary_op)
二元形式:transform(InputIt1 first1, InputIt1 last1, InputIt2 first2, OutputIt d_first, BinaryOperation binary_op)
返回值 :指向目标范围最后元素的下一个位置的迭代器。
C++20 :std::ranges::transform,支持范围和投影,返回包含输入和输出迭代器的结果对象。
示例 :
1 2 3 4 5 6 7 8 9 10 11 12 std::vector<int > in = {1 ,2 ,3 };std::vector<int > out (in.size()) ; std::transform (in.begin (), in.end (), out.begin (), [](int x){ return x*x; }); std::ranges::transform (in, out.begin (), [](int x){ return x*x; }); std::vector<int > a = {1 ,2 ,3 }, b = {4 ,5 ,6 }, c (3 ); std::transform (a.begin (), a.end (), b.begin (), c.begin (), std::plus <int >()); std::ranges::transform (a, b, c.begin (), std::plus <int >());
replace / replace_if
功能 :将范围内等于某值(或满足谓词)的元素替换为新值。
原型 :
replace(ForwardIt first, ForwardIt last, const T& old_value, const T& new_value)
replace_if(ForwardIt first, ForwardIt last, UnaryPredicate p, const T& new_value)
返回值 :void。
C++20 :std::ranges::replace, std::ranges::replace_if。
示例 :
1 2 3 4 std::vector<int > v = {1 ,2 ,3 ,2 ,1 }; std::replace (v.begin (), v.end (), 2 , 99 ); std::ranges::replace (v, 2 , 99 );
remove / remove_if
功能 :从范围中移除等于某值(或满足谓词)的元素,但不会改变容器大小 ,它只是将“保留”的元素移动到前面,返回指向新逻辑结尾的迭代器。
原型 :
remove(ForwardIt first, ForwardIt last, const T& value)
remove_if(ForwardIt first, ForwardIt last, UnaryPredicate p)
返回值 :指向新范围末尾的迭代器。
注意 :通常配合容器的 erase 成员函数一起使用(称为 erase-remove 惯用法)。
C++20 :std::ranges::remove, std::ranges::remove_if。
示例 :
1 2 3 4 5 6 7 std::vector<int > v = {1 ,2 ,3 ,2 ,1 };auto new_end = std::remove (v.begin (), v.end (), 2 ); v.erase (new_end, v.end ()); auto result = std::ranges::remove (v, 2 ); v.erase (result.begin (), result.end ());
reverse
功能 :反转范围内元素的顺序。
原型 :reverse(BidirectionalIt first, BidirectionalIt last)
返回值 :void。
C++20 :std::ranges::reverse。
示例 :
1 2 3 4 std::vector<int > v = {1 ,2 ,3 ,4 }; std::reverse (v.begin (), v.end ()); std::ranges::reverse (v);
rotate
功能 :将范围向左旋转,使得 middle 指向的元素成为新范围的第一个元素。
原型 :rotate(ForwardIt first, ForwardIt middle, ForwardIt last)
返回值 :指向原来第一个元素所在新位置的迭代器(C++11后)。
C++20 :std::ranges::rotate。
示例 :
1 2 3 4 std::vector<int > v = {1 ,2 ,3 ,4 ,5 }; std::rotate (v.begin (), v.begin ()+2 , v.end ()); std::ranges::rotate (v, v.begin ()+2 );
unique
功能 :移除范围内连续 的重复元素(只保留第一个)。通常用于排序后的序列。
原型 :unique(ForwardIt first, ForwardIt last),也可以带自定义比较谓词。
返回值 :指向新范围末尾的迭代器。
注意 :不会改变容器大小,需要配合 erase 使用。
C++20 :std::ranges::unique。
示例 :
1 2 3 4 5 6 7 std::vector<int > v = {1 ,1 ,2 ,2 ,3 ,4 ,4 ,5 };auto last = std::unique (v.begin (), v.end ()); v.erase (last, v.end ()); auto result = std::ranges::unique (v); v.erase (result.begin (), result.end ());
C++20 新算法:shift_left / shift_right
功能 :将范围内的元素向左或向右移动 n 个位置。移动后,空出的位置元素变为可移动后的状态(通常被移动后的对象处于有效但未指定的状态)。对于 shift_left,范围的前 n 个元素被移除;对于 shift_right,范围的后 n 个元素被移除。
原型 :
shift_left(ForwardIt first, ForwardIt last, typename std::iterator_traits<ForwardIt>::difference_type n)
shift_right(ForwardIt first, ForwardIt last, typename std::iterator_traits<ForwardIt>::difference_type n)
返回值 :返回新的范围末尾的迭代器(对于 shift_left,返回 last - n;对于 shift_right,返回 first + n)。注意,这个算法不会改变容器大小,只是移动元素。
注意 :要求 n >= 0。如果 n >= last-first,则行为未定义(但某些实现可能返回 first/last)。
C++20 :std::shift_left, std::shift_right。
示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <algorithm> #include <vector> #include <iostream> int main () { std::vector<int > v = {1 ,2 ,3 ,4 ,5 }; auto new_end = std::shift_left (v.begin (), v.end (), 2 ); v.erase (new_end, v.end ()); v = {1 ,2 ,3 ,4 ,5 }; auto new_begin = std::shift_right (v.begin (), v.end (), 2 ); std::fill (v.begin (), new_begin, 0 ); }
排序和相关操作 sort
功能 :对范围 [first, last) 进行升序排序(默认使用 operator<)。
原型 :sort(RandomIt first, RandomIt last) 或 sort(RandomIt first, RandomIt last, Compare comp)
复杂度 :平均 O(N log N)。
C++20 :std::ranges::sort,支持投影。
示例 :
1 2 3 4 5 6 7 8 9 10 std::vector<int > v = {4 ,2 ,5 ,1 ,3 }; std::sort (v.begin (), v.end ()); std::sort (v.begin (), v.end (), std::greater <int >()); std::ranges::sort (v);struct Person { std::string name; int age; }; std::vector<Person> people = {{"Alice" ,30 }, {"Bob" ,25 }}; std::ranges::sort (people, {}, &Person::age);
stable_sort
功能 :稳定排序,保持相等元素的相对顺序。
原型 :同 sort。
C++20 :std::ranges::stable_sort。
partial_sort
功能 :重排范围,使得 [first, middle) 包含整个范围中最小的 middle-first 个元素并已排序,其余元素置于 [middle, last) 中(顺序未指定)。
原型 :partial_sort(RandomIt first, RandomIt middle, RandomIt last, Compare comp = <)
C++20 :std::ranges::partial_sort。
nth_element
功能 :重排范围,使得第 nth 位置的元素就是整个范围排序后应处于该位置的元素,且所有在它之前的元素都不大于它,之后的元素都不小于它。
原型 :nth_element(RandomIt first, RandomIt nth, RandomIt last, Compare comp = <)
C++20 :std::ranges::nth_element。
binary_search
功能 :在已排序范围内检查是否存在等于某值的元素。
原型 :binary_search(ForwardIt first, ForwardIt last, const T& value),也可带比较函数。
返回值 :bool。
C++20 :std::ranges::binary_search。
示例 :
1 2 3 4 std::vector<int > v = {1 ,2 ,3 ,4 ,5 };bool found = std::binary_search (v.begin (), v.end (), 3 ); bool found2 = std::ranges::binary_search (v, 3 );
lower_bound / upper_bound
功能 :在已排序范围内查找第一个不小于 (lower_bound)或大于 (upper_bound)给定值的元素位置。
原型 :lower_bound(ForwardIt first, ForwardIt last, const T& value)
返回值 :迭代器。
C++20 :std::ranges::lower_bound, std::ranges::upper_bound。
示例 :
1 2 3 4 5 6 7 std::vector<int > v = {1 ,2 ,2 ,2 ,3 ,4 };auto low = std::lower_bound (v.begin (), v.end (), 2 ); auto up = std::upper_bound (v.begin (), v.end (), 2 ); auto low2 = std::ranges::lower_bound (v, 2 );
merge
功能 :合并两个已排序范围到目标范围(结果也是有序的)。
原型 :merge(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt d_first)
返回值 :指向目标范围末尾的迭代器。
C++20 :std::ranges::merge。
示例 :
1 2 3 4 std::vector<int > a = {1 ,3 ,5 }, b = {2 ,4 ,6 }, c (6 ); std::merge (a.begin (), a.end (), b.begin (), b.end (), c.begin ()); std::ranges::merge (a, b, c.begin ());
includes
功能 :检查已排序范围 [first1, last1) 是否包含另一个已排序范围 [first2, last2) 中的所有元素。
原型 :includes(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2)
返回值 :bool。
C++20 :std::ranges::includes。
示例 :
1 2 3 4 std::vector<int > a = {1 ,2 ,3 ,4 ,5 }, b = {2 ,4 };bool in = std::includes (a.begin (), a.end (), b.begin (), b.end ()); bool in2 = std::ranges::includes (a, b);
set_union / set_intersection
功能 :对两个已排序范围执行集合操作(并集、交集等),结果写入目标范围。
原型 :
set_union(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt d_first)
set_intersection(...)
返回值 :指向目标范围末尾的迭代器。
C++20 :std::ranges::set_union, std::ranges::set_intersection 等。
示例 :
1 2 3 4 5 6 std::vector<int > a = {1 ,2 ,3 ,4 }, b = {3 ,4 ,5 ,6 }, u, i; std::set_union (a.begin (), a.end (), b.begin (), b.end (), std::back_inserter (u)); std::set_intersection (a.begin (), a.end (), b.begin (), b.end (), std::back_inserter (i)); std::ranges::set_union (a, b, std::back_inserter (u));
min / max
功能 :返回两个值中的较小/较大者。
原型 :min(const T& a, const T& b),也可接受比较函数。
返回值 :const T&(通常返回引用)。
C++20 :新增 std::ranges::min, std::ranges::max,可接受初始化列表和投影。此外,std::ranges::minmax 返回一对。
示例 :
1 2 3 int m = std::min (3 , 5 ); std::ranges::min ({3 ,1 ,4 });
min_element / max_element
功能 :返回范围中的最小/最大元素的迭代器。
原型 :min_element(ForwardIt first, ForwardIt last),也可带比较函数。
返回值 :迭代器。
C++20 :std::ranges::min_element, std::ranges::max_element。
示例 :
1 2 3 4 std::vector<int > v = {3 ,1 ,4 ,1 ,5 };auto it = std::min_element (v.begin (), v.end ()); auto it2 = std::ranges::min_element (v);
数值算法 accumulate
功能 :对范围元素进行累加(或给定二元操作)。
原型 :
accumulate(InputIt first, InputIt last, T init) (默认加法)
accumulate(InputIt first, InputIt last, T init, BinaryOperation op)
返回值 :累加结果。
C++20 :无直接范围版本,但可使用 std::ranges::fold_left(C++23 才有),或结合范围视图使用 std::accumulate 与 std::ranges::begin/end。
示例 :
1 2 3 std::vector<int > v = {1 ,2 ,3 ,4 };int sum = std::accumulate (v.begin (), v.end (), 0 ); int product = std::accumulate (v.begin (), v.end (), 1 , std::multiplies <int >());
inner_product
功能 :计算两个序列的内积(对应元素相乘再累加),或类似操作。
原型 :
inner_product(InputIt1 first1, InputIt1 last1, InputIt2 first2, T init)
也可指定加法和乘法运算。
C++20 :std::ranges::inner_product 尚未引入(C++23 有 fold 变种),但可用范围版本循环。
示例 :
1 2 std::vector<int > a = {1 ,2 ,3 }, b = {4 ,5 ,6 };int dot = std::inner_product (a.begin (), a.end (), b.begin (), 0 );
partial_sum
功能 :计算范围的部分和,并写入目标范围。
原型 :partial_sum(InputIt first, InputIt last, OutputIt d_first),也可指定二元操作。
返回值 :指向目标范围末尾的迭代器。
C++20 :std::ranges::partial_sum(可能作为范围算法存在?实际上 C++20 未包含,但 C++23 会包含。可手动使用迭代器版本。)
示例 :
1 2 std::vector<int > v = {1 ,2 ,3 ,4 }, ps (v.size ()); std::partial_sum (v.begin (), v.end (), ps.begin ());
adjacent_difference
功能 :计算范围内相邻元素的差值,并写入目标范围。
原型 :adjacent_difference(InputIt first, InputIt last, OutputIt d_first),也可指定二元操作。
C++20 :std::ranges::adjacent_difference 也未在 C++20 中,需用迭代器版本。
示例 :
1 2 std::vector<int > v = {1 ,3 ,6 ,10 }, diff (v.size ()); std::adjacent_difference (v.begin (), v.end (), diff.begin ());
其他常用算法 swap
功能 :交换两个对象的值。
原型 :swap(T& a, T& b)(定义在 <utility> 中,但算法库也包含)。
C++20 :std::ranges::swap 是 CPO(定制点对象),但通常使用 std::swap 即可。
示例 :
1 2 int a=1 , b=2 ; std::swap (a, b);
iter_swap
功能 :交换两个迭代器指向的元素。
原型 :iter_swap(ForwardIt1 a, ForwardIt2 b)
C++20 :std::ranges::iter_swap 也是 CPO。
示例 :
1 2 std::vector<int > v = {1 ,2 }; std::iter_swap (v.begin (), v.begin ()+1 );
generate
功能 :通过反复调用函数对象生成值,填充范围。
原型 :generate(ForwardIt first, ForwardIt last, Generator g)
C++20 :std::ranges::generate。
示例 :
1 2 3 4 5 std::vector<int > v (5 ) ;int n = 0 ; std::generate (v.begin (), v.end (), [&n]{ return n++; }); std::ranges::generate (v, [&n]{ return n++; });
C++20 新特性详述 1. 范围库(Ranges) C++20 引入了范围库,提供了一组新的算法重载(位于 std::ranges 命名空间),它们:
接受范围对象(如容器、视图)作为参数,而不必传递两个迭代器。
支持投影(projection),可以在比较或操作之前对元素进行变换。
返回更丰富的结果类型(例如 ranges::copy 返回包含输入和输出迭代器的结构)。
与范围适配器(views)无缝协作,实现惰性求值和组合。
示例:使用范围算法和投影
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <algorithm> #include <vector> #include <iostream> struct Person { std::string name; int age; };int main () { std::vector<Person> people = {{"Alice" , 30 }, {"Bob" , 25 }, {"Charlie" , 35 }}; std::ranges::sort (people, {}, &Person::age); std::ranges::for_each(people, [](const std::string& name) { std::cout << name << ' ' ; }, &Person::name); auto over30 = people | std::views::filter ([](const Person& p) { return p.age > 30 ; }) | std::views::transform (&Person::name); for (const auto & name : over30) { std::cout << name << ' ' ; } }
2. 新算法:shift_left / shift_right 已在“修改式序列操作”中介绍。
3. constexpr 支持 C++20 将许多算法标记为 constexpr,允许在编译时执行这些算法。例如:
1 2 3 4 5 6 7 8 9 constexpr int square (int x) { return x * x; }constexpr std::array<int , 4> arr = {1 ,2 ,3 ,4 };constexpr auto squared = []{ std::array<int , 4 > result{}; std::transform (arr.begin (), arr.end (), result.begin (), square); return result; }();
注意:并非所有算法都可在编译时使用(例如需要动态内存的算法如 sort 通常不是 constexpr,但部分如 transform、copy 等可以)。
4. 范围适配器(Views) 虽然不是算法,但范围适配器常与算法一起使用。常用视图:
std::views::filter(pred):筛选满足谓词的元素。
std::views::transform(f):对每个元素应用函数。
std::views::take(n):取前 n 个元素。
std::views::drop(n):跳过前 n 个元素。
std::views::reverse:反转视图。
std::views::keys / values:用于 pair/tuple 容器(如 map)。
这些视图支持惰性求值,可与范围算法组合。
注意事项
迭代器范围 :所有算法操作的区间都是左闭右开 [first, last),即包含 first 但不包含 last。
有效性 :确保目标范围有足够的空间,或使用 back_inserter 等插入迭代器。
排序要求 :binary_search、lower_bound、merge、set_* 等算法要求输入范围已排序。
谓词 :许多算法接受自定义谓词(函数指针、lambda 表达式、函数对象),确保谓词不修改元素(除非算法允许)。
复杂度 :熟悉算法的复杂度,选择合适的算法(如 sort 通常优于 stable_sort 若不需要稳定性)。
C++20 范围算法 :使用范围版本时注意返回值类型,通常返回结构体包含迭代器,便于获取更多信息。
constexpr :虽然许多算法现在是 constexpr,但标准库的实现可能要求迭代器操作也是 constexpr,因此在编译时使用受限。
结语 STL 算法是 C++ 标准库的精华,C++20 的范围库让它们更加强大和易用。熟练掌握这些算法可以让我们写出更简洁、高效且易于维护的代码。本文列举的算法覆盖了日常开发中绝大部分需求,并结合 C++20 新特性提供了更新。建议在实践中多使用、多查阅,逐渐内化于心。
(注:本文基于 C++20 标准,部分 C++23 特性未涉及,请参考具体编译器支持情况。)