一、前言:为什么STL算法是刷题&开发神器?
在前面我们彻底吃透了C++语法、面向对象、智能指针、模板、STL所有容器。容器负责存数据,算法负责处理数据,二者结合才是完整的STL体系。
在算法刷题和企业开发中,手写排序、查找、去重、遍历不仅代码冗余、效率低下,还极易出现边界BUG。而STL算法库是官方高度优化、经过千万项目验证、底层极致优化的通用算法库。
可以这么说:熟练掌握STL算法,能让你的刷题速度翻倍,工程代码量减半。
很多开发者只会简单sort排序,不懂二分查找、unique去重、条件筛选、批量变换,遇到复杂场景依旧手写逻辑,低效且易错。
本篇文章一次性讲透STL所有高频算法,包含底层原理、标准写法、自定义规则、Lambda适配、全网踩坑点,零基础一站式通关,彻底闭环STL所有知识点!
二、STL算法整体分类与核心特性
2.1 算法五大分类
-
遍历算法:批量处理容器所有元素(for_each)
-
排序算法:无序序列重排(sort)
-
查找算法:精准查找、条件查找、二分查找
-
修改算法:去重、替换、反转、拷贝、合并
-
统计算法:最值查找、元素计数、条件统计
2.2 核心通用规则(必考)
-
STL算法不依赖容器,全部通过迭代器操作数据,适配所有STL容器;
-
算法左闭右开:[begin, end),包含起始迭代器,不包含末尾迭代器;
-
所有算法支持自定义规则:函数指针、仿函数、Lambda表达式;
-
算法不会改变容器容量,仅修改元素内容或顺序。
三、遍历算法:for_each 万能遍历
for_each 是STL通用遍历算法,替代传统for循环,支持遍历过程自定义操作,适配所有容器。
3.1 基础语法
for_each(起始迭代器, 末尾迭代器, 处理函数);
3.2 实战代码(三种遍历方式)
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 普通函数遍历
void print(int val)
{
cout << val << " ";
}
int main()
{
vector<int> v = {1,2,3,4,5};
// 1. 普通函数遍历
for_each(v.begin(), v.end(), print);
cout << endl;
// 2. Lambda表达式遍历(工程最常用)
for_each(v.begin(), v.end(), [](int val){
cout << val * 2 << " ";
});
return 0;
}
优势:遍历逻辑与业务解耦,支持批量自定义处理,代码更简洁优雅。
四、排序算法:sort底层原理与自定义排序
sort是STL最核心、最高频的算法,也是面试必考底层原理。
4.1 sort底层原理(面试满分答案)
STL sort并非单一排序算法,是三者结合的混合排序:
-
数据量较大:快速排序(分区排序,效率最高)
-
数据量较小(小于16个元素):插入排序(小数组常数时间更优)
-
递归深度过深:堆排序(防止快排最坏情况栈溢出)
整体时间复杂度稳定 O(nlogn),是工业级最优排序算法。
4.2 默认升序排序
cpp
vector<int> v = {5,2,9,1,3};
sort(v.begin(), v.end());
// 默认升序:1 2 3 5 9
4.3 自定义降序 + 结构体排序(工程刚需)
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Person
{
string name;
int age;
};
int main()
{
vector<Person> vp = {{"张三",18},{"李四",20},{"王五",16}};
// 自定义规则:按年龄降序
sort(vp.begin(), vp.end(), [](const Person& p1, const Person& p2){
return p1.age > p2.age;
});
for(auto& p : vp)
{
cout << p.name << " " << p.age << endl;
}
return 0;
}
核心规则:比较函数返回 true 表示「第一个元素应该排在第二个元素前面」。
五、查找算法:find / find_if / binary_search
5.1 find 精准值查找
用于普通容器遍历查找,时间复杂度 O(n),找到返回迭代器,失败返回 end()
cpp
vector<int> v = {10,20,30,40};
auto it = find(v.begin(), v.end(), 30);
if(it != v.end())
{
cout << "找到元素:" << *it << endl;
}
vector<int> v = {10,20,30,40};
auto it = find(v.begin(), v.end(), 30);
if(it != v.end())
{
cout << "找到元素:" << *it << endl;
}
5.2 find_if 条件查找(高阶用法)
无需精准匹配,根据自定义条件查找,极大提升灵活度
cpp
// 查找第一个大于20的元素
auto it = find_if(v.begin(), v.end(), [](int val){
return val > 20;
});
5.3 binary_search 二分查找(高效O(logn))
硬性前提:容器必须有序
仅返回bool值,判断元素是否存在,不返回迭代器,效率远高于find
cpp
sort(v.begin(), v.end());
bool flag = binary_search(v.begin(), v.end(), 30);
六、去重算法:unique全网最标准写法
unique是刷题最高频、坑最多的去重算法!90%开发者都会用错。
6.1 unique核心原理
-
unique 不会直接删除元素,仅将重复元素后置;
-
返回值是去重后有效区间的末尾迭代器;
-
必须先排序,再去重,否则只能去除相邻重复元素。
6.2 彻底去重标准代码(模板直接复用)
cpp
vector<int> v = {2,2,1,1,3,3,2};
// 1. 先排序
sort(v.begin(), v.end());
// 2. 去重移位,获取有效末尾
auto new_end = unique(v.begin(), v.end());
// 3. 真正删除冗余元素
v.erase(new_end, v.end());
// 结果:1 2 3
必考口诀:先排序、再去重、最后erase删除
七、最值与计数算法
7.1 最值查找 max_element / min_element
cpp
vector<int> v = {5,1,9,3,7};
int max_val = *max_element(v.begin(), v.end());
int min_val = *min_element(v.begin(), v.end());
7.2 元素计数 count / count_if
cpp
// 统计元素2的个数
int cnt1 = count(v.begin(), v.end(), 2);
// 统计大于5的元素个数
int cnt2 = count_if(v.begin(), v.end(), [](int val){return val > 5;});
八、常用变换算法:反转/替换/拷贝/合并
8.1 reverse 反转序列
cpp
reverse(v.begin(), v.end()); // 整体反转容器元素
8.2 replace 元素替换
cpp
replace(v.begin(), v.end(), 2, 99); // 所有2替换为99
8.3 copy 容器拷贝
cpp
vector<int> v2(10);
copy(v.begin(), v.end(), v2.begin());
8.4 merge 有序合并
两个有序序列合并为一个有序序列
cpp
vector<int> a = {1,3,5};
vector<int> b = {2,4,6};
vector<int> res(6);
merge(a.begin(),a.end(),b.begin(),b.end(),res.begin());
九、算法三大适配方式:函数指针、仿函数、Lambda
STL算法的强大之处在于支持自定义规则,三种适配方式全覆盖:
9.1 函数指针(简单场景)
9.2 仿函数(复杂带状态场景)
9.3 Lambda表达式(工程首选、最简)
C++11后全部优先使用Lambda,代码内联、简洁、无需额外定义函数/类,是目前工业界统一规范。
十、全网高频踩坑合集(必看避坑)
坑1:unique不去重,数据毫无变化
忘记先排序!unique只能去除相邻重复,无序序列必须先sort再去重。
坑2:binary_search无序数组查找
二分查找依赖有序区间,无序数组使用直接逻辑错误,查找结果错乱。
坑3:sort自定义比较函数写反逻辑
比较函数返回值逻辑颠倒,导致升序变降序、排序错乱,刷题高频BUG。
坑4:忽略算法左闭右开区间
遍历、查找、操作区间不包含end迭代器,截取子序列极易越界。
坑5:find失败后直接解引用
未判断迭代器是否为end就取值,直接野指针崩溃。
十一、大厂面试满分标准答案
Q1:STL sort底层采用什么排序?为什么性能极强?
STL sort是自适应混合排序:大数据量使用快速排序,小数据量(小于16)使用插入排序,递归过深触发堆排序兜底。综合三者优势,规避了快排最坏情况、插入排序大数据低效的问题,整体时间复杂度稳定O(nlogn),是工业级最优排序实现。
Q2:unique去重为什么必须先排序?
unique的去重逻辑仅扫描并移除相邻重复元素,无序容器中重复元素分散,无法全部去除;先排序可让所有重复元素相邻聚集,配合unique移位+erase删除,实现真正全局去重。
Q3:find和binary_search的区别?
find是线性查找,O(n)复杂度,适配所有容器,返回元素迭代器;binary_search是二分查找,O(logn)复杂度,仅适用于有序容器,仅返回是否存在的布尔值,查找效率远高于线性查找。
Q4:STL算法和手写算法相比有什么优势?
STL算法经过官方极致优化、边界完备、无BUG、通用性强、适配所有容器;手写算法容易出现边界问题、逻辑漏洞、性能低效,且代码冗余,工程开发与刷题优先使用STL原生算法。
Q5:Lambda表达式在STL算法中的作用?
用于快速自定义算法规则,替代传统函数指针和仿函数,代码内联简洁、可读性高、灵活度强,是目前C++工程开发STL算法自定义规则的首选方式。
十二、今日总结
彻底通关 C++ STL全套算法体系,完成C++基础+进阶+STL全闭环:
✅ STL算法整体架构、分类与通用迭代器规则
✅ for_each万能遍历、多方式自定义处理
✅ sort混合排序底层原理、升降序+结构体自定义排序
✅ 线性查找+二分查找全套场景适配
✅ unique去重底层逻辑、全网标准去重模板
✅ 最值、计数、反转、替换、合并高频算法实战
✅ Lambda适配算法、工程最优编码规范
✅ 高频踩坑点与面试满分答题思路
至此,C++从零基础语法到STL全套高阶内容全部学完,具备完整的刷题、开发、面试能力!