std::deque(双端队列,double-ended queue)是 C++ 标准库中的一个序列容器,支持在头部和尾部均以 O(1) 时间复杂度插入和删除元素,同时支持随机访问(通过下标 [])。它弥补了 vector 头部操作低效和 list 无法随机访问的不足。

deque的精妙之处在于,它通过一个"分段连续"的数据结构,巧妙地模拟了"整体连续"的假象,从而在O(1)时间内高效地在两端进行数据存取。
核心架构:中控器 + 缓冲区
std::deque 并非单一连续内存,而是由一段段连续缓冲区组成,并由一个核心中央控制器------中控器(通常也称为 "map")来统一管理。当在两端添加新元素时,若当前缓冲区已满,deque会动态分配新的缓冲区,并将其地址记录到中控器中,
中控器(_M_map):一个指针数组,每个指针指向一个缓冲区(块)
┌─────────────────────────────────────────────────────┐
│ ptr0 → 缓冲区0 │ ptr1 → 缓冲区1 │ ptr2 → 缓冲区2 │ ...
└─────────────────────────────────────────────────────┘
缓冲区0(固定大小连续内存):
┌────┬────┬────┬────┬────┐
│ e0 │ e1 │ e2 │ │ │
└────┴────┴────┴────┴────┘
缓冲区1:
┌────┬────┬────┬────┬────┐
│ e3 │ e4 │ e5 │ e6 │ │
└────┴────┴────┴────┴────┘
cpp
protected: // Internal typedefs
// 元素的指標的指標(pointer of pointer of T)
typedef pointer* map_pointer;
typedef simple_alloc<value_type, Alloc> data_allocator;
typedef simple_alloc<pointer, Alloc> map_allocator;
static size_type buffer_size() {
return __deque_buf_size(BufSiz, sizeof(value_type));
}
static size_type initial_map_size() { return 8; }
protected: // Data members
iterator start;
iterator finish;
map_pointer map;
size_type map_size;
迭代器 start 指向第一个元素(e0)
迭代器 finish 指向最后一个元素之后(例如 e6 后面的位置)
其核心的数据结构主要由以下几个关键指针组成:
-
map pointer:二级指针,它指向一个动态数组中控器。
-
map size:中控器当前的最大容量,即它最多能存储多少个指向缓冲区的指针。 -
start和finish:这两个iterator迭代器,分别标志了deque的头部和尾部,它们各自都包含了指向当前缓冲区的信息。
迭代器的精妙设计
deque的随机访问迭代器是实现其"连续性"假象的关键。它的实现相当复杂,核心在于能够处理不同缓冲区之间的跳转。

实现上述逻辑的关键是缓冲区大小 参数__deque_buf_size。在GCC的实现中,deque会选用512 / sizeof(T)和1之间的最大值作为缓冲区的容量,除非元素类型T较大。
cpp
inline size_t __deque_buf_size(size_t n, size_t sz)
{
return n != 0 ? n : (sz < 512 ? size_t(512 / sz) : size_t(1));
}
实例:
cpp
#include <iostream>
#include <deque>
int main() {
std::deque<int> dq;
dq.push_back(10);
dq.push_back(20);
dq.push_back(30);
// 当前: [10, 20, 30]
dq.push_front(5);
dq.push_front(1);
// 当前: [1, 5, 10, 20, 30]
std::cout << "Third element: " << dq[2] << std::endl; // 10
std::cout << "Front: " << dq.front() << ", Back: " << dq.back() << std::endl;
std::cout << "All elements: ";
for (int x : dq) std::cout << x << ' ';
std::cout << std::endl;
dq.pop_front(); // 移除 1
// 当前: [5, 10, 20, 30]
dq.pop_back(); // 移除 30
// 当前: [5, 10, 20]
auto it = dq.begin() + 1; // 指向 10 的位置
dq.insert(it, 99); // 在 10 之前插入 99
// 当前: [5, 99, 10, 20]
it = dq.begin() + 2; // 指向 10
dq.erase(it); // 删除 10
// 当前: [5, 99, 20]
dq.clear();
std::cout << "Size after clear: " << dq.size() << std::endl;
std::deque<int> dq2(5, 100); // 5个100
std::deque<int> dq3 = {1,2,3,4,5};
std::deque<int> dq4(dq3.begin(), dq3.end());
return 0;
}
//运行结果
Third element: 10
Front: 1, Back: 30
All elements: 1 5 10 20 30
Size after clear: 0
与vector对比
两者都是 C++ 标准库中的序列容器,都支持随机访问,但在底层实现和性能特点上有显著区别。
deque vs vector
| 特性 | std::deque |
std::vector |
|---|---|---|
| 头部插入/删除 | O(1) -- 高效 | O(n) -- 需移动所有元素 |
| 尾部插入/删除 | O(1) | O(1) 均摊(可能扩容) |
| 中间插入/删除 | O(n)(但移动元素较少) | O(n)(移动后续全部) |
随机访问 [] |
O(1)(常数稍大,需两次解引用) | O(1)(指针直接计算) |
| 内存布局 | 分段连续(多块内存) | 单块连续内存 |
| 内存预留 | 无 reserve 功能 |
有 reserve() 预分配 |
| 重新分配时迭代器失效 | push_front/back 不使已有迭代器失效 |
扩容时所有迭代器失效 |
| 内存开销 | 稍高(需中控器指针数组) | 极低(仅元素本身) |
| 适用场景 | 需要在两端频繁操作 + 随机访问 | 尾部操作为主 + 随机访问 + 内存连续性要求高 |
选择建议
-
优先用
vector:大多数情况,尤其是尾部插入为主、需要连续内存(如传给 C 接口)、对缓存性能要求极高时。 -
考虑用
deque:当确实需要在头部也进行大量插入/删除,并且仍需要随机访问时。例如双端队列、滑动窗口、某些调度器。
如果不需要随机访问,只做 FIFO 队列,应直接用 std::queue(默认底层就是 deque)。