概述

继上一篇的排序与查找算法之后,本文将介绍 STL algorithm 中同样重要的数值计算和变换类算法。这些工具在日常开发中使用频率极高,熟练掌握能显著减少手写循环的代码量。

使用这些算法需要 #include <numeric>(数值算法)和 #include <algorithm>(变换算法)。

accumulate —— 累加

std::accumulate 对区间内的元素依次执行累加操作。它位于 <numeric> 头文件中。

基本累加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <numeric>
#include <vector>

int main() {
std::vector<int> v = {1, 2, 3, 4, 5};

// 基本用法:求和
int sum = std::accumulate(v.begin(), v.end(), 0);
std::cout << "Sum: " << sum << std::endl; // 15

// 指定初始值(注意类型)
long long bigSum = std::accumulate(v.begin(), v.end(), 0LL);

// 初始值不为 0 的情况
sum = std::accumulate(v.begin(), v.end(), 100); // 100 + 1 + 2 + ... + 5 = 115
std::cout << "Sum with init 100: " << sum << std::endl;

return 0;
}

自定义操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <numeric>
#include <vector>

int main() {
std::vector<int> v = {1, 2, 3, 4, 5};

// 求乘积
int product = std::accumulate(v.begin(), v.end(), 1, std::multiplies<int>());
std::cout << "Product: " << product << std::endl; // 120

// 求字符串拼接
std::vector<std::string> words = {"Hello", " ", "World", "!"};
std::string result = std::accumulate(words.begin(), words.end(), std::string());
std::cout << "Concat: " << result << std::endl; // Hello World!

// 用 lambda 自定义操作
// 求最大值
int maxVal = std::accumulate(v.begin(), v.end(), v[0], [](int a, int b) {
return std::max(a, b);
});
std::cout << "Max: " << maxVal << std::endl; // 5

return 0;
}

⚠️ 初始值的陷阱

1
2
3
4
5
6
7
8
std::vector<int> v = {1, 2, 3};

// ❌ 用 0 作为初始值,结果类型是 int,可能溢出
// int sum = std::accumulate(v.begin(), v.end(), 0);

// ✅ 用 0LL 或 0.0 确保结果类型正确
long long sum = std::accumulate(v.begin(), v.end(), 0LL);
double avg = std::accumulate(v.begin(), v.end(), 0.0) / v.size();

accumulate 的返回值类型由初始值决定,而不是元素类型。这是非常常见的坑。

partial_sum —— 前缀和

std::partial_sum 计算前缀和序列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <numeric>
#include <vector>

int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
std::vector<int> result(v.size());

// 前缀和
std::partial_sum(v.begin(), v.end(), result.begin());
for (int x : result) {
std::cout << x << " "; // 1 3 6 10 15
}
std::cout << std::endl;

// 前缀积
std::partial_sum(v.begin(), v.end(), result.begin(), std::multiplies<int>());
for (int x : result) {
std::cout << x << " "; // 1 2 6 24 120
}
std::cout << std::endl;

// 自定义前缀和:首项为1的等比数列
std::vector<int> base(5, 2); // {2, 2, 2, 2, 2}
std::partial_sum(base.begin(), base.end(), result.begin(), [](int a, int b) {
return a * b; // 2 4 8 16 32
});
for (int x : result) {
std::cout << x << " ";
}
std::cout << std::endl;

return 0;
}

实际应用:区间和查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <numeric>
#include <vector>

int main() {
std::vector<int> arr = {3, 1, 4, 1, 5, 9, 2, 6};

// 预计算前缀和
std::vector<int> prefix(arr.size() + 1, 0);
std::partial_sum(arr.begin(), arr.end(), prefix.begin() + 1);

// prefix: 0 3 4 8 9 14 23 25 31

// 区间 [l, r] 的和 = prefix[r+1] - prefix[l]
auto rangeSum = [&](int l, int r) {
return prefix[r + 1] - prefix[l];
};

std::cout << "Sum [0,2]: " << rangeSum(0, 2) << std::endl; // 3+1+4=8
std::cout << "Sum [2,5]: " << rangeSum(2, 5) << std::endl; // 4+1+5+9=19
std::cout << "Sum [0,7]: " << rangeSum(0, 7) << std::endl; // 31

return 0;
}

adjacent_difference —— 相邻差分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <numeric>
#include <vector>

