蓝易云 - C++STL函数
以下内容为“蓝易云”工程团队量身打造:用最新 C++20/23 STL打出“快、稳、低成本”三板斧。我们直奔主题:该用哪些函数、如何组合、复杂度与边界。少空话,多可复制代码。⚙️🚀
一、STL心法:容器选型先于算法
-
吞吐优先:
std::vector+ 连续内存,配<algorithm>/<ranges>,极致缓存友好。 -
查找/计数:
std::unordered_map/set;有序遍历再用std::map/set。 -
批量运算:优先非修改性算法(如
for_each、transform),写时再落地。 -
不要手搓循环能用算法就用,复杂度一眼可见,回归成本低。🙂
二、函数速览(高频且企业好用)—vditor表格 ✅
| 场景 | 函数 | 复杂度 | 关键点/红线 |
|---|---|---|---|
| 排序/去重 | ranges::sort / unique / erase |
O(n log n) / O(n) |
先排后 unique,最后 erase 真正删 |
| 选择前K | nth_element / partial_sort |
O(n) / O(n log k) |
Top-K 选 nth_element 更稳 |
| 条件删除 | erase_if(C++20) |
O(n) |
容器原生,无需“删除后收缩”套路 |
| 累积/扫描 | accumulate / reduce / inclusive_scan |
O(n) |
并行策略见下节 |
| 映射变换 | transform / ranges::transform |
O(n) |
纯函数式更易测 |
| 查找 | find / binary_search / lower_bound |
O(n)/O(log n) |
有序容器才谈二分 |
| 分割/拼接 | views::split / views::join |
视图惰性 | 零拷贝管线,别提前实化 |
| 计数表 | unordered_map::operator[] / try_emplace |
摊销 O(1) | 统计最顺手;注意负载因子 |
三、范式一:排序+去重+条件删除(向量场景最常见)
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<int> a = {5,3,9,3,1,9,7,2};
ranges::sort(a); // 1) 排序:O(n log n)
a.erase(ranges::unique(a).begin(), a.end()); // 2) 去重:相邻重复折叠
erase_if(a, [](int x){ return x % 2 == 0; }); // 3) 删偶数:O(n)
for (int v : a) cout << v << " ";
}
解释:
-
ranges::sort使用投影与自定义比较更灵活; -
unique只挪动不删除,最后erase才真正减容; -
erase_if是 C++20 原生接口,避免“手写remove_if+erase套路”。结果:有序、去重、只留奇数。✅
四、范式二:Top-K(nth_element 比全排更快)
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<int> a = {6,1,9,3,5,8,2,7,4};
size_t k = 3;
nth_element(a.begin(), a.begin()+k, a.end(), greater<int>()); // 前k大
a.resize(k); // 只取前k,无需额外排序
// 若需要有序展示,再 partial_sort(a.begin(), a.end(), ...)
for (int v: a) cout << v << " ";
}
解释:
-
nth_element线性期望复杂度,定位分界即可; -
若“既要Top-K又要有序”,再
partial_sort二次处理; -
大流量榜单/风控阈值选取场景非常合适。📈
五、范式三:计数与聚合(哈希表 + 扫描)
#include <bits/stdc++.h>
using namespace std;
int main(){
vector<string> logs = {"u1","u2","u1","u3","u2","u1"};
unordered_map<string,int> cnt;
for (auto &id: logs) ++cnt[id]; // 摊销 O(1) 计数
// 聚合:统计出现>=2的用户数
int m = transform_reduce(cnt.begin(), cnt.end(), 0,
plus<>(), [](auto &p){ return p.second >= 2; });
cout << m;
}
解释:
-
unordered_map做频次统计最顺手; -
transform_reduce一步完成“映射+归约”,语义清晰; -
若需并行,可加执行策略
std::execution::par(见下一节)。
六、并行算法:写法不变,速度“白捡”
#include <bits/stdc++.h>
#include <execution>
using namespace std;
int main(){
vector<double> v(1'000'000, 1.0);
// 并行求和(浮点结果为近似,业务允许则可用)
double s = reduce(execution::par, v.begin(), v.end(), 0.0);
cout << s;
}
解释:
-
reduce可并行,且结合树形规约; -
浮点并行规约非严格可结合,微小误差是预期;计费等强一致业务请用整数或定点。⚠️
七、Ranges 管线:零拷贝、可读性爆表
#include <bits/stdc++.h>
#include <ranges>
using namespace std;
int main(){
vector<int> a = {1,2,3,4,5,6,7,8,9};
auto pipe = a | views::filter([](int x){ return x%2; }) // 取奇数
| views::transform([](int x){ return x*x; }) // 平方
| views::take(3); // 取前三
for (int x : pipe) cout << x << " ";
}
解释:
-
views::filter/transform/take全是惰性视图,不复制底层数据; -
管线式表达把业务意图一行看清,非常利于Code Review与重构。✨
八、字符串拆分与数值解析(高性能写法)
#include <bits/stdc++.h>
#include <charconv> // from_chars
#include <ranges>
using namespace std;
int main(){
string s = "10,20,30";
vector<int> out; out.reserve(3);
for (auto part : s | views::split(',') | views::transform([](auto v){
return string_view(&*v.begin(), ranges::distance(v)); })) {
int x{};
auto [p, ec] = from_chars(part.data(), part.data()+part.size(), x);
if(ec==errc{}) out.push_back(x);
}
cout << out.size();
}
解释:
-
views::split按分隔符切视图; -
from_chars零分配、零本地化开销,远优于stoi(异常+区域设置慢); -
适合日志解析/网关层高频路径。🔥
九、红线与易错点(务必记住)
-
迭代器失效:对
vector插入/删除会使迭代器失效;删除请用erase_if或“索引回写”。 -
比较器自反性:
sort比较器必须定义严格弱序,否则 UB。 -
二分前需有序:
binary_search/lower_bound的先决条件是“全序”。 -
浮点并行规约有误差:金融/结算请别用。
-
哈希表装载因子:超载会退化,
reserve()预留容量,控制 rehash。
十、工作流程(vditor/Markdown:Mermaid)
flowchart LR
A[确定数据形态] --> B{需要顺序?}
B -- 是 --> C[vector + ranges 算法]
B -- 否 --> D[unordered_map/set + 计数/哈希]
C --> E{是否Top-K?}
E -- 是 --> F[nth_element / partial_sort]
E -- 否 --> G[sort + unique + erase_if]
D --> H[transform_reduce / 并行reduce]
G --> I[评估迭代器安全]
H --> I
I --> J[单测与复杂度确认]
十一、原理解释表(可直接粘贴 ✅)
| 要点 | 作用 | 为什么有效 | 使用边界 |
|---|---|---|---|
| ranges 管线 | 可读性高、零拷贝 | 惰性视图+组合子 | 落地前再 to<vector> |
| nth_element | 线性选前K | 避免全序排序 | 结果未排序需再处理 |
| erase_if | 条件删除一气呵成 | 少写错 remove-erase 惯用法 |
C++20+ |
| from_chars | 快速解析数值 | 无异常、无本地化 | 仅基础数制 |
| 并行reduce | 白捡吞吐 | 不改接口即可提速 | 浮点近似 |
十二、综合小例:日志Top-K用户(端到端)
#include <bits/stdc++.h>
using namespace std;
int main(){
vector<string> logs = {/*...大量uid...*/};
unordered_map<string,int> cnt; cnt.reserve(logs.size()/2);
for (auto &u: logs) ++cnt[u]; // 计数
vector<pair<string,int>> v; v.reserve(cnt.size());
transform(cnt.begin(), cnt.end(), back_inserter(v),
[](auto &p){ return p; }); // 映射到向量
size_t k = 10;
if (v.size() > k) {
nth_element(v.begin(), v.begin()+k, v.end(),
[](auto &a, auto &b){ return a.second > b.second; });
v.resize(k); // 前K条
sort(v.begin(), v.end(),
[](auto &a, auto &b){ return a.second > b.second; }); // 展示有序
} else {
sort(v.begin(), v.end(),
[](auto &a, auto &b){ return a.second > b.second; });
}
for (auto &e: v) cout << e.first << " " << e.second << "\n";
}
解释:
-
哈希计数 +
transform打平成向量; -
nth_element定位 Top-K,避免全量排序; -
最后
sort只是对K条排序,展示友好,复杂度受控。🎯
收口
STL 的价值不在“会不会用某个函数”,而在能否用更少代码表达更强语义。把 <ranges> 管线、nth_element、erase_if、from_chars、并行 reduce 这些“新常识”握在手里,你的 C++ 代码就具备了更高吞吐、更少Bug、更易维护的工程特质。需要我根据你们现网热点路径,输出容器/算法替换建议 + 复杂度评审表,我可以直接给到“可落地清单”。
更多推荐



所有评论(0)