C/C++ 常用容器功能汇总

1. 说明

C 语言本身不提供标准通用容器模板,常见数据结构通常需要:

  • 手动实现;
  • 使用第三方库;
  • 使用平台提供的链表、哈希表等接口。

C++ 标准模板库 STL 提供了丰富的容器、迭代器和算法。现代 C++ 开发应优先使用标准容器。


2. 容器分类

2.1 序列容器

按元素排列顺序组织数据:

  • std::array
  • std::vector
  • std::deque
  • std::list
  • std::forward_list

2.2 有序关联容器

通常基于平衡搜索树,元素保持有序:

  • std::set
  • std::multiset
  • std::map
  • std::multimap

2.3 无序关联容器

通常基于哈希表,元素不保证顺序:

  • std::unordered_set
  • std::unordered_multiset
  • std::unordered_map
  • std::unordered_multimap

2.4 容器适配器

基于其他容器封装特定访问方式:

  • std::stack
  • std::queue
  • std::priority_queue

3. 容器总览

容器 主要特点 随机访问 头部插入 尾部插入 中间插入 查找
array 固定长度连续数组 O(1) 不支持 不支持 不支持 O(n)
vector 动态连续数组 O(1) O(n) 均摊 O(1) O(n) O(n)
deque 双端动态序列 O(1) O(1) O(1) O(n) O(n)
list 双向链表 不支持 O(1) O(1) O(1) O(n)
forward_list 单向链表 不支持 O(1) 不直接支持 O(1) O(n)
set 有序唯一键 不支持 --- --- O(log n) O(log n)
map 有序键值对 不支持 --- --- O(log n) O(log n)
unordered_set 哈希唯一键 不支持 --- --- 平均 O(1) 平均 O(1)
unordered_map 哈希键值对 不支持 --- --- 平均 O(1) 平均 O(1)

复杂度为典型情况。无序容器在严重哈希冲突时,最坏可能退化为 O(n)。


4. 通用成员函数

大多数 STL 容器支持以下接口。

cpp 复制代码
container.empty();       // 是否为空
container.size();        // 元素个数
container.clear();       // 清空
container.begin();       // 首元素迭代器
container.end();         // 尾后迭代器
container.cbegin();      // const 迭代器
container.cend();

遍历:

cpp 复制代码
for (const auto& item : container) {
    // 只读访问
}

修改元素:

cpp 复制代码
for (auto& item : container) {
    // 可修改访问
}

5. std::array

固定长度、连续存储、长度在编译期确定。

cpp 复制代码
#include <array>

std::array<int, 4> values{1, 2, 3, 4};

values[0] = 10;
int x = values.at(1);

特点:

  • 无动态内存分配;
  • 可使用 STL 算法;
  • 支持 size()begin()end()
  • at() 越界时抛出异常;
  • 适合固定维度数据、查找表、小型缓冲区。

6. std::vector

最常用的动态数组。

cpp 复制代码
#include <vector>

std::vector<int> values;
values.reserve(100);

values.push_back(10);
values.emplace_back(20);
values.pop_back();

int first = values.front();
int last = values.back();

常用接口:

cpp 复制代码
values.size();
values.capacity();
values.reserve(1000);
values.resize(100);
values.shrink_to_fit();
values.insert(values.begin(), 5);
values.erase(values.begin());

特点:

  • 内存连续,缓存友好;
  • 支持随机访问;
  • 尾部插入高效;
  • 扩容可能导致指针、引用和迭代器失效;
  • 中间插入删除需要移动后续元素。

通常没有特殊理由时,序列数据优先考虑 vector


7. std::deque

双端队列,支持头尾高效插入删除。

cpp 复制代码
#include <deque>

std::deque<int> data;

data.push_back(10);
data.push_front(5);
data.pop_back();
data.pop_front();

特点:

  • 头尾操作通常为 O(1);
  • 支持随机访问;
  • 元素一般不是一整块连续内存;
  • 不适合把 data() 当作连续缓冲区;
  • 常作为 queuestack 的底层容器。

8. std::list

双向链表。

cpp 复制代码
#include <list>

std::list<int> values{1, 2, 3};

auto it = values.begin();
++it;
values.insert(it, 10);
values.erase(it);

特点:

  • 已知位置时插入、删除为 O(1);
  • 不支持随机访问;
  • 每个节点有额外指针开销;
  • 缓存局部性通常较差;
  • 迭代器通常较稳定;
  • 支持 splice() 在链表间移动节点。

不要因为"中间插入快"就默认选择 list。实际工程中,vector 常因缓存优势表现更好。


9. std::forward_list

单向链表,比 list 更节省节点开销。

cpp 复制代码
#include <forward_list>

std::forward_list<int> values{1, 2, 3};

values.push_front(0);
values.insert_after(values.before_begin(), -1);
values.erase_after(values.before_begin());

特点:

  • 仅支持单向遍历;
  • 不提供常数时间 size()
  • 使用 insert_aftererase_after
  • 适合极低开销单向链式结构。

10. std::setstd::multiset

cpp 复制代码
#include <set>

std::set<int> unique_values;
unique_values.insert(3);
unique_values.insert(1);
unique_values.insert(3);  // 不会重复插入

std::multiset<int> repeated_values;
repeated_values.insert(3);
repeated_values.insert(3);

特点:

  • 自动排序;
  • set 键唯一;
  • multiset 允许重复;
  • 查找、插入、删除通常为 O(log n);
  • 适合范围查询和有序遍历。

11. std::mapstd::multimap

cpp 复制代码
#include <map>
#include <string>