int main() {
std::vector<int> v = {1, 3, 6, 10, 15}; // 这是前缀和

std::vector<int> diff(v.size());
std::adjacent_difference(v.begin(), v.end(), diff.begin());

for (int x : diff) {
std::cout << x << " "; // 1 2 3 4 5(还原原数组)
}
std::cout << std::endl;

// 自定义差分
std::vector<int> v2 = {1, 2, 6, 24, 120}; // 前缀积
std::adjacent_difference(v2.begin(), v2.end(), diff.begin(),
std::divides<int>());
for (int x : diff) {
std::cout << x << " "; // 1 2 3 4 5
}
std::cout << std::endl;

return 0;
}

next_permutation —— 全排列

std::next_permutation 将当前排列变为字典序的下一个排列。如果已经是最后一个排列,则变为第一个排列并返回 false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <algorithm>
#include <vector>

int main() {
std::vector<int> v = {1, 2, 3};

// 输出所有排列
do {
for (int x : v) {
std::cout << x << " ";
}
std::cout << std::endl;
} while (std::next_permutation(v.begin(), v.end()));

// 1 2 3
// 1 3 2
// 2 1 3
// 2 3 1
// 3 1 2
// 3 2 1

return 0;
}

⚠️ 使用前必须排序

1
2
3
4
5
6
7
8
9
10
std::vector<int> v = {3, 1, 2};

// ❌ 直接调用 next_permutation 会漏掉排列
// 它从当前状态开始生成后续排列,不是全排列

// ✅ 先排序到最小字典序
std::sort(v.begin(), v.end());
do {
// ...
} while (std::next_permutation(v.begin(), v.end()));

prev_permutation —— 上一个排列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <algorithm>
#include <vector>

int main() {
std::vector<int> v = {3, 2, 1}; // 最大排列

do {
for (int x : v) std::cout << x << " ";
std::cout << std::endl;
} while (std::prev_permutation(v.begin(), v.end()));
// 从 3 2 1 倒序到 1 2 3

return 0;
}

transform —— 元素变换

std::transform 对每个元素应用一个函数,将结果写入目标位置。

一元变换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <algorithm>
#include <vector>
#include <cctype>
#include <string>

int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
std::vector<int> result(v.size());

// 每个元素平方
std::transform(v.begin(), v.end(), result.begin(), [](int x) {
return x * x;
});
for (int x : result) std::cout << x << " "; // 1 4 9 16 25
std::cout << std::endl;

// 字符串转大写
std::string s = "hello world";
std::transform(s.begin(), s.end(), s.begin(), ::toupper);
std::cout << s << std::endl; // HELLO WORLD

// 原地变换(source 和 destination 相同)
std::vector<int> v2 = {1, -2, 3, -4, 5};
std::transform(v2.begin(), v2.end(), v2.begin(), [](int x) {
return x > 0 ? x : -x; // 取绝对值
});
for (int x : v2) std::cout << x << " "; // 1 2 3 4 5
std::cout << std::endl;

return 0;
}

二元变换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <algorithm>
#include <vector>

int main() {
std::vector<int> a = {1, 2, 3, 4, 5};
std::vector<int> b = {10, 20, 30, 40, 50};
std::vector<int> result(a.size());

// 逐元素相加
std::transform(a.begin(), a.end(), b.begin(), result.begin(),
[](int x, int y) { return x + y; });
for (int x : result) std::cout << x << " "; // 11 22 33 44 55
std::cout << std::endl;

// 逐元素取较大值
std::transform(a.begin(), a.end(), b.begin(), result.begin(),
[](int x, int y) { return std::max(x, y); });
for (int x : result) std::cout << x << " "; // 10 20 30 40 50
std::cout << std::endl;

return 0;
}

fill 与 fill_n —— 填充

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <algorithm>
#include <vector>

int main() {
std::vector<int> v(10);

// 全部填充为 42
std::fill(v.begin(), v.end(), 42);
for (int x : v) std::cout << x << " "; // 42 42 42 ... (10个)
std::cout << std::endl;

// 填充前 5 个元素
std::fill(v.begin(), v.begin() + 5, 99);
for (int x : v) std::cout << x << " "; // 99 99 99 99 99 42 42 42 42 42
std::cout << std::endl;

// fill_n:填充 n 个元素
std::fill_n(v.begin(), 3, 0); // 前3个填0
for (int x : v) std::cout << x << " "; // 0 0 0 99 99 42 42 42 42 42
std::cout << std::endl;

// ⚠️ fill_n 在 vector 上使用时注意不要超出范围
std::vector<int> v2;
// std::fill_n(v2.begin(), 5, 1); // ❌ UB!v2 为空
v2.resize(5);
std::fill_n(v2.begin(), 5, 1); // ✅ OK

return 0;
}

