C++ STL(Standard Template Library)中主要提供了三大类通用容器,用于存储和管理不同类型和结构的数据。它们分别是:
STL 容器总览
| 容器类别 | 容器 | 说明 | 
|---|---|---|
| 序列式容器 (Sequence Containers) | vector,deque,list,forward_list,array | 元素按插入顺序排列,类似数组 | 
| 关联式容器 (Associative Containers) | set,map,multiset,multimap | 自动排序,基于平衡树(红黑树) | 
| 无序关联容器 (Unordered Associative Containers) | unordered_set,unordered_map,unordered_multiset,unordered_multimap | 无序哈希表,查找插入效率高 | 
| 容器适配器 (Container Adapters) | stack,queue,priority_queue | 封装其他容器,提供特定行为 | 
序列式容器(有顺序,按位置存储)
| 容器名 | 特点 | 常见用途 | 
|---|---|---|
| vector | 动态数组,支持随机访问,尾部插入快 | 默认首选容器,替代 C 数组 | 
| deque | 双端队列,支持首尾插入删除 | 双端栈、滚动窗口 | 
| list | 双向链表,插入删除快,不能随机访问 | 频繁插入/删除位置稳定 | 
| forward_list | 单向链表,内存更小(C++11) | 内嵌链表、只需前向遍历 | 
| array | 定长数组,封装 C 风格数组(C++11) | 栈上存储,性能好,长度固定 | 
一、序列式容器特性对比速查表
| 特性维度 | vector | array | deque | list | forward_list | 
|---|---|---|---|---|---|
| 底层结构 | 动态数组(连续内存) | 静态数组(连续内存) | 分段数组 | 双向链表 | 单向链表 | 
| 大小可变 | ✅ 是 | ❌ 否 | ✅ 是 | ✅ 是 | ✅ 是(无 size()) | 
| 下标访问 ( [],at()) | ✅ 支持 | ✅ 支持 | ✅ 支持 | ❌ 不支持 | ❌ 不支持 | 
| 头部操作 ( push_front) | ❌ 慢(需移动) | ❌ | ✅ 高效 | ✅ 高效 | ✅ 高效 | 
| 尾部操作 ( push_back) | ✅ 高效 | ❌ | ✅ 高效 | ✅ 高效 | ❌ 无此接口 | 
| 中间插入/删除 | ❌ 慢(移动元素) | ❌ | ⚠️ 中等偏慢 | ✅ 高效 | ✅ 但不方便 | 
| 空间管理函数 | ✅ reserve()、shrink_to_fit() | ❌ | ❌ | ❌ | ❌ | 
| 支持 data() | ✅ | ✅ | ✅(C++17 起) | ❌ | ❌ | 
| 支持 size() | ✅ | ✅ | ✅ | ✅ | ❌(需手动计数) | 
| 迭代器类型 | 随机访问 | 随机访问 | 随机访问 | 双向 | 单向(forward only) | 
| STL算法兼容性 | ✅ 全兼容 | ✅ 全兼容 | ✅ 全兼容 | ⚠️ 不能用 std::sort() | 同左 | 
| 稳定性(指针/引用稳定) | ❌ 增删会失效 | ✅ | ❌ | ✅ 稳定 | ✅ 稳定 | 
| 排序支持 | std::sort() | std::sort() | std::sort() | .sort()(成员函数) | .sort() | 
| 内存分配位置 | 堆 | 栈 | 堆(分段) | 堆(节点) | 堆(节点) | 
| 适用场景 | 随机访问、连续优化、频繁尾部插入 | 定长数组替代品、轻量 | 双端队列、滑动窗口 | 频繁中间修改 | 单向处理流、大量前插、轻量链表 | 
deque 内存分块不支持空间管理函数,vector 是一片连续的内存,支持 data()、reserve()、shrink_to_fit() 等内存操作;
list 和forward_list内部结构是链表,没有下标操作,链表自带排序api;
array 在编译时就确定了大小,不能动态改变元素的数量,内存分配在栈上;
- vector:适合连续内存优化,支持- data()、- reserve()、- shrink_to_fit()等内存操作,尾部插入效率高,但头部和中间插入较慢。
- array:固定大小的轻量容器,栈上分配,支持下标访问和- data(),不支持任何动态操作。
- deque:支持头部和尾部插入操作,适合双端队列场景,但不支持空间管理函数,底层为分段内存结构。
- list:双向链表容器,适合频繁的中间插入和删除,元素地址稳定,但不支持下标访问,也不能用- std::sort()(需要用成员- sort())。
- forward_list:单向链表容器,接口更轻量,不支持尾部插入和- size(),只能使用- insert_after()/- erase_after()等后置操作,适合嵌入式或轻量场景。
二、共性 API 总结
几乎所有序列式容器都支持如下操作(但有些容器支持受限):
| 类别 | API | 说明 | 
|---|---|---|
| 容器大小 | size() | 返回当前元素个数 | 
| empty() | 是否为空 | |
| max_size() | 可容纳最大元素数 | |
| 元素访问 | front()/back() | 返回头/尾元素(不能用于 forward_list的back()) | 
| 迭代器 | begin()/end() | 常规迭代器 | 
| cbegin()/cend() | const 版本 | |
| rbegin()/rend() | 反向迭代器 | |
| 修改操作 | insert() | 插入元素( forward_list仅支持 after) | 
| erase() | 删除元素( forward_list仅支持 after) | |
| push_back()/pop_back() | 尾部插入/删除( forward_list无) | |
| push_front()/pop_front() | 头部插入/删除( vector无) | |
| clear() | 清空容器 | |
| resize() | 改变大小 | |
| 其他 | swap() | 交换两个容器 | 
| assign() | 批量赋值(如指定区间或重复值) | 
二、每个容器详细 API + 特性对比
1. vector<T>
        
            
            
              cpp
              
              
            
          
          #include <vector>- std::vector是一个动态数组容器