std::map<std::string, int> scores;

scores["Alice"] = 90;
scores.insert({"Bob", 85});
scores.emplace("Carol", 95);

安全查找:

cpp 复制代码
auto it = scores.find("Alice");

if (it != scores.end()) {
    int score = it->second;
}

注意:

cpp 复制代码
int value = scores["Unknown"];

operator[] 在键不存在时会创建一个默认值元素。

只查询不插入时,可使用:

cpp 复制代码
int value = scores.at("Alice");

12. std::unordered_setstd::unordered_map

cpp 复制代码
#include <unordered_map>
#include <unordered_set>

std::unordered_set<int> ids{1, 2, 3};

std::unordered_map<int, std::string> names;
names.emplace(1, "camera");
names.emplace(2, "lidar");

特点:

  • 平均 O(1) 查找;
  • 不保证元素顺序;
  • 需要哈希函数和相等比较;
  • 扩容重新哈希会使迭代器失效;
  • 可通过 reserve() 降低重复扩容成本。
cpp 复制代码
names.reserve(1000);
names.max_load_factor(0.75f);

13. std::stack

后进先出 LIFO。

cpp 复制代码
#include <stack>

std::stack<int> st;

st.push(1);
st.push(2);

int top = st.top();
st.pop();

常用接口:

  • push
  • emplace
  • top
  • pop
  • empty
  • size

注意:pop() 不返回被删除的元素。


14. std::queue

先进先出 FIFO。

cpp 复制代码
#include <queue>

std::queue<int> q;

q.push(1);
q.push(2);

int first = q.front();
int last = q.back();

q.pop();

常用接口:

  • push
  • emplace
  • front
  • back
  • pop
  • empty
  • size

15. std::priority_queue

优先级队列,默认最大元素优先。

cpp 复制代码
#include <queue>
#include <vector>
#include <functional>

std::priority_queue<int> max_heap;
max_heap.push(3);
max_heap.push(10);
max_heap.push(5);

int max_value = max_heap.top();

最小堆:

cpp 复制代码
std::priority_queue<
    int,
    std::vector<int>,
    std::greater<int>
> min_heap;

典型用途:

  • Top-K;
  • Dijkstra;
  • 任务调度;
  • 实时选择最高优先级元素。

16. 常用 STL 算法

cpp 复制代码
#include <algorithm>
#include <numeric>
#include <vector>

std::vector<int> values{4, 1, 3, 2};

std::sort(values.begin(), values.end());

auto it = std::find(values.begin(), values.end(), 3);

int count = std::count(values.begin(), values.end(), 2);

int sum = std::accumulate(values.begin(), values.end(), 0);

常见算法:

  • sort
  • stable_sort
  • find
  • find_if
  • count
  • count_if
  • lower_bound
  • upper_bound
  • binary_search
  • remove_if
  • transform
  • accumulate

删除满足条件的元素:

cpp 复制代码
values.erase(
    std::remove_if(
        values.begin(),
        values.end(),
        [](int x) { return x < 0; }
    ),
    values.end()
);

C++20 可使用:

cpp 复制代码
std::erase_if(values, [](int x) { return x < 0; });

17. 容器选择建议

需求 推荐容器
默认动态序列 vector
固定长度数组 array
频繁头尾插入删除 deque
已知节点位置频繁链式操作 list
需要自动排序且键唯一 set
需要有序键值映射 map
需要快速查找,不关心顺序 unordered_map / unordered_set
后进先出 stack
先进先出 queue
始终获取最高或最低优先级 priority_queue

18. 工程注意事项

  1. 优先使用值语义和 RAII。
  2. 避免在容器中存储具有裸指针所有权的对象。
  3. 提前知道数据规模时,给 vectorunordered_map 调用 reserve()
  4. 只读遍历使用 const auto&,避免不必要拷贝。
  5. 插入复杂对象时优先考虑 emplace,但不要机械替换所有 push
  6. 修改容器结构后,注意迭代器、指针、引用是否失效。
  7. 对外暴露容器时,避免让调用方依赖具体底层类型。
  8. 根据访问模式、内存局部性和数据规模选择容器,而不只看理论复杂度。
相关推荐
不甘先生9 小时前
Go 中 type、方法与指针接收者:从 str_name.Name() 看懂 Go 的类型系统
开发语言·后端·golang
tudousisi2229 小时前
CSP 第3题训练 Day 5|CSP202509C HTTP 头信息|Blog A
c++
quantdash_cc9 小时前
Python 股票 K 线数据质量校验:字段、缺失值、重复行和价格异常
开发语言·python·数据分析·量化交易·股票数据·quantdash
Zenova EdgeOS9 小时前
工业网关重试机制:从固定间隔到指数退避的工程实战
开发语言·网络·php
闻缺陷则喜何志丹10 小时前
【动态规划】P10726 [GESP202406 八级] 空间跳跃|普及+
c++·算法·动态规划·洛谷
嘻哈baby10 小时前
Go 函数中的参数为什么不支持默认值?
java·开发语言·jvm
一晌小贪欢10 小时前
python-第29天:Python面向对象之多态与抽象类
开发语言·python·数据可视化·面向对象·python办公·python多态
Chester_199910 小时前
CSP202312C.树上搜索
开发语言·数据结构·c++·蓝桥杯·stl
Figo_Cheung10 小时前
Figo共振网络宇宙演化论(RNC) :从原初对称破缺到全息大和谐的演化路径研究
开发语言·php·量子计算
重生之我来学Python10 小时前
Git-SVN 混合开发,从入门到精通!
开发语言·git·svn·github