Post

CPP STL

CPP STL

C++ STL(Standard Template Library,标准模板库)主要分为 六大组件容器 (Containers)算法 (Algorithms)迭代器 (Iterators)函数对象 (Functors)适配器 (Adaptors)分配器 (Allocators),通过模板将数据结构、算法和迭代策略解耦,提供了一套通用的、高效的、类型安全的数据结构组件,避免了程序员重复造轮子。

序列式容器

容器底层结构特点典型复杂度适用场景
std::vector动态数组内存连续,支持随机访问,尾部插入快,中间插入慢随机访问 O(1),尾插 O(1)摊销默认首选容器,缓存友好
std::deque分段连续数组头尾插入/删除都快,支持随机访问随机访问 O(1),头尾插删 O(1)双端队列,滑动窗口
std::list双向链表任意位置插入/删除快,不支持随机访问插入/删除 O(1),查找 O(n)频繁在中间插入/删除
std::forward_list单向链表比 list 更省空间,只能向前遍历同 list极端内存敏感场景
std::array静态数组固定大小,栈上分配,零开销抽象全部 O(1)替代原生数组,安全且兼容STL

std::vector

std::vector可变大小的动态数组,提供了与原生数组相近的性能和随机访问能力,同时自动管理内存。

vector 使用动态内存分配(通常是 operator newallocate)在堆上申请内存块,其元素被存储在连续的内存地址中,通过 construct / destroy 进行对象构造与析构。

std::vector 的底层是三段式指针(或指针+偏移量)结构:

1
2
3
T* start;        // 指向已分配内存的起始位置
T* finish;       // 指向最后一个有效元素的下一个位置(即 end())
T* end_of_storage; // 指向已分配内存的末尾(capacity 的上限)

注:GCC (libstdc++) / Clang (libc++)采用 2 倍 扩容,MSVC (Visual Studio)采用 1.5 倍 扩容。

构造

方法说明
vector<T> v;默认构造,空容器
vector<T> v(n, val);n 个 val 的拷贝
vector<T> v(begin, end);迭代器区间构造
v.assign(n, val);重新赋值为 n 个 val
v = other; / v = move(other);拷贝 / 移动赋值

std::vector<double> vectors;创建后,vectors是一个空的 vector 容器,因为容器中没有元素,所以没有为其分配空间。当添加第一个元素(比如使用 push_back() 函数)时,vector 会自动分配内存。vector<T> v(n);可指定初始容量为n,默认值为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
std::vector<int> v1;              // 初始为空,没有内存开辟
std::vector<int> v2{1, 2, 3};     // 直接调用构造函数
std::vector<int> v3 = {1, 2, 3};  // C++17 起,直接构造,等于上一条

std::vector<int> v4(10);                // 10个0
std::vector<int> v5(10, 42);     // 10个42

std::vector<int> src = {10, 20, 30, 40, 50};
std::vector<int> v6(src.begin() + 1, src.begin() + 4);       // {20, 30, 40}
std::vector<int> v7(std::begin(src), std::end(src)); // {10, 20, 30, 40, 50}

std::vector<int> v8(src);          // {10, 20, 30, 40, 50}
std::vector<int> v9 = src;         // {10, 20, 30, 40, 50}

std::vector<int> v10(std::move(src)); // src is empty

std::vector<int> v11;         // 初始为空,没有内存开辟
v11.assign(10, 1);    // 10个1
v11.assign({1, 2, 3});     // {1, 2, 3}

std::vector<std::string> v12;
v12.push_back(std::string("hello"));  // 构造临时 string 再移动进 vector
v12.emplace_back("world");               // 直接在 vector 内存中调用string的构造函数

std::vector<std::vector<int>> matrix(3, std::vector<int>(4, 0)); // 3行4列的0矩阵

元素访问

方法说明边界检查
v[i]下标访问不检查
v.at(i)下标访问越界抛 out_of_range
v.front() / v.back()首元素 / 尾元素 的引用空容器未定义
v.data()返回指向首元素的裸指针 T*
1
2
3
4
5
6
std::vector<int> v = {1, 2, 3, 4, 5};
std::cout << "v[0]: " << v[0] << std::endl;
std::cout << "v.at(0): " << v.at(0) << std::endl;
std::cout << "v.front(): " << v.front() << std::endl;
std::cout << "v.back(): " << v.back() << std::endl;
std::cout << "*v.data(): " << *v.data() << std::endl;

容量

方法说明
v.size()当前元素个数
v.capacity()不重新分配前提下可容纳的元素数
v.empty()是否为空
v.reserve(n)预分配至少容纳 n 个元素的空间
v.shrink_to_fit()请求释放多余容量(C++11,不强制)

修改

方法说明均摊复杂度
v.push_back(val)尾部追加(先创建临时对象,再移动到vector中)O(1)
v.emplace_back(val)尾部追加(不创建临时对象,直接在vector中构造) 
v.pop_back()尾部弹出O(1)
v.insert(pos, val)在迭代器 pos 前插入O(n)
v.erase(pos) / v.erase(first, last)删除单个 / 区间元素O(n)
v.clear()清空所有元素(size=0,capacity 不变)O(n)
v.resize(n, val)调整大小,新增元素以 val 填充
v.swap(other)交换两个 vector 的内部数据O(1)

