1. C++标准库算法深度解析
作为一名C++开发者,我经常看到新手在面对标准库算法时感到困惑。实际上,STL算法是C++最强大的武器之一,合理运用可以大幅提升代码质量和开发效率。今天我就带大家深入探索这些算法的使用技巧和底层原理。
2. 非修改序列算法:安全的数据观察者
2.1 查找算法的实战应用
find和find_if可能是最常用的查找算法了。在实际项目中,我经常用它们来替代手写循环。比如在一个用户管理系统中查找特定ID的用户:
cpp复制struct User {
int id;
string name;
};
vector<User> users = {{1, "Alice"}, {2, "Bob"}, {3, "Charlie"}};
// 查找ID为2的用户
auto it = find_if(users.begin(), users.end(), [](const User& u) {
return u.id == 2;
});
if (it != users.end()) {
cout << "Found user: " << it->name << endl;
}
经验之谈:对于自定义类型,
find_if配合lambda表达式比find更灵活,因为可以直接访问对象成员。
2.2 计数算法的性能考量
count和count_if看似简单,但在大数据量下需要注意性能。我曾经在一个日志分析系统中错误地使用了count_if导致性能问题:
cpp复制// 低效写法:每次调用都创建lambda
int criticalCount = count_if(logs.begin(), logs.end(),
[](const LogEntry& e) { return e.level == LogLevel::CRITICAL; });
// 优化写法:将lambda提取为静态变量
static auto isCritical = [](const LogEntry& e) {
return e.level == LogLevel::CRITICAL;
};
int criticalCount = count_if(logs.begin(), logs.end(), isCritical);
2.3 遍历算法的现代替代方案
虽然for_each仍然有用,但在C++11之后,范围for循环通常更直观。不过for_each在某些场景下仍有优势:
cpp复制vector<int> data = {1, 2, 3, 4, 5};
// 传统for_each
for_each(data.begin(), data.end(), [](int& x) { x *= 2; });
// C++11范围for
for (auto& x : data) { x *= 2; }
// for_each的优势:可以指定范围
for_each(data.begin()+1, data.end()-1, [](int& x) { x += 10; });
3. 修改序列算法:高效的数据变形者
3.1 复制算法的内存管理技巧
copy和copy_if使用时最容易犯的错误就是目标容器空间不足。我吃过这个亏:
cpp复制vector<int> src = {1, 2, 3, 4, 5};
vector<int> dest(3); // 空间不足!
// 危险!可能导致内存越界
copy(src.begin(), src.end(), dest.begin());
// 安全做法1:预先分配足够空间
vector<int> dest2(src.size());
copy(src.begin(), src.end(), dest2.begin());
// 安全做法2:使用back_inserter
vector<int> dest3;
copy(src.begin(), src.end(), back_inserter(dest3));
3.2 变换算法的并行潜力
transform不仅用于简单转换,还能实现并行处理。结合C++17的并行算法:
cpp复制vector<int> input(1000000);
vector<int> output(input.size());
// 普通transform
transform(input.begin(), input.end(), output.begin(),
[](int x) { return x * x; });
// 并行transform (C++17)
transform(execution::par, input.begin(), input.end(), output.begin(),
[](int x) { return x * x; });
3.3 删除算法的正确姿势
remove和erase的组合是STL中最容易误解的模式之一。我曾经花了半天调试一个相关bug:
cpp复制vector<int> nums = {1, 2, 3, 2, 4, 2, 5};
// 错误示范:只调用remove
remove(nums.begin(), nums.end(), 2);
// nums现在是{1,3,4,5,4,2,5},size仍然是7
// 正确做法:erase-remove惯用法
nums.erase(remove(nums.begin(), nums.end(), 2), nums.end());
// nums现在是{1,3,4,5},size变为4
4. 排序算法:数据组织的艺术
4.1 排序算法的选择策略
sort和stable_sort的选择需要考虑稳定性和性能。在最近的项目中,我遇到了一个需要保持相对顺序的场景:
cpp复制struct Transaction {
int amount;
time_t timestamp;
};
vector<Transaction> transactions = {...};
// 按金额排序,但相同金额保持时间顺序
stable_sort(transactions.begin(), transactions.end(),
[](const Transaction& a, const Transaction& b) {
return a.amount < b.amount;
});
4.2 二分查找的高效应用
lower_bound和upper_bound是处理有序数据的利器。在实现一个时间范围查询时:
cpp复制vector<time_t> eventTimes = {...}; // 已排序
// 查找第一个不小于指定时间的事件
auto first = lower_bound(eventTimes.begin(), eventTimes.end(), targetTime);
// 查找第一个大于指定时间的事件
auto last = upper_bound(eventTimes.begin(), eventTimes.end(), targetTime);
// 获取时间范围内的所有事件
for (auto it = first; it != last; ++it) {
processEvent(*it);
}
5. 数值算法:数学计算的利器
5.1 累加算法的灵活运用
accumulate不只是求和,还能实现各种归约操作。比如计算几何平均值:
cpp复制vector<double> values = {...};
// 计算几何平均值
double product = accumulate(values.begin(), values.end(), 1.0,
[](double a, double b) { return a * b; });
double geometricMean = pow(product, 1.0/values.size());
5.2 内积算法的多维应用
inner_product不仅能计算点积,还能实现其他运算。比如计算两个向量的L2距离:
cpp复制vector<double> a = {...};
vector<double> b = {...};
// 计算平方差和
double sumSq = inner_product(a.begin(), a.end(), b.begin(), 0.0,
plus<double>(),
[](double x, double y) { return (x-y)*(x-y); });
double l2Distance = sqrt(sumSq);
6. 算法选择与性能优化
6.1 算法复杂度实战分析
理解算法复杂度对性能优化至关重要。这是我总结的常见算法复杂度:
| 算法 | 平均复杂度 | 最坏复杂度 | 适用场景 |
|---|---|---|---|
| sort | O(n log n) | O(n log n) | 通用排序 |
| stable_sort | O(n log n) | O(n log n) | 需要稳定排序 |
| partial_sort | O(n log k) | O(n log k) | 只关心前k个元素 |
| nth_element | O(n) | O(n²) | 找第n大元素 |
6.2 内存访问模式优化
算法性能不仅取决于复杂度,还受内存访问模式影响。比如vector和list的性能差异:
cpp复制list<int> lst = {...};
vector<int> vec(lst.begin(), lst.end());
// vector通常更快,因为内存连续
sort(vec.begin(), vec.end());
// list需要特殊算法
lst.sort(); // 使用list的成员函数
7. 现代C++中的算法增强
7.1 并行算法实战
C++17引入了并行算法,可以轻松利用多核性能:
cpp复制vector<int> data(1000000);
// 并行排序
sort(execution::par, data.begin(), data.end());
// 并行transform
vector<int> results(data.size());
transform(execution::par,
data.begin(), data.end(), results.begin(),
[](int x) { return complexCalculation(x); });
7.2 范围视图的优雅组合
C++20的范围库让算法组合更优雅:
cpp复制vector<int> data = {...};
// 传统方式
vector<int> temp;
copy_if(data.begin(), data.end(), back_inserter(temp),
[](int x) { return x % 2 == 0; });
sort(temp.begin(), temp.end());
// C++20范围方式
auto result = data | views::filter([](int x) { return x % 2 == 0; })
| ranges::to<vector>()
| ranges::actions::sort;
8. 常见陷阱与最佳实践
8.1 迭代器失效问题
在修改容器时最容易犯的错误就是迭代器失效:
cpp复制vector<int> data = {1, 2, 3, 4, 5};
// 危险!erase会使迭代器失效
for (auto it = data.begin(); it != data.end(); ++it) {
if (*it % 2 == 0) {
data.erase(it); // 错误!
}
}
// 正确做法
for (auto it = data.begin(); it != data.end(); ) {
if (*it % 2 == 0) {
it = data.erase(it); // erase返回下一个有效迭代器
} else {
++it;
}
}
8.2 谓词的设计原则
谓词(predicate)的质量直接影响算法表现:
cpp复制// 不好的谓词:有状态,可能导致意外行为
int threshold = 5;
auto badPredicate = [&threshold](int x) {
return x > threshold++; // 修改了外部状态
};
// 好的谓词:无状态,纯函数
auto goodPredicate = [](int x, int threshold) {
return x > threshold;
};
8.3 算法组合的艺术
合理组合算法可以解决复杂问题。比如删除所有重复元素:
cpp复制vector<int> data = {1, 2, 2, 3, 3, 3, 4, 5, 5};
// 先排序
sort(data.begin(), data.end());
// 再unique-erase
data.erase(unique(data.begin(), data.end()), data.end());
9. 性能测试与算法选择
9.1 实际性能对比测试
通过实际测试了解不同算法的性能差异:
cpp复制vector<int> bigData(1000000);
// 填充测试数据...
// 测试sort
auto start = chrono::high_resolution_clock::now();
sort(bigData.begin(), bigData.end());
auto end = chrono::high_resolution_clock::now();
cout << "sort: " << chrono::duration_cast<chrono::milliseconds>(end-start).count() << "ms\n";
// 测试stable_sort
start = chrono::high_resolution_clock::now();
stable_sort(bigData.begin(), bigData.end());
end = chrono::high_resolution_clock::now();
cout << "stable_sort: " << chrono::duration_cast<chrono::milliseconds>(end-start).count() << "ms\n";
9.2 算法选择的决策树
我总结了一个简单的算法选择决策流程:
-
是否需要修改容器?
- 否:使用非修改算法(find, count, for_each等)
- 是:
- 需要排序?使用排序算法
- 需要转换?使用transform
- 需要过滤?使用remove_if+erase
-
数据量大小?
- 小数据:选择最简单实现
- 大数据:考虑并行算法或更高效的算法
-
是否需要稳定性?
- 是:选择stable_前缀算法
- 否:选择普通算法
10. 高级技巧与模式
10.1 自定义迭代器适配算法
通过自定义迭代器扩展算法功能。比如实现一个步进迭代器:
cpp复制template<typename Iter>
class StepIterator {
Iter it;
size_t step;
public:
// 实现迭代器必要接口...
StepIterator& operator++() { advance(it, step); return *this; }
// ...其他操作
};
vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9};
// 每隔2个元素处理一次
for_each(StepIterator(data.begin(), 2), StepIterator(data.end(), 2),
[](int x) { process(x); });
10.2 算法策略模式
将算法作为策略参数传递,提高灵活性:
cpp复制template<typename Iter, typename Compare>
void customSort(Iter begin, Iter end, Compare comp) {
// 可能的前处理
sort(begin, end, comp);
// 可能的后处理
}
// 使用示例
vector<Person> people = {...};
customSort(people.begin(), people.end(),
[](const Person& a, const Person& b) { return a.age < b.age; });
10.3 算法性能剖析技巧
使用profiler分析算法热点:
cpp复制void expensiveOperation() {
vector<int> data = generateLargeData();
PROFILE_SCOPE("Sorting");
sort(data.begin(), data.end());
PROFILE_SCOPE("Processing");
for_each(data.begin(), data.end(), complexCalculation);
}
11. C++20/23算法新特性
11.1 范围算法的便利性
C++20引入的范围算法大大简化了代码:
cpp复制vector<int> data = {...};
// 传统方式
sort(data.begin(), data.end());
auto it = lower_bound(data.begin(), data.end(), 42);
// C++20范围方式
ranges::sort(data);
auto it = ranges::lower_bound(data, 42);
11.2 视图的惰性求值
C++20的视图提供了强大的惰性求值能力:
cpp复制vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9};
// 传统方式:立即求值
vector<int> temp;
copy_if(data.begin(), data.end(), back_inserter(temp),
[](int x) { return x % 2 == 0; });
transform(temp.begin(), temp.end(), temp.begin(),
[](int x) { return x * x; });
// C++20视图方式:惰性求值
auto view = data | views::filter([](int x) { return x % 2 == 0; })
| views::transform([](int x) { return x * x; });
// 实际使用时才会计算
for (auto x : view) { process(x); }
12. 跨平台算法注意事项
12.1 浮点数比较的陷阱
不同平台浮点数处理可能有差异,需要特别注意:
cpp复制vector<double> values = {...};
// 危险:直接比较浮点数
auto it = find(values.begin(), values.end(), 0.1);
// 安全做法:使用容差比较
auto it = find_if(values.begin(), values.end(),
[](double x) { return abs(x - 0.1) < 1e-9; });
12.2 内存对齐的影响
某些算法对内存对齐敏感,特别是在SIMD优化时:
cpp复制// 确保数据对齐以获得最佳性能
alignas(32) vector<double> alignedData(1000);
// 使用对齐内存的算法可能更快
sort(alignedData.begin(), alignedData.end());
13. 算法调试技巧
13.1 可视化调试辅助
在调试复杂算法时,可视化中间结果很有帮助:
cpp复制template<typename Iter>
void printRange(Iter begin, Iter end) {
copy(begin, end, ostream_iterator<decltype(*begin)>(cout, " "));
cout << endl;
}
vector<int> data = {...};
// 调试排序过程
partial_sort(data.begin(), data.begin()+10, data.end());
printRange(data.begin(), data.end());
13.2 契约式编程验证
使用断言验证算法前提条件:
cpp复制template<typename Iter>
void binarySearchHelper(Iter begin, Iter end) {
// 验证输入范围已排序
assert(is_sorted(begin, end));
// ...实现代码
}
14. 算法教育与学习路径
14.1 循序渐进的学习方法
根据我的经验,建议按这个顺序掌握STL算法:
- 基础算法:find, count, for_each, copy
- 修改算法:transform, replace, remove
- 排序算法:sort, stable_sort, partial_sort
- 数值算法:accumulate, inner_product
- 高级算法:partition, nth_element, inplace_merge
14.2 推荐练习项目
这些实际项目能帮助巩固算法知识:
- 实现一个联系人搜索功能(使用find_if)
- 开发一个成绩分析工具(使用sort, accumulate)
- 构建简单的数据库查询引擎(使用lower_bound, upper_bound)
- 编写日志分析工具(使用count_if, max_element)
15. 算法在领域特定问题中的应用
15.1 游戏开发中的算法应用
在游戏开发中,STL算法大有用武之地:
cpp复制vector<GameObject> objects = {...};
// 每帧更新所有活跃对象
for_each(objects.begin(), objects.end(),
[](GameObject& obj) { if (obj.isActive()) obj.update(); });
// 按距离排序渲染对象
sort(objects.begin(), objects.end(),
[](const GameObject& a, const GameObject& b) {
return a.distanceToCamera() < b.distanceToCamera();
});
15.2 金融计算中的算法选择
金融计算对性能要求极高,需要精心选择算法:
cpp复制vector<double> portfolioReturns = {...};
// 计算VaR (Value at Risk)
sort(portfolioReturns.begin(), portfolioReturns.end());
double var = portfolioReturns[portfolioReturns.size() * 0.05];
// 计算协方差
double mean = accumulate(portfolioReturns.begin(), portfolioReturns.end(), 0.0)
/ portfolioReturns.size();
double variance = inner_product(portfolioReturns.begin(), portfolioReturns.end(),
portfolioReturns.begin(), 0.0)
/ portfolioReturns.size() - mean * mean;
16. 算法优化案例分析
16.1 从O(n²)到O(n log n)的优化
我曾经优化过一个匹配算法:
cpp复制// 原始O(n²)版本
vector<Pair> matches;
for (auto& a : listA) {
for (auto& b : listB) {
if (isMatch(a, b)) {
matches.emplace_back(a, b);
}
}
}
// 优化O(n log n)版本
sort(listA.begin(), listA.end());
sort(listB.begin(), listB.end());
vector<Pair> matches;
auto itA = listA.begin();
auto itB = listB.begin();
while (itA != listA.end() && itB != listB.end()) {
if (itA->key == itB->key) {
matches.emplace_back(*itA, *itB);
++itA; ++itB;
} else if (itA->key < itB->key) {
++itA;
} else {
++itB;
}
}
16.2 内存访问模式优化案例
优化一个图像处理算法:
cpp复制// 原始版本:列优先访问
for (int x = 0; x < width; ++x) {
for (int y = 0; y < height; ++y) {
processPixel(image[y][x]);
}
}
// 优化版本:行优先访问
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
processPixel(image[y][x]);
}
}
17. 算法与数据结构的协同
17.1 根据数据结构选择算法
不同数据结构适合不同算法:
cpp复制// vector:适合随机访问算法
vector<int> vec = {...};
sort(vec.begin(), vec.end());
// list:使用成员函数更高效
list<int> lst = {...};
lst.sort(); // 比通用的sort(lst.begin(), lst.end())更高效
// deque:适合两端操作
deque<int> dq = {...};
sort(dq.begin(), dq.end()); // 可行但不如vector高效
17.2 自定义数据结构适配STL算法
使自定义类型支持STL算法:
cpp复制class CustomContainer {
// 实现必要的迭代器接口
class Iterator {
// 实现迭代器必需的操作...
};
public:
Iterator begin() { return Iterator(...); }
Iterator end() { return Iterator(...); }
};
// 现在可以像STL容器一样使用算法
CustomContainer container;
auto it = find_if(container.begin(), container.end(), predicate);
18. 算法设计模式与思想
18.1 分治算法实现
使用STL算法实现分治策略:
cpp复制template<typename Iter>
void parallelQuickSort(Iter begin, Iter end) {
auto size = distance(begin, end);
if (size <= 1000) {
sort(begin, end);
return;
}
auto pivot = *next(begin, size/2);
auto middle1 = partition(begin, end,
[pivot](const auto& x) { return x < pivot; });
auto middle2 = partition(middle1, end,
[pivot](const auto& x) { return !(pivot < x); });
#pragma omp parallel sections
{
#pragma omp section
parallelQuickSort(begin, middle1);
#pragma omp section
parallelQuickSort(middle2, end);
}
}
18.2 贪心算法示例
使用STL实现贪心算法:
cpp复制vector<Task> tasks = {...};
// 贪心策略:按结束时间排序
sort(tasks.begin(), tasks.end(),
[](const Task& a, const Task& b) { return a.end < b.end; });
vector<Task> schedule;
Time lastEnd = 0;
// 选择兼容任务
copy_if(tasks.begin(), tasks.end(), back_inserter(schedule),
[&lastEnd](const Task& t) {
if (t.start >= lastEnd) {
lastEnd = t.end;
return true;
}
return false;
});
19. 算法测试与验证
19.1 单元测试策略
为算法编写全面的单元测试:
cpp复制void testSortAlgorithm() {
vector<int> empty;
vector<int> single = {1};
vector<int> sorted = {1, 2, 3};
vector<int> reversed = {3, 2, 1};
vector<int> random = {4, 1, 3, 2, 5};
auto testSort = [](auto& data) {
auto copy = data;
sort(copy.begin(), copy.end());
assert(is_sorted(copy.begin(), copy.end()));
assert(equal(copy.begin(), copy.end(),
set<int>(data.begin(), data.end()).begin()));
};
testSort(empty);
testSort(single);
testSort(sorted);
testSort(reversed);
testSort(random);
}
19.2 边界条件测试
特别注意边界条件的测试:
cpp复制void testBoundaryConditions() {
// 空范围
vector<int> empty;
assert(find(empty.begin(), empty.end(), 42) == empty.end());
// 单个元素
vector<int> single = {42};
assert(find(single.begin(), single.end(), 42) == single.begin());
// 所有元素相同
vector<int> allSame(100, 42);
assert(count(allSame.begin(), allSame.end(), 42) == 100);
// 超大范围
vector<int> large(1'000'000);
iota(large.begin(), large.end(), 0);
assert(lower_bound(large.begin(), large.end(), 999'999) == prev(large.end()));
}
20. 算法性能调优进阶
20.1 缓存友好算法设计
优化内存访问模式提高缓存命中率:
cpp复制// 不好的访问模式
for (int i = 0; i < N; ++i) {
for (int j = 0; j < M; ++j) {
process(matrix[j][i]); // 列优先访问
}
}
// 好的访问模式
for (int j = 0; j < M; ++j) {
for (int i = 0; i < N; ++i) {
process(matrix[j][i]); // 行优先访问
}
}
20.2 SIMD向量化优化
利用现代CPU的SIMD指令加速算法:
cpp复制// 普通实现
void addArrays(float* a, float* b, float* c, size_t n) {
for (size_t i = 0; i < n; ++i) {
c[i] = a[i] + b[i];
}
}
// SIMD优化版本
#include <immintrin.h>
void addArraysSIMD(float* a, float* b, float* c, size_t n) {
size_t i = 0;
for (; i + 8 <= n; i += 8) {
__m256 va = _mm256_load_ps(a + i);
__m256 vb = _mm256_load_ps(b + i);
__m256 vc = _mm256_add_ps(va, vb);
_mm256_store_ps(c + i, vc);
}
// 处理剩余元素
for (; i < n; ++i) {
c[i] = a[i] + b[i];
}
}
21. 算法在并发环境中的应用
21.1 线程安全算法设计
在多线程环境中安全使用STL算法:
cpp复制vector<int> sharedData = {...};
mutex mtx;
void threadSafeProcess() {
vector<int> localCopy;
{
lock_guard<mutex> lock(mtx);
localCopy = sharedData; // 复制数据
}
// 在本地副本上安全操作
sort(localCopy.begin(), localCopy.end());
// ...其他处理
{
lock_guard<mutex> lock(mtx);
// 必要时写回结果
}
}
21.2 并行算法模式
实现高效的并行算法:
cpp复制template<typename Iter, typename Func>
void parallelForEach(Iter begin, Iter end, Func f, size_t minChunk = 1000) {
auto size = distance(begin, end);
if (size < minChunk) {
for_each(begin, end, f);
return;
}
auto mid = begin;
advance(mid, size/2);
future<void> left = async(launch::async,
[begin, mid, &f, minChunk] {
parallelForEach(begin, mid, f, minChunk);
});
parallelForEach(mid, end, f, minChunk);
left.get();
}
22. 算法与异常安全
22.1 异常安全保证
理解不同算法的异常安全保证:
cpp复制vector<Resource> resources = {...};
// sort提供基本异常安全保证:发生异常时容器状态有效但不保证顺序
try {
sort(resources.begin(), resources.end());
} catch (...) {
// 即使异常,resources仍然是有效vector
}
// copy通常提供强异常安全保证
vector<Resource> copy;
try {
copy.resize(resources.size());
copy(resources.begin(), resources.end(), copy.begin());
} catch (...) {
// 如果异常,copy保持原状
}
22.2 编写异常安全的谓词
确保谓词不会泄漏资源:
cpp复制class ResourceUser {
Resource* res;
public:
bool operator()(int x) const {
Resource temp; // 可能抛出异常
return temp.process(x) > threshold;
}
~ResourceUser() { delete res; }
};
// 安全用法:使用RAII包装
auto predicate = [](int x) {
ResourceUser ru;
return ru(x);
};
23. 算法与元编程
23.1 类型泛型算法
编写适用于任何类型的算法:
cpp复制template<typename Iter>
auto sum(Iter begin, Iter end) -> typename iterator_traits<Iter>::value_type {
using T = typename iterator_traits<Iter>::value_type;
return accumulate(begin, end, T{});
}
// 可以用于任何数值类型
vector<int> ints = {1, 2, 3};
vector<double> doubles = {1.1, 2.2, 3.3};
auto intSum = sum(ints.begin(), ints.end());
auto doubleSum = sum(doubles.begin(), doubles.end());
23.2 编译时算法选择
使用SFINAE选择不同算法实现:
cpp复制template<typename Iter>
enable_if_t<is_arithmetic_v<typename iterator_traits<Iter>::value_type>, void>
fastSort(Iter begin, Iter end) {
// 对数值类型使用快速排序
sort(begin, end);
}
template<typename Iter>
enable_if_t<!is_arithmetic_v<typename iterator_traits<Iter>::value_type>, void>
fastSort(Iter begin, Iter end) {
// 对其他类型使用稳定排序
stable_sort(begin, end);
}
24. 算法与现代C++特性
24.1 概念约束算法
C++20概念让算法接口更清晰:
cpp复制template<random_access_iterator Iter, typename Comp = less<>>
requires sortable<Iter, Comp>
void quickSort(Iter begin, Iter end, Comp comp = {}) {
if (distance(begin, end) <= 1) return;
auto pivot = partition(begin, end, comp);
quickSort(begin, pivot, comp);
quickSort(pivot, end, comp);
}
24.2 协程与算法结合
使用协程实现惰性算法:
cpp复制generator<int> fibonacci() {
int a = 0, b = 1;
while (true) {
co_yield a;
tie(a, b) = make_pair(b, a + b);
}
}
void useFibonacci() {
auto fib = fibonacci();
auto begin = fib.begin();
auto end = fib.end();
// 查找第一个大于1000的斐波那契数
auto it = find_if(begin, end, [](int x) { return x > 1000; });
if (it != end) {
cout << *it << endl;
}
}
25. 算法与领域特定语言(DSL)
25.1 创建算法DSL
构建表达力强的算法DSL:
cpp复制auto where = [](auto pred) {
return views::filter(pred);
};
auto select = [](auto proj) {
return views::transform(proj);
};
vector<Person> people = {...};
// 类似SQL的查询
auto results = people
| where([](const Person& p) { return p.age > 18; })
| select([](const Person& p) { return p.name; });
for (const auto& name : results) {
cout << name << endl;
}
25.2 流式算法接口
设计流畅的算法调用链:
cpp复制class AlgorithmChain {
vector<int> data;
public:
AlgorithmChain(vector<int> d) : data(move(d)) {}
AlgorithmChain& sort() { std::sort(data.begin(), data.end()); return *this; }
AlgorithmChain& unique() { data.erase(unique(data.begin(), data.end()), data.end()); return *this; }
AlgorithmChain& reverse() { std::reverse(data.begin(), data.end()); return *this; }
vector<int> get() && { return move(data); }
};
auto result = AlgorithmChain({3,1,4,1,5,9,2,6})
.sort()
.unique()
.reverse()
.get();
26. 算法与性能剖析
26.1 使用微基准测试
精确测量算法性能:
cpp复制void benchmarkSort() {
vector<int> data(1'000'000);
iota(data.begin(), data.end(), 0);
shuffle(data.begin(), data.end(), default_random_engine{});
auto start = chrono::high_resolution_clock::now();
sort(data.begin(), data.end());
auto end = chrono::high_resolution_clock::now();
cout << "Sort took: "
<< chrono::duration_cast<chrono::microseconds>(end-start).count()
<< "μs\n";
}
26.2 热点分析技巧
使用perf等工具分析算法热点:
cpp复制void hotspotFunction() {
vector<int> data = generateData();
// 使用likely/unlikely指导分支预测
sort(data.begin(), data.end(), [](int a, int b) {
if (a % 2 == 0) [[likely]] {
return a < b;
} else [[unlikely]] {
return b < a;
}
});
}
27. 算法与编译器优化
27.1 帮助编译器优化
编写编译器友好的算法代码:
cpp复制void compilerFriendlySum(const vector<int>& data) {
// 使用局部变量帮助编译器优化
int sum = 0;
for (int x : data) {
sum += x;
}
// 防止被优化掉
asm volatile("" ::"r"(sum) : "memory");
}
27.2 内联与优化提示
使用属性指导编译器优化:
cpp复制[[gnu::always_inline]] inline void smallHelper(int& x) {
x *= 2;
}
void processVector(vector<int>& data) {
for_each(data.begin(), data.end(), [](int& x) {
smallHelper(x);
});
}
28. 算法与硬件特性
28.1 考虑CPU缓存行
优化算法内存访问模式:
cpp复制struct BadLayout {
int a;
char padding[60]; // 假设缓存行大小64字节
int b;
};
struct GoodLayout {
int a;
int b;
char padding[56];
};
vector<GoodLayout> data(1000);
// 并行处理a和b不会引起缓存行竞争
parallel_for(0, 1000, [&](int i) {
data[i].a++;
data[i].b++;
});
28.2 利用硬件指令
使用特定硬件指令加速算法:
cpp复制int countZeros(uint64_t x) {
// 使用POPCNT指令
return __builtin_popcountll(~x);
}
void countAllZeros(const vector<uint64_t>& data) {
vector<int> counts(data.size());
transform(data.begin(), data.end(), counts.begin(), countZeros);
}
29. 算法与安全编程
29.1 安全边界检查
确保算法在边界条件下安全:
cpp复制template<typename Iter>
Iter safeFind(Iter begin, Iter end, const typename iterator_traits<Iter>::value_type& value) {
static