ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

C++ STL算法详解:从基础查找到高级排序

C++ STL算法详解:从基础查找到高级排序 1. STL算法概述与分类STLStandard Template Library是C标准库的核心组成部分提供了丰富的通用算法和数据结构。这些算法通过迭代器与容器解耦可以在不同容器上复用同一套操作逻辑。STL算法主要分为以下几大类非修改序列算法不改变容器内容如查找、计数等修改序列算法会改变容器内容如复制、替换等排序和相关算法包括排序、二分查找等堆算法构建和操作堆结构数值算法数学计算相关其他实用算法这些算法大多定义在algorithm头文件中数值算法则在numeric中。现代CC17/20还引入了并行版本可以通过执行策略参数启用多线程加速。2. 非修改序列算法详解2.1 查找算法2.1.1 find与find_iffind是最基础的线性查找算法时间复杂度O(n)vectorint v {1,3,5,7,9}; auto it find(v.begin(), v.end(), 5); // 查找值为5的元素 if (it ! v.end()) cout Found at index: distance(v.begin(), it);find_if则支持谓词查找更灵活auto it find_if(v.begin(), v.end(), [](int x){ return x 5 x % 2 0; }); // 查找第一个大于5的偶数注意对于已排序范围应优先使用lower_bound等二分查找算法效率更高O(log n)2.1.2 find_first_of与find_endfind_first_of查找任意匹配元素vectorint main {1,2,3,4,5}; vectorint targets {0,4,8}; auto it find_first_of(main.begin(), main.end(), targets.begin(), targets.end()); // 找到4返回指向4的迭代器find_end则查找最后出现的子序列vectorint seq {1,2,3,1,2,4}; vectorint sub {1,2}; auto it find_end(seq.begin(), seq.end(), sub.begin(), sub.end()); // 找到第二个{1,2}子序列2.2 条件检查算法2.2.1 all_of/any_of/none_of这些算法检查范围内元素是否满足特定条件vectorint nums {2,4,6,8}; bool allEven all_of(nums.begin(), nums.end(), [](int x){ return x % 2 0; }); // true bool hasOdd any_of(nums.begin(), nums.end(), [](int x){ return x % 2 ! 0; }); // false bool noNegative none_of(nums.begin(), nums.end(), [](int x){ return x 0; }); // true2.2.2 equal与mismatchequal比较两个范围是否相同vectorint a {1,2,3}; vectorint b {1,2,3}; bool same equal(a.begin(), a.end(), b.begin()); // truemismatch返回第一个不匹配的位置vectorint c {1,2,4}; auto [it1, it2] mismatch(a.begin(), a.end(), c.begin()); // it1指向a的3it2指向c的42.3 计数算法2.3.1 count与count_ifcount统计特定值出现的次数vectorint v {1,2,2,3,2,4}; int cnt count(v.begin(), v.end(), 2); // 3count_if按条件统计int evenCnt count_if(v.begin(), v.end(), [](int x){ return x % 2 0; }); // 43. 修改序列算法详解3.1 复制与变换3.1.1 copy与copy_ifcopy实现基础复制vectorint src {1,2,3,4,5}; vectorint dest(5); copy(src.begin(), src.end(), dest.begin());copy_if条件复制vectorint evens; copy_if(src.begin(), src.end(), back_inserter(evens), [](int x){ return x % 2 0; }); // evens: {2,4}技巧使用back_inserter可自动处理目标容器空间不足的情况3.1.2 transformtransform对元素进行转换vectorint nums {1,2,3}; vectorint squares; transform(nums.begin(), nums.end(), back_inserter(squares), [](int x){ return x * x; }); // squares: {1,4,9}双范围版本vectorint a {1,2,3}; vectorint b {4,5,6}; vectorint sums; transform(a.begin(), a.end(), b.begin(), back_inserter(sums), [](int x, int y){ return x y; }); // sums: {5,7,9}3.2 替换算法3.2.1 replace与replace_ifreplace替换特定值vectorint v {1,2,3,2,5}; replace(v.begin(), v.end(), 2, 20); // v: {1,20,3,20,5}replace_if条件替换replace_if(v.begin(), v.end(), [](int x){ return x 10; }, 0); // v: {1,0,3,0,5}3.2.2 replace_copy不修改原容器的替换版本vectorint result; replace_copy(v.begin(), v.end(), back_inserter(result), 3, 300); // v不变result: {1,0,300,0,5}3.3 删除算法3.3.1 remove与remove_ifremove逻辑删除元素vectorint v {1,2,3,2,4}; auto new_end remove(v.begin(), v.end(), 2); // v: {1,3,4,2,4} v.erase(new_end, v.end()); // 物理删除v: {1,3,4}remove_if条件删除v.erase(remove_if(v.begin(), v.end(), [](int x){ return x % 2 0; }), v.end()); // 删除所有偶数重要remove系列算法只是把要保留的元素前移必须配合erase才能真正删除3.3.2 unique去除连续重复元素vectorint v {1,1,2,2,3,3,3,4,5}; auto last unique(v.begin(), v.end()); // v: {1,2,3,4,5,3,3,4,5} v.erase(last, v.end()); // v: {1,2,3,4,5}注意unique只处理相邻重复如需全局去重应先排序3.4 其他修改算法3.4.1 reverse反转序列vectorint v {1,2,3,4,5}; reverse(v.begin(), v.end()); // v: {5,4,3,2,1}3.4.2 rotate旋转序列vectorint v {1,2,3,4,5}; rotate(v.begin(), v.begin()2, v.end()); // v: {3,4,5,1,2}3.4.3 shuffle随机重排random_device rd; mt19937 g(rd()); vectorint v {1,2,3,4,5}; shuffle(v.begin(), v.end(), g); // 如{3,1,5,2,4}4. 排序与相关算法4.1 基础排序4.1.1 sort快速排序实现vectorint v {5,3,1,4,2}; sort(v.begin(), v.end()); // 升序{1,2,3,4,5} sort(v.begin(), v.end(), greaterint()); // 降序{5,4,3,2,1}自定义比较vectorpairint,string ps {{2,b},{1,a},{3,c}}; sort(ps.begin(), ps.end(), [](auto a, auto b){ return a.first b.first; // 按first升序 });4.1.2 stable_sort稳定排序保持相等元素顺序vectorpairint,string ps {{1,a},{2,b},{1,c}}; stable_sort(ps.begin(), ps.end(), [](auto a, auto b){ return a.first b.first; }); // 保持{1,a}在{1,c}之前4.2 部分排序4.2.1 partial_sort部分排序vectorint v {5,3,1,4,2,6}; partial_sort(v.begin(), v.begin()3, v.end()); // 前三个是最小的有序元素{1,2,3,5,4,6}4.2.2 nth_element快速选择vectorint v {5,3,1,4,2,6}; nth_element(v.begin(), v.begin()2, v.end()); // v[2]是第3小的元素左边它右边它4.3 二分查找需在已排序范围上使用vectorint v {1,3,3,5,7}; bool found binary_search(v.begin(), v.end(), 3); // true auto lb lower_bound(v.begin(), v.end(), 3); // 第一个3的位置 auto ub upper_bound(v.begin(), v.end(), 3); // 第一个3的位置 pairdecltype(lb),decltype(ub) bounds equal_range(v.begin(), v.end(), 3); // bounds是[lb,ub)区间包含所有34.4 合并操作4.4.1 merge合并两个已排序序列vectorint a {1,3,5}, b {2,4,6}, result(6); merge(a.begin(), a.end(), b.begin(), b.end(), result.begin()); // result: {1,2,3,4,5,6}4.4.2 inplace_merge原地合并vectorint v {1,3,5,2,4,6}; inplace_merge(v.begin(), v.begin()3, v.end()); // v: {1,2,3,4,5,6}5. 堆算法STL提供了一套堆操作算法vectorint v {4,1,3,2,5}; // 构建最大堆 make_heap(v.begin(), v.end()); // v: {5,4,3,2,1} // 添加元素 v.push_back(6); push_heap(v.begin(), v.end()); // v: {6,4,5,2,1,3} // 弹出堆顶 pop_heap(v.begin(), v.end()); // 将最大元素移到末尾{5,4,3,2,1,6} int max v.back(); // 6 v.pop_back(); // 堆排序 sort_heap(v.begin(), v.end()); // v: {1,2,3,4,5}6. 数值算法6.1 accumulate累加或自定义操作vectorint v {1,2,3,4,5}; int sum accumulate(v.begin(), v.end(), 0); // 15 int product accumulate(v.begin(), v.end(), 1, multipliesint()); // 1206.2 inner_product内积计算vectorint a {1,2,3}, b {4,5,6}; int dot inner_product(a.begin(), a.end(), b.begin(), 0); // 1*42*53*6326.3 partial_sum部分和vectorint v {1,2,3,4,5}, result(5); partial_sum(v.begin(), v.end(), result.begin()); // {1,3,6,10,15}6.4 adjacent_difference相邻差值vectorint v {1,2,3,4,5}, result(5); adjacent_difference(v.begin(), v.end(), result.begin()); // {1,1,1,1,1}6.5 iota填充序列vectorint v(5); iota(v.begin(), v.end(), 10); // {10,11,12,13,14}7. 其他实用算法7.1 generate生成填充vectorint v(5); int n 0; generate(v.begin(), v.end(), [n](){ return n; }); // {0,1,2,3,4}7.2 集合操作需已排序范围vectorint a {1,2,3,4,5}, b {3,4,5,6,7}, result; // 并集 set_union(a.begin(), a.end(), b.begin(), b.end(), back_inserter(result)); // result: {1,2,3,4,5,6,7} // 交集 result.clear(); set_intersection(a.begin(), a.end(), b.begin(), b.end(), back_inserter(result)); // result: {3,4,5} // 差集(a-b) result.clear(); set_difference(a.begin(), a.end(), b.begin(), b.end(), back_inserter(result)); // result: {1,2} // 对称差集 result.clear(); set_symmetric_difference(a.begin(), a.end(), b.begin(), b.end(), back_inserter(result)); // result: {1,2,6,7}8. 算法选择与性能优化8.1 算法复杂度对比算法类别典型算法时间复杂度适用场景线性查找find, countO(n)无序小数据集二分查找lower_boundO(log n)已排序数据集排序sortO(n log n)需要有序数据堆操作push_heapO(log n)优先级队列集合操作set_unionO(nm)已排序集合8.2 常见性能陷阱在无序范围上使用二分查找必须先排序否则结果错误频繁调用erase导致数据搬移批量删除比单次删除更高效不必要的拷贝使用移动语义或原地算法减少拷贝谓词函数开销大简单谓词可内联优化复杂谓词可能成为瓶颈8.3 并行算法C17现代C支持并行执行策略#include execution vectorint v {...}; // 并行排序 sort(execution::par, v.begin(), v.end()); // 并行transform vectorint result(v.size()); transform(execution::par, v.begin(), v.end(), result.begin(), [](int x){ return x * x; });执行策略选项execution::seq- 顺序执行默认execution::par- 并行执行execution::par_unseq- 并行向量化9. 实战经验与技巧9.1 容器选择影响算法性能vector随机访问快适合大多数算法list插入删除快但缺乏随机访问部分算法不适用deque两端操作高效中间操作较慢9.2 迭代器失效问题修改容器可能导致迭代器失效vectorint v {1,2,3,4}; auto it v.begin() 2; v.insert(v.begin(), 0); // it可能失效安全做法操作后重新获取迭代器使用索引代替迭代器先收集修改点再批量处理9.3 自定义类型算法支持要使自定义类型支持STL算法实现比较运算符struct Point { int x, y; bool operator(const Point p) const { return x p.x || (x p.x y p.y); } };或提供自定义谓词sort(points.begin(), points.end(), [](const Point a, const Point b){ return a.x b.x; });9.4 算法组合技巧链式算法应用vectorint processData(vectorint input) { vectorint result; // 去重-筛选-变换 sort(input.begin(), input.end()); auto last unique(input.begin(), input.end()); copy_if(input.begin(), last, back_inserter(result), [](int x){ return x 0 x % 2 0; }); transform(result.begin(), result.end(), result.begin(), [](int x){ return x * 2; }); return result; }10. 现代C中的算法增强10.1 C11/14改进Lambda表达式简化谓词编写移动语义减少算法中的拷贝开销auto类型推导简化迭代器声明10.2 C17新特性并行算法execution::parsample算法随机抽样clamp限制值范围gcd/lcm数学算法10.3 C20扩展范围库Ranges更简洁的算法调用方式#include ranges vectorint v {1,2,3,4,5}; auto even v | views::filter([](int x){ return x % 2 0; }); // even包含{2,4}概念约束更安全的算法接口starts_with/ends_with字符串算法11. 性能优化案例11.1 高效去重传统方式sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end());C20优化ranges::sort(v); auto [first, last] ranges::unique(v); v.erase(first, last);11.2 批量删除技巧低效方式// 错误每次erase都导致元素移动 for(auto it v.begin(); it ! v.end();) { if(condition(*it)) it v.erase(it); else it; }高效方式v.erase(remove_if(v.begin(), v.end(), [](auto x){ return condition(x); }), v.end());11.3 查找优化线性查找auto it find(v.begin(), v.end(), value);如果频繁查找应先排序sort(v.begin(), v.end()); auto it lower_bound(v.begin(), v.end(), value); if(it ! v.end() *it value) { /* 找到 */ }12. 跨平台注意事项12.1 实现差异不同STL实现可能有算法复杂度保证不同并行算法支持程度不同特殊优化情况不同12.2 稳定性保证stable_sort保证稳定sort通常不稳定但某些实现对小数组使用插入排序并行算法可能影响稳定性12.3 内存使用某些算法需要额外内存stable_sort通常需要O(n)额外空间inplace_merge理论上可原地但实现可能使用缓冲区13. 测试与调试技巧13.1 边界条件测试空容器单元素容器全等元素容器已排序/逆序数据13.2 谓词验证确保谓词满足严格弱序排序相关算法无副作用并行算法不修改元素非修改算法13.3 迭代器有效性检查使用_GLIBCXX_DEBUG等调试模式检测迭代器错误g -D_GLIBCXX_DEBUG your_program.cpp14. 扩展阅读与资源14.1 推荐书籍《Effective STL》Scott Meyers《C标准库》Nicolai Josuttis《算法导论》Thomas H. Cormen14.2 在线资源cppreference.comC Core GuidelinesSTL源码如libstdc、libc14.3 进阶主题自定义分配器与算法SIMD向量化优化GPU加速算法15. 总结与最佳实践经过对STL算法的系统梳理我们可以总结出以下最佳实践选择合适的算法根据数据特性和需求选择最匹配的算法注意复杂度了解算法的时间/空间复杂度避免性能陷阱利用现代C特性使用lambda、并行执行等新特性简化代码重视安全性检查迭代器有效性处理边界条件性能敏感处实测不同实现、不同数据规模下表现可能不同保持代码可读性适当注释复杂的算法组合STL算法是C高效编程的利器掌握它们能显著提升开发效率和代码质量。建议从常用算法开始逐步扩展到更复杂的应用场景最终达到灵活组合、游刃有余的境界。
返回列表