std::deque
(Double-Ended Queue,双端队列),它是 C++ STL(标准模板库)中的一个容器。下面详细介绍它的特性、用法及实现原理。
1. std::deque
概述
std::deque
(双端队列)是一种动态数组,支持在 头部和尾部 高效插入和删除元素(O(1) 时间复杂度)。
- 头文件:
#include <deque>
- 底层结构:通常由多个固定大小的数组(块)组成,通过一个中央映射表(map)管理这些块,使其能动态扩展。
- 特点:
- 随机访问(
operator[]
或at()
)效率接近std::vector
(O(1))。 - 在头部插入/删除(
push_front
/pop_front
)比std::vector
更高效(vector
是 O(n))。 - 不保证元素在内存中连续存储(但大多数实现仍保持局部连续)。
- 随机访问(
2. std::deque
的核心操作
(1)初始化
#include <deque>
std::deque<int> dq1; // 空 deque
std::deque<int> dq2(5, 100); // 5 个 100: {100, 100, 100, 100, 100}
std::deque<int> dq3(dq2.begin(), dq2.end()); // 拷贝 dq2
std::deque<int> dq4 = {1, 2, 3, 4}; // 初始化列表
(2)元素访问
方法 | 说明 | 示例 |
---|---|---|
dq[i] | 访问第 i 个元素(不检查越界) | int x = dq[2]; |
dq.at(i) | 访问第 i 个元素(越界抛异常) | int y = dq.at(2); |
dq.front() | 返回第一个元素 | int first = dq.front(); |
dq.back() | 返回最后一个元素 | int last = dq.back(); |
(3)修改操作
方法 | 说明 | 示例 |
---|---|---|
dq.push_back(x) | 尾部插入 x | dq.push_back(10); |
dq.push_front(x) | 头部插入 x | dq.push_front(20); |
dq.pop_back() | 删除尾部元素 | dq.pop_back(); |
dq.pop_front() | 删除头部元素 | dq.pop_front(); |
dq.insert(pos, x) | 在 pos 位置插入 x | dq.insert(dq.begin()+1, 5); |
dq.erase(pos) | 删除 pos 位置的元素 | dq.erase(dq.begin()); |
dq.clear() | 清空所有元素 | dq.clear(); |
(4)容量查询
方法 | 说明 | 示例 |
---|---|---|
dq.size() | 返回元素个数 | size_t n = dq.size(); |
dq.empty() | 判断是否为空 | if (dq.empty()) {...} |
3. std::deque
的底层实现
(1)数据结构
std::deque
通常由多个固定大小的 块(chunks) 组成,并通过一个 中央控制表(map) 管理这些块的指针:
Map (索引表):
[0] -> [块1: |x|x|x|x|]
[1] -> [块2: |x|x|x|x|]
[2] -> [块3: |x|x|x|x|]
- 每个块的大小固定(如 512 字节)。
- 插入元素时,如果当前块已满,会分配新块并更新
map
。
(2)性能分析
操作 | 时间复杂度 | 说明 |
---|---|---|
push_back | O(1) | 可能触发新块分配 |
push_front | O(1) | 可能触发新块分配 |
pop_back | O(1) | 可能释放空块 |
pop_front | O(1) | 可能释放空块 |
operator[] | O(1) | 通过 map 计算块偏移 |
insert | O(n) | 中间插入需移动元素 |
4. std::deque
vs std::vector
vs std::list
特性 | std::deque | std::vector | std::list |
---|---|---|---|
随机访问 | ✅ O(1) | ✅ O(1) | ❌ O(n) |
头部插入/删除 | ✅ O(1) | ❌ O(n) | ✅ O(1) |
尾部插入/删除 | ✅ O(1) | ✅ O(1)(均摊) | ✅ O(1) |
中间插入/删除 | ❌ O(n) | ❌ O(n) | ✅ O(1)(已知迭代器) |
内存连续性 | ❌(局部连续) | ✅(完全连续) | ❌(节点分散) |
适用场景:
- 需要高效头尾操作 →
std::deque
- 需要快速随机访问 →
std::vector
- 需要频繁中间插入 →
std::list
5. 示例代码
#include <iostream>
#include <deque>
int main() {
std::deque<int> dq = {10, 20, 30};
dq.push_front(5); // 头部插入: {5, 10, 20, 30}
dq.push_back(40); // 尾部插入: {5, 10, 20, 30, 40}
std::cout << "Front: " << dq.front() << "\n"; // 5
std::cout << "Back: " << dq.back() << "\n"; // 40
dq.pop_front(); // 移除头部: {10, 20, 30, 40}
dq.pop_back(); // 移除尾部: {10, 20, 30}
for (int x : dq) { // 遍历
std::cout << x << " "; // 10 20 30
}
return 0;
}
6. 总结
std::deque
适合需要 频繁头尾操作 的场景(如队列 + 栈结合需求)。- 虽然支持随机访问,但 中间插入/删除较慢(不如
std::list
)。 - 内存占用比
std::vector
稍高(因存储map
和分块结构)。
如果需要高性能双端操作,std::deque
是最佳选择之一! 🚀