generate 与 generate_n —— 生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <algorithm>
#include <vector>
#include <random>

int main() {
std::vector<int> v(10);

// 填充随机数
std::mt19937 rng(42); // 固定种子的随机数引擎
std::generate(v.begin(), v.end(), [&rng]() {
return rng() % 100;
});
for (int x : v) std::cout << x << " ";
std::cout << std::endl;

// 生成递增序列
int counter = 1;
std::generate(v.begin(), v.end(), [&counter]() {
return counter++;
});
for (int x : v) std::cout << x << " "; // 1 2 3 4 5 6 7 8 9 10
std::cout << std::endl;

// generate_n:生成 n 个
counter = 0;
std::generate_n(v.begin(), 5, [&counter]() {
counter += 2;
return counter; // 2 4 6 8 10
});
for (int x : v) std::cout << x << " "; // 2 4 6 8 10 6 7 8 9 10
std::cout << std::endl;

// 生成斐波那契数列
int a = 0, b = 1;
std::generate(v.begin(), v.end(), [&a, &b]() {
int result = a;
int next = a + b;
a = b;
b = next;
return result;
});
for (int x : v) std::cout << x << " "; // 0 1 1 2 3 5 8 13 21 34
std::cout << std::endl;

return 0;
}

iota —— 递增填充

std::iota(希腊字母 ι,读作 iota)以递增的方式填充序列,从给定初始值开始。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <numeric>
#include <vector>

int main() {
std::vector<int> v(10);

// 从 0 开始递增填充
std::iota(v.begin(), v.end(), 0);
for (int x : v) std::cout << x << " "; // 0 1 2 3 4 5 6 7 8 9
std::cout << std::endl;

// 从 1 开始
std::iota(v.begin(), v.end(), 1);
for (int x : v) std::cout << x << " "; // 1 2 3 4 5 6 7 8 9 10
std::cout << std::endl;

// 生成字母序列
std::vector<char> letters(26);
std::iota(letters.begin(), letters.end(), 'a');
for (char c : letters) std::cout << c; // abcdefghijklmnopqrstuvwxyz
std::cout << std::endl;

return 0;
}

实际应用:生成索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <numeric>
#include <vector>
#include <algorithm>

int main() {
std::vector<int> data = {30, 10, 20, 50, 40};
int n = data.size();

// 生成索引 0, 1, 2, 3, 4
std::vector<int> idx(n);
std::iota(idx.begin(), idx.end(), 0);

// 按照对应 data 值的大小排序索引
std::sort(idx.begin(), idx.end(), [&](int i, int j) {
return data[i] < data[j];
});

// 输出排序后的值
std::cout << "Sorted by index: ";
for (int i : idx) {
std::cout << data[i] << " "; // 10 20 30 40 50
}
std::cout << std::endl;

return 0;
}

merge —— 合并有序序列

std::merge 将两个已排序的序列合并为一个新的有序序列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <algorithm>
#include <vector>

int main() {
std::vector<int> a = {1, 3, 5, 7};
std::vector<int> b = {2, 4, 6, 8};
std::vector<int> result(a.size() + b.size());

// 合并两个有序数组
std::merge(a.begin(), a.end(), b.begin(), b.end(), result.begin());

for (int x : result) {
std::cout << x << " "; // 1 2 3 4 5 6 7 8
}
std::cout << std::endl;

// 有重复元素的情况
std::vector<int> c = {1, 2, 4, 5};
std::vector<int> d = {2, 3, 5, 6};
std::vector<int> result2(c.size() + d.size());

std::merge(c.begin(), c.end(), d.begin(), d.end(), result2.begin());
for (int x : result2) {
std::cout << x << " "; // 1 2 2 3 4 5 5 6
}
std::cout << std::endl;

return 0;
}

merge 降序序列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <algorithm>
#include <vector>

int main() {
std::vector<int> a = {7, 5, 3, 1};
std::vector<int> b = {8, 6, 4, 2};
std::vector<int> result(a.size() + b.size());

std::merge(a.begin(), a.end(), b.begin(), b.end(), result.begin(),
std::greater<int>());
for (int x : result) {
std::cout << x << " "; // 8 7 6 5 4 3 2 1
}
std::cout << std::endl;

return 0;
}

inplace_merge —— 原地合并

