std::deque 和 **std::list** 都是 C++ 标准模板库(STL)中的序列容器(Sequence Containers)。
它们都属于 <container> 库的一部分,用于存储和管理元素序列,但它们的底层实现、性能特点和适用场景有所不同。
1. std::deque (双端队列)
std::deque 是平衡了随机访问效率和两端操作效率的最佳选择。
类型:序列容器(Sequence Container)
- 操作效率 :
- 尾部插入 (
push_back):**O(1)** - 头部取值 (
front):**O(1)** - 头部删除 (
pop_front):O(1) (这是它优于std::vector的关键)
- 尾部插入 (
- 额外优势 :
- 支持随机访问(如
dq[i]),虽然比vector稍慢,但远快于list。 - 内存由分段连续块组成,扩容时不需要像
vector那样整体移动数据。
- 支持随机访问(如
- 适用场景:绝大多数需要"尾进头出"或"两端操作"的场景,如滑动窗口、任务队列。
- 底层结构:由多个固定大小的缓冲区组成,通过索引表管理。
- 支持随机访问 :可以通过下标
[]或at()快速访问任意元素 - 分段连续:整体不连续,但局部块内连续。不能保证整个容器内存 contiguous。
- 迭代器失效:仅在首尾插入导致扩容时可能失效;中间插入/删除通常只影响被操作位置的迭代器。
cpp
#include <deque>
#include <iostream>
int main() {
std::deque<int> dq;
// 1. 尾部插入
dq.push_back(10);
dq.push_back(20);
dq.push_back(30);
// 2. 头部取元素 (不删除)
if (!dq.empty()) {
std::cout << "Front element: " << dq.front() << std::endl; // 输出 10
}
// 3. 头部删除 (如果需要取出并移除)
dq.pop_front();
return 0;
}
2. std::list (双向链表)
如果你不需要随机访问(下标访问),且主要在中间位置也有频繁插入/删除需求,std::list 是很好的选择。
- 类型:序列容器(Sequence Container)。
- 操作效率 :
- 尾部插入 (
push_back):**O(1)** - 头部取值 (
front):**O(1)** - 头部删除 (
pop_front):**O(1)**
- 尾部插入 (
- 劣势 :
- 不支持随机访问 :无法使用
[]或at(),遍历需 O(N)。 - 内存开销大:每个节点需存储前后指针。
- 缓存不友好 :节点分散在堆内存中,遍历速度慢于
deque。
- 不支持随机访问 :无法使用
- 底层结构 :双向链表-节点分散在堆内存中,每个节点包含数据及指向前后节点的指针。
- 支持随机访问 :不支持 -访问第 N 个元素需要遍历链表,复杂度为 **O(N)**。
- 连续性:节点分散分配,缓存局部性较差。
- 迭代器失效 :指向其他元素的迭代器在插入/删除操作中永远保持有效(除了被删除节点的迭代器)。
3. std::queue(队列适配器)
如果你的操作严格遵循 FIFO(先进先出) 模式,即只允许尾部插入 和头部取出/删除 ,可以使用容器适配器 std::queue。
- 类型 :容器适配器(Container Adapter),不是独立的容器。
- 底层结构 :默认基于
std::deque实现,也可以指定使用std::list或std::vector(需满足特定接口要求)作为底层容器。 - 特点:封装了底层容器,仅暴露队列操作接口(FIFO:先进先出),限制了用户的操作权限(如禁止随机访问、禁止头部插入等)。
- 底层默认容器 :
std::deque。 - 接口限制 :
push():尾部插入。front():获取头部元素引用。pop():删除头部元素(注意:pop返回 void,需先front取值再pop)。
- 优势:语义清晰,防止误用其他不支持的操作(如随机访问或头部插入)。
cpp
#include <queue>
#include <iostream>
int main() {
std::queue<int> q;
// 尾部插入
q.push(10);
q.push(20);
// 头部取元素
std::cout << "Front: " << q.front() << std::endl; // 输出 10
// 头部删除
q.pop();
return 0;
}