欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 会展 > 第10章:优化数据结构_《C++性能优化指南》notes

第10章:优化数据结构_《C++性能优化指南》notes

2025/4/2 2:49:48 来源:https://blog.csdn.net/lianghudream/article/details/146541893  浏览:    关键词:第10章:优化数据结构_《C++性能优化指南》notes

优化数据结构

      • 一、重点知识解析
      • 二、代码示例与性能测试
      • 三、关键优化策略总结
      • 第十章核心知识点总结
      • 多选题
      • 多选题答案与解析
      • 设计题
      • 设计题答案解析


一、重点知识解析

  1. std::vector
    特点
  • 连续内存存储,随机访问O(1)
  • 尾部插入/删除高效,中间操作O(n)
  • 动态扩容机制(容量翻倍)

优化点

  • 预分配内存(reserve)
  • 优先使用emplace_back减少拷贝
  1. std::deque
    特点
  • 分块连续存储,支持高效头尾插入
  • 中间插入效率低于list
  • 迭代器非连续,随机访问较慢
  1. std::list
    特点
  • 双向链表结构,插入/删除O(1)
  • 不支持随机访问
  • 内存占用高(每个节点含两个指针)
  1. std::map vs std::unordered_map
    | 特性 | std::map | std::unordered_map |
    |---------------------|-------------------|---------------------|
    | 实现 | 红黑树 | 哈希表 |
    | 查找复杂度 | O(log n) | 平均O(1) |
    | 元素有序 | 是 | 否 |
    | 内存占用 | 较低 | 较高(桶结构) |

二、代码示例与性能测试

示例1:vector预分配优化

#include <iostream>
#include <vector>
#include <chrono>void test_vector_reserve() {const int N = 1e6;// 不预分配auto start = std::chrono::high_resolution_clock::now();std::vector<int> v1;for (int i=0; i<N; ++i) v1.push_back(i);auto end = std::chrono::high_resolution_clock::now();std::cout << "Without reserve: " << std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count() << " ms\n";// 预分配start = std::chrono::high_resolution_clock::now();std::vector<int> v2;v2.reserve(N);for (int i=0; i<N; ++i) v2.push_back(i);end = std::chrono::high_resolution_clock::now();std::cout << "With reserve: " << std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count() << " ms\n";
}int main() {test_vector_reserve();return 0;
}

编译运行

g++ -O2 -std=c++11 vector_test.cpp -o vector_test
./vector_test

输出示例

Without reserve: 23 ms
With reserve: 8 ms

示例2:list vs vector遍历性能