当两个有序子序列在同一个容器中相邻时,可以用 inplace_merge

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <algorithm>
#include <vector>

int main() {
std::vector<int> v = {1, 3, 5, 2, 4, 6};
// 前半部分 [0,3) 和后半部分 [3,6) 各自有序

std::inplace_merge(v.begin(), v.begin() + 3, v.end());
for (int x : v) {
std::cout << x << " "; // 1 2 3 4 5 6
}
std::cout << std::endl;

return 0;
}

更多实用算法速览

remove / remove_if —— 移除元素(不实际删除)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <algorithm>
#include <vector>

int main() {
std::vector<int> v = {1, 2, 3, 4, 5, 2, 6, 2, 7};

// 移除所有 2
auto newEnd = std::remove(v.begin(), v.end(), 2);
v.erase(newEnd, v.end());
for (int x : v) std::cout << x << " "; // 1 3 4 5 6 7
std::cout << std::endl;

// remove_if:移除满足条件的元素
v = {1, 2, 3, 4, 5, 6, 7, 8, 9};
auto ne = std::remove_if(v.begin(), v.end(), [](int x) {
return x % 2 == 0; // 移除偶数
});
v.erase(ne, v.end());
for (int x : v) std::cout << x << " "; // 1 3 5 7 9
std::cout << std::endl;

return 0;
}

replace / replace_if —— 替换元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <algorithm>
#include <vector>

int main() {
std::vector<int> v = {1, 2, 3, 2, 4, 2, 5};

// 把所有 2 替换为 99
std::replace(v.begin(), v.end(), 2, 99);
for (int x : v) std::cout << x << " "; // 1 99 3 99 4 99 5
std::cout << std::endl;

// replace_if:满足条件的替换
v = {1, 2, 3, 4, 5, 6, 7};
std::replace_if(v.begin(), v.end(), [](int x) {
return x % 2 == 0;
}, 0);
for (int x : v) std::cout << x << " "; // 1 0 3 0 5 0 7
std::cout << std::endl;

return 0;
}

for_each —— 遍历操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <algorithm>
#include <vector>

int main() {
std::vector<int> v = {1, 2, 3, 4, 5};

// for_each 对每个元素执行操作
std::for_each(v.begin(), v.end(), [](int& x) {
x *= 2;
});
for (int x : v) std::cout << x << " "; // 2 4 6 8 10
std::cout << std::endl;

// 带索引遍历
int index = 0;
std::for_each(v.begin(), v.end(), [&index](int x) {
std::cout << "[" << index++ << "] = " << x << std::endl;
});

return 0;
}

常见陷阱总结

1. accumulate 初始值类型决定返回类型

1
2
3
std::vector<double> v = {1.5, 2.5, 3.5};
// ❌ double sum = accumulate(v.begin(), v.end(), 0); // 0 是 int,结果被截断!
// ✅ double sum = accumulate(v.begin(), v.end(), 0.0);

2. remove 不实际删除元素

unique 一样,removeremove_if 只是把不需要的元素移到后面,必须搭配 erase

1
2
3
4
5
// ❌ 只调用 remove,vector 大小不变
// std::remove(v.begin(), v.end(), 2);

// ✅ erase-remove 惯用法
v.erase(std::remove(v.begin(), v.end(), 2), v.end());

3. next_permutation 需要排序后的起点

1
2
3
4
5
6
std::vector<int> v = {2, 1, 3};

// ❌ 直接用 next_permutation 可能漏排列
// ✅ sort 后再用
std::sort(v.begin(), v.end()); // {1, 2, 3}
do { /* ... */ } while (std::next_permutation(v.begin(), v.end()));

总结

本系列两篇文章覆盖了 STL algorithm 中最核心的算法。记住一个原则:优先使用标准库算法而不是手写循环——这不仅代码更简洁,而且经过充分优化和测试。当你在写一个 for 循环来对元素做变换、查找或计算时,先想想 <algorithm> 里有没有现成的。

算法 用途 头文件
accumulate 累加/累乘 <numeric>
partial_sum 前缀和 <numeric>
iota 递增填充 <numeric>
adjacent_difference 差分 <numeric>
transform 元素变换 <algorithm>
fill/fill_n 填充值 <algorithm>
generate/generate_n 生成值 <algorithm>
next_permutation 下一排列 <algorithm>
merge 合并有序序列 <algorithm>
remove/remove_if 移除元素 <algorithm>
replace/replace_if 替换元素 <algorithm>
for_each 遍历操作 <algorithm>