1. C++算法基础概述
作为一名从C++98时代走过来的老程序员,我至今记得第一次用冒泡排序解决实际问题时的成就感。算法是编程世界的基石,而C++因其高性能特性成为算法实现的首选语言之一。今天我们就来聊聊那些每个C++开发者都必须掌握的基础算法。
在C++中实现算法有其独特优势:STL提供了丰富的容器和算法库,模板机制允许我们编写通用算法,而底层内存控制能力则让性能优化成为可能。但要注意,C++算法学习容易陷入两个极端:要么停留在理论层面纸上谈兵,要么过早陷入性能优化的泥潭。我的建议是:先理解算法本质,再考虑C++特性带来的优化空间。
2. 排序算法实现与对比
2.1 冒泡排序的C++实现
让我们从一个经典的例子开始:
cpp复制void bubbleSort(int arr[], int n) {
for (int i = 0; i < n-1; i++) {
bool swapped = false;
for (int j = 0; j < n-i-1; j++) {
if (arr[j] > arr[j+1]) {
std::swap(arr[j], arr[j+1]);
swapped = true;
}
}
if (!swapped) break; // 提前终止优化
}
}
这个实现有几个值得注意的C++细节:
- 使用std::swap而不是手动交换,更安全高效
- 添加swapped标志实现提前终止
- 数组作为指针传递是C++处理原生数组的典型方式
实际项目中,除非处理特别小的数据集(如n<10),否则不建议使用冒泡排序。我曾在一个嵌入式项目中用它处理5个元素的温度数据,效果不错。
2.2 快速排序的STL风格实现
现代C++更推荐这种写法:
cpp复制template<typename RandomIt>
void quickSort(RandomIt first, RandomIt last) {
if (first == last) return;
auto pivot = *std::next(first, std::distance(first,last)/2);
auto middle1 = std::partition(first, last,
[pivot](const auto& em){ return em < pivot; });
auto middle2 = std::partition(middle1, last,
[pivot](const auto& em){ return !(pivot < em); });
quickSort(first, middle1);
quickSort(middle2, last);
}
这种实现的优势:
- 模板化支持任意随机访问迭代器
- 使用STL算法避免裸循环
- lambda表达式使比较逻辑更清晰
2.3 排序算法性能实测对比
我在i7-11800H处理器上测试了不同算法对100,000个int的排序时间:
| 算法 | 时间(ms) | 适用场景 |
|---|---|---|
| std::sort | 12 | 通用场景 |
| 快速排序 | 15 | 需要稳定性的场景 |
| 归并排序 | 18 | 链表排序 |
| 冒泡排序 | >5000 | 教学演示 |
实际项目中,90%的情况直接使用std::sort即可。只有在需要特殊比较逻辑或处理非连续内存时,才需要自定义排序算法。
3. 查找算法深度解析
3.1 二分查找的边界处理艺术
二分查找看似简单,但边界条件处理是常见坑点。这是经过实战检验的实现:
cpp复制int binarySearch(const std::vector<int>& nums, int target) {
int left = 0;
int right = nums.size(); // 注意是开区间
while (left < right) {
int mid = left + (right - left) / 2; // 避免溢出
if (nums[mid] == target) {
return mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid; // 保持开区间一致性
}
}
return -1;
}
关键点说明:
- 使用[left, right)开区间约定简化边界判断
- mid计算方式防止整数溢出
- 循环条件left < right保证终止
3.2 STL中的查找算法家族
C++提供了丰富的查找工具:
cpp复制std::vector<int> v{1,2,3,4,5};
// 简单查找
auto it = std::find(v.begin(), v.end(), 3);
// 二分查找(要求已排序)
bool exists = std::binary_search(v.begin(), v.end(), 3);
// 下界/上界查找
auto lower = std::lower_bound(v.begin(), v.end(), 3);
auto upper = std::upper_bound(v.begin(), v.end(), 3);
选择建议:
- 无序小数据:std::find
- 有序大数据:std::lower_bound
- 只需判断存在性:std::binary_search
4. 算法实战:合并两个有序数组
来看一个leetcode风格的题目:合并两个已排序数组,结果存储在第一个数组中。假设第一个数组有足够空间。
4.1 逆向双指针解法
cpp复制void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int p1 = m - 1, p2 = n - 1;
int tail = m + n - 1;
while (p1 >= 0 || p2 >= 0) {
if (p1 == -1) {
nums1[tail--] = nums2[p2--];
} else if (p2 == -1) {
nums1[tail--] = nums1[p1--];
} else if (nums1[p1] > nums2[p2]) {
nums1[tail--] = nums1[p1--];
} else {
nums1[tail--] = nums2[p2--];
}
}
}
这个解法有几个精妙之处:
- 从后向前填充避免覆盖未处理的元素
- 处理了其中一个数组先遍历完的边界情况
- 时间复杂度O(m+n),空间复杂度O(1)
4.2 测试用例设计要点
好的算法实现需要全面的测试:
cpp复制TEST(MergeTest, Basic) {
vector<int> nums1{1,2,3,0,0,0};
vector<int> nums2{2,5,6};
merge(nums1, 3, nums2, 3);
EXPECT_EQ(nums1, vector<int>({1,2,2,3,5,6}));
}
TEST(MergeTest, EmptyNums2) {
vector<int> nums1{1};
vector<int> nums2{};
merge(nums1, 1, nums2, 0);
EXPECT_EQ(nums1, vector<int>({1}));
}
TEST(MergeTest, EmptyNums1) {
vector<int> nums1{0};
vector<int> nums2{1};
merge(nums1, 0, nums2, 1);
EXPECT_EQ(nums1, vector<int>({1}));
}
5. 算法效率分析与优化
5.1 时间复杂度实战分析
以快速排序为例,理论上是O(nlogn),但实际表现受多种因素影响:
- 分区均衡性:最坏情况下可能退化为O(n²)
- 小数组处理:当n<16时,插入排序通常更快
- 缓存局部性:递归实现可能不如迭代版本缓存友好
5.2 内存访问模式优化
现代CPU的缓存机制使得访问连续内存比随机访问快得多。例如:
cpp复制// 缓存不友好版本
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
process(matrix[j][i]); // 列优先访问
}
}
// 缓存友好版本
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
process(matrix[i][j]); // 行优先访问
}
}
在图像处理算法中,这种优化可能带来5-10倍的性能提升。
5.3 使用SSE指令集加速
对于计算密集型算法,可以使用SIMD指令:
cpp复制#include <emmintrin.h>
void sseAdd(float* a, float* b, float* c, int n) {
for (int i = 0; i < n; i += 4) {
__m128 va = _mm_load_ps(a + i);
__m128 vb = _mm_load_ps(b + i);
__m128 vc = _mm_add_ps(va, vb);
_mm_store_ps(c + i, vc);
}
}
这种优化需要对硬件架构有深入了解,但在图像处理、科学计算等领域效果显著。
6. STL算法深度应用
6.1 常用算法一览
STL提供了超过100种算法,最常用的有:
- 排序相关:sort, stable_sort, partial_sort
- 查找相关:find, find_if, binary_search
- 数值处理:accumulate, inner_product
- 集合操作:set_union, set_intersection
6.2 自定义比较函数
STL算法的强大之处在于高度可定制:
cpp复制struct Person {
string name;
int age;
};
vector<Person> people = {...};
// 按年龄排序
std::sort(people.begin(), people.end(),
[](const Person& a, const Person& b) {
return a.age < b.age;
});
// 使用成员函数作为谓词
std::sort(people.begin(), people.end(),
&Person::operator<);
6.3 算法组合技巧
STL算法可以像乐高一样组合使用:
cpp复制// 找出所有偶数并平方
vector<int> data = {...};
vector<int> result;
std::transform(
std::begin(data), std::end(data),
std::back_inserter(result),
[](int x) { return x % 2 == 0 ? x * x : 0; });
// 移除所有0元素
result.erase(
std::remove(result.begin(), result.end(), 0),
result.end());
这种函数式编程风格使代码更简洁,但要注意性能影响。
7. 实际项目中的算法选择
7.1 游戏开发中的算法
在游戏引擎中常见的算法应用:
- A*寻路算法:用于NPC移动
- 空间分区算法:四叉树/八叉树管理场景
- 碰撞检测:分离轴定理(SAT)
7.2 高频交易系统
这类系统对算法性能极其敏感:
- 使用基数排序处理订单簿
- 环形缓冲区实现无锁队列
- 位操作替代除法运算
7.3 嵌入式系统约束
在资源受限环境中的考量:
- 避免动态内存分配
- 使用查表法替代复杂计算
- 选择确定性算法(避免最坏情况)
8. 算法学习进阶路线
8.1 经典教材推荐
- 《算法导论》:理论全面但数学要求高
- 《算法(第4版)》:更实用的视角
- 《STL源码剖析》:理解STL实现细节
8.2 在线练习平台
- LeetCode:面试准备
- Codeforces:算法竞赛
- HackerRank:分类练习
8.3 性能分析工具
- Google Benchmark:微基准测试
- perf(Linux):系统级分析
- VTune(Intel):硬件事件分析
9. 常见陷阱与调试技巧
9.1 迭代器失效问题
cpp复制vector<int> v = {1,2,3,4,5};
for (auto it = v.begin(); it != v.end(); ++it) {
if (*it % 2 == 0) {
v.erase(it); // 危险!迭代器失效
}
}
正确做法:
cpp复制for (auto it = v.begin(); it != v.end(); ) {
if (*it % 2 == 0) {
it = v.erase(it); // 接收返回值
} else {
++it;
}
}
9.2 浮点数比较陷阱
cpp复制// 错误方式
if (a == b) {...}
// 正确方式
bool almostEqual(double a, double b, double epsilon = 1e-6) {
return fabs(a - b) < epsilon;
}
9.3 调试复杂递归
使用打印缩进辅助调试:
cpp复制void quickSortHelper(vector<int>& nums, int left, int right, int depth = 0) {
string indent(depth * 2, ' ');
cout << indent << "Sorting [" << left << "," << right << "]" << endl;
// ...递归逻辑
}
10. 现代C++中的算法新特性
10.1 并行算法
C++17引入的并行执行策略:
cpp复制vector<int> v = {...};
// 并行排序
std::sort(std::execution::par, v.begin(), v.end());
// 并行变换
std::transform(std::execution::par,
v.begin(), v.end(), v.begin(),
[](int x) { return x * 2; });
10.2 Range库(C++20)
更简洁的算法表达:
cpp复制vector<int> v = {1,2,3,4,5};
// 过滤偶数并平方
auto result = v | std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * x; });
10.3 概念约束(C++20)
使算法接口更安全:
cpp复制template<std::random_access_iterator Iter>
void mySort(Iter first, Iter last) {
// 实现保证迭代器是随机访问的
}
11. 性能优化实战案例
11.1 缓存友好设计
优化矩阵乘法:
cpp复制// 原始版本
void matmul(const vector<vector<float>>& a,
const vector<vector<float>>& b,
vector<vector<float>>& c) {
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
for (int k = 0; k < n; ++k) {
c[i][j] += a[i][k] * b[k][j];
}
}
}
}
// 优化版本:调整循环顺序
void matmulOpt(const vector<vector<float>>& a,
const vector<vector<float>>& b,
vector<vector<float>>& c) {
for (int i = 0; i < n; ++i) {
for (int k = 0; k < n; ++k) {
float s = a[i][k];
for (int j = 0; j < n; ++j) {
c[i][j] += s * b[k][j];
}
}
}
}
实测在n=1024时,优化版本快8倍。
11.2 避免不必要的拷贝
cpp复制// 低效版本
vector<int> process(vector<int> data) { // 值传递
// 处理data
return data;
}
// 高效版本
void process(vector<int>& data) { // 引用传递
// 处理data
}
对于大对象,不必要的拷贝可能成为性能瓶颈。
11.3 预分配内存
cpp复制vector<int> v;
v.reserve(1000000); // 预分配
for (int i = 0; i < 1000000; ++i) {
v.push_back(i); // 不会触发多次扩容
}
12. 多线程算法注意事项
12.1 线程安全基础
STL算法通常不是线程安全的:
cpp复制vector<int> v = {...};
// 危险:多个线程同时修改
#pragma omp parallel for
for (int i = 0; i < v.size(); ++i) {
v[i] = process(v[i]);
}
12.2 并行归约模式
cpp复制vector<int> v(1000000, 1);
int sum = 0;
#pragma omp parallel for reduction(+:sum)
for (int i = 0; i < v.size(); ++i) {
sum += v[i];
}
12.3 任务并行化
cpp复制vector<future<int>> results;
for (int i = 0; i < 10; ++i) {
results.push_back(async(launch::async, [i]{
return compute(i);
}));
}
int total = 0;
for (auto& f : results) {
total += f.get();
}
13. 算法设计模式
13.1 分治法模板
cpp复制ResultType divideAndConquer(Problem problem) {
if (problem.isSimple()) {
return problem.solveDirectly();
}
auto subproblems = problem.split();
ResultType result;
for (auto& sub : subproblems) {
result += divideAndConquer(sub);
}
return result;
}
13.2 动态规划实现要点
- 定义状态表示
- 建立状态转移方程
- 确定初始条件
- 选择计算顺序(自顶向下/自底向上)
13.3 贪心算法适用场景
贪心算法适用于具有贪心选择性质的问题,即局部最优能导致全局最优。典型例子:
- 霍夫曼编码
- Dijkstra算法
- 活动选择问题
14. 测试与验证方法
14.1 单元测试框架
使用Catch2测试算法:
cpp复制TEST_CASE("Binary search") {
vector<int> v = {1,3,5,7,9};
SECTION("Existing element") {
REQUIRE(binarySearch(v, 5) == 2);
}
SECTION("Non-existing element") {
REQUIRE(binarySearch(v, 4) == -1);
}
}
14.2 模糊测试
生成随机输入验证算法鲁棒性:
cpp复制void fuzzTestSort() {
random_device rd;
mt19937 gen(rd());
uniform_int_distribution<> dis(-1000, 1000);
for (int i = 0; i < 1000; ++i) {
vector<int> v(1000);
generate(v.begin(), v.end(), [&](){ return dis(gen); });
auto v1 = v;
auto v2 = v;
std::sort(v1.begin(), v1.end());
mySort(v2.begin(), v2.end());
REQUIRE(v1 == v2);
}
}
14.3 性能回归测试
使用Google Benchmark监控性能变化:
cpp复制static void BM_QuickSort(benchmark::State& state) {
vector<int> v(state.range(0));
iota(v.begin(), v.end(), 0);
shuffle(v.begin(), v.end(), default_random_engine());
for (auto _ : state) {
quickSort(v.begin(), v.end());
}
state.SetComplexityN(state.range(0));
}
BENCHMARK(BM_QuickSort)->Range(8, 8<<10)->Complexity();
15. 算法可视化工具
15.1 控制台可视化
简单的排序可视化:
cpp复制void visualizeSort(vector<int>& v) {
for (int val : v) {
cout << string(val, '*') << " (" << val << ")\n";
}
cout << endl;
}
void bubbleSortVisualized(vector<int>& v) {
for (size_t i = 0; i < v.size()-1; ++i) {
for (size_t j = 0; j < v.size()-i-1; ++j) {
if (v[j] > v[j+1]) {
swap(v[j], v[j+1]);
visualizeSort(v);
this_thread::sleep_for(100ms);
}
}
}
}
15.2 使用外部库
SFML图形化展示:
cpp复制void drawSorting(SortAlgorithm& algo) {
RenderWindow window(VideoMode(800, 600), "Sorting Visualizer");
while (window.isOpen()) {
Event event;
while (window.pollEvent(event)) {
if (event.type == Event::Closed)
window.close();
}
if (!algo.isDone()) {
algo.step();
}
window.clear();
algo.draw(window);
window.display();
}
}
16. 算法竞赛技巧
16.1 常用代码片段
快速输入输出:
cpp复制ios::sync_with_stdio(false);
cin.tie(nullptr);
// 大量数据输入
int x;
while (cin >> x) {
// 处理x
}
16.2 位运算优化
常用技巧:
- x & (x-1) 清除最低位的1
- x & -x 获取最低位的1
- (x + y - 1) / y 实现ceil(x/y)
16.3 调试宏
cpp复制#define LOCAL
#ifdef LOCAL
#define debug(...) logger(#__VA_ARGS__, __VA_ARGS__)
template<typename... Args>
void logger(string vars, Args&&... values) {
cerr << vars << " = ";
string delim = "";
(..., (cerr << delim << values, delim = ", "));
cerr << endl;
}
#else
#define debug(...) 0
#endif
17. 跨平台开发注意事项
17.1 数据类型大小差异
cpp复制// 安全的方式
#include <cstdint>
int32_t x; // 保证是32位
uint64_t y; // 保证是64位无符号
17.2 字节序问题
cpp复制uint32_t swapEndian(uint32_t val) {
return ((val << 24) & 0xff000000) |
((val << 8) & 0x00ff0000) |
((val >> 8) & 0x0000ff00) |
((val >> 24) & 0x000000ff);
}
17.3 浮点一致性
不同编译器/平台可能有不同的浮点运算结果。对需要确定性的算法,考虑使用定点数。
18. 内存管理高级技巧
18.1 自定义分配器
cpp复制template<typename T>
class PoolAllocator {
public:
using value_type = T;
PoolAllocator(size_t chunkSize = 1024)
: chunkSize_(chunkSize) {}
T* allocate(size_t n) {
if (n > chunkSize_) {
throw bad_alloc();
}
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* p, size_t n) {
::operator delete(p);
}
private:
size_t chunkSize_;
};
18.2 内存池实现
cpp复制class MemoryPool {
public:
explicit MemoryPool(size_t blockSize, size_t blockCount)
: blockSize_(blockSize) {
pool_ = ::operator new(blockSize * blockCount);
freeBlocks_.reserve(blockCount);
for (size_t i = 0; i < blockCount; ++i) {
freeBlocks_.push_back(
static_cast<char*>(pool_) + i * blockSize);
}
}
void* allocate() {
if (freeBlocks_.empty()) {
throw bad_alloc();
}
void* block = freeBlocks_.back();
freeBlocks_.pop_back();
return block;
}
void deallocate(void* block) {
freeBlocks_.push_back(static_cast<char*>(block));
}
~MemoryPool() {
::operator delete(pool_);
}
private:
size_t blockSize_;
void* pool_;
vector<void*> freeBlocks_;
};
19. 异常安全与算法
19.1 基本保证
cpp复制void safeAlgorithm(vector<int>& v) {
auto temp = v; // 先拷贝
try {
// 可能抛出异常的操作
process(temp);
} catch (...) {
// 发生异常时原数据不变
return;
}
v.swap(temp); // 无异常才替换
}
19.2 强异常安全
cpp复制void strongGuaranteeAlgorithm(vector<int>& v) {
vector<int> newV;
newV.reserve(v.size());
// 所有可能失败的操作都在修改newV
for (const auto& item : v) {
newV.push_back(process(item));
}
// 只有这里可能抛出的异常会影响外部状态
v.swap(newV);
}
20. 现代C++特性在算法中的应用
20.1 移动语义优化
cpp复制template<typename T>
void fastSort(vector<T>& v) {
if (v.size() <= 1) return;
T pivot = std::move(v.back());
v.pop_back();
vector<T> less, greater;
for (auto& item : v) {
if (item < pivot) {
less.push_back(std::move(item));
} else {
greater.push_back(std::move(item));
}
}
fastSort(less);
fastSort(greater);
v = std::move(less);
v.push_back(std::move(pivot));
v.insert(v.end(),
std::make_move_iterator(greater.begin()),
std::make_move_iterator(greater.end()));
}
20.2 constexpr算法
cpp复制constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
static_assert(factorial(5) == 120, "");
20.3 概念约束算法
cpp复制template<typename Iter>
requires std::random_access_iterator<Iter>
void mySort(Iter first, Iter last) {
// 实现
}
21. 领域特定算法案例
21.1 游戏开发:A*寻路
cpp复制struct Node {
int x, y;
float g, h;
Node* parent;
float f() const { return g + h; }
bool operator<(const Node& other) const {
return f() > other.f(); // 小顶堆
}
};
vector<Node*> aStarFindPath(Node* start, Node* end) {
priority_queue<Node> openSet;
unordered_set<Node*> closedSet;
start->g = 0;
start->h = heuristic(start, end);
openSet.push(*start);
while (!openSet.empty()) {
Node current = openSet.top();
openSet.pop();
if (¤t == end) {
return reconstructPath(current);
}
closedSet.insert(¤t);
for (auto neighbor : getNeighbors(current)) {
if (closedSet.count(neighbor)) continue;
float tentativeG = current.g + distance(current, *neighbor);
if (tentativeG < neighbor->g) {
neighbor->parent = ¤t;
neighbor->g = tentativeG;
neighbor->h = heuristic(*neighbor, end);
openSet.push(*neighbor);
}
}
}
return {}; // 无路径
}
21.2 金融计算:蒙特卡洛模拟
cpp复制double monteCarloPi(int samples) {
random_device rd;
mt19937 gen(rd());
uniform_real_distribution<> dis(0.0, 1.0);
int hits = 0;
for (int i = 0; i < samples; ++i) {
double x = dis(gen);
double y = dis(gen);
if (x*x + y*y <= 1.0) {
++hits;
}
}
return 4.0 * hits / samples;
}
21.3 图像处理:Sobel算子
cpp复制void sobelEdgeDetection(const Image& src, Image& dst) {
const int kernelX[3][3] = {{-1,0,1},{-2,0,2},{-1,0,1}};
const int kernelY[3][3] = {{-1,-2,-1},{0,0,0},{1,2,1}};
for (int y = 1; y < src.height-1; ++y) {
for (int x = 1; x < src.width-1; ++x) {
int gx = 0, gy = 0;
for (int ky = -1; ky <= 1; ++ky) {
for (int kx = -1; kx <= 1; ++kx) {
int pixel = src.at(x+kx, y+ky);
gx += pixel * kernelX[ky+1][kx+1];
gy += pixel * kernelY[ky+1][kx+1];
}
}
int magnitude = sqrt(gx*gx + gy*gy);
dst.at(x,y) = clamp(magnitude, 0, 255);
}
}
}
22. 算法设计哲学思考
22.1 时空权衡的艺术
在实际项目中,我们经常需要在时间和空间之间做出权衡。比如:
- 查表法 vs 实时计算
- 缓存中间结果 vs 按需计算
- 压缩存储 vs 解压速度
22.2 可读性与性能平衡
不要过早优化。先写出清晰正确的代码,再分析性能瓶颈:
cpp复制// 清晰版本
vector<int> filterEven(const vector<int>& v) {
vector<int> result;
copy_if(v.begin(), v.end(), back_inserter(result),
[](int x) { return x % 2 == 0; });
return result;
}
// [优化版本](https://taotoken.net?utm_source=general)(必要时)
void filterEvenInPlace(vector<int>& v) {
v.erase(remove_if(v.begin(), v.end(),
[](int x) { return x % 2 != 0; }), v.end());
}
22.3 通用性与特化
STL算法通过迭代器抽象实现了高度通用性,但在性能关键路径可能需要特化:
cpp复制// 通用版本
template<typename Iter>
void sort(Iter first, Iter last) {
// 通用实现
}
// 特化版本
template<>
void sort(vector<int>::iterator first, vector<int>::iterator last) {
// 针对int vector的优化实现
}
23. 持续学习资源推荐
23.1 开源项目学习
值得研究的开源C++项目:
- STL实现:libstdc++, libc++
- 计算库:Eigen, Blaze
- 游戏引擎:Unreal Engine, Godot
23.2 会议演讲推荐
C++算法相关优质演讲:
- "Optimizing the Emergent Structures of C++" - Andrei Alexandrescu
- "Practical Performance Practices" - Chandler Carruth
- "SIMD Programming" - Daniel Lemire
23.3 社区与论坛
活跃的C++社区:
- Stack Overflow C++标签
- Reddit r/cpp
- CppCon Slack频道
24. 职业发展中的算法应用
24.1 面试准备重点
大厂常考算法题型:
- 链表操作:反转、环检测
- 树遍历:前中后序、层序
- 动态规划:背包问题、编辑距离
- 图算法:DFS、BFS、拓扑排序
24.2 工作中的应用场景
不同岗位的算法侧重点:
- 游戏开发:几何算法、物理模拟
- 高频交易:低延迟数据结构
- 机器学习:矩阵运算优化
- 嵌入式系统:资源受限算法
24.3 个人项目建议
构建算法作品集的思路:
- 实现经典算法并可视化
- 参与开源算法项目
- 写技术博客分析算法细节
- 解决LeetCode难题并分享解法
25. 算法与数据结构选择指南
25.1 常见场景速查表
| 需求 | 推荐数据结构 | 适用算法 |
|---|---|---|
| 快速查找 | 哈希表 | 哈希查找 |
| 有序数据 | 平衡二叉搜索树 | 二分查找 |
| 频繁插入删除 | 链表 | 指针操作 |
| 图关系 | 邻接表/矩阵 | DFS/BFS |
| 优先级管理 | 堆 | 堆排序 |
25.2 容器选择决策树
- 需要快速随机访问?→ vector
- 频繁在两端操作?→ deque
- 需要快速查找?→ unordered_set/map
- 需要有序遍历?→ set/map
- 内存紧凑?→ array
25.3 算法复杂度速查
| 算法 | 平均 | 最坏 |
|---|---|---|
| 快速排序 | O(nlogn) | O(n²) |
| 归并排序 | O(nlogn) | O(nlogn) |
| 堆排序 | O(nlogn) | O(nlogn) |
| 插入排序 | O(n²) | O(n²) |
| 计数排序 | O(n+k) | O(n+k) |
26. 性能分析实战
26.1 使用perf工具
Linux性能分析基本流程:
bash复制perf stat ./algorithm # 基本统计
perf record ./algorithm # 采样
perf report # 查看结果
26.2 热点分析案例
分析排序算法热点:
code复制Overhead Command Shared Object Symbol
35.12% sort sort [.] partition
28.76% sort sort [.] swapElements
15.43% sort libc-2.31.so [.] malloc
12.21% sort sort [.] quickSort
26.3 缓存分析
使用perf检查缓存命中率:
bash复制perf stat -e cache-references,cache-misses ./algorithm
27. 并发算法模式
27.1 MapReduce实现
cpp复制template<typename MapFunc, typename ReduceFunc>
auto mapReduce(const vector<int>& data,
MapFunc map, ReduceFunc reduce,
size_t numThreads) {
vector<future<decltype(map(0))>> futures;
vector<decltype(map(0))> results;
// Map阶段
for (size_t i = 0; i < numThreads; ++i) {
futures.push_back(async(launch::async, [&, i]{
auto start = i * data.size() / numThreads;
auto end = (i + 1) * data.size() / numThreads;
return map(data.begin() + start, data.begin() + end);
}));
}
for (auto& f : futures) {
results.push_back(f.get());
}
// Reduce阶段
return reduce(results.begin(), results.end());
}
27.2 无锁队列
cpp复制template<typename T>
class LockFreeQueue {
struct Node {
T value;
atomic<Node*> next;
Node(T val) : value(val), next(nullptr) {}
};
atomic<Node*> head;
atomic