std::deque

std::deque(double-ended queue 双端队列)在头部和尾部插入、删除都非常高效。

std::deque 通常由多个固定大小的内存块组成,再通过内部结构管理这些块。可以随机访问,但不保证元素连续存储。

  • 随机访问 O(1)
  • 头部,尾部插入push_back() / pop_back()
操作vectordeque
push_back()O(1) 均摊O(1)
pop_back()O(1)O(1)
push_front()O(n)O(1)
pop_front()O(n)O(1)
operator[]O(1)O(1)
at()O(1)O(1)
中间插入O(n)O(n)
中间删除O(n)O(n)
连续内存不是
支持随机访问

初始化

1
2
3
4
5
6
7
8
#include <deque>

std::deque<int> dq1; // 空deque
std::deque<int> dq2(5); // 指定数量[0,0,0,0,0]
std::deque<int> dq3(5, 100); // 指定数量和值 [100,100,100,100,100]

std::vector<int> v{1, 2, 3, 4};          
std::deque<int> dq4(v.begin(), v.end());  // 使用另一个容器初始化

关联式容器

容器底层结构特点典型复杂度
std::set / std::multiset红黑树有序集合,只存 Key查找、插入、删除均为 $O(log n)$
std::map / std::multimap红黑树有序映射,存 Key-Value 对查找、插入、删除均为 $O(log n)$

无序容器

容器底层结构特点典型复杂度
std::unordered_set / std::unordered_multiset哈希表无序集合,只存 Key平均 O(1),最差 O(n)(哈希冲突严重时)
std::unordered_map / std::unordered_multimap哈希表无序映射,存 Key-Value 对平均 O(1),最差 O(n)(哈希冲突严重时)

容器适配器

容器底层结构特点典型复杂度
std::stack默认用 deque  
std::queue默认用 deque  
std::priority_queue默认用 vector + make_heap  
容器分类底层数据结构随机访问头部插删尾部插删中间插删查找 (Key)迭代器类型迭代器稳定性有序性典型适用场景
std::array序列式静态数组 (栈)✅ O(1)❌ O(n)❌ O(n)❌ O(n)❌ O(n)Random Access✅ 永不失效固定大小数据、替代原生数组、高性能嵌入式
std::vector序列式动态数组 (堆)✅ O(1)❌ O(n)⚡ O(1)*❌ O(n)❌ O(n)Contiguous⚠️ 扩容时全部失效默认首选、缓存友好、批量数据处理
std::deque序列式分段连续数组✅ O(1)⚡ O(1)⚡ O(1)❌ O(n)❌ O(n)Random Access⚠️ 头尾插删可能失效双端队列、滑动窗口、需要头尾操作的缓冲区
std::list序列式双向链表❌ O(n)⚡ O(1)⚡ O(1)⚡ O(1)❌ O(n)Bidirectional✅ 仅被删元素失效频繁任意位置插入/删除、需要稳定迭代器引用
std::forward_list序列式单向链表❌ O(n)⚡ O(1)❌ O(n)⚡ O(1)❌ O(n)Forward✅ 仅被删元素失效极端内存敏感、只需前向遍历的链表操作
std::set关联式红黑树❌ O(n)❌ -❌ -❌ -✅ O(log n)Bidirectional✅ 仅被删元素失效✅ 升序去重且需有序、范围查询、有序集合运算
std::multiset关联式红黑树❌ O(n)❌ -❌ -❌ -✅ O(log n)Bidirectional✅ 仅被删元素失效✅ 升序允许重复键的有序集合
std::map关联式红黑树❌ O(n)❌ -❌ -❌ -✅ O(log n)Bidirectional✅ 仅被删元素失效✅ Key升序有序键值映射、需要按Key范围遍历
std::multimap关联式红黑树❌ O(n)❌ -❌ -❌ -✅ O(log n)Bidirectional✅ 仅被删元素失效✅ Key升序一对多有序映射
std::unordered_set无序哈希表❌ O(n)❌ -❌ -❌ -⚡ O(1)***Forward⚠️ Rehash时全部失效高速去重查找、不关心顺序
std::unordered_multiset无序哈希表❌ O(n)❌ -❌ -❌ -⚡ O(1)***Forward⚠️ Rehash时全部失效允许重复的高速查找
std::unordered_map无序哈希表❌ O(n)❌ -❌ -❌ -⚡ O(1)***Forward⚠️ Rehash时全部失效高频键值查找、缓存/索引系统
std::unordered_multimap无序哈希表❌ O(n)❌ -❌ -❌ -⚡ O(1)***Forward⚠️ Rehash时全部失效一对多高速映射
std::stack适配器默认 deque⚡ O(1)🚫 无-LIFO 栈、递归模拟、表达式求值
std::queue适配器默认 deque⚡ O(1)⚡ O(1)🚫 无-FIFO 队列、BFS、任务调度
std::priority_queue适配器默认 vector+heap⚡ O(log n)🚫 无-⚡ 堆序Top-K问题、Dijkstra、事件驱动调度
This post is licensed under CC BY 4.0 by the author.