算法概述与遍历算法:算法分类、for_each遍历、transform遍历
大家好,欢迎来到这一讲。今天咱们聊聊STL里最基础、也最常用的东西——算法。
说实话,我刚接触STL那会儿,面对几十个算法名字,头都大了。什么排序、查找、修改、删除……感觉像背单词。后来我慢慢发现,其实算法没那么神秘。说白了,STL算法就是一组操作容器元素的函数模板。它们不依赖具体容器,只靠迭代器来工作。这一点,你想想看,是不是很巧妙?
算法的分类
STL算法大致可以分为几大类。我个人习惯按操作性质来分,这样好记:
| 分类 | 说明 | 常见算法举例 |
|---|---|---|
| 只读算法 | 不修改元素,只读取 | for_each、find、count |
| 修改算法 | 会修改元素值 | transform、copy、replace |
| 排序算法 | 对序列进行排序 | sort、stable_sort |
| 数值算法 | 数值计算相关 | accumulate、inner_product |
嗯,这里要注意:只读算法和修改算法是咱们日常用得最多的。今天咱们就重点讲两个遍历相关的算法:for_each 和 transform。
核心要点:STL算法通过迭代器与容器解耦。你不需要知道容器内部结构,只要提供一对迭代器,算法就能工作。这就是泛型编程的魅力。
for_each:最简单的遍历
for_each 是STL里最朴素的遍历算法。它的作用就是:对容器中的每个元素,执行同一个操作。
我在项目中遇到过这样一个场景:需要把日志容器里的每条记录都打印出来。用 for_each 一行搞定,比手写循环清爽多了。
#include <iostream>
#include <vector>
#include <algorithm>
void print(int n) {
std::cout << n << " ";
}
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
std::for_each(vec.begin(), vec.end(), print);
// 输出:1 2 3 4 5
return 0;
}
你看,for_each 接受三个参数:起始迭代器、结束迭代器、以及一个可调用对象(函数、函数对象、lambda都行)。
小技巧:我个人更推荐用lambda表达式,尤其是操作逻辑简单的时候。代码更紧凑,可读性也更好。
std::for_each(vec.begin(), vec.end(), [](int n) {
std::cout << n * 2 << " ";
});
// 输出:2 4 6 8 10
为什么说 for_each 是只读算法?因为它不强制修改元素。但注意,如果你在lambda里传引用,还是可以改的。不过那样就违背了“只读”的初衷。我建议你保持语义清晰:只读就用 for_each,要修改就用 transform。
transform:遍历并转换
transform 比 for_each 多了一层意思:它遍历元素,然后把结果写到另一个地方。说白了,就是“遍历 + 转换 + 输出”。
我曾经在数据处理模块里,需要把一个 vector<int> 里的每个值都平方,存到另一个 vector 里。用 transform 简直不要太顺手。
#include <iostream>
#include <vector>
#include <algorithm>
int square(int x) {
return x * x;
}
int main() {
std::vector<int> src = {1, 2, 3, 4, 5};
std::vector<int> dst(src.size());
std::transform(src.begin(), src.end(), dst.begin(), square);
// dst 现在为:1 4 9 16 25
return 0;
}
transform 有四个参数:源起始、源结束、目标起始、转换函数。注意目标容器必须提前分配好空间,否则会出问题。
避坑指南:我曾经因为忘记给目标容器 resize,导致程序崩溃。如果你不确定目标容器大小,可以用 std::back_inserter 来动态插入。
std::vector<int> dst;
std::transform(src.begin(), src.end(), std::back_inserter(dst), square);
// 这样就不用提前分配空间了
transform 还有一个双输入版本,可以对两个序列做操作。比如把两个 vector 对应元素相加:
std::vector<int> a = {1, 2, 3};
std::vector<int> b = {4, 5, 6};
std::vector<int> c;
std::transform(a.begin(), a.end(), b.begin(), std::back_inserter(c),
[](int x, int y) { return x + y; });
// c 为:5 7 9
for_each vs transform:怎么选?
这两个算法看起来有点像,但用途不同。我总结了一下:
- for_each:关注“副作用”。比如打印、记录日志、发送信号。它不关心返回值。
- transform:关注“转换结果”。它把处理后的值存到目标容器,原容器不变。
你想想看,如果你只是想看一眼每个元素,用 for_each。如果你想生成一个新的、经过变换的序列,用 transform。别混用,代码意图会更清晰。
知识结构图
下面这张图帮你理清今天的内容脉络:
总结
今天咱们聊了STL算法的分类,重点讲了 for_each 和 transform 这两个遍历算法。记住:
for_each适合做“遍历 + 副作用”,比如打印、记录。transform适合做“遍历 + 转换”,生成新序列。- 两者都依赖迭代器,不依赖具体容器类型。
嗯,其实STL算法用多了你会发现,它们就像工具箱里的螺丝刀——每种都有特定的用途。选对了,代码既简洁又高效。选错了,就会绕弯路。希望今天的分享对你有帮助。