- 元素连续存储,支持随机访问
- 尾部插入/删除效率高,插入中间位置效率较低
std::vector 的常用构造函数构造函数
| 构造函数 | 说明 | 
|---|---|
| vector() | 默认构造,空容器 | 
| vector(size_t n) | 创建含 n个默认值元素的容器 | 
| vector(size_t n, const T& val) | 创建含 n个值为val的元素 | 
| vector(initializer_list<T>) | 使用花括号初始化列表构造(C++11) | 
| vector(iter1, iter2) | 通过迭代器区间构造 | 
| vector(const vector& other) | 拷贝构造 | 
| vector(vector&& other) | 移动构造(C++11) | 
具体例子:
| 构造方式 | 示例 | 说明 | 
|---|---|---|
| 默认构造 | vector<int> v; | 创建空容器 | 
| 指定大小 | vector<int> v(5); | 创建 5 个默认元素(0) | 
| 指定大小和值 | vector<int> v(3, 9); | 创建 3 个值为 9 的元素 | 
| 初始化列表 | vector<int> v = {1, 2}; | 推荐用法,清晰直观 | 
| 区间构造 | vector<int> v(v2.begin(), v2.end()); | 从其他容器复制子区间 | 
| 拷贝构造 | vector<int> v(v2); | 拷贝另一个 vector | 
| 移动构造 | vector<int> v(std::move(v2)); | 高效转移所有权(C++11) | 
示例代码:
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <vector>
int main() {
    // 1. 默认构造
    std::vector<int> v1;
    std::cout << "v1.size = " << v1.size() << "\n";
    // 2. 构造 n 个默认值元素
    std::vector<int> v2(5);
    std::cout << "v2: ";
    for (int x : v2) std::cout << x << " ";  // 输出 0 0 0 0 0
    std::cout << "\n";
    // 3. 构造 n 个指定值
    std::vector<int> v3(4, 7);  // 4 个 7
    std::cout << "v3: ";
    for (int x : v3) std::cout << x << " ";  // 输出 7 7 7 7
    std::cout << "\n";
    // 4. 使用初始化列表
    std::vector<int> v4 = {1, 2, 3, 4};
    std::cout << "v4: ";
    for (int x : v4) std::cout << x << " ";  // 输出 1 2 3 4
    std::cout << "\n";
    // 5. 通过迭代器范围构造(从 v4 拷贝前两个元素)
    std::vector<int> v5(v4.begin(), v4.begin() + 2);
    std::cout << "v5: ";
    for (int x : v5) std::cout << x << " ";  // 输出 1 2
    std::cout << "\n";
    // 6. 拷贝构造
    std::vector<int> v6 = v3;
    std::cout << "v6: ";
    for (int x : v6) std::cout << x << " ";  // 输出 7 7 7 7
    std::cout << "\n";
    // 7. 移动构造(C++11)
    std::vector<int> v7 = std::move(v6);
    std::cout << "v7: ";
    for (int x : v7) std::cout << x << " ";  // 输出 7 7 7 7
    std::cout << "\n";
}增(插入)操作
- push_back(val)--- 在尾部插入一个元素
            
            
              cpp
              
              
            
          
          std::vector<int> v;
v.push_back(10);
v.push_back(20);  // v = {10, 20}- insert(pos, val)--- 在指定位置插入一个元素
            
            
              cpp
              
              
            
          
          auto it = v.begin();  // 指向第一个元素
v.insert(it + 1, 15); // 在索引1处插入 15:v = {10, 15, 20}- insert(pos, count, val)--- 插入多个相同元素
            
            
              cpp
              
              
            
          
          v.insert(v.begin(), 3, 7); // v = {7, 7, 7, 10, 15, 20}- insert(pos, range_begin, range_end)--- 插入迭代器范围
            
            
              cpp
              
              
            
          
          std::vector<int> other = {1, 2};
v.insert(v.end(), other.begin(), other.end());  // 尾部插入 1, 2删(删除)操作
- pop_back()--- 删除最后一个元素
            
            
              cpp
              
              
            
          
          v.pop_back();  // 删除末尾元素 2- erase(pos)--- 删除指定位置的元素
            
            
              cpp
              
              
            
          
          v.erase(v.begin() + 1);  // 删除索引1处元素- erase(begin, end)--- 删除范围内元素
            
            
              cpp
              
              
            
          
          v.erase(v.begin(), v.begin() + 2);  // 删除前两个元素- clear()--- 清空所有元素
            
            
              cpp
              
              
            
          
          v.clear();  // v.size() == 0改(修改)操作
- 使用 operator[]修改元素
            
            
              cpp
              
              
            
          
          std::vector<int> v = {1, 2, 3};
v[1] = 10;  // v = {1, 10, 3}- 使用 at(index)(带越界检查)
            
            
              cpp
              
              
            
          
          v.at(2) = 99;  // v = {1, 10, 99}⚠️ 如果越界,at() 会抛出 std::out_of_range 异常。
查(访问)操作
- 随机访问
            
            
              cpp
              
              
            
          
          int x = v[0];
int y = v.at(1);  // 推荐安全版本- 访问首尾元素
            
            
              cpp
              
              
            
          
          int first = v.front();
int last = v.back();- 遍历(推荐 range-for)
            
            
              cpp
              
              
            
          
          for (int val : v) {
    std::cout << val << " ";
}也可以用传统迭代器:
            
            
              cpp
              
              
            
          
          for (auto it = v.begin(); it != v.end(); ++it) {
    std::cout << *it << " ";
}其他操作
resize(n) --- 修改容器大小
            
            
              cpp
              
              
            
          
          v.resize(5);  // 增大到5个元素,自动补 0
