算法概述与遍历算法:算法分类、for_each遍历、transform遍历

大家好,欢迎来到这一讲。今天咱们聊聊STL里最基础、也最常用的东西——算法。

说实话,我刚接触STL那会儿,面对几十个算法名字,头都大了。什么排序、查找、修改、删除……感觉像背单词。后来我慢慢发现,其实算法没那么神秘。说白了,STL算法就是一组操作容器元素的函数模板。它们不依赖具体容器,只靠迭代器来工作。这一点,你想想看,是不是很巧妙?

算法的分类

STL算法大致可以分为几大类。我个人习惯按操作性质来分,这样好记:

分类 说明 常见算法举例
只读算法 不修改元素,只读取 for_eachfindcount
修改算法 会修改元素值 transformcopyreplace
排序算法 对序列进行排序 sortstable_sort
数值算法 数值计算相关 accumulateinner_product

嗯,这里要注意:只读算法修改算法是咱们日常用得最多的。今天咱们就重点讲两个遍历相关的算法:for_eachtransform

核心要点: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:遍历并转换

transformfor_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 特点 • 遍历 + 转换 • 结果写入目标容器 • 支持双输入版本 其他算法 • sort / stable_sort • find / count • accumulate

总结

今天咱们聊了STL算法的分类,重点讲了 for_eachtransform 这两个遍历算法。记住:

  • for_each 适合做“遍历 + 副作用”,比如打印、记录。
  • transform 适合做“遍历 + 转换”,生成新序列。
  • 两者都依赖迭代器,不依赖具体容器类型。

嗯,其实STL算法用多了你会发现,它们就像工具箱里的螺丝刀——每种都有特定的用途。选对了,代码既简洁又高效。选错了,就会绕弯路。希望今天的分享对你有帮助。

公众号:蓝海资料掘金营,微信 deep3321