队列<queue>和双端队列<deque>
队列是一种遵循先进先出(First-In-First-Out,FIFO)原则的线性数据结构。可以把它想象成现实生活中人们排队等待服务的场景,最先进入队列的人会最先得到服务并离开队列,而后来的人则需要在队列尾部依次等待。
常见操作:
- 入队(push):也称为插入操作,将一个新元素添加到队列的尾部(队尾进队)。
- 出队(pop):也称为删除操作,移除并返回队列头部的元素(队首出队)。
- 判空(empty):检查队列是否为空,如果队列为空则返回
true,否则返回false。 - 获取队头元素(front):返回队列头部的元素,但不移除它(获取队首)。
- 获取队尾元素(back):返回队列尾部的元素,但不移除它(获取队尾)。
- 获取队列大小(size):返回队列中当前元素的数量。
#include <iostream>
#include <queue>
using namespace std;
int main() {
queue<int> myQueue;// 1. 定义一个存储 int 类型元素的队列
myQueue.push(10);myQueue.push(20);
myQueue.push(30);// 2. 入队操作(push):将元素添加到队列尾部
cout << "当前队列的大小是: " << myQueue.size() <<endl;// 3. 获取队列大小(size):返回队列中元素的数量
// 4. 判断队列是否为空(empty):如果队列为空返回 true,否则返回 false
if (myQueue.empty()) {cout << "队列是空的。" << endl;}
else {std::cout << "队列不是空的。" << std::endl;}
// 5. 获取队头元素(front):返回队列头部的元素,但不移除它
cout << "队头元素是: " << myQueue.front() << endl;
// 6. 获取队尾元素(back):返回队列尾部的元素,但不移除它
cout << "队尾元素是: " << myQueue.back() << endl;
// 7. 出队操作(pop):移除队列头部的元素
myQueue.pop();
cout << "执行一次出队操作后,队头元素变为: " << myQueue.front() << endl;
return 0;
}
<deque>(double-ended queue,双端队列)是一种双向开口的线性数据结构,兼顾了「动态数组的随机访问」和「链表的双端高效操作」,是 STL 中功能最灵活的容器之一。它也是 std::queue 和 std::stack 的默认底层适配器(替代 vector 解决双端操作效率问题)。
deque 常用操作
需包含头文件 <deque>,支持大部分与 vector 兼容的接口,同时新增双端操作专属 API:
| 功能 | 函数名 | 时间复杂度 | 说明 |
|---|---|---|---|
| 双端插入 | push_front(x) | O(1) | 队首插入元素 x |
push_back(x) | O(1) | 队尾插入元素 x(同 vector) | |
| 双端删除 | pop_front() | O(1) | 队首删除元素(无返回值) |
pop_back() | O(1) | 队尾删除元素(同 vector) | |
| 随机访问 | operator[](index) | O(1) | 访问索引 index 元素(无越界检查) |
at(index) | O(1) | 访问索引 index 元素(有越界抛出异常) | |
| 元素访问 | front() | O(1) | 获取队首元素(不删除) |
back() | O(1) | 获取队尾元素(不删除) | |
| 容量操作 | size() | O(1) | 获取当前元素个数 |
empty() | O(1) | 判断是否为空 | |
resize(n, val) | O(n) | 调整容量为 n,不足补 val | |
clear() | O(n) | 清空所有元素(容量可能保留) | |
| 中间操作 | insert(pos, x) | O(n) | 在迭代器 pos 处插入 x |
erase(pos) | O(n) | 删除迭代器 pos 处元素 |
deque 底层实现
deque 之所以能兼顾「双端 O (1) 操作」和「随机访问 O (1)」,核心是 「分段连续存储 + 中控数组」 结构:
- 分段连续存储:数据不存储在单一连续数组中,而是分成多个固定大小的「数据块」(比如每个块存 4096 字节);
- 中控数组(map):一个指针数组,每个元素指向一个数据块。通过中控数组可以快速定位数据块,再通过块内偏移定位元素;
- 扩容逻辑:
- 队尾满时:新增一个数据块,中控数组尾部添加该块指针;
- 队首满时:新增一个数据块,中控数组头部添加该块指针;
- 中控数组满时:扩容中控数组(通常翻倍),但仅需拷贝指针(无需移动数据块,效率远高于 vector)。
示意图(简化):
中控数组(指针数组):[块1指针, 块2指针, 块3指针, ...]
块1:[a0, a1, a2, ..., a(k-1)] (连续)
块2:[a(k), a(k+1), ..., a(2k-1)](连续)
块3:[a(2k), ...] (连续)
为什么随机访问 O (1)?
要访问索引 i 的元素:
- 计算
i所在的数据块索引:block_idx = i / 块大小; - 计算块内偏移:
offset = i % 块大小; - 通过中控数组的
block_idx找到数据块,再通过offset访问元素 —— 两步均为 O (1)。

浙公网安备 33010602011771号