一、容器的通用概念和 API
几乎所有 STL 容器(除少数比如 array 特例)都有一套共通的接口。
1. 常用类型别名
cpp
using value_type = T; // 元素类型
using size_type = std::size_t; // 大小类型
using iterator = ...; // 迭代器类型
using const_iterator = ...; // 只读迭代器
一般用法:
cpp
std::vector<int> v;
std::vector<int>::iterator it = v.begin();
std::vector<int>::size_type n = v.size();
实际写代码时很多时候用 auto 更方便。
2. 容器大小相关 API
几乎所有容器都有:
cpp
c.size(); // 返回当前元素个数
c.empty(); // 是否为空
c.max_size(); // 最大可容纳元素个数(一般很少用)
例子:
cpp
std::vector<int> v = {1, 2, 3};
if (!v.empty()) {
std::cout << "size = " << v.size() << '\n';
}
3. 迭代器与遍历相关 API
通用:
cpp
c.begin(); // 指向第一个元素
c.end(); // 指向最后一个元素"之后"的位置
c.cbegin(); // const 版本
c.cend();
遍历示例:
cpp
std::vector<int> v = {1, 2, 3};
// 传统迭代器写法
for (auto it = v.begin(); it != v.end(); ++it) {
std::cout << *it << ' ';
}
// range-for 写法(最常用)
for (int x : v) {
std::cout << x << ' ';
}
4. 修改容器:clear、swap
cpp
c.clear(); // 删除所有元素
c.swap(other); // 交换两个容器的内容,几乎是 O(1)
例子:
cpp
std::vector<int> a = {1, 2};
std::vector<int> b = {3, 4, 5};
a.swap(b); // 现在 a = {3,4,5}, b = {1,2}
二、顺序容器:vector、string、deque、list
先重点讲最常用的 vector 和 string,其他顺序容器 API 非常类似。
1. std::vector<T> 常用 API
vector 是最常用容器之一:支持随机访问,尾部增删非常快。
1.1 构造与初始化
cpp
std::vector<int> v1; // 空
std::vector<int> v2(10); // 10 个 0
std::vector<int> v3(10, 42); // 10 个 42
std::vector<int> v4 = {1, 2, 3}; // 列表初始化
1.2 访问元素
cpp
v[i]; // 不做边界检查
v.at(i); // 做边界检查,越界会抛异常
v.front(); // 第一个元素
v.back(); // 最后一个元素
v.data(); // 返回底层数组指针(T*)
例子:
cpp
std::vector<int> v = {10, 20, 30};
int a = v[0]; // 10
int b = v.at(1); // 20
int first = v.front();
int last = v.back();
int* p = v.data(); // 可以用于和 C API 交互
1.3 修改:push_back / pop_back / insert / erase
cpp
v.push_back(x); // 尾部插入
v.pop_back(); // 删除尾部一个元素
v.insert(pos, x); // 在迭代器 pos 前插入 x
v.erase(pos); // 删除 pos 位置的元素
v.erase(first, last); // 删除 [first, last) 区间
v.clear(); // 清空
例子:
cpp
std::vector<int> v = {1, 2, 4};
v.push_back(5); // v: 1 2 4 5
// 在第二个元素前插入 100
auto it = v.begin();
++it; // 指向元素 2
v.insert(it, 100); // v: 1 100 2 4 5
// 删除第一个元素
v.erase(v.begin()); // v: 100 2 4 5
1.4 预分配和容量:reserve / capacity / shrink_to_fit
cpp
v.reserve(n); // 预留容量,减少扩容次数
v.capacity(); // 当前容量(不等于 size)
v.shrink_to_fit(); // 建议收缩容量(实现可以不理会)
例子(写性能敏感代码时常用):
cpp
std::vector<int> v;
v.reserve(100000);
for (int i = 0; i < 100000; ++i) {
v.push_back(i);
}
2. std::string 常用 API(和 vector 很像,但多了字符串操作)
很多用法直接类比 vector<char>,再加上一些字符串专属 API。
2.1 常见构造
cpp
std::string s; // 空
std::string s1 = "hello"; // 从 C 字符串构造
std::string s2(5, 'a'); // "aaaaa"
2.2 访问
cpp
s[i]; // 不做检查
s.at(i); // 边界检查
s.front(); // 第一个字符
s.back(); // 最后一个字符
s.c_str(); // const char*,用于 C 接口
2.3 拼接和追加
cpp
s += "world";
s.push_back('!');
s.append("!!!");
例子:
cpp
std::string s = "hello";
s += " ";
s.append("world");
s.push_back('!');
std::cout << s; // "hello world!"
2.4 截取:substr
std::string sub = s.substr(pos); // 从 pos 到结尾
std::string sub2 = s.substr(pos, len); // 从 pos 开始 len 个
例:
cpp
std::string s = "abcdef";
std::string a = s.substr(2); // "cdef"
std::string b = s.substr(1, 3); // "bcd"
2.5 查找:find 系列
cpp
auto pos = s.find("abc"); // 找到返回下标,找不到返回 string::npos
auto pos2 = s.find('x');
例:
cpp
std::string s = "hello world";
auto pos = s.find("world"); // pos = 6
if (pos != std::string::npos) {
std::cout << "found at " << pos << '\n';
}
3. std::deque<T> 常用 API(双端队列)
特点:头尾增删都快,支持随机访问。
常用 API 和 vector 基本一样,外加:
cpp
d.push_front(x);
d.pop_front();
d.push_back(x);
d.pop_back();
例子:
cpp
std::deque<int> dq;
dq.push_back(2);
dq.push_front(1); // dq: 1 2
dq.push_back(3); // dq: 1 2 3
dq.pop_front(); // dq: 2 3
4. std::list<T> 常用 API(双向链表)
特点:任意位置插入/删除 O(1),但随机访问很慢(没有 operator[])。
基础的 push_back / push_front / insert / erase 同样有,还有:
cpp
l.push_front(x);
l.pop_front();
l.remove(value); // 删除所有等于 value 的元素
l.sort(); // 链表内部排序
l.reverse(); // 反转链表
例子:
cpp
std::list<int> lst = {3, 1, 4, 1};
lst.remove(1); // 删除所有值为 1 的节点 -> {3,4}
lst.push_front(5); // {5,3,4}
lst.sort(); // {3,4,5}
lst.reverse(); // {5,4,3}
三、有序关联容器:set / map(红黑树)
有序关联容器底层一般是红黑树(平衡二叉搜索树),键是有序的。
1. std::set<T> 常用 API
只存 key,自动去重、自动排序。
1.1 插入与删除
cpp
set.insert(x);
set.erase(x); // 按值删除
set.erase(it); // 按迭代器删除
例子:
cpp
std::set<int> s;
s.insert(3);
s.insert(1);
s.insert(2); // s 内部为 {1,2,3}
s.erase(2); // 删除元素 2
1.2 查找与存在性判断
cpp
auto it = s.find(x); // 找不到则返回 s.end()
bool ok = s.count(x); // 0 或 1
例子:
cpp
if (s.count(3)) {
std::cout << "exists\n";
}
1.3 遍历(有序)
cpp
for (int x : s) {
std::cout << x << ' '; // 按从小到大输出
}
2. std::map<Key, T> 常用 API
键值对容器:key 有序、唯一。
2.1 插入与访问
cpp
std::map<std::string, int> mp;
mp["Alice"] = 100; // 若不存在则插入,存在则修改
mp.insert({"Bob", 90});
mp.emplace("Carol", 95);
访问:
cpp
int v1 = mp["Alice"]; // 若不存在会插入一个默认值
int v2 = mp.at("Alice"); // 不存在则抛异常
2.2 查找和删除
cpp
auto it = mp.find("Bob");
if (it != mp.end()) {
std::cout << it->first << " : " << it->second << '\n';
}
mp.erase("Bob");
mp.erase(it);
2.3 遍历(按 key 有序)
cpp
for (auto& p : mp) {
std::cout << p.first << " => " << p.second << '\n';
}
四、哈希关联容器:unordered_set / unordered_map
底层是哈希表,平均 O(1) 插入 / 查找 / 删除,元素无序。
常用 API 和有序版非常相似,只是遍历顺序不再有序。
1. std::unordered_set<T> 常用 API
cpp
std::unordered_set<int> st;
// 插入
st.insert(10);
st.insert(20);
// 查找
if (st.count(10)) {}
// 删除
st.erase(10);
// 遍历(无序)
for (int x : st) {
std::cout << x << ' ';
}
2. std::unordered_map<Key, T> 常用 API
cpp
std::unordered_map<std::string, int> mp;
// 插入 / 修改
mp["Alice"] = 100;
mp.insert({"Bob", 90});
mp.emplace("Carol", 95);
// 查找
if (mp.count("Bob")) {
std::cout << mp["Bob"] << '\n';
}
// 使用 find 避免插入默认值
auto it = mp.find("Dave");
if (it != mp.end()) {
std::cout << it->second;
}
// 删除
mp.erase("Bob");
mp.erase(it);
// 遍历(无序)
for (auto& p : mp) {
std::cout << p.first << " => " << p.second << '\n';
}
五、容器适配器:stack、queue、priority_queue
这些不是"真正的底层容器",而是基于其它容器封装出的接口。
1. std::stack<T> 常用 API
默认基于 deque 实现,典型"栈"结构:后进先出。
cpp
std::stack<int> st;
st.push(1);
st.push(2);
st.push(3);
while (!st.empty()) {
std::cout << st.top() << ' '; // 3 2 1
st.pop();
}
常用 API:
cpp
st.push(x);
st.pop();
st.top(); // 栈顶元素
st.empty();
st.size();
2. std::queue<T> 常用 API
默认基于 deque 实现,典型"队列":先进先出。
cpp
std::queue<int> q;
q.push(1);
q.push(2);
q.push(3);
while (!q.empty()) {
std::cout << q.front() << ' '; // 1 2 3
q.pop();
}
常用 API:
cpp
q.push(x); // 入队(尾部)
q.pop(); // 出队(头部)
q.front(); // 队头
q.back(); // 队尾
q.empty();
q.size();
3. std::priority_queue<T> 常用 API(优先队列)
默认是"大根堆":top() 是当前最大的元素。
cpp
std::priority_queue<int> pq;
pq.push(3);
pq.push(10);
pq.push(5);
while (!pq.empty()) {
std::cout << pq.top() << ' '; // 10 5 3
pq.pop();
}
常用 API:
cpp
pq.push(x);
pq.pop();
pq.top();
pq.empty();
pq.size();
若要"小根堆",定义方式为:
cpp
std::priority_queue<int, std::vector<int>, std::greater<int>> minpq;
六、综合记忆小结(按类型分类)
-
通用(几乎所有容器)
size,empty,clear,begin/end,swap -
顺序容器(vector/string/deque/list)
-
增删:
push_back,pop_back,push_front,pop_front,insert,erase -
访问:
operator[],at,front,back,data/c_str -
其他:
reserve/capacity(vector)、substr/find(string)、sort/reverse/remove(list)
-
-
有序关联(set/map)
-
插入:
insert,emplace -
查找:
find,count -
删除:
erase -
遍历:范围 for,每次
set得到元素本身,map得到pair<key,value>
-
-
哈希关联(unordered_set/unordered_map)
API 基本与 set/map 一致,只是遍历无序,多了
bucket_count、load_factor(一般需要时再查)。 -
容器适配器(stack/queue/priority_queue)
- 核心就是
push/pop/top或front/back,配合empty/size。
- 核心就是