#include <iostream>
#include <vector>
#include <list>
#include <chrono>const int N = 1e6;template<typename T>
void traverse_container(T& container) {auto start = std::chrono::high_resolution_clock::now();for (auto& x : container) {volatile int temp = x; // 防止优化}auto end = std::chrono::high_resolution_clock::now();std::cout << "Traverse time: "<< std::chrono::duration_cast<std::chrono::microseconds>(end-start).count()<< " μs\n";
}int main() {// 测试vectorstd::vector<int> vec(N);for (int i=0; i<N; ++i) vec[i] = i;std::cout << "Vector: ";traverse_container(vec);// 测试liststd::list<int> lst;for (int i=0; i<N; ++i) lst.push_back(i);std::cout << "List: ";traverse_container(lst);return 0;
}

输出示例

Vector: 1254 μs
List: 6328 μs

结论:连续存储的vector遍历速度明显快于list


示例3:map vs unordered_map查找性能

#include <iostream>
#include <map>
#include <unordered_map>
#include <chrono>
#include <random>const int N = 1e6;template<typename T>
void test_map(T& m) {std::mt19937 rng;std::uniform_int_distribution<int> dist(0, N*2);// 插入测试auto start = std::chrono::high_resolution_clock::now();for (int i=0; i<N; ++i) {m[dist(rng)] = i;}auto end = std::chrono::high_resolution_clock::now();std::cout << "Insert: " << std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count()<< " ms\n";// 查找测试start = std::chrono::high_resolution_clock::now();for (int i=0; i<N; ++i) {volatile auto it = m.find(dist(rng));}end = std::chrono::high_resolution_clock::now();std::cout << "Find: "<< std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count()<< " ms\n";
}int main() {std::cout << "std::map:\n";std::map<int, int> m;test_map(m);std::cout << "\nstd::unordered_map:\n";std::unordered_map<int, int> um;test_map(um);return 0;
}

输出示例

std::map:
Insert: 342 ms
Find: 289 msstd::unordered_map:
Insert: 128 ms 
Find: 85 ms

结论:哈希表在插入和查找上显著快于红黑树实现的有序map


三、关键优化策略总结

  1. vector优化

    • 优先使用reserve()预分配内存
    • 使用emplace_back代替push_back
    • 避免在中间位置插入
  2. 关联容器选择

    • 需要有序访问 → map/set
    • 追求查找速度 → unordered_map/unordered_set
    • 预设置哈希表桶数量(reserve
  3. 迭代器选择

    • 随机访问 → vector/deque
    • 频繁插入删除 → list
  4. 内存局部性

    • 优先选择连续存储容器(vector)
    • 避免过度使用链表结构

第十章核心知识点总结

第十章重点讲解了C++标准库容器的性能特性和优化策略,核心内容包括:

  1. 序列容器性能比较

    • vector:连续内存,快速随机访问,尾部插入高效,中间插入O(n)
    • deque:分块连续内存,头尾插入高效,中间插入O(n)
    • list:双向链表,任意位置插入O(1),内存不连续
    • forward_list:单向链表,更省内存但功能受限
  2. 关联容器特点

    • map/set:红黑树实现,有序存储,查找O(log n)
    • unordered_map/set:哈希表实现,平均O(1)查找,最差O(n)
  3. 内存管理机制

    • vector动态扩容策略(2倍增长)
    • 预分配内存(reserve/resize)
  4. 算法复杂度与缓存局部性

    • 连续内存结构对缓存更友好
    • 链表结构指针跳转导致缓存命中率低

多选题

  1. 关于std::vector插入操作的正确说法是?
    A. 尾部插入时间复杂度O(1)
    B. 中间插入需要移动后续所有元素
    C. 预分配内存可以消除所有复制开销
    D. 扩容时采用倍增策略减少内存分配次数

  2. std::unordered_map相比std::map的优势包括?
    A. 支持范围查询
    B. 内存占用更小
    C. 平均查找时间更优
    D. 元素自动排序

  3. 优化std::list遍历性能的有效方法是?
    A. 使用迭代器代替索引访问
    B. 预分配节点内存池
    C. 改用std::forward_list
    D. 增加缓存块大小

  4. std::deque适合的场景是?
    A. 需要频繁中间插入
    B. 同时进行头尾操作
    C. 内存碎片敏感场景
    D. 要求严格连续内存

  5. 关于容器迭代器失效规则,正确的有?
    A. vector插入导致所有迭代器失效
    B. map插入不会使迭代器失效
    C. unordered_maprehash后迭代器失效
    D. list删除仅影响被删元素迭代器

  6. 提升关联容器查找性能的方法包括?
    A. 为unordered_map设计高质量哈希函数
    B. 对map使用自定义排序谓词
    C. 预分配桶数量减少冲突
    D. 使用lower_bound进行二分查找

  7. std::forward_list相比std::list的缺点是?
    A. 不支持反向遍历
    B. 删除元素需要前驱节点
    C. 每个节点额外存储两个指针
    D. 插入速度更慢

  8. 关于缓存局部性的正确描述是?
    A. vector遍历比list更快
    B. 随机访问破坏空间局部性
    C. 小对象更适合用链表存储
    D. 内存对齐影响缓存利用率

  9. 选择std::array而非std::vector的理由包括?
    A. 需要运行时动态扩容
    B. 避免堆内存分配
    C. 更快的随机访问速度
    D. 支持STL算法

  10. 优化容器内存使用的策略有?
    A. 使用shrink_to_fit释放多余内存
    B. 为vector预留精确容量
    C. 用emplace_back避免临时对象
    D. 优先选择连续内存容器


多选题答案与解析

  1. ABD
    C错误:预分配减少但无法完全消除复制(如中间插入仍需移动)

  2. BC
    A/D是map的特性,哈希表查找平均O(1)但最差O(n)

  3. B
    内存池减少动态分配开销,链表遍历本身无法优化缓存局部性

  4. B
    deque设计目标就是高效头尾操作,C错误因分块存储仍有碎片

  5. BCD
    vector插入可能导致扩容使所有迭代器失效,但未扩容时仅后部失效

  6. AC
    D适用于有序容器,B不影响查找复杂度

  7. AB
    forward_list每个节点只需一个指针,但删除需要前驱节点

  8. ABD
    C错误:小对象用链表反而增加内存碎片

  9. B
    array栈上分配,但随机访问速度与vector相当

  10. ACD
    B过度预留会增加内存占用


设计题

题目1:实时日志系统
需求:实现一个高并发日志系统,支持每秒百万级日志条目追加和批量读取,要求内存高效。

设计要点

  1. 使用std::deque分块存储,避免vector扩容时的全量复制
  2. 批量处理时利用缓存局部性
  3. 测试代码:
#include <deque>
#include <chrono>struct LogEntry { /*...*/ };int main() {std::deque<LogEntry> logs;// 并发写入测试auto start = std::chrono::high_resolution_clock::now();for (int i=0; i<1'000'000; ++i) {logs.emplace_back(/*...*/);}auto end = std::chrono::high_resolution_clock::now();// 输出写入耗时
}

题目2:游戏实体管理
需求:管理数万个动态变化的游戏实体,需快速遍历和频繁插入删除。

解决方案

  1. 使用std::vector配合标记删除,牺牲删除速度换取遍历性能
  2. 测试代码:
std::vector<Entity> entities;
std::vector<size_t> free_indices;void RemoveEntity(size_t id) {entities[id].alive = false;free_indices.push_back(id);
}void AddEntity(const Entity& e) {if (!free_indices.empty()) {entities[free_indices.back()] = e;free_indices.pop_back();} else {entities.push_back(e);}
}// 遍历时跳过标记为dead的实体

题目3:金融交易缓存
需求:实现股票代码到最新报价的映射,要求纳秒级查询延迟。

设计选择

  1. 使用std::unordered_map预分配足够桶数量
  2. 测试代码:
#include <unordered_map>
#include <string>struct Quote { /*...*/ };
std::unordered_map<std::string, Quote> cache(1000000);// 预分配桶
cache.reserve(1000000);// 查询测试
auto it = cache.find("AAPL");

题目4:科学计算矩阵存储
需求:存储大型稀疏矩阵,支持快速按行列访问。

数据结构

  1. 使用std::map<tuple<int,int>, double>存储非零元素
  2. 测试代码:
#include <map>
#include <tuple>std::map<std::tuple<int,int>, double> matrix;void SetValue(int row, int col, double val) {matrix[{row, col}] = val;
}// 行遍历优化:使用lower_bound搜索行号

