十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

C++标准库算法详解:从查找排序到数值计算

C++标准库算法详解:从查找排序到数值计算 1. C标准库算法概览C标准库提供了丰富的算法主要定义在algorithm和numeric头文件中。这些算法可以大大简化日常开发工作避免重复造轮子。根据功能特性我们可以将这些算法分为以下几大类非修改序列算法不改变容器内容如find、count等修改序列算法会改变容器内容如copy、transform等排序和相关算法如sort、binary_search等堆算法如make_heap、push_heap等数值算法如accumulate、inner_product等这些算法大多以迭代器作为参数因此可以适用于各种容器类型具有很高的通用性。掌握这些算法能显著提升代码质量和开发效率。2. 非修改序列算法详解2.1 查找算法查找算法是最常用的非修改序列算法主要包括以下几种2.1.1 find和find_iffind用于查找特定值find_if则使用谓词进行条件查找vectorint 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系列算法的时间复杂度为O(n)对于大型容器应考虑使用更高效的查找方式如二分查找需先排序2.1.2 find_end和searchfind_end查找子序列最后一次出现的位置search查找子序列第一次出现的位置vectorint nums {1, 2, 3, 1, 2, 3}; vectorint sub {1, 2}; // 查找最后一次出现的位置 auto last find_end(nums.begin(), nums.end(), sub.begin(), sub.end()); if (last ! nums.end()) { cout Last starts at: distance(nums.begin(), last) endl; // 输出3 } // 查找第一次出现的位置 auto first search(nums.begin(), nums.end(), sub.begin(), sub.end()); cout First starts at: distance(nums.begin(), first) endl; // 输出02.2 计数算法2.2.1 count和count_ifcount统计特定值出现的次数count_if统计满足条件的元素数量vectorint vec {1, 2, 3, 2, 4, 2}; // 统计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; }); // 结果为42.3 遍历算法2.3.1 for_eachfor_each对范围内的每个元素应用一个函数vectorint vec {1, 2, 3, 4, 5}; // 将每个元素乘以2 for_each(vec.begin(), vec.end(), [](int x) { x * 2; }); // vec变为{2, 4, 6, 8, 10}提示C17引入了for_each_n可以指定处理前n个元素2.4 比较算法2.4.1 equal和mismatchequal判断两个范围是否相等mismatch返回第一个不匹配的位置vectorint a {1, 2, 3}; vectorint 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; // 3 vs 4 }2.4.2 all_of/any_of/none_of这些算法检查范围内元素是否满足特定条件vectorint 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_neg none_of(vec.begin(), vec.end(), [](int x) { return x 0; }); // true3. 修改序列算法详解3.1 复制算法3.1.1 copy和copy_ifcopy复制整个范围copy_if只复制满足条件的元素vectorint src {1, 2, 3, 4, 5}; vectorint dest(5); // 需预先分配空间 // 复制所有元素 copy(src.begin(), src.end(), dest.begin()); // dest: [1,2,3,4,5] // 复制偶数到新容器 vectorint evens; copy_if(src.begin(), src.end(), back_inserter(evens), [](int x) { return x % 2 0; }); // evens: [2,4]注意使用back_inserter可以自动扩展容器无需预先分配空间3.2 变换算法3.2.1 transformtransform对元素进行转换并存储结果vectorint nums {1, 2, 3}; vectorint squares(3); // 计算平方 transform(nums.begin(), nums.end(), squares.begin(), [](int x) { return x * x; }); // squares: [1,4,9] // 两个范围相加 vectorint a {1, 2, 3}; vectorint b {4, 5, 6}; vectorint sum(3); transform(a.begin(), a.end(), b.begin(), sum.begin(), [](int x, int y) { return x y; }); // sum: [5,7,9]3.3 替换算法3.3.1 replace系列replace直接替换元素replace_copy复制时替换vectorint 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 vectorint res; replace_copy(nums.begin(), nums.end(), back_inserter(res), 3, 300); // res: [1,0,300,0,5]3.4 删除算法3.4.1 remove系列remove逻辑删除元素需配合erase物理删除vectorint 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]重要remove只是将不删除的元素前移返回新的逻辑结尾必须配合erase才能真正删除3.5 其他修改算法3.5.1 unique去除连续重复元素vectorint 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}3.5.2 reverse反转元素顺序vectorint vec {1, 2, 3, 4, 5}; reverse(vec.begin(), vec.end()); // vec: {5, 4, 3, 2, 1}3.5.3 rotate旋转元素vectorint vec {1, 2, 3, 4, 5}; rotate(vec.begin(), vec.begin() 2, vec.end()); // vec: {3, 4, 5, 1, 2}3.5.4 shuffle随机打乱元素vectorint vec {1, 2, 3, 4, 5}; random_device rd; mt19937 g(rd()); shuffle(vec.begin(), vec.end(), g); // 随机顺序如{3,1,5,2,4}4. 排序和相关算法4.1 排序算法4.1.1 sort和stable_sortsort是快速排序stable_sort是稳定排序vectorint vec {5, 3, 1, 4, 2}; // 默认升序 sort(vec.begin(), vec.end()); // vec: {1,2,3,4,5} // 降序 sort(vec.begin(), vec.end(), greaterint()); // vec: {5,4,3,2,1} // 自定义比较 sort(vec.begin(), vec.end(), [](int a, int b) { return a b; }); // 稳定排序保持相等元素的相对顺序 vectorpairint, int pairs {{1,2}, {2,1}, {1,1}, {2,2}}; stable_sort(pairs.begin(), pairs.end(), [](const auto a, const auto b) { return a.first b.first; });4.1.2 partial_sort部分排序vectorint vec {5, 3, 1, 4, 2, 6}; // 将最小的3个元素放在前面并排序 partial_sort(vec.begin(), vec.begin() 3, vec.end()); // vec前三个元素是1,2,3后面是未排序的4,5,64.1.3 nth_element找到第n小的元素vectorint vec {5, 3, 1, 4, 2, 6}; // 找到第三小的元素 nth_element(vec.begin(), vec.begin() 2, vec.end()); // vec[2]是3左边3右边34.2 二分查找需在已排序的容器上使用4.2.1 binary_search判断元素是否存在vectorint sorted {1, 3, 3, 5, 7}; bool exists binary_search(sorted.begin(), sorted.end(), 3); // true4.2.2 lower_bound和upper_bound查找边界vectorint sorted {1, 3, 3, 5, 7}; // 第一个不小于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; // 34.2.3 equal_range同时获取上下界vectorint sorted {1, 3, 3, 5, 7}; auto range equal_range(sorted.begin(), sorted.end(), 3); // range.first指向第一个3range.second指向第一个大于3的元素4.3 合并算法4.3.1 merge合并两个已排序的范围vectorint a {1, 3, 5}; vectorint b {2, 4, 6}; vectorint merged(a.size() b.size()); merge(a.begin(), a.end(), b.begin(), b.end(), merged.begin()); // merged: [1,2,3,4,5,6]5. 堆算法STL提供了堆操作算法5.1 make_heap构建堆vectorint vec {4, 1, 3, 2, 5}; make_heap(vec.begin(), vec.end()); // 最大堆vec: {5,4,3,2,1}5.2 push_heap和pop_heap堆操作// 添加元素到堆 vec.push_back(6); push_heap(vec.begin(), vec.end()); // vec: {6,4,5,2,1,3} // 弹出堆顶元素 pop_heap(vec.begin(), vec.end()); // 将最大元素移到末尾vec: {5,4,3,2,1,6} int max_val vec.back(); // 获取最大值6 vec.pop_back(); // 移除最大值5.3 sort_heap堆排序sort_heap(vec.begin(), vec.end()); // 升序排列vec: {1,2,3,4,5}6. 数值算法6.1 accumulate累加或自定义操作vectorint vec {1, 2, 3, 4, 5}; // 求和 int sum accumulate(vec.begin(), vec.end(), 0); // 15 // 求积 int product accumulate(vec.begin(), vec.end(), 1, multipliesint()); // 1206.2 inner_product内积或自定义操作vectorint a {1, 2, 3}; vectorint b {4, 5, 6}; // 内积 int dot inner_product(a.begin(), a.end(), b.begin(), 0); // 1*42*53*6326.3 iota填充递增序列vectorint vec(5); iota(vec.begin(), vec.end(), 10); // vec: {10,11,12,13,14}6.4 partial_sum部分和vectorint src {1, 2, 3, 4, 5}; vectorint dst(src.size()); partial_sum(src.begin(), src.end(), dst.begin()); // dst: {1,3,6,10,15}6.5 adjacent_difference相邻差值vectorint src {1, 2, 3, 4, 5}; vectorint dst(src.size()); adjacent_difference(src.begin(), src.end(), dst.begin()); // dst: {1,1,1,1,1}7. 其他实用算法7.1 generate用生成函数填充vectorint vec(5); int n 0; generate(vec.begin(), vec.end(), [n]() { return n; }); // vec: {0,1,2,3,4}7.2 includes检查包含关系vectorint vec1 {1, 2, 3, 4, 5}; vectorint vec2 {2, 4}; bool includes includes(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()); // true7.3 集合算法7.3.1 set_union并集vectorint v1 {1, 2, 3, 4, 5}; vectorint v2 {3, 4, 5, 6, 7}; vectorint result; set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), back_inserter(result)); // result: {1,2,3,4,5,6,7}7.3.2 set_intersection交集result.clear(); set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), back_inserter(result)); // result: {3,4,5}7.3.3 set_difference差集result.clear(); set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), back_inserter(result)); // result: {1,2}7.3.4 set_symmetric_difference对称差集result.clear(); set_symmetric_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), back_inserter(result)); // result: {1,2,6,7}8. 算法使用经验与技巧8.1 算法选择指南查找操作无序数据find、find_if有序数据binary_search、lower_bound子序列查找search、find_end排序需求普通排序sort稳定排序stable_sort部分排序partial_sort、nth_element修改容器复制copy、copy_if变换transform删除removeerase8.2 性能考虑对于大型数据集优先使用O(n log n)算法如sort避免多次遍历可考虑组合算法内存考虑stable_sort需要额外内存inplace_merge可减少内存使用预分配空间使用back_inserter可避免预分配已知大小时预分配可提高性能8.3 常见陷阱迭代器失效修改容器可能导致迭代器失效特别小心erase和insert操作范围错误确保目标范围足够大使用back_inserter避免越界谓词设计确保谓词是纯函数无副作用对于排序确保比较函数是严格弱序8.4 C17/20新特性并行算法许多算法支持并行执行策略如sort(execution::par, ...)新算法sample随机采样clamp限制值范围gcd/lcm数学运算范围库(C20)更简洁的范围操作如sort(my_vec)代替sort(my_vec.begin(), my_vec.end())
返回列表