v.resize(2);  // 缩小为2个元素reserve(capacity) --- 预分配内存(避免多次扩容)
            
            
              cpp
              
              
            
          
          v.reserve(100);  // 分配100个元素空间,提高效率shrink_to_fit() --- 回收冗余空间
vector::shrink_to_fit() 是 C++11 引入的一个标准库成员函数,作用是:请求 释放多余的内存容量,使 vector 的容量(capacity)收缩到与当前元素个数(size)相同,减少内存占用。
            
            
              cpp
              
              
            
          
          v.shrink_to_fit();data() 返回指向 vector 内部元素数组的指针
std::vector::data() 是一个成员函数,其作用是:返回指向 vector 内部元素数组的指针(也就是首元素的指针),你可以通过它访问底层的原始连续内存。
data()给你一个T\*指针 ,指向vector内部的连续数组。
            
            
              cpp
              
              
            
          
          T* data();              // 返回可写指针(C++11 起)
const T* data() const;  // 返回只读指针示例
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <vector>
int main() {
    std::vector<int> v = {1, 2, 3};
    int* ptr = v.data();
    ptr[1] = 42;  // 直接通过指针修改 vector 中的元素
    std::cout << v[1] << "\n";  // 输出 42
}注意事项
- vector的元素是 连续的内存块 ,所以- data()指针指向的是合法数组首地址。
- 如果 vector是空的(size() == 0),返回的指针 不是nullptr,但不能解引用。
- 若后续对 vector执行了插入/删除/扩容操作,原data()返回的指针可能会失效。
| 表达式 | 含义 | 备注 | 
|---|---|---|
| v.data() | 返回首元素地址指针 | 推荐方式,语义清晰 | 
| &v[0] | 同样返回首地址 | C++03 常用,不安全(空 vector 会崩溃) | 
示例完整程序
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <vector>
int main() {
    std::vector<int> v;
    // 增
    v.push_back(1);
    v.push_back(2);
    v.insert(v.begin(), 0);      // 插入到开头
    v.insert(v.end(), 2, 99);    // 插入两个 99
    // 改
    v[1] = 100;        // 修改第 1 个元素
    v.at(2) = 200;
    // 查
    std::cout << "Front: " << v.front() << ", Back: " << v.back() << "\n";
    std::cout << "遍历:";
    for (int x : v) std::cout << x << " ";
    std::cout << "\n";
    // 删
    v.pop_back();       // 删除最后一个 99
    v.erase(v.begin()); // 删除第一个元素
    v.clear();          // 清空
    std::cout << "Size after clear: " << v.size() << "\n";
}总结表格:vector 增删改查 API
| 操作 | 方法 | 说明 | 
|---|---|---|
| 增 | push_back(val) | 尾部插入 | 
| insert(pos, val) | 指定位置插入 | |
| 删 | pop_back() | 删除末尾元素 | 
| erase(pos) | 删除指定元素 | |
| clear() | 清空 | |
| 改 | v[i] = x,v.at(i) | 修改第 i 个元素 | 
| 查 | v[i],v.at(i) | 访问第 i 个元素 | 
| front(),back() | 首尾元素 | |
| begin(),end() | 迭代器遍历 | 
2. deque<T>
std::deque(double-ended queue )是由多个连续的小块内存(称为块、block、buffer)*构成的*分段动态数组 ,这些小块由一个中央控制结构(map,通常是指针数组)*统一管理,因此整体上*不连续 。deque 支持头部操作但不支持空间管理函数,vector 适合连续内存优化,支持 data()、reserve()、shrink_to_fit() 等内存操作。 vector 和 deque 在 API 层面上虽然大多数接口相同,但也存在以下几点显著差异**:
构造函数
与vector的构造函数很类似
| 构造函数形式 | 功能描述 | 示例 | 
|---|---|---|
| deque() | 默认构造,创建空容器 | std::deque<int> dq; | 
| deque(size_type n) | 创建包含 n个默认值元素的容器 | std::deque<int> dq(5); | 
| deque(size_type n, const T& val) | 创建包含 n个值为val的元素 | std::deque<int> dq(3, 42); | 
| deque(InputIt first, InputIt last) | 用迭代器区间 [first, last)构造容器 | std::deque<int> dq(vec.begin(), vec.end()); | 
| deque(const deque& other) | 拷贝构造函数,复制另一个容器 | std::deque<int> dq2(dq1); | 
| deque(deque&& other)(C++11) | 移动构造函数,接管另一个容器资源 | std::deque<int> dq2(std::move(dq1)); | 
| deque(std::initializer_list<T> ilist)(C++11) | 使用初始化列表构造容器 | std::deque<int> dq = {1, 2, 3}; | 
与 vector API 上的区别
| 功能类别 | vector | deque | 说明 | 
|---|---|---|---|
| 头部插入/删除 | ❌ 无 push_front/pop_front | ✅ 有 push_front/pop_front | 这是最明显的区别之一 | 
| 内存管理 | ✅ reserve()/capacity()/shrink_to_fit() | ❌ 不支持这些函数 | deque不暴露连续空间概念 | 
| 随机访问 | ✅ operator[],at() | ✅ 同样支持 | 接口一样,性能略有差别 | 
| 构造函数与赋值 | ✅ 基本一致 | ✅ 基本一致 | 如构造、 assign()等均支持 | 
| 数据指针获取 | ✅ data() | ⚠️ 无 data() | deque元素非连续,无法获取底层指针 | 
| 空间大小控制 | ✅ capacity()表示分配空间大小 | ❌ 无此 API | deque内部是block链表,不存在 capacity | 
与 vector 内存结构 上的区别
| 特性 | vector | deque | 
|---|---|---|
| 内存结构 | 一整块连续内存 | 多块分段内存(非连续) | 
| 指针访问 | data()可用 | 无 data()(不能整体访问) | 
| 局部性(cache 命中) | 优秀(线性) | 较差(跨 block) | 
| 增长方式 | 一次性重新分配更大空间 | 增加新的 block | 
deque内部结构图(简化示意)
std::deque 内存布局示意:
        map(指针数组)
        ↓
  +-----+-----+-----+-----+
  |  →  |  →  |  →  |  →  |   ← 控制中心
  +-----+-----+-----+-----+
     ↓     ↓     ↓     ↓
   block  block block block
   [x x]  [x x] [x x] [x x] ← 实际元素在这些 block 中存储(固定大小)- 每个 block 通常存储 512~4096 字节左右(依元素类型大小决定)
- 当 deque 两端插入导致空间不足时,它会:
- 分配新 block(前/后扩展)
- 可能重新分配指针数组(map)
 
实验验证deque 内存不连续
你可以验证 deque 是否连续:
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <deque>
int main() {
    std::deque<int> dq;
    // 插入较多元素,促使 deque 使用多个 block
    for (int i = 0; i < 1000; ++i) {
        dq.push_back(i);
    }
    // 打印地址,并检测地址跳变
    const void* last_addr = nullptr;
    for (size_t i = 0; i < dq.size(); ++i) {
        const void* addr = static_cast<const void*>(&dq[i]);
        if (last_addr != nullptr) {
            // 判断是否连续(以 sizeof(int) 为步长)
            std::ptrdiff_t diff = static_cast<const char*>(addr) - static_cast<const char*>(last_addr);
            if (diff != sizeof(int)) {
                std::cout << "⚠️  Address jump detected at i = " << i
                          << " (diff = " << diff << " bytes)\n";
            }
        }
        // 输出地址
        std::cout << "dq[" << i << "] = " << dq[i]
                  << " | address: " << addr << "\n";
        last_addr = addr;
    }
    return 0;
}常用函数一览表
| 类别 | 函数 | 说明 | 
|---|---|---|
| 增 | push_back(val) | 尾部插入 | 
| push_front(val) | 头部插入 | |
| insert(pos, val) | 插入元素 | |
| 删 | pop_back()/pop_front() | 删除尾/头元素 | 
| erase(pos)/clear() | 删除指定位置 / 全部 | |
| 改 | dq[i] = x/dq.at(i) | 修改第 i 个元素 | 
| 查 | dq[i]/at(i)/front()/back() | 访问元素 | 
| 其他 | size(),empty(),begin(),end() | 常规容器操作 | 
3. list<T>
虽然 std::list 和 std::deque 都支持双端插入和删除 ,但它们在底层结构、功能特性和 API 上存在一些明显区别。
deque是 双端数组容器 ,支持随机访问;
list是 双向链表容器,支持常量时间的中间插入删除。
构造函数
| 构造函数形式 | 功能描述 | 示例 | 
|---|---|---|
| list() | 默认构造,创建空链表容器 | std::list<int> lst; | 
| list(size_type n) | 创建包含 n个默认值元素的链表 | std::list<int> lst(5); | 
| list(size_type n, const T& val) | 创建包含 n个值为val的元素 | std::list<int> lst(3, 42); | 
| list(InputIt first, InputIt last) | 用迭代器区间 [first, last)构造链表 | std::list<int> lst(vec.begin(), vec.end()); | 
| list(const list& other) | 拷贝构造,复制另一个链表 | std::list<int> lst2(lst1); | 
| list(list&& other)(C++11) | 移动构造,接管另一个链表资源 | std::list<int> lst2(std::move(lst1)); | 
| list(std::initializer_list<T> ilist)(C++11) | 初始化列表构造 | std::list<int> lst = {1, 2, 3}; | 
API 区别总览(重点)
| 功能类别 | std::deque | std::list | 区别说明 | 
|---|---|---|---|
| 随机访问 | ✅ 支持 operator[],at() | ❌ 不支持 | list 无法直接访问第 i 个元素 | 
| 访问接口 | .at(i),[i],.front(),.back() | .front(),.back() | list 没有下标访问 | 
| 迭代器 | 双向迭代器 | 双向迭代器 | 都是 bidirectional(list 不能做随机跳跃) | 
| 插入位置 | .insert(pos, val) | ✅ 同上 | 接口类似,list 插入效率更高 | 
| 删除元素 | .erase(pos) | .erase(pos),.remove(val) | list 多了 remove()和unique()等 | 
| 移动元素 | ❌ 不支持 | ✅ splice() | list 可直接移动其他 list 的节点(不拷贝) | 
| 排序 | ❌ 需 std::sort(必须 random access) | ✅ 内建 .sort() | list 内建排序,不用 std::sort | 
| 内存结构 | 分段数组 | 双向链表 | list 节点分散,不连续 | 
| 内存局部性 | 好 | 差 | deque 在性能上通常比 list 更好 | 
list 独有 API
        
            
            
              cpp
              
              
            
          
          std::list<int> lst = {1, 2, 2, 3, 3, 4};
lst.remove(2);      // 删除所有值为2的元素
lst.unique();       // 去重:相邻重复元素只保留一个
lst.sort();         // 内建排序(非随机访问)4. forward_list<T>(C++11)
std::forward_list是单向链表 ,轻量、高效但功能少;
std::list是双向链表,功能强大但相对更重。
forward_list vs list API 对比总览
| 功能类别 | std::forward_list | std::list | 区别说明 | 
|---|---|---|---|
| 链表类型 | 单向链表 | 双向链表 | 方向不同,结构完全不同 | 
| 迭代器类型 | 单向( forward_iterator) | 双向( bidirectional_iterator) | forward_list只能从前往后走 | 
| 遍历方向 | 只能正向遍历 | 支持正向和反向遍历 | list支持reverse_iterator | 
| 头部插入 | .push_front()/.emplace_front() | ✅ 支持 | |
| 尾部插入 | ❌ 无 .push_back() | ✅ .push_back()/.emplace_back() | |
| 中间插入 | .insert_after(pos, val) | .insert(pos, val) | 插入方式不同 | 
| 中间删除 | .erase_after(pos) | .erase(pos) | 删除方式也不同 | 
| 删除所有指定值 | .remove(val) | .remove(val) | ✅ 两者都支持 | 
| 排序 | .sort() | .sort() | ✅ 都内建排序(非 std::sort) | 
| 合并 | .merge() | .merge() | ✅ 都支持(要求排序) | 
| 去重 | .unique() | .unique() | ✅ 都支持 | 
| 大小查询 | ❌ 无 .size() | ✅ .size() | forward_list需手动统计 | 
| 反转 | .reverse() | .reverse() | ✅ 都支持 | 
| splice(转移节点) | .splice_after() | .splice() | 名字和语义不同 | 
示例对比
            
            
              cpp
              
              
            
          
          std::list<int> lst = {1, 2, 3, 4};
auto it = lst.begin();
++it;
lst.insert(it, 99);  // 在第二个位置插入
lst.erase(it);       // 删除第二个元素
std::forward_list<int> fl = {1, 2, 3, 4};
auto it = fl.before_begin();  // 注意:必须使用 before_begin()
fl.insert_after(it, 99);      // 在开头插入
fl.erase_after(it);           // 删除开头后一个元素本质结构差异图示:
std::forward_list (单向):
[1] → [2] → [3] → nullptr
std::list (双向):
nullptr ← [1] ⇄ [2] ⇄ [3] → nullptr为什么没有push_back api
性能问题 :push_back 操作意味着在链表的末尾添加一个新元素。然而,在 std::forward_list 中,要找到链表的末尾需要从头开始遍历整个链表,因为每个节点只知道它的下一个节点是谁,而不知道它在整个链表中的位置或者如何快速访问最后一个节点。这意味着 push_back 的时间复杂度是 O(n),其中 n 是链表的长度。对于一个以高效插入和删除为卖点的数据结构来说,这不是理想的设计选择。
总结重点区别表
| 特性 | forward_list | list | 
|---|---|---|
| 链表方向 | 单向 | 双向 | 
| 迭代器 | 单向迭代器 | 双向迭代器 | 
| 尾部插入 | ❌ 无 | ✅ 有 | 
| 中间插入 | .insert_after() | .insert() | 
| 删除方式 | .erase_after() | .erase() | 
| 是否支持 .size() | ❌ 无 | ✅ 有 | 
| 是否轻量 | ✅ | ❌ 略重 | 
5. array<T, N>(C++11)
std::array 是 C++11 引入的 定长数组容器 ,在头文件 <array> 中定义,提供了类似 STL 容器的接口但底层是栈上固定大小数组。
std::array<T, N>是 C++ 中封装了原生数组的 STL 容器,大小固定、性能接近 C 数组,但支持标准容器接口。
构造函数
| 构造/初始化形式 | 说明 | 示例 | 
|---|---|---|
| 默认构造 | 元素未初始化(内置类型无默认值,需注意) | std::array<int, 3> a; | 
| 列表初始化(推荐) | 直接初始化所有元素,大小固定必须匹配 | std::array<int, 3> a = {1,2,3}; | 
| 拷贝构造/赋值 | 支持,拷贝元素 | std::array<int, 3> b(a); | 
常用 API 总览
| 类别 | 成员函数 | 说明 | 
|---|---|---|
| 元素访问 | at(i) | 安全访问(越界抛异常) | 
| operator[i] | 非安全访问(无越界检查) | |
| front() | 返回首元素 | |
| back() | 返回尾元素 | |
| data() | 返回底层原始数组指针(T*) | |
| 容量 | size() | 返回元素数量(固定) | 
| empty() | 是否为空(即 size == 0) | |
| 迭代器 | begin(), end() | 正向迭代器 | 
| rbegin(), rend() | 反向迭代器 | |
| cbegin(), cend() | const 版本 | |
| 修改操作 | fill(val) | 全部元素填充为指定值 | 
| swap(other) | 与另一个 std::array交换内容 | 
示例代码:快速上手
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <array>
int main() {
    std::array<int, 5> arr = {1, 2, 3, 4, 5};
    // 元素访问
    std::cout << arr[0] << "\n";        // 1
    std::cout << arr.at(1) << "\n";     // 2(越界抛异常)
    std::cout << arr.front() << "\n";   // 1
    std::cout << arr.back() << "\n";    // 5
    // 容量
    std::cout << "size: " << arr.size() << "\n";    // 5
    std::cout << "empty: " << arr.empty() << "\n";  // false
    // 迭代器
    for (int x : arr) {
        std::cout << x << " ";   // 1 2 3 4 5
    }
    std::cout << "\n";
    // 修改操作
    arr.fill(9);
    for (int x : arr) std::cout << x << " ";  // 9 9 9 9 9
}std::array vs std::vector
| 类别 | std::vector | std::array | 差异说明 | 
|---|---|---|---|
| 大小可变性 | ✅ 动态变化 | ❌ 固定大小 | vector支持增删元素,array不支持 | 
| 元素访问 | operator[],at(),front(),back() | 同上 | 元素访问接口一致 | 
| 获取裸指针 | .data() | .data() | 相同 | 
| 容量相关 | .size(),.capacity(),.empty(),.resize(),.reserve() | .size(),.empty() | array无.capacity()和.resize() | 
| 修改操作 | .push_back(),.pop_back(),.insert(),.erase() | ❌ 无这些操作 | array不支持动态修改 | 
| 元素填充 | ❌ 无 .fill() | ✅ .fill(val) | array独有 | 
| 交换内容 | .swap() | .swap() | 相同 | 
| 迭代器 | .begin(),.end(),rbegin(),rend()等 | 同上 | 支持一致的 STL 迭代器接口 | 
| 内存位置 | 堆上分配(new) | 栈上分配 | array更轻量,vector更灵活 | 
关联式容器(自动排序,基于红黑树)
| 容器名 | 特点 | 键是否唯一 | 复杂度 | 
|---|---|---|---|
| set | 自动排序的集合 | ✅ 是 | O(log n) | 
| multiset | 可重复元素集合 | ❌ 否 | O(log n) | 
| map | 键值对(key-value) | ✅ 是 | O(log n) | 
| multimap | 可重复键的 map | ❌ 否 | O(log n) | 
| 功能 / 容器 | std::map | std::multimap | std::set | std::multiset | 
|---|---|---|---|---|
| 键唯一性 | 是 | 否,允许重复键 | 是 | 否,允许重复元素 | 
| 元素类型 | pair<const Key, Value> | pair<const Key, Value> | Key | Key | 
| 插入方式 | insert,emplace,operator[] | insert,emplace | insert,emplace | insert,emplace | 
| 访问元素 | 支持 operator[],at() | 不支持 | 只支持查找 | 只支持查找 | 
| 查找元素 | find(key),count(key) (0/1) | find(key),count(key) (≥0) | find(val),count(val) (0/1) | find(val),count(val) (≥0) | 
| 范围查询 | lower_bound,upper_bound,equal_range | 同 map | 同 map | 同 map | 
| 删除元素 | erase(key),erase(iterator) | erase(key)删除所有匹配元素 | erase(value),erase(iterator) | erase(value),erase(iterator) | 
| 元素排序 | 默认按键升序 | 默认按键升序 | 默认按元素升序 | 默认按元素升序 | 
| 是否允许重复 | 否 | 是 | 否 | 是 | 
| 是否支持下标 | 是 | 否 | 否 | 否 | 
| 默认底层容器 | 红黑树 | 红黑树 | 红黑树 | 红黑树 | 
| 典型用途 | 唯一键值映射 | 一键多值映射 | 唯一有序集合 | 允许重复元素的有序集合 | 
std::pair
std::pair 是 C++ 标准库中一个非常基础的模板类,定义在 <utility> 头文件里。它的作用是 将两个类型可以不同的值"成对"存储起来,是构建简单复合数据结构的便捷工具。
主要作用
- 成对存储两个数据 ,可以是不同类型,比如 int和std::string。
- 常用来表示键值对(比如 std::map的元素类型就是std::pair<const Key, Value>)。
- 用于函数返回多个值。
- 用作容器元素,方便存储关联的两个数据。
定义示例
            
            
              cpp
              
              
            
          
          template <class T1, class T2>
struct pair {
    T1 first;
    T2 second;
};典型用法示例
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <utility>  // std::pair
#include <string>
int main() {
    std::pair<int, std::string> p(1, "apple");
    std::cout << "first: " << p.first << "\n";   // 输出 1
    std::cout << "second: " << p.second << "\n"; // 输出 apple
    // 使用 std::make_pair 自动推导类型
    auto p2 = std::make_pair(2, 3.14);
    std::cout << p2.first << ", " << p2.second << "\n"; // 2, 3.14
    return 0;
}常见操作
- p.first:访问第一个元素
- p.second:访问第二个元素
- 比较运算符(==, < 等)基于成员逐个比较
在 STL 中的应用
- 
std::map和std::multimap存储元素的类型是std::pair<const Key, Value>
- 
std::unordered_map也是以std::pair表示元素
- 
迭代器解包常用结构绑定: cppfor (const auto& [key, val] : myMap) { // 直接访问 key 和 val }
map
关联容器 API 对比:map vs multimap vs unordered_map
| 特性/容器 | std::map | std::multimap | std::unordered_map | 
|---|---|---|---|
| 是否允许重复 key | ❌ 否 | ✅ 是 | ❌ 否 | 
| key 排序方式 | ✅ 按 key 升序(默认用 <) | ✅ 按 key 升序 | ❌ 无序(基于哈希) | 
| 底层结构 | 平衡二叉搜索树(通常是红黑树) | 平衡二叉搜索树 | 哈希表 | 
| 平均时间复杂度 | O(log n) | O(log n) | O(1)(最坏 O(n)) | 
| 元素类型 | pair<const Key, T> | pair<const Key, T> | pair<const Key, T> | 
| 访问元素方式 | operator[],at() | ❌ 不支持 [],只能用insert/find | operator[],at() | 
| 插入方式 | insert,emplace,[] | insert,emplace | insert,emplace,[] | 
| 删除方式 | erase(key)/erase(iterator) | 同上 | 同上 | 
| 遍历顺序是否稳定/有序 | ✅ 有序 | ✅ 有序 | ❌ 无序 | 
| 边界操作支持 | ✅ lower_bound,upper_bound | ✅ equal_range,lower_bound, 等 | ❌ 不支持边界操作 | 
| 遍历所有重复 key 的值 | 不适用(key 唯一) | ✅ equal_range(key) | 不适用(key 唯一) | 
| 典型使用场景 | 一一映射、排序要求的数据结构 | 一对多映射(如成绩→学生) | 快速哈希查找映射 | 
示例代码对比
std::map 和std::unordered_map
std::map 和 std::multimap和std::unordered_map插入遍历是一致的,不同的是 std::multimap有时候可能遍历某个键的所有元素,使用 equal_range。
equal_range也可以用在 std::map和std::unordered_map,但是因为键是唯一的,没有必要。
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <map>
int main() {
    std::map<std::string, int> mp;
    mp["apple"] = 3;
    mp["banana"] = 5;
    mp.insert({"orange", 7});    // 插入
    mp.emplace("pear", 2);       // 原地构造
    std::cout << mp["apple"] << "\n";     // 3
    if (mp.find("banana") != mp.end())
        std::cout << "found\n";
    mp.erase("orange");          // 删除元素
    std::cout << "C++17 及以上遍历:" << "\n";     
    
    //遍历元素 范围基 for(结构化绑定,C++17 及以上)
    for (const auto& [key, value] : mp) {
        std::cout << key << ": " << value << "\n";
    }
    std::cout << "传统迭代器遍历:" << "\n";     
    
    // 传统迭代器遍历写法
    for (auto it = mp.begin(); it != mp.end(); ++it) {
        std::cout << it->first << ": " << it->second << "\n";
    }
    return 0;
}std::multimap
        
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <map>
int main() {
    std::multimap<std::string, int> mmap;
    mmap.insert({"apple", 3});
    mmap.insert({"apple", 4});  // 允许重复 key
    std::cout << "C++17 及以上遍历:" << "\n";
    // 遍历所有元素(包含重复键)
    for (const auto& [key, value] : mmap) {
        std::cout << key << ": " << value << "\n";
    }
    
    std::cout << "遍历某个键的所有元素:" << "\n";
    // 如果想遍历某个键的所有元素,使用 equal_range
    auto range = mmap.equal_range("apple");
    std::cout << "All values for key 'apple': ";
    for (auto it = range.first; it != range.second; ++it) {
        std::cout << it->second << " ";
    }
    std::cout << "\n";
}set
三种set对比
| 特性/容器 | std::set | std::multiset | std::unordered_set | 
|---|---|---|---|
| 元素是否唯一 | ✅ 是(自动去重) | ❌ 否(允许重复) | ✅ 是(自动去重) | 
| 元素是否有序 | ✅ 是(默认升序) | ✅ 是(默认升序) | ❌ 否(无序) | 
| 底层结构 | 红黑树 | 红黑树 | 哈希表 | 
| 时间复杂度(插/删/查) | O(log n) | O(log n) | 平均 O(1),最坏 O(n) | 
| 插入方式 | insert,emplace | 同左 | 同左 | 
| 删除方式 | erase(val),erase(it) | 同左 | 同左 | 
| 查找方式 | find(val),count(val) | find(val),count(val) | find(val),count(val) | 
| 范围操作( lower_bound等) | ✅ 支持 | ✅ 支持 | ❌ 不支持(哈希无顺序) | 
| 遍历顺序是否固定 | ✅ 升序 | ✅ 升序 | ❌ 无序 | 
示例代码
1. std::set 示例:唯一且自动升序
        
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <set>
int main() {
    std::set<int> s;
    s.insert(3);
    s.insert(1);
    s.insert(2);
    s.insert(2); // 重复元素不会插入
    for (int val : s) {
        std::cout << val << " ";  // 输出:1 2 3
    }
    if (s.count(2)) std::cout << "\nFound 2\n";
    s.erase(2); // 删除元素
}2. std::multiset 示例:允许重复元素
        
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <set>
int main() {
    std::multiset<int> ms;
    ms.insert(2);
    ms.insert(1);
    ms.insert(2); // 允许插入重复元素
    for (int val : ms) {
        std::cout << val << " ";  // 输出:1 2 2
    }
    std::cout << "\nCount of 2: " << ms.count(2) << "\n"; // 输出 2
    ms.erase(ms.find(2)); // 只删一个 2
}3. std::unordered_set 示例:唯一但无序
        
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <unordered_set>
int main() {
    std::unordered_set<int> us;
    us.insert(2);
    us.insert(3);
    us.insert(1);
    us.insert(2); // 重复插入无效
    for (int val : us) {
        std::cout << val << " ";  // 输出顺序不确定
    }
    if (us.count(3)) std::cout << "\nFound 3\n";
    us.erase(1); // 删除元素
}容器适配器(不是独立容器,是"封装器")
| 容器名 | 底层实现 | 特点 | 
|---|---|---|
| stack | 默认用 deque | 后进先出(LIFO) | 
| queue | 默认用 deque | 先进先出(FIFO) | 
| priority_queue | 默认用 vector+make_heap | 优先队列(最大堆/最小堆) | 
三种容器适配器对比
| 适配器 | 行为模型 | 默认底层容器 | 支持的接口 | 
|---|---|---|---|
| stack | 后进先出(LIFO) | deque | push(),pop(),top() | 
| queue | 先进先出(FIFO) | deque | push(),pop(),front(),back() | 
| priority_queue | 最大堆(默认) | vector | push(),pop(),top(),可自定义比较器 | 
std::stack
stack常用 API 一览表
| 函数名 | 说明 | 时间复杂度 | 
|---|---|---|
| push(const T& val) | 将元素压入栈顶 | O(1) | 
| pop() | 移除栈顶元素(不返回) | O(1) | 
| top() | 返回栈顶元素(不移除) | O(1) | 
| empty() | 判断栈是否为空 | O(1) | 
| size() | 返回元素数量 | O(1) | 
| emplace(args...) | 原地构造元素(C++11) | O(1) | 
| swap(other) | 与另一个 stack交换 | O(1) | 
示例代码
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <stack>
int main() {
    std::stack<int> st;
    st.push(10);
    st.push(20);
    st.push(30);
    std::cout << "Top: " << st.top() << "\n";  // 30
    st.pop();
    std::cout << "Top after pop: " << st.top() << "\n";  // 20
    std::cout << "Size: " << st.size() << "\n";  // 2
    std::cout << "Is empty? " << std::boolalpha << st.empty() << "\n";  // false
    // 原地构造(适合自定义类型)
    std::stack<std::pair<int, std::string>> pst;
    pst.emplace(1, "Hello");
    std::cout << pst.top().second << "\n";  // Hello
}注意事项
- 
pop()不会返回值 ,只能先用top()查看再pop()。cppint x = st.pop(); // ❌ 编译错误
- 
默认底层容器是 std::deque,也可以使用std::vector、std::list(但不推荐list)。cppstd::stack<int, std::vector<int>> vstack;
std::queue
std::queue 是 C++ STL 中的容器适配器 ,实现的是**先进先出(FIFO)**的队列行为。
- 默认基于 std::deque实现。
- 只能从队尾添加元素 ,从队头移除元素。
- 不支持迭代器、下标访问等功能。
常用 API 总览
| 函数名 | 说明 | 时间复杂度 | 
|---|---|---|
| push(const T& val) | 将元素加入队尾 | O(1) | 
| emplace(args...) | 在队尾原地构造元素 | O(1) | 
| pop() | 移除队头元素(不返回) | O(1) | 
| front() | 返回队头元素(不移除) | O(1) | 
| back() | 返回队尾元素 | O(1) | 
| empty() | 判断是否为空 | O(1) | 
| size() | 返回元素个数 | O(1) | 
| swap(other) | 与另一个 queue 交换内容 | O(1) | 
示例代码
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <queue>
int main() {
    std::queue<int> q;
    q.push(10);
    q.push(20);
    q.push(30);
    std::cout << "Front: " << q.front() << "\n";  // 10
    std::cout << "Back: " << q.back() << "\n";    // 30
    q.pop();  // 移除10
    std::cout << "New Front: " << q.front() << "\n";  // 20
    std::cout << "Size: " << q.size() << "\n";    // 2
    std::cout << "Empty? " << std::boolalpha << q.empty() << "\n";  // false
}注意事项
- 
pop()不返回值:cppint x = q.pop(); // ❌ 编译错误!需要先 front()再pop():cppint x = q.front(); q.pop();
- 
不能访问中间元素或用下标: cppq[0]; // ❌ 错误 for (auto it = q.begin(); ...) // ❌ 错误
- 
可以使用其他底层容器(如 list),但必须支持front()/back()/push_back()/pop_front():cppstd::queue<int, std::list<int>> q2;
std::priority_queue
std::priority_queue 是 C++ STL 中的容器适配器 ,实现的是一个堆(heap)结构的优先队列。
- 默认行为:大顶堆(最大元素优先出队)
- 底层容器 :默认使用 std::vector
- 排序工具 :基于 std::make_heap/push_heap/pop_heap实现
常用 API 总览
| 函数名 | 功能 | 时间复杂度 | 
|---|---|---|
| push(const T& val) | 插入元素 | O(log n) | 
| emplace(args...) | 原地构造元素(C++11) | O(log n) | 
| pop() | 移除优先级最高的元素 | O(log n) | 
| top() | 查看优先级最高的元素 | O(1) | 
| empty() | 判断是否为空 | O(1) | 
| size() | 返回元素数量 | O(1) | 
| swap(other) | 与另一个 priority_queue交换内容 | O(1) | 
示例代码(默认大顶堆)
            
            
              cpp
              
              
            
          
          #include <iostream>
#include <queue>
int main() {
    std::priority_queue<int> pq;
    pq.push(10);
    pq.push(30);
    pq.push(20);
    std::cout << pq.top() << "\n"; // 30
    pq.pop();                      // 删除 30
    std::cout << pq.top() << "\n"; // 20
}小顶堆写法(自定义比较器)
            
            
              cpp
              
              
            
          
          #include <queue>
#include <vector>
#include <functional>
std::priority_queue<int, std::vector<int>, std::greater<>> minHeap;
minHeap.push(5);
minHeap.push(2);
minHeap.push(8);
// 最小值优先
std::cout << minHeap.top() << "\n";  // 2存储结构说明
            
            
              cpp
              
              
            
          
          template<
    class T,
    class Container = std::vector<T>,
    class Compare = std::less<typename Container::value_type>
> class priority_queue;- T: 元素类型
- Container: 底层容器(默认- std::vector)
- Compare: 比较器(默认- std::less<T>,即大顶堆)
自定义类型示例
            
            
              cpp
              
              
            
          
          struct Task {
    int priority;
    std::string name;
    bool operator<(const Task& other) const {
        return priority < other.priority;  // 大顶堆:priority 高的优先
    }
};
std::priority_queue<Task> taskQ;
taskQ.push({5, "compile"});
taskQ.push({10, "build"});
std::cout << taskQ.top().name;  // build不支持的操作(适配器特性)
| 操作 | 是否支持 | 原因说明 | 
|---|---|---|
| 下标访问 | ❌ | 仅支持访问 top() | 
| 迭代器遍历 | ❌ | 不支持遍历 | 
| 排序顺序访问 | ❌ | 每次只能访问"堆顶最大"元素 | 
总结对比表
| 名称 | 行为模型 | 默认底层容器 | 主要接口 | 是否可自定义比较器 | 
|---|---|---|---|---|
| stack | LIFO | deque | push,pop,top | ❌ | 
| queue | FIFO | deque | push,pop,front,back | ❌ | 
| priority_queue | 堆 | vector | push,pop,top | ✅ 支持 |