题目5:内存敏感型应用优化
需求:在嵌入式系统中管理动态数据,避免内存碎片。

方案

  1. 使用自定义内存池+std::vector预分配
  2. 测试代码:
#include <vector>
#include <memory_pool>template<typename T>
class PoolAllocator { /*...*/ };std::vector<int, PoolAllocator<int>> data;
data.reserve(1024); // 预分配固定内存块

设计题答案解析

  1. 实时日志系统
    选择deque因其分块特性避免大规模数据复制,适合高频追加。测试中可对比vector的扩容耗时差异。

  2. 游戏实体管理
    Vector+标记删除在遍历时通过紧凑内存布局提升缓存命中率,适合每帧遍历全部实体的场景。

  3. 金融交易缓存
    unordered_map预分配桶减少rehash,哈希查找O(1)满足低延迟要求。测试需验证不同哈希函数性能。

  4. 稀疏矩阵存储
    使用有序map支持高效范围查询,但更新较慢。对比unordered_map可权衡插入/查询性能。

  5. 嵌入式内存优化
    内存池分配器避免频繁堆操作,vector预分配消除动态增长开销。可通过valgrind验证内存碎片情况。


测试用例示例(矩阵存储)

#include <iostream>
#include <map>
#include <tuple>int main() {std::map<std::tuple<int,int>, double> matrix;// 插入测试matrix[{100, 200}] = 3.14;matrix[{500, 600}] = 2.718;// 查找测试auto it = matrix.find({100, 200});if (it != matrix.end()) {std::cout << "Found: " << it->second << std::endl;}// 行遍历示例int target_row = 100;auto lower = matrix.lower_bound({target_row, 0});auto upper = matrix.lower_bound({target_row+1, 0});for (auto it=lower; it!=upper; ++it) {std::cout << "Row " << target_row << " has value " << it->second << std::endl;}
}

其他详细答案,稍后补充

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词