C++ STL 常用算法
C++ 标准模板库(STL)提供了丰富的算法库(定义在 <algorithm> 头文件中),这些算法多为通用函数模板,可配合容器和迭代器高效操作数据。
1、非修改序列算法
这些算法不会改变它们所操作的容器中的元素。
1.1 find 和 find_if
find(begin, end, value):查找第一个等于value的元素,返回迭代器(未找到返回end)。find_if(begin, end, predicate):查找第一个满足谓词的元素。find_end(begin, end, sub_begin, sub_end):查找子序列最后一次出现的位置。
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
// 查找子序列
vector<int> sub = {3, 5};
auto it3 = find_end(nums.begin(), nums.end(), sub.begin(), sub.end());
if (it3 != nums.end()) {
cout << "subsequence starts at index: " << it3 - nums.begin() << endl; // 输出:1
}
1.2 count 和 count_if
count(begin, end, value):统计等于value的元素个数。count_if(begin, end, predicate):统计满足谓词(predicate)的元素个数。
std::vector<int> vec = {1, 2, 3, 2, 4, 2};
int cnt = std::count(vec.begin(), vec.end(), 2); // 计数2的个数,结果为3
int even_cnt = std::count_if(vec.begin(), vec.end(), [](int x) {
return x % 2 == 0;
}); // 偶数个数,结果为4
1.3 for_each
对范围内的每个元素应用一个函数
std::vector<int> vec = {1, 2, 3, 4, 5};
std::for_each(vec.begin(), vec.end(), [](int& x) {
x *= 2; // 将每个元素乘以2
});
// 现在vec变为{2, 4, 6, 8, 10}
1.4 equal 与 mismatch
equal(b1, e1, b2):判断两个范围[b1,e1)和[b2, b2+(e1-b1))是否相等。mismatch(b1, e1, b2):返回两个范围中第一个不相等元素的迭代器对(pair)。
vector<int> a = {1, 2, 3};
vector<int> b = {1, 2, 4};
vector<int> c = {1, 2, 3, 4};
// 比较a和b的前3个元素
bool is_equal = equal(a.begin(), a.end(), b.begin());
cout << "a == b? " << boolalpha << is_equal << endl; // 输出:false
// 查找a和c的第一个不匹配元素
auto mis = mismatch(a.begin(), a.end(), c.begin());
if (mis.first != a.end()) {
cout << "mismatch: " << *mis.first << " vs " << *mis.second << endl; // 无输出(a和c前3元素相等)
}
1.5 all_of, any_of, none_of
检查范围内元素是否全部、存在或没有满足条件的
std::vector<int> vec = {2, 4, 6, 8};
bool all_even = std::all_of(vec.begin(), vec.end(), [](int x) {
return x % 2 == 0;
}); // true
bool any_odd = std::any_of(vec.begin(), vec.end(), [](int x) {
return x % 2 != 0;
}); // false
bool none_negative = std::none_of(vec.begin(), vec.end(), [](int x) {
return x < 0;
}); // true
2、修改序列算法
这些算法会修改它们所操作的容器中的元素。
2.1 copy 和 copy_if
copy(begin, end, dest):将[begin, end)中的元素复制到dest开始的位置。copy_if(begin, end, dest, predicate):复制满足谓词的元素到dest。
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(dest) 会自动调用 push_back,无需提前分配空间。
2.2 transform
对范围内的每个元素应用一个函数,并将结果存储在另一个范围内
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]
// 两容器元素相加(双参数转换)
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]
2.3 replace、replace_if与 replace_copy
replace(begin, end, old_val, new_val):将所有old_val替换为new_val。replace_if(begin, end, predicate, new_val):替换满足谓词的元素。replace_copy(begin, end, dest, old_val, new_val):复制时替换元素(不修改原容器)。
vector<int> nums = {1, 2, 3, 2, 5};
// 替换所有2为20
replace(nums.begin(), nums.end(), 2, 20); // nums: [1,20,3,20,5]
// 替换大于10的元素为0
replace_if(nums.begin(), nums.end(), [](int x) {
return x > 10;
}, 0); // nums: [1,0,3,0,5]
// 复制时替换3为300(原容器不变)
vector<int> res;
replace_copy(nums.begin(), nums.end(), back_inserter(res), 3, 300); // res: [1,0,300,0,5]
2.4 remove、remove_if 与 erase
remove(begin, end, value):将等于value的元素 “移动” 到容器末尾,返回新的逻辑尾迭代器(不实际删除元素,需配合erase)。remove_if(begin, end, predicate):移动满足谓词的元素到末尾。
vector<int> nums = {1, 2, 3, 2, 4};
// 逻辑删除所有2(移动到末尾)
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删除偶数
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]
2.5 unique
移除范围内连续的重复元素,返回新的逻辑结尾迭代器。通常与erase结合使用。
std::vector<int> vec = {1, 1, 2, 2, 3, 3, 3, 4, 5};
auto last = std::unique(vec.begin(), vec.end());
vec.erase(last, vec.end()); // vec变为{1, 2, 3, 4, 5}
2.6 reverse
反转范围内的元素顺序
std::vector<int> vec = {1, 2, 3, 4, 5};
std::reverse(vec.begin(), vec.end()); // vec变为{5, 4, 3, 2, 1}
2.7 rotate
旋转范围内的元素,使中间元素成为新的第一个元素
std::vector<int> vec = {1, 2, 3, 4, 5};
std::rotate(vec.begin(), vec.begin() + 2, vec.end()); // 以3为起点旋转,vec变为{3, 4, 5, 1, 2}
2.8 shuffle
随机重排范围内的元素(需要C++11或更高版本)
#include <random>
#include <algorithm>
std::vector<int> vec = {1, 2, 3, 4, 5};
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(vec.begin(), vec.end(), g); // 随机打乱vec中的元素
3、排序和相关算法
3.1 sort、stable_sort 与 partial_sort
sort(begin, end):对元素进行快速排序(不稳定,平均时间复杂度 O (n log n))。stable_sort(begin, end):稳定排序(相等元素相对位置不变)。partial_sort(begin, mid, end):部分排序,使[begin, mid)为整个范围中最小的元素并排序。
std::vector<int> vec = {5, 3, 1, 4, 2};
std::sort(vec.begin(), vec.end()); // 默认升序,vec变为{1, 2, 3, 4, 5}
std::sort(vec.begin(), vec.end(), std::greater<int>()); // 降序,vec变为{5, 4, 3, 2, 1}
std::sort(vec.begin(), vec.end(), [](int a, int b) {
return a < b;
}); // 升序,自定义比较
std::vector<std::pair<int, int>> vec = {{1, 2}, {2, 1}, {1, 1}, {2, 2}};
std::stable_sort(vec.begin(), vec.end(), [](const auto& a, const auto& b) {
return a.first < b.first; // 按first排序,保持相等元素的相对顺序
});
std::vector<int> vec = {5, 3, 1, 4, 2, 6};
// 将最小的3个元素放在前面并排序
std::partial_sort(vec.begin(), vec.begin() + 3, vec.end());
// 现在vec前三个元素是1, 2, 3,后面是未排序的4, 5, 6
3.2 nth_element
重新排列范围,使得指定位置的元素等于排序后的元素,并且左边的元素都不大于它,右边的元素都不小于它
std::vector<int> vec = {5, 3, 1, 4, 2, 6};
// 找到第三小的元素(索引2)
std::nth_element(vec.begin(), vec.begin() + 2, vec.end());
// 现在vec[2]是3,它左边的元素<=3,右边的>=3
3.3 binary_search、lower_bound、upper_bound
需在已排序的容器上使用
binary_search(begin, end, value):判断value是否存在(返回bool)。lower_bound(begin, end, value):返回第一个不小于value的元素迭代器。upper_bound(begin, end, value):返回第一个大于value的元素迭代器。
vector<int> sorted = {1, 3, 3, 5, 7}; // 必须先排序
// 判断3是否存在
bool exists = binary_search(sorted.begin(), sorted.end(), 3); // true
// 查找第一个>=3的元素
auto lb = lower_bound(sorted.begin(), sorted.end(), 3);
cout << "lower_bound index: " << lb - sorted.begin() << endl; // 输出:1
// 查找第一个>3的元素
auto ub = upper_bound(sorted.begin(), sorted.end(), 3);
cout << "upper_bound index: " << ub - sorted.begin() << endl; // 输出:3
3.4 merge
合并两个已排序的范围到新容器(保持排序)
vector<int> a = {1, 3, 5};
vector<int> b = {2, 4, 6};
vector<int> merged(a.size() + b.size());
// 合并a和b(均需已排序)
merge(a.begin(), a.end(), b.begin(), b.end(), merged.begin()); // merged: [1,2,3,4,5,6]
4、堆算法
STL提供了将范围作为堆来操作的算法,包括make_heap, push_heap, pop_heap, sort_heap等。
std::vector<int> vec = {4, 1, 3, 2, 5};
std::make_heap(vec.begin(), vec.end()); // 构建最大堆,vec变为{5, 4, 3, 2, 1}
vec.push_back(6);
std::push_heap(vec.begin(), vec.end()); // 将新元素加入堆,vec变为{6, 4, 5, 2, 1, 3}
std::pop_heap(vec.begin(), vec.end()); // 将最大元素移到末尾,vec变为{5, 4, 3, 2, 1, 6}
int max_val = vec.back(); // 获取最大元素6
vec.pop_back(); // 移除最大元素
std::sort_heap(vec.begin(), vec.end()); // 将堆排序为升序序列,vec变为{1, 2, 3, 4, 5}
5、最小/最大值算法
5.1 min 和 max
返回两个值或初始化列表中的最小/最大值
int a = 5, b = 3;
int min_val = std::min(a, b); // 3
int max_val = std::max(a, b); // 5
auto min_of_list = std::min({4, 2, 8, 5, 1}); // 1
auto max_of_list = std::max({4, 2, 8, 5, 1}); // 8
5.2 min_element 和 max_element
返回范围内的最小/最大元素的迭代器
std::vector<int> vec = {3, 1, 4, 2, 5};
auto min_it = std::min_element(vec.begin(), vec.end()); // 指向1
auto max_it = std::max_element(vec.begin(), vec.end()); // 指向5
5.3 minmax_element (C++11)
同时返回范围内的最小和最大元素的迭代器
std::vector<int> vec = {3, 1, 4, 2, 5};
auto minmax = std::minmax_element(vec.begin(), vec.end());
// minmax.first指向1,minmax.second指向5
6、数值算法(在<numeric>中)
6.1 accumulate
计算范围内元素的累加和(或自定义操作)
#include <numeric>
std::vector<int> vec = {1, 2, 3, 4, 5};
int sum = std::accumulate(vec.begin(), vec.end(), 0); // 和,初始值为0,结果为15
int product = std::accumulate(vec.begin(), vec.end(), 1, std::multiplies<int>()); // 乘积,初始值为1,结果为120
6.2 inner_product
计算两个范围的内积(或自定义操作)
std::vector<int> a = {1, 2, 3};
std::vector<int> b = {4, 5, 6};
int dot = std::inner_product(a.begin(), a.end(), b.begin(), 0); // 1*4 + 2*5 + 3*6 = 32
6.3 iota
用连续递增的值填充范围
std::vector<int> vec(5);
std::iota(vec.begin(), vec.end(), 10); // 填充为10, 11, 12, 13, 14
6.4 partial_sum
计算部分和,将结果存储在目标范围内
std::vector<int> src = {1, 2, 3, 4, 5};
std::vector<int> dst(src.size());
std::partial_sum(src.begin(), src.end(), dst.begin()); // dst变为{1, 3, 6, 10, 15}
6.5 adjacent_difference
计算相邻元素的差值,将结果存储在目标范围内
std::vector<int> src = {1, 2, 3, 4, 5};
std::vector<int> dst(src.size());
std::adjacent_difference(src.begin(), src.end(), dst.begin()); // dst变为{1, 1, 1, 1, 1}
7、其他
7.1 generate
用生成函数填充范围
std::vector<int> vec(5);
int n = 0;
std::generate(vec.begin(), vec.end(), [&n]() {
return n++;
}); // 填充为0, 1, 2, 3, 4
7.2 generate_n
用生成函数填充范围的开始n个元素
std::vector<int> vec(5);
int n = 10;
std::generate_n(vec.begin(), 3, [&n]() {
return n++;
}); // 前三个元素为10, 11, 12,后两个保持不变
7.3 includes
检查一个排序范围是否包含另一个排序范围的所有元素
std::vector<int> vec1 = {1, 2, 3, 4, 5};
std::vector<int> vec2 = {2, 4};
bool includes = std::includes(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()); // true
7.3 set_union, set_intersection, set_difference, set_symmetric_difference
执行集合操作:并集、交集、差集和对称差集
std::vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<int> v2 = {3, 4, 5, 6, 7};
std::vector<int> result;
// 并集
std::set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(result));
// result为{1, 2, 3, 4, 5, 6, 7}
// 交集
result.clear();
std::set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(result));
// result为{3, 4, 5}
// 差集 (v1 - v2)
result.clear();
std::set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(result));
// result为{1, 2}
// 对称差集 (v1 ∪ v2 - v1 ∩ v2)
result.clear();
std::set_symmetric_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(result));
// result为{1, 2, 6, 7}
8、常见问题
sort与stable_sort的区别?sort采用快速排序(实际是 introsort 算法),不稳定(相等元素的相对位置可能改变),平均时间复杂度 O (n log n)。stable_sort采用归并排序,稳定(相等元素相对位置不变),时间复杂度 O (n log n),但空间开销略大。
为什么
remove算法需要配合erase使用?
remove算法的原理是 “覆盖” 要删除的元素,将保留的元素移到前面,返回新的逻辑尾迭代器,但不修改容器的实际大小。erase则通过迭代器范围真正删除元素,修改容器大小。因此需结合使用:container.erase(remove(...), container.end())。哪些算法需要容器是已排序的?
二分查找系列(binary_search、lower_bound、upper_bound)、集合算法(set_intersection、set_union等)、merge等,这些算法依赖有序性实现高效操作(如二分查找 O (log n))。
C++ STL 常用算法的更多相关文章
- [C++ STL] 常用算法总结
1 概述 STL算法部分主要由头文件<algorithm>,<numeric>,<functional>组成.要使用 STL中的算法函数必须包含头文件<alg ...
- 28.STL常用算法
#include <algorithm> 算法 常用版本 描述 返回Type std::find() find(_InIt _Fisrt,_InIt _Last, _Ty& _Va ...
- C++ STL——常用算法
目录 一 常用查找算法 二 常用遍历算法 注:原创不易,转载请务必注明原作者和出处,感谢支持! 注:内容来自某培训课程,不一定完全正确! 一 常用查找算法 /* find算法 查找元素 @param ...
- c++ STL常用算法使用方法
#include <string> #include <vector> #include <functional> #include <iostream> ...
- 常用的STL查找算法
常用的STL查找算法 <effective STL>中有句忠告,尽量用算法替代手写循环:查找少不了循环遍历,在这里总结下常用的STL查找算法: 查找有三种,即点线面: 点就是查找目标为单个 ...
- STL——配接器、常用算法使用
学习STL,必然会用到它里面的适配器和一些常用的算法.它们都是STL中的重要组成部分. 适配器 在STL里可以用一些容器适配得到适配器.例如其中的stack和queue就是由双端队列deque容器适配 ...
- C++ STL 常用算术和生成算法
C++ STL 常用算术和生成算法 accumulate() accumulate: 对指定范围内的元素求和,然后结果再加上一个由val指定的初始值. #include<numeric> ...
- C++ STL 常用排序算法
C++ STL 常用排序算法 merge() 以下是排序和通用算法:提供元素排序策略 merge: 合并两个有序序列,存放到另一个序列. 例如: vecIntA,vecIntB,vecIntC是用ve ...
- C++ STL 常用查找算法
C++ STL 常用查找算法 adjacent_find() 在iterator对标识元素范围内,查找一对相邻重复元素,找到则返回指向这对元素的第一个元素的迭代器.否则返回past-the-end. ...
- C++ STL 常用遍历算法
C++ STL 常用遍历算法 STL的容器算法迭代器的设计理念 1) STL的容器通过类模板技术,实现数据类型和容器模型的分离 2) STL的迭代器技术实现了遍历容器的统一方法:也为STL的算法提供了 ...
随机推荐
- vue_条件渲染、列表渲染
条件渲染 <html lang="en"> <head> <meta charset="UTF-8"> <title& ...
- ORACLE--SQL日常问题和技巧1(列变行、自定义查询结构、将字符串分割为多条记录)
1.ORACLE列变行加合计: 查询结果为 1 SELECT 2 nvl( F, '合计' ) F, 3 sum( S1 ) S1, 4 sum( S2 ) S2, 5 sum( S3 ) S3 6 ...
- 这几个 Vibe Coding 经验,真的建议学!
大家好,我是 Immerse,一名独立开发者.内容创作者.AGI 实践者. 关注公众号:#沉浸式趣谈,获取最新文章(更多内容只在公众号更新) 个人网站:https://yaolifeng.com 也同 ...
- 生成一个Cylinder
简介 用到了上一个博客的,AddPolygon. 生成的是一个筒状物体. 代码 // CreateCylinder.cpp: 定义控制台应用程序的入口点. // //#include "st ...
- java 泛型 3 反射机制
简介 说实话,不太理解反射机制. 不过好像应该是对于特定对于泛型的接口. code package com.company; import java.lang.reflect.*; import ja ...
- SciTech-Mathmatics-因式分解定理:UNIQUE FACTORIZATION THEOREM + Science Hackathon Prizes@Kaggle.com
SciTech-Mathmatics-UNIQUE FACTORIZATION THEOREM 因式分解趣谈: 一元 多项式方程: 解一元N次方程,其实就是对 一元N次方程"因式分解&quo ...
- babylon.js 学习笔记(2)
如何在网页中嵌入设计好的模型? 接上回继续,我们设计好精美的模型后,最终总要展示给客户,比如利用playground画了1个方块: const createScene = () => { con ...
- Win10正式版如何查看电脑Wifi密码的问题
一些电脑基地的用户,说从Win10正式版计算机中如何提取已经保存的Wi-Fi密码?其实,这个还是很简单的,下面,技术员小编就来分享具体的提取方法. 在 Windows 10 中,查看已保存的 Wi-F ...
- 一款基于 .NET 开源免费、轻量快速、跨平台的 PDF 阅读器
前言 今天大姚给大家分享一款 .NET 开源免费.轻量快速.跨平台的 PDF 阅读器:Caly. 项目介绍 Caly 是一款 .NET 开源免费(MIT license).轻量快速.跨平台的 PDF ...
- go语言请求http接口示例 并解析json
本例请求了天气api接口 对接流程 注册一个账号, 对接免费实况天气接口 阅读接口文档 http://tianqiapi.com/index/doc?version=day 请求接口 解析json 开 ...