C++标准模板库(STL)提供了丰富的算法,这些算法主要定义在<algorithm>和<numeric>头文件中。作为C++开发者,熟练掌握这些算法能极大提升开发效率和代码质量。STL算法通常通过迭代器操作容器,具有高度的通用性和灵活性。
STL算法大致可分为以下几类:
find和find_if是最常用的查找算法:
cpp复制vector<int> nums = {1, 3, 5, 7, 9};
// 查找值为5的元素
auto it = find(nums.begin(), nums.end(), 5);
if (it != nums.end()) {
cout << "found: " << *it << endl; // 输出:5
}
// 查找第一个大于6的元素
auto it2 = find_if(nums.begin(), nums.end(), [](int x) {
return x > 6;
});
cout << "first >6: " << *it2 << endl; // 输出:7
注意:
find_end用于查找子序列最后一次出现的位置,与search(查找第一次出现)相对应。
count和count_if用于统计满足条件的元素数量:
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
});
// 现在vec变为{2, 4, 6, 8, 10}
equal和mismatch用于比较两个序列:
cpp复制vector<int> a = {1, 2, 3};
vector<int> b = {1, 2, 4};
bool is_equal = equal(a.begin(), a.end(), b.begin()); // false
auto mis = mismatch(a.begin(), a.end(), b.begin());
if (mis.first != a.end()) {
cout << "mismatch at: " << *mis.first << " vs " << *mis.second << endl;
// 输出:mismatch at: 3 vs 4
}
all_of、any_of和none_of用于检查元素是否满足特定条件:
cpp复制vector<int> vec = {2, 4, 6, 8};
bool all_even = all_of(vec.begin(), vec.end(), [](int x) {
return x % 2 == 0;
}); // true
bool any_odd = any_of(vec.begin(), vec.end(), [](int x) {
return x % 2 != 0;
}); // false
bool none_negative = none_of(vec.begin(), vec.end(), [](int x) {
return x < 0;
}); // true
copy和copy_if用于复制元素:
cpp复制vector<int> src = {1, 2, 3, 4, 5};
vector<int> dest(5); // 需预先分配足够空间
copy(src.begin(), src.end(), dest.begin()); // dest: [1,2,3,4,5]
vector<int> evens;
copy_if(src.begin(), src.end(), back_inserter(evens), [](int x) {
return x % 2 == 0;
}); // evens: [2,4]
提示:使用
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;
}); // squares: [1,4,9]
双序列版本:
cpp复制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(), [](int x, int y) {
return x + y;
}); // sum: [5,7,9]
replace系列算法用于替换元素:
cpp复制vector<int> nums = {1, 2, 3, 2, 5};
replace(nums.begin(), nums.end(), 2, 20); // nums: [1,20,3,20,5]
replace_if(nums.begin(), nums.end(), [](int x) {
return x > 10;
}, 0); // nums: [1,0,3,0,5]
vector<int> res;
replace_copy(nums.begin(), nums.end(), back_inserter(res), 3, 300); // res: [1,0,300,0,5]
remove和erase配合使用实现真正删除:
cpp复制vector<int> nums = {1, 2, 3, 2, 4};
auto new_end = remove(nums.begin(), nums.end(), 2); // nums: [1,3,4,2,2]
nums.erase(new_end, nums.end()); // nums: [1,3,4]
使用lambda表达式:
cpp复制nums = {1, 2, 3, 4, 5};
nums.erase(remove_if(nums.begin(), nums.end(), [](int x) {
return x % 2 == 0;
}), nums.end()); // nums: [1,3,5]
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()); // vec变为{1, 2, 3, 4, 5}
注意:
unique只去除相邻的重复元素,使用前通常需要先排序。
reverse反转序列:
cpp复制vector<int> vec = {1, 2, 3, 4, 5};
reverse(vec.begin(), vec.end()); // vec变为{5, 4, 3, 2, 1}
rotate旋转序列:
cpp复制vector<int> vec = {1, 2, 3, 4, 5};
rotate(vec.begin(), vec.begin() + 2, vec.end()); // vec变为{3, 4, 5, 1, 2}
shuffle随机打乱:
cpp复制vector<int> vec = {1, 2, 3, 4, 5};
random_device rd;
mt19937 g(rd());
shuffle(vec.begin(), vec.end(), g); // 随机打乱
sort是最常用的排序算法:
cpp复制vector<int> vec = {5, 3, 1, 4, 2};
sort(vec.begin(), vec.end()); // 默认升序,vec变为{1, 2, 3, 4, 5}
sort(vec.begin(), vec.end(), greater<int>()); // 降序
stable_sort保持相等元素的相对顺序:
cpp复制vector<pair<int, int>> vec = {{1, 2}, {2, 1}, {1, 1}, {2, 2}};
stable_sort(vec.begin(), vec.end(), [](const auto& a, const auto& b) {
return a.first < b.first;
});
partial_sort部分排序:
cpp复制vector<int> vec = {5, 3, 1, 4, 2, 6};
partial_sort(vec.begin(), vec.begin() + 3, vec.end());
// 前三个元素是1, 2, 3,后面未排序
nth_element用于选择第n小的元素:
cpp复制vector<int> vec = {5, 3, 1, 4, 2, 6};
nth_element(vec.begin(), vec.begin() + 2, vec.end());
// vec[2]是3,左边<=3,右边>=3
需在已排序的容器上使用:
cpp复制vector<int> sorted = {1, 3, 3, 5, 7};
bool exists = binary_search(sorted.begin(), sorted.end(), 3); // true
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());
// merged: [1,2,3,4,5,6]
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()); // 堆排序
accumulate计算累加和或自定义操作:
cpp复制vector<int> vec = {1, 2, 3, 4, 5};
int sum = accumulate(vec.begin(), vec.end(), 0); // 15
int product = accumulate(vec.begin(), vec.end(), 1, multiplies<int>()); // 120
inner_product计算两个序列的内积:
cpp复制vector<int> a = {1, 2, 3};
vector<int> b = {4, 5, 6};
int dot = inner_product(a.begin(), a.end(), b.begin(), 0); // 32
iota用连续值填充序列:
cpp复制vector<int> vec(5);
iota(vec.begin(), vec.end(), 10); // 10,11,12,13,14
partial_sum计算部分和:
cpp复制vector<int> src = {1, 2, 3, 4, 5};
vector<int> dst(src.size());
partial_sum(src.begin(), src.end(), dst.begin()); // {1,3,6,10,15}
adjacent_difference计算相邻元素差:
cpp复制vector<int> src = {1, 2, 3, 4, 5};
vector<int> dst(src.size());
adjacent_difference(src.begin(), src.end(), dst.begin()); // {1,1,1,1,1}
generate和generate_n用生成函数填充序列:
cpp复制vector<int> vec(5);
int n = 0;
generate(vec.begin(), vec.end(), [&n]() { return n++; }); // 0,1,2,3,4
generate_n(vec.begin(), 3, [&n]() { return n++; }); // 前三个元素被替换
includes检查包含关系:
cpp复制vector<int> vec1 = {1, 2, 3, 4, 5};
vector<int> vec2 = {2, 4};
bool includes = includes(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()); // true
集合操作:
cpp复制vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2 = {3, 4, 5, 6, 7};
vector<int> result;
set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), back_inserter(result)); // {1,2,3,4,5,6,7}
result.clear();
set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), back_inserter(result)); // {3,4,5}
result.clear();
set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), back_inserter(result)); // {1,2}
result.clear();
set_symmetric_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), back_inserter(result)); // {1,2,6,7}
查找操作:
find、find_ifbinary_search、lower_bound、upper_boundsearch、find_end排序需求:
sortstable_sortpartial_sortnth_element修改操作:
copy、copy_iftransformreplace系列remove+erasesort通常比stable_sort快,但会改变相等元素的顺序binary_search系列算法要求容器已排序,但时间复杂度为O(log n)remove算法只是逻辑删除,需要配合erase完成物理删除迭代器失效:某些算法会修改容器,导致迭代器失效
cpp复制vector<int> vec = {1, 2, 3, 4, 5};
auto it = vec.begin() + 2;
vec.erase(remove(vec.begin(), vec.end(), 3), vec.end());
// it可能已经失效,不能再使用
未排序容器使用二分查找:
cpp复制vector<int> vec = {5, 3, 1, 4, 2};
// 错误!容器未排序
bool found = binary_search(vec.begin(), vec.end(), 3);
未预分配空间的复制:
cpp复制vector<int> src = {1, 2, 3};
vector<int> dest;
// 错误!dest没有足够空间
copy(src.begin(), src.end(), dest.begin());
使用lambda表达式简化谓词:
cpp复制vector<int> vec = {1, 2, 3, 4, 5};
auto it = find_if(vec.begin(), vec.end(), [](int x) {
return x > 3 && x % 2 == 0;
});
使用auto简化迭代器声明:
cpp复制auto it = max_element(vec.begin(), vec.end());
范围for循环与算法结合:
cpp复制for (auto& x : vec) {
x *= 2; // 替代简单的for_each
}
C++17的并行算法:
cpp复制#include <execution>
vector<int> vec = {...};
sort(execution::par, vec.begin(), vec.end()); // 并行排序
cpp复制vector<string> words = {"apple", "banana", "apple", "cherry", "banana", "apple"};
map<string, int> word_counts;
for_each(words.begin(), words.end(), [&](const string& word) {
word_counts[word]++;
});
// 按词频排序
vector<pair<string, int>> sorted_counts(word_counts.begin(), word_counts.end());
sort(sorted_counts.begin(), sorted_counts.end(), [](const auto& a, const auto& b) {
return a.second > b.second;
});
cpp复制vector<Data> dataset = {...};
// 移除无效数据
dataset.erase(remove_if(dataset.begin(), dataset.end(), [](const Data& d) {
return !d.is_valid();
}), dataset.end());
cpp复制vector<int> scores = {78, 92, 85, 91, 87, 90, 76, 88};
// 找出前3高分
partial_sort(scores.begin(), scores.begin() + 3, scores.end(), greater<int>());
vector<int> top3(scores.begin(), scores.begin() + 3);
cpp复制vector<vector<int>> sorted_lists = {{1,4,7}, {2,5,8}, {3,6,9}};
vector<int> merged;
for (const auto& list : sorted_lists) {
vector<int> temp(merged.size() + list.size());
merge(merged.begin(), merged.end(), list.begin(), list.end(), temp.begin());
merged.swap(temp);
}
// merged: {1,2,3,4,5,6,7,8,9}
| 算法 | 时间复杂度 | 稳定性 | 适用场景 |
|---|---|---|---|
| sort | O(n log n) | 不稳定 | 通用排序 |
| stable_sort | O(n log n) | 稳定 | 需要保持相等元素顺序 |
| partial_sort | O(n log k) | 不稳定 | 只关心前k个元素 |
| nth_element | O(n) | 不稳定 | 找第k大元素 |
| 算法 | 时间复杂度 | 要求 | 返回结果 |
|---|---|---|---|
| find | O(n) | 无 | 第一个匹配元素 |
| find_if | O(n) | 无 | 第一个满足条件的元素 |
| binary_search | O(log n) | 已排序 | 是否存在(bool) |
| lower_bound | O(log n) | 已排序 | 第一个不小于目标的位置 |
| 算法 | 作用 | 是否修改容器大小 | 典型用法 |
|---|---|---|---|
| remove | 逻辑删除 | 否 | vec.erase(remove(...), vec.end()) |
| erase | 物理删除 | 是 | 直接删除元素 |
| remove_if | 条件删除 | 否 | 配合erase使用 |
当标准算法不满足需求时,可以自定义算法:
cpp复制template<typename Iterator, typename Predicate>
Iterator find_last_if(Iterator first, Iterator last, Predicate pred) {
Iterator result = last;
while (first != last) {
if (pred(*first)) {
result = first;
}
++first;
}
return result;
}
// 使用示例
vector<int> vec = {1, 3, 5, 2, 4, 6};
auto it = find_last_if(vec.begin(), vec.end(), [](int x) { return x % 2 == 0; });
// it指向6
迭代器适配器可以扩展算法功能:
cpp复制vector<int> vec = {1, 2, 3, 4, 5};
// 反向遍历
for_each(vec.rbegin(), vec.rend(), [](int x) { cout << x << " "; });
// 过滤流
copy_if(vec.begin(), vec.end(),
ostream_iterator<int>(cout, " "),
[](int x) { return x % 2 == 0; });
C++17引入了并行执行策略:
cpp复制#include <execution>
vector<int> vec = {...};
// 并行排序
sort(execution::par, vec.begin(), vec.end());
// 并行transform
vector<int> result(vec.size());
transform(execution::par,
vec.begin(), vec.end(),
result.begin(),
[](int x) { return x * x; });
C++20引入了范围库,简化算法调用:
cpp复制#include <ranges>
vector<int> vec = {1, 2, 3, 4, 5};
// 过滤偶数并平方
auto view = vec | views::filter([](int x) { return x % 2 == 0; })
| views::transform([](int x) { return x * x; });
for (auto x : view) {
cout << x << " "; // 输出: 4 16
}
经过对C++标准算法的全面探讨,我们可以总结出以下最佳实践:
remove+erase、sort+unique等在实际开发中,建议:
最后,记住STL算法的设计哲学:"通用但不万能"。在某些特殊场景下,自定义算法或手写循环可能是更好的选择。关键是根据具体需求,选择最合适的工具。