容器数据接口及时间复杂度总结
以下是 C++ STL 容器的底层数据结构及增删查时间复杂度总结:
| 容器 |
底层数据结构 |
插入(平均) |
删除(平均) |
查找(平均) |
备注 |
vector |
动态数组 |
O(n) |
O(n) |
O(1)(索引) |
尾部插入/删除 O(1) |
deque |
分块数组 + 双端队列 |
O(1)(头尾) |
O(1)(头尾) |
O(1)(索引) |
中间插入/删除 O(n) |
list |
双向链表 |
O(1) |
O(1) |
O(n) |
需已知位置迭代器 |
forward_list |
单向链表 |
O(1) |
O(1) |
O(n) |
需已知前驱迭代器 |
array |
静态数组 |
不支持 |
不支持 |
O(1)(索引) |
固定大小 |
stack |
默认基于 deque |
O(1) |
O(1) |
无 |
适配器,LIFO |
queue |
默认基于 deque |
O(1) |
O(1) |
无 |
适配器,FIFO |
priority_queue |
默认基于 vector + 堆 |
O(log n) |
O(log n) |
O(1)(顶部) |
适配器,堆结构 |
set |
红黑树 |
O(log n) |
O(log n) |
O(log n) |
元素唯一,有序 |
multiset |
红黑树 |
O(log n) |
O(log n) |
O(log n) |
元素可重复,有序 |
map |
红黑树 |
O(log n) |
O(log n) |
O(log n) |
键唯一,有序 |
multimap |
红黑树 |
O(log n) |
O(log n) |
O(log n) |
键可重复,有序 |
unordered_set |
哈希表 |
O(1) |
O(1) |
O(1) |
元素唯一,无序 |
unordered_multiset |
哈希表 |
O(1) |
O(1) |
O(1) |
元素可重复,无序 |
unordered_map |
哈希表 |
O(1) |
O(1) |
O(1) |
键唯一,无序 |
unordered_multimap |
哈希表 |
O(1) |
O(1) |
O(1) |
键可重复,无序 |
关键说明:
- 哈希容器 的时间复杂度为平均情况,最坏情况(全冲突)为 O(n)
- 树形容器(set/map)始终保持有序状态
- 序列容器 的插入/删除复杂度取决于操作位置
- 适配器容器(stack/queue/priority_queue)仅支持特定操作
此表格提供了常见操作的时间复杂度概览,实际性能可能受具体实现和数据分布影响。
C++ STL 容器常用操作示例及公共方法总结
各容器增删改查遍历常用示例
1. vector
#include <vector>
#include <algorithm>
#include <iostream>
void vector_example() {
std::vector<int> vec = {1, 2, 3};
// 增
vec.push_back(4); // 尾部插入
vec.insert(vec.begin() + 1, 5); // 指定位置插入
vec.emplace_back(6); // 原位构造
// 删
vec.pop_back(); // 尾部删除
vec.erase(vec.begin() + 1); // 删除指定位置
vec.erase(std::remove(vec.begin(), vec.end(), 3), vec.end()); // 删除特定值
// 改
vec[0] = 10; // 通过索引修改
vec.at(1) = 20; // 带边界检查的修改
std::replace(vec.begin(), vec.end(), 2, 200); // 替换特定值
// 查
int val = vec[2]; // 通过索引访问
auto it = std::find(vec.begin(), vec.end(), 5); // 查找元素
bool has_value = std::binary_search(vec.begin(), vec.end(), 10); // 二分查找
// 遍历
std::cout << "vector遍历: ";
for (size_t i = 0; i < vec.size(); ++i) {
std::cout << vec[i] << " ";
}
std::cout << std::endl;
// 迭代器遍历
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
// 范围for循环
for (const auto& element : vec) {
std::cout << element << " ";
}
}
2. list
#include <list>
#include <iostream>
void list_example() {
std::list<int> lst = {1, 2, 3};
// 增
lst.push_back(4); // 尾部插入
lst.push_front(0); // 头部插入
lst.insert(std::next(lst.begin()), 5); // 指定位置插入
// 删
lst.pop_back(); // 尾部删除
lst.pop_front(); // 头部删除
lst.erase(std::next(lst.begin())); // 删除指定位置
lst.remove(2); // 删除所有值为2的元素
// 改
*lst.begin() = 10; // 通过迭代器修改
std::replace(lst.begin(), lst.end(), 3, 30);
// 查
auto it = std::find(lst.begin(), lst.end(), 5);
bool found = (it != lst.end());
// 遍历
std::cout << "list遍历: ";
for (const auto& element : lst) {
std::cout << element << " ";
}
std::cout << std::endl;
}
3. deque
#include <deque>
#include <iostream>
void deque_example() {
std::deque<int> dq = {1, 2, 3};
// 增
dq.push_back(4); // 尾部插入
dq.push_front(0); // 头部插入
dq.insert(dq.begin() + 2, 5); // 指定位置插入
// 删
dq.pop_back(); // 尾部删除
dq.pop_front(); // 头部删除
dq.erase(dq.begin() + 1); // 删除指定位置
// 改
dq[0] = 10;
dq.at(1) = 20;
// 查
int front = dq.front();
int back = dq.back();
// 遍历
std::cout << "deque遍历: ";
for (const auto& element : dq) {
std::cout << element << " ";
}
std::cout << std::endl;
}
4. map
#include <map>
#include <iostream>
void map_example() {
std::map<int, std::string> m = {{1, "one"}, {2, "two"}, {3, "three"}};
// 增
m[4] = "four"; // 使用下标插入
m.insert({5, "five"}); // 使用insert插入
m.emplace(6, "six"); // 原位构造
// 删
m.erase(2); // 按键删除
m.erase(m.find(3)); // 按迭代器删除
// 改
m[1] = "ONE"; // 修改值
m.at(4) = "FOUR"; // 带检查的修改
// 查
auto it = m.find(1); // 查找键
if (it != m.end()) {
std::cout << "找到: " << it->second << std::endl;
}
bool exists = m.count(5) > 0; // 检查键是否存在
// 遍历
std::cout << "map遍历: ";
for (const auto& pair : m) {
std::cout << "{" << pair.first << ": " << pair.second << "} ";
}
std::cout << std::endl;
}
5. set
#include <set>
#include <iostream>
void set_example() {
std::set<int> s = {1, 2, 3, 4, 5};
// 增
s.insert(6);
s.emplace(7);
// 删
s.erase(2);
s.erase(s.find(3));
// 改: set元素不可直接修改,需要先删除再插入
// 查
auto it = s.find(4);
bool exists = s.count(5) > 0;
auto lb = s.lower_bound(3); // 第一个不小于3的元素
auto ub = s.upper_bound(3); // 第一个大于3的元素
// 遍历
std::cout << "set遍历: ";
for (const auto& element : s) {
std::cout << element << " ";
}
std::cout << std::endl;
}
6. unordered_map
#include <unordered_map>
#include <iostream>
void unordered_map_example() {
std::unordered_map<int, std::string> um = {{1, "one"}, {2, "two"}};
// 增
um[3] = "three";
um.insert({4, "four"});
// 删
um.erase(2);
// 改
um[1] = "ONE";
// 查
auto it = um.find(1);
bool exists = um.count(3) > 0;
// 遍历
std::cout << "unordered_map遍历: ";
for (const auto& pair : um) {
std::cout << "{" << pair.first << ": " << pair.second << "} ";
}
std::cout << std::endl;
}
容器公共方法总结
所有容器共有的方法
| 方法 |
描述 |
示例 |
size() |
返回元素数量 |
vec.size() |
empty() |
检查是否为空 |
if (cont.empty()) |
clear() |
清空所有元素 |
cont.clear() |
begin()/end() |
返回迭代器 |
auto it = cont.begin() |
cbegin()/cend() |
返回const迭代器 |
auto cit = cont.cbegin() |
swap(other) |
交换两个容器 |
cont1.swap(cont2) |
序列容器特有方法 (vector, list, deque, array)
| 方法 |
描述 |
适用容器 |
front() |
访问第一个元素 |
除forward_list外 |
back() |
访问最后一个元素 |
除forward_list外 |
push_back() |
尾部插入 |
vector, list, deque |
pop_back() |
尾部删除 |
vector, list, deque |
push_front() |
头部插入 |
list, deque |
pop_front() |
头部删除 |
list, deque |
insert(pos, value) |
指定位置插入 |
所有序列容器 |
erase(pos) |
删除指定位置 |
所有序列容器 |
关联容器特有方法 (set, map, multiset, multimap)
| 方法 |
描述 |
示例 |
find(key) |
查找元素 |
s.find(key) |
count(key) |
统计出现次数 |
m.count(key) |
lower_bound(key) |
返回第一个不小于key的迭代器 |
s.lower_bound(key) |
upper_bound(key) |
返回第一个大于key的迭代器 |
s.upper_bound(key) |
equal_range(key) |
返回匹配key的范围 |
auto range = m.equal_range(key) |
无序容器特有方法 (unordered_*)
| 方法 |
描述 |
示例 |
bucket_count() |
返回桶的数量 |
um.bucket_count() |
load_factor() |
返回负载因子 |
um.load_factor() |
rehash(n) |
设置桶的数量 |
um.rehash(100) |
reserve(n) |
预留空间 |
um.reserve(1000) |
迭代器相关方法
| 方法 |
描述 |
示例 |
begin()/end() |
正向迭代器 |
for (auto it = cont.begin(); ...) |
rbegin()/rend() |
反向迭代器 |
for (auto rit = cont.rbegin(); ...) |
cbegin()/cend() |
const正向迭代器 |
只读遍历 |
crbegin()/crend() |
const反向迭代器 |
只读反向遍历 |
容量相关方法
| 方法 |
描述 |
适用容器 |
size() |
当前元素数量 |
除forward_list外 |
max_size() |
最大可能大小 |
所有 |
resize(n) |
调整大小 |
序列容器 |
capacity() |
当前容量 |
vector, string |
reserve(n) |
预留容量 |
vector, string, unordered_* |
shrink_to_fit() |
减少容量到适合 |
vector, deque, string |
使用建议总结
选择容器的指导原则:
- 需要随机访问 →
vector, deque, array
- 频繁在中间插入删除 →
list, forward_list
- 需要有序存储和快速查找 →
set, map
- 只需要快速查找,不关心顺序 →
unordered_set, unordered_map
- 需要双端操作 →
deque
- 固定大小 →
array
性能提示:
vector 在尾部操作高效,中间操作昂贵
list 在任何位置插入删除都高效,但访问慢
- 关联容器保持元素有序,查找为 O(log n)
- 无序容器查找为平均 O(1),最坏 O(n)
这些示例和方法覆盖了 STL 容器的主要使用场景,掌握这些操作能够满足日常开发的大部分需求。