C++的STL(Standard Template Library)容器提供了强大的功能,熟悉这些容器的高级用法可以大大提高代码的效率和可读性。
1. vector
a. 动态调整容量
reserve
vsresize
reserve(n)
:预分配空间,但不影响当前大小(size()
)。resize(n)
:调整大小,多余的空间会用默认值填充。
std::vector<int> v; v.reserve(100); // 提高性能,避免多次内存分配 v.resize(10, -1); // 调整大小并用-1填充多余位置
b. 高效删除元素
- 删除单个元素:
使用erase
:v.erase(v.begin() + index); // 删除指定位置元素
- 删除多个元素:
v.erase(v.begin() + start, v.begin() + end); // 删除指定区间
- 使用
remove_if
删除满足条件的元素:v.erase(std::remove_if(v.begin(), v.end(), [](int x) { return x % 2 == 0; }), v.end());
c. 去重
std::unique
结合erase
去除连续重复元素:std::sort(v.begin(), v.end()); // 必须先排序 v.erase(std::unique(v.begin(), v.end()), v.end());
d. 迭代器与遍历
- 使用
reverse_iterator
进行反向遍历:for (auto it = v.rbegin(); it != v.rend(); ++it) { std::cout << *it << " "; }
e. 二维向量
- 初始化二维向量:
std::vector<std::vector<int>> matrix(3, std::vector<int>(4, 0));
f. 与数组的互操作
- 使用
data()
访问底层数组:int* arr = v.data(); arr[0] = 100; // 直接修改底层数组
2. set
a. 自动排序
set
中的元素总是有序的(默认升序,使用自定义比较函数可实现降序)。
std::set<int> s = {5, 1, 3};
for (int x : s) {
std::cout << x << " "; // 输出:1 3 5
}
b. 查找与统计
find
:if (s.find(3) != s.end()) { std::cout << "Found 3\n"; }
- 计数(多重集合
multiset
中有用):int count = s.count(3); // 对于set,总是返回0或1
c. 区间操作
- 使用
lower_bound
和upper_bound
:auto it = s.lower_bound(3); // 第一个不小于3的元素 auto it2 = s.upper_bound(3); // 第一个大于3的元素
d. 自定义比较函数
- 使用仿函数(升序/降序切换):
struct Descend { bool operator()(int a, int b) const { return a > b; } }; std::set<int, Descend> s = {1, 2, 3}; for (int x : s) { std::cout << x << " "; // 输出:3 2 1 }
e. 合并两个集合
- 使用
std::set_union
:std::set<int> s1 = {1, 2, 3}; std::set<int> s2 = {2, 3, 4}; std::vector<int> result; std::set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), std::back_inserter(result));
f. 有序插入的高效性
set
的插入操作复杂度为 (O(\log n)),比vector
中未排序的插入后排序更高效。
2.1. 结合使用vector
和set
- 快速去重并保持顺序:
std::vector<int> v = {4, 1, 2, 2, 3, 1}; std::set<int> s(v.begin(), v.end()); v.assign(s.begin(), s.end()); // 转换回vector
- 排序后去重:
std::sort(v.begin(), v.end()); v.erase(std::unique(v.begin(), v.end()), v.end());
2.2. 常用算法与容器结合
a. 搜索
使用std::binary_search
:
std::vector<int> v = {1, 2, 3, 4, 5};
std::sort(v.begin(), v.end());
if (std::binary_search(v.begin(), v.end(), 3)) {
std::cout << "Found\n";
}
b. 集合操作
- 求交集:
std::vector<int> result; std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), std::back_inserter(result));
- 求并集:
std::set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), std::back_inserter(result));
c. 部分排序
- 对前k个元素排序:
std::partial_sort(v.begin(), v.begin() + k, v.end());
- 获取前k小的元素:
std::nth_element(v.begin(), v.begin() + k - 1, v.end());
2.3. 性能优化技巧
- 预分配空间(
vector
)
使用reserve
避免多次分配内存。 - 减少拷贝
使用std::move
或emplace_back
:v.emplace_back(std::move(obj));
- 优先选择适合的容器
- 数据频繁插入:
set
或unordered_set
- 顺序访问:
vector
- 数据频繁插入:
C++ STL 除了常见的 vector
和 set
外,还有许多其他容器类型,每种都有其独特的特性和高级用法。以下是 STL 其他主要容器及其高级用法:
3. list
(双向链表)
list
是一个双向链表,适合频繁的插入和删除操作,但随机访问性能较差。
高级用法
-
高效插入和删除:
- 插入或删除在任何位置的复杂度均为 ( O(1) )(在已知位置的迭代器情况下)。
std::list<int> lst = {1, 2, 3}; auto it = lst.begin(); ++it; // 指向第二个元素 lst.insert(it, 10); // 在2前插入10 lst.erase(it); // 删除2
-
双端操作:
push_front
和push_back
:
lst.push_front(0); // 在前插入 lst.push_back(4); // 在后插入
-
与
vector
比较:- 当需要频繁地在中间插入或删除元素时,
list
比vector
更高效。
- 当需要频繁地在中间插入或删除元素时,
-
排序与去重:
list
自带sort
和unique
方法,无需 STL 算法:
lst.sort(); // 排序 lst.unique(); // 去重(仅连续重复元素)
4. deque
(双端队列)
deque
是一种双端动态数组,支持快速的首尾插入和删除。
高级用法
-
双端操作:
std::deque<int> dq = {1, 2, 3}; dq.push_front(0); // 在前插入 dq.push_back(4); // 在后插入 dq.pop_front(); // 移除前面的元素 dq.pop_back(); // 移除后面的元素
-
适用场景:
- 当需要频繁在容器的两端进行操作时,
deque
优于vector
(vector
的push_front
效率低)。
- 当需要频繁在容器的两端进行操作时,
-
随机访问:
- 与
vector
一样支持下标操作:
dq[2] = 42;
- 与
5. map
和 multimap
(关联容器)
map
是键值对形式的有序关联容器,自动按键排序;multimap
允许键重复。
高级用法
-
插入元素:
std::map<std::string, int> m; m["apple"] = 3; // 插入或更新键值对 m.insert({"banana", 2}); // 显式插入
-
查找元素:
auto it = m.find("apple"); if (it != m.end()) { std::cout << "Found: " << it->second << std::endl; }
-
区间查找 (
lower_bound
和upper_bound
):auto it = m.lower_bound("b"); // 第一个 >= "b" 的键 auto it2 = m.upper_bound("b"); // 第一个 > "b" 的键
-
遍历时修改值:
for (auto& [key, value] : m) { value += 1; // 修改所有值 }
-
多重键值 (
multimap
):std::multimap<std::string, int> mm; mm.insert({"apple", 1}); mm.insert({"apple", 2}); // 同键可插入多个值
6. unordered_map
和 unordered_set
(哈希容器)
基于哈希表的容器,无序但查找/插入性能为 ( O(1) )。
高级用法
-
哈希函数:
可以自定义哈希函数:struct MyHash { std::size_t operator()(const std::pair<int, int>& p) const { return std::hash<int>()(p.first) ^ std::hash<int>()(p.second); } }; std::unordered_map<std::pair<int, int>, int, MyHash> umap;
-
快速查找与统计:
std::unordered_set<int> uset = {1, 2, 3}; if (uset.find(2) != uset.end()) { std::cout << "Found 2\n"; }
7. stack
和 queue
(适配器容器)
stack
和 queue
是对其他容器的封装,分别实现栈(后进先出)和队列(先进先出)。
高级用法
-
stack
:std::stack<int> stk; stk.push(1); stk.push(2); std::cout << stk.top(); // 获取栈顶元素 stk.pop(); // 弹出栈顶元素
-
queue
:std::queue<int> q; q.push(1); q.push(2); std::cout << q.front(); // 获取队头 std::cout << q.back(); // 获取队尾 q.pop(); // 移除队头
-
priority_queue
(优先队列):std::priority_queue<int> pq; // 默认最大堆 pq.push(3); pq.push(1); pq.push(2); std::cout << pq.top(); // 输出最大值
自定义比较函数(最小堆):
std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap;
8. bitset
(位集合)
bitset
是一种高效的二进制位操作容器,适合存储和操作大量布尔值。
高级用法
-
初始化:
std::bitset<8> b(42); // 8位,值为42(101010) std::cout << b; // 输出:00101010
-
按位操作:
b.set(0); // 设置第0位为1 b.reset(1); // 设置第1位为0 b.flip(); // 翻转所有位
-
按位统计:
std::cout << b.count(); // 统计1的数量 std::cout << b.any(); // 是否有1 std::cout << b.none(); // 是否全为0
9. tuple
(元组容器)
tuple
是一种可存储多种类型值的固定长度容器。
高级用法
-
创建和访问:
std::tuple<int, std::string, double> t(1, "hello", 3.14); std::cout << std::get<0>(t); // 访问第一个元素
-
解构绑定 (C++17):
auto [x, y, z] = t; // 解构元组
-
比较:
std::tuple<int, int> t1(1, 2), t2(2, 1); std::cout << (t1 < t2); // 支持字典序比较
10. array
(定长数组)
std::array
是 C++ 提供的定长数组封装,功能类似 vector
。
高级用法
-
初始化:
std::array<int, 5> arr = {1, 2, 3, 4, 5};
-
数组操作:
std::cout << arr.size(); std::cout << arr.front(); // 获取首元素 std::cout << arr.back(); // 获取尾元素
总结
容器类型 | 特点 |
---|---|
vector | 动态数组,适合随机访问和尾部操作 |
list | 双向链表,适合频繁插入和删除操作 |
deque | 双端队列,支持高效的首尾插入和删除 |
set /map | 有序关联容器,按键排序,适合范围查找和去重 |
unordered_set | 基于哈希表,无序,查找和插入性能 ( O(1) ) |
stack /queue | 适配器容器,分别实现栈和队列 |
bitset | 位操作容器,高效存储和操作布尔值 |
tuple | 多种类型固定长度容器,适合存储结构化数据 |
array | 定长数组,适合需要固定大小的数组场景 |
STL容器迭代器定义变量方法
在 C++ 中,STL 容器提供了迭代器(iterator
)作为访问其元素的一种抽象方式。不同容器的迭代器类型可能有所区别,但基本用法是类似的。以下是定义和使用迭代器变量的方式:
通用方式:使用 typename Container::iterator
或 auto
明确指定迭代器类型
std::vector<int> vec = {1, 2, 3};
std::vector<int>::iterator it = vec.begin(); // 定义普通迭代器
使用 auto
推导类型(推荐)
std::vector<int> vec = {1, 2, 3};
auto it = vec.begin(); // 自动推导迭代器类型
不同容器的迭代器定义方式
1. std::vector
(连续内存的动态数组)
std::vector<int> vec = {1, 2, 3};
// 普通迭代器(可读可写)
std::vector<int>::iterator it = vec.begin();
*it = 10; // 修改值
// 常量迭代器(只读)
std::vector<int>::const_iterator cit = vec.cbegin();
int value = *cit; // 读取值,不能修改
// 反向迭代器(从末尾向前遍历)
std::vector<int>::reverse_iterator rit = vec.rbegin();
*rit = 20;
// 常量反向迭代器
std::vector<int>::const_reverse_iterator crit = vec.crbegin();
int lastValue = *crit; // 读取末尾值
2. std::list
(双向链表)
std::list<int> lst = {4, 5, 6};
// 普通迭代器
std::list<int>::iterator it = lst.begin();
*it = 10;
// 常量迭代器
std::list<int>::const_iterator cit = lst.cbegin();
int value = *cit; // 只读
// 反向迭代器
std::list<int>::reverse_iterator rit = lst.rbegin();
*rit = 30;
// 常量反向迭代器
std::list<int>::const_reverse_iterator crit = lst.crbegin();
int lastValue = *crit; // 读取值
3. std::set
和 std::map
(有序关联容器)
std::set<int> s = {1, 2, 3};
std::map<int, std::string> m = {{1, "one"}, {2, "two"}};
// set 的普通迭代器
std::set<int>::iterator it = s.begin();
int value = *it; // 获取元素值(只读)
// map 的普通迭代器
std::map<int, std::string>::iterator mit = m.begin();
int key = mit->first; // 获取键
std::string val = mit->second; // 获取值并可修改
mit->second = "uno";
// 常量迭代器
std::map<int, std::string>::const_iterator cmit = m.cbegin();
int ckey = cmit->first; // 只读键
std::string cval = cmit->second; // 只读值
// 反向迭代器
std::set<int>::reverse_iterator rit = s.rbegin();
int rvalue = *rit;
// map 的反向迭代器
std::map<int, std::string>::reverse_iterator rmit = m.rbegin();
int rkey = rmit->first; // 读取键
std::string rval = rmit->second; // 读取值并可修改
4. std::unordered_set
和 std::unordered_map
(无序关联容器)
std::unordered_set<int> us = {1, 2, 3};
std::unordered_map<int, std::string> um = {{1, "one"}, {2, "two"}};
// unordered_set 的普通迭代器
std::unordered_set<int>::iterator it = us.begin();
int value = *it;
// unordered_map 的普通迭代器
std::unordered_map<int, std::string>::iterator mit = um.begin();
int key = mit->first; // 获取键
std::string val = mit->second; // 获取值并可修改
mit->second = "uno";
// 常量迭代器
std::unordered_map<int, std::string>::const_iterator cmit = um.cbegin();
int ckey = cmit->first; // 只读键
std::string cval = cmit->second; // 只读值
5. std::deque
(双端队列)
std::deque<int> dq = {1, 2, 3};
// 普通迭代器
std::deque<int>::iterator it = dq.begin();
*it = 10;
// 常量迭代器
std::deque<int>::const_iterator cit = dq.cbegin();
int value = *cit; // 只读
// 反向迭代器
std::deque<int>::reverse_iterator rit = dq.rbegin();
*rit = 20;
// 常量反向迭代器
std::deque<int>::const_reverse_iterator crit = dq.crbegin();
int lastValue = *crit; // 读取值
小结
-
STL 容器迭代器的定义方式类似,但根据容器类型和用途不同,迭代器可以分为普通迭代器、常量迭代器、反向迭代器等。
-
推荐使用
auto
或range-based for loop
(基于范围的循环)来简化代码:for (auto it = container.begin(); it != container.end(); ++it) { // 使用 *it 访问元素 }
-
在现代 C++ 中,基于范围的循环更简洁:
for (const auto& elem : container) { // 访问 elem }