目录
[1.定义 deque 对象](#1.定义 deque 对象)
[二、向 deque 对象中添加元素](#二、向 deque 对象中添加元素)
[三、deque 常用迭代器](#三、deque 常用迭代器)
[四、deque 常用运算符](#四、deque 常用运算符)
[五、deque 常用成员函数](#五、deque 常用成员函数)
[1.empty 成员函数](#1.empty 成员函数)
[2.front 成员函数](#2.front 成员函数)
[3.back 成员函数](#3.back 成员函数)
[4.push_front 成员函数](#4.push_front 成员函数)
[5.pop_front 成员函数](#5.pop_front 成员函数)
[6.push_back 成员函数](#6.push_back 成员函数)
[7.pop_back 成员函数](#7.pop_back 成员函数)
[六、扩展知识:C++ 动态创建二维数组](#六、扩展知识:C++ 动态创建二维数组)
[1.使用 deque 创建二维数组](#1.使用 deque 创建二维数组)
[2.使用 vector 创建二维数组](#2.使用 vector 创建二维数组)
[练习 1:deque 基本操作](#练习 1:deque 基本操作)
[练习 2:使用 deque 实现队列](#练习 2:使用 deque 实现队列)
[练习 3:使用 deque 处理二维数组](#练习 3:使用 deque 处理二维数组)
前言
容器deque是一个双向队列(double-ended queue) ,可以在队列的两端进行元素的插入和删除 操作。deque 和 vector 非常相似。也采用dynamic array(动态数组) 来管理元素,提供随机访向,并有着和 vector 几乎一模一样的接口。
和vector相同,第一个参数表示类型,第二个参数不写,使用默认即可。
deque和vector相比,有如下特点:
1.两端都能快速的插入和删除元素(vector只能在尾部快速的插入和删除数据)。
2.deque的内存重分配优于vector,因为从其内部结构可以看出,deque在扩容时不必复制所有数据,而vector在内存重新分配时需要复制所有的数据。
deque和vector相似的地方:
1.在中间插入和删除数据速度相对较慢,因为要移到其它的数据。
2.迭代器属于随机访问迭代器。
总之,需要频繁的在两端插入或删除数据时,使用deque。
一、定义及初始化
deque(double-ended queue,双端队列)是 C++ 标准库中的一个顺序容器,定义在 <deque> 头文件中,位于 std 命名空间内。它支持在两端高效地插入和删除元素,同时也支持随机访问。
使用 deque之前,必须先包含头文件<deque>
cpp
#include <deque>
1.定义 deque 对象
cpp
#include <deque>
// 定义一个存储 int 类型的 deque
std::deque<int> dq;
// 定义一个存储 std::string 类型的 deque
std::deque<std::string> strDq;
2.初始化方式
-
默认初始化:创建一个空 deque
cppstd::deque<int> dq; // 空 deque -
列表初始化:
cppstd::deque<int> dq = {1, 2, 3, 4, 5}; std::deque<int> dq2{1, 2, 3, 4, 5}; // 与上面等价 -
指定元素个数和初始值:
cppstd::deque<int> dq(5, 10); // 包含 5 个元素,每个元素值为 10 -
指定元素个数:
cppstd::deque<int> dq(10); // 包含 10 个元素,每个元素值为 0(int 的默认值) -
使用另一个 deque 初始化:
cppstd::deque<int> dq1 = {1, 2, 3, 4, 5}; std::deque<int> dq2 = dq1; // 拷贝初始化 std::deque<int> dq3(dq1); // 拷贝构造 -
使用迭代器范围初始化:
cppstd::deque<int> dq1 = {1, 2, 3, 4, 5}; std::deque<int> dq2(dq1.begin(), dq1.end()); // 包含 dq1 的所有元素 std::deque<int> dq3(dq1.begin(), dq1.begin() + 3); // 包含 dq1 的前 3 个元素
二、向 deque 对象中添加元素
deque 支持在两端高效地添加元素,也可以在中间插入元素(但效率较低)。
1.在前端添加元素
-
push_front 成员函数:
- 功能:在 deque 前端添加一个元素
- 参数:要添加的元素值
- 返回值:无
cppstd::deque<int> dq = {3, 4, 5}; dq.push_front(2); // dq 现在包含 {2, 3, 4, 5} dq.push_front(1); // dq 现在包含 {1, 2, 3, 4, 5} -
emplace_front 成员函数(C++11):
- 功能:在 deque 前端直接构造一个元素
- 参数:构造元素所需的参数
- 返回值:无
- 优点:避免了额外的拷贝或移动操作,比 push_front 更高效
cppstd::deque<std::pair<int, std::string>> dq; dq.emplace_front(1, "one"); // 直接在前端构造 pair 对象 dq.push_front({2, "two"}); // 先构造临时 pair 对象,再移动到前端
2.在后端添加元素
-
push_back 成员函数:
- 功能:在 deque 后端添加一个元素
- 参数:要添加的元素值
- 返回值:无
cppstd::deque<int> dq = {1, 2, 3}; dq.push_back(4); // dq 现在包含 {1, 2, 3, 4} dq.push_back(5); // dq 现在包含 {1, 2, 3, 4, 5} -
emplace_back 成员函数(C++11):
-
功能:在 deque 后端直接构造一个元素
-
参数:构造元素所需的参数
-
返回值:无
-
优点:避免了额外的拷贝或移动操作,比 push_back 更高效
std::deque<std::pair<int, std::string>> dq;
dq.emplace_back(1, "one"); // 直接在后端构造 pair 对象
dq.push_back({2, "two"}); // 先构造临时 pair 对象,再移动到后端
-
3.在中间插入元素
insert 成员函数:
- 功能:在指定位置插入元素
- 参数:
- 版本 1:
pos, value- 在迭代器 pos 前插入 value - 版本 2:
pos, count, value- 在迭代器 pos 前插入 count 个 value - 版本 3:
pos, first, last- 在迭代器 pos 前插入迭代器范围 [first, last) 中的元素
- 版本 1:
- 返回值:指向新插入的第一个元素的迭代器
- 说明:在中间插入元素的效率较低,因为需要移动元素
cpp
std::deque<int> dq = {1, 2, 4, 5};
// 在位置 2 前插入元素 3
auto it = dq.insert(dq.begin() + 2, 3); // dq 现在包含 {1, 2, 3, 4, 5}
// 在位置 0 前插入 2 个 0
dq.insert(dq.begin(), 2, 0); // dq 现在包含 {0, 0, 1, 2, 3, 4, 5}
// 在末尾插入另一个 deque 的元素
std::deque<int> dq2 = {6, 7, 8};
dq.insert(dq.end(), dq2.begin(), dq2.end()); // dq 现在包含 {0, 0, 1, 2, 3, 4, 5, 6, 7, 8}
三、deque 常用迭代器
deque 容器提供了多种迭代器类型,用于遍历容器中的元素:
|------------|-----------------------------|
| 迭代器 | 含义 |
| d.begin() | 第一个元素的迭代器 |
| d.end() | 最后一个元素的下一个位置迭代器(尾后迭代器或尾迭代器) |
| d.cbegin() | 第一个元素的常量迭代器(不修改元素内容) |
| d.cend() | 尾后常量迭代器(不修改元素内容) |
| d.rbegin() | 从后往前的第一个迭代器 |
| d.rend() | 从后往前的最后一个迭代器 |
使用示例:
cpp
std::deque<int> dq = {1, 2, 3, 4, 5};
// 使用正向迭代器遍历
for (auto it = dq.begin(); it != dq.end(); ++it) {
std::cout << *it << " "; // 输出: 1 2 3 4 5
}
std::cout << std::endl;
// 使用反向迭代器遍历
for (auto it = dq.rbegin(); it != dq.rend(); ++it) {
std::cout << *it << " "; // 输出: 5 4 3 2 1
}
std::cout << std::endl;
// 使用常量迭代器遍历(不能修改元素)
for (auto it = dq.cbegin(); it != dq.cend(); ++it) {
std::cout << *it << " "; // 输出: 1 2 3 4 5
// *it = 10; // 错误:常量迭代器不能修改元素
}
std::cout << std::endl;
四、deque 常用运算符
deque 类重载了多种运算符,方便容器操作:
-
赋值运算符:
=:将一个 deque 赋值给另一个 deque
cppstd::deque<int> dq1 = {1, 2, 3}; std::deque<int> dq2; dq2 = dq1; // dq2 现在包含 {1, 2, 3} -
比较运算符:
==:判断两个 deque 是否相等!=:判断两个 deque 是否不相等<:判断一个 deque 是否小于另一个 deque(字典序)<=:判断一个 deque 是否小于或等于另一个 deque>:判断一个 deque 是否大于另一个 deque>=:判断一个 deque 是否大于或等于另一个 deque
cppstd::deque<int> dq1 = {1, 2, 3}; std::deque<int> dq2 = {1, 2, 4}; bool b1 = (dq1 == dq2); // false bool b2 = (dq1 < dq2); // true(第三个元素 3 < 4) -
下标运算符:
[]:访问指定位置的元素(不进行边界检查)
cppstd::deque<int> dq = {10, 20, 30}; int x = dq[0]; // x = 10 dq[1] = 25; // dq 现在包含 {10, 25, 30} -
成员访问运算符:
at():访问指定位置的元素(进行边界检查)
cppstd::deque<int> dq = {10, 20, 30}; int x = dq.at(0); // x = 10 try { x = dq.at(5); // 抛出 out_of_range 异常 } catch (const std::out_of_range& e) { std::cout << "Exception: " << e.what() << std::endl; }
五、deque 常用成员函数
|--------------------|--------------------------------|
| deque d的成员函数 | 含义 |
| d.empty() | 判断是否为空,重要函数 |
| d.size() | 返回d的数据个数,重要函数 |
| d.front() | 返回第一个元素的引用 |
| d.back() | 返回最后一个元素的引用 |
| d.insert() | 插入一个或多个元素,这个效率较差,重要函数,O(n) |
| d.erase() | 删除一个或多个元素,重要函数,O(n) |
| d.push_front() | 头插,重要函数,O(1) |
| d.push_back() | 尾插,重要函数,O(1) |
| d.pop_front() | 头删,重要函数,O(1) |
| d.pop_back() | 尾删,重要函数,O(1) |
| d.swap() | 交换两个deque的值 |
| d.clear() | 清空数据 |
1.empty 成员函数
- 功能:判断 deque 是否为空
- 参数:无
- 返回值 :如果 deque 为空返回
true,否则返回false
cpp
std::deque<int> dq1;
std::deque<int> dq2 = {1, 2, 3};
std::cout << dq1.empty(); // 输出: 1 (true)
std::cout << dq2.empty(); // 输出: 0 (false)
2.front 成员函数
- 功能:返回 deque 中第一个元素的引用
- 参数:无
- 返回值:第一个元素的引用
- 说明:如果 deque 为空,行为未定义
cpp
std::deque<int> dq = {1, 2, 3};
std::cout << dq.front(); // 输出: 1
dq.front() = 10; // dq 现在包含 {10, 2, 3}
3.back 成员函数
- 功能:返回 deque 中最后一个元素的引用
- 参数:无
- 返回值:最后一个元素的引用
- 说明:如果 deque 为空,行为未定义
cpp
std::deque<int> dq = {1, 2, 3};
std::cout << dq.back(); // 输出: 3
dq.back() = 30; // dq 现在包含 {1, 2, 30}
4.push_front 成员函数
- 功能:在 deque 前端添加一个元素
- 参数:要添加的元素值
- 返回值:无
cpp
std::deque<int> dq = {2, 3, 4};
dq.push_front(1); // dq 现在包含 {1, 2, 3, 4}
5.pop_front 成员函数
- 功能:删除 deque 前端的一个元素
- 参数:无
- 返回值:无
- 说明:如果 deque 为空,行为未定义
cpp
std::deque<int> dq = {1, 2, 3, 4};
dq.pop_front(); // dq 现在包含 {2, 3, 4}
dq.pop_front(); // dq 现在包含 {3, 4}
6.push_back 成员函数
- 功能:在 deque 后端添加一个元素
- 参数:要添加的元素值
- 返回值:无
cpp
std::deque<int> dq = {1, 2, 3};
dq.push_back(4); // dq 现在包含 {1, 2, 3, 4}
dq.push_back(5); // dq 现在包含 {1, 2, 3, 4, 5}
7.pop_back 成员函数
- 功能:删除 deque 后端的一个元素
- 参数:无
- 返回值:无
- 说明:如果 deque 为空,行为未定义
cpp
std::deque<int> dq = {1, 2, 3, 4, 5};
dq.pop_back(); // dq 现在包含 {1, 2, 3, 4}
dq.pop_back(); // dq 现在包含 {1, 2, 3}
六、扩展知识:C++ 动态创建二维数组
1.使用 deque 创建二维数组
deque 可以方便地用于创建和管理二维数组,尤其是当数组大小需要动态调整时。
cpp
#include <iostream>
#include <deque>
int main() {
// 创建一个 3x4 的二维 deque
std::deque<std::deque<int>> matrix(3, std::deque<int>(4, 0));
// 初始化二维数组
int value = 1;
for (size_t i = 0; i < matrix.size(); ++i) {
for (size_t j = 0; j < matrix[i].size(); ++j) {
matrix[i][j] = value++;
}
}
// 打印二维数组
std::cout << "Matrix: " << std::endl;
for (const auto& row : matrix) {
for (int x : row) {
std::cout << x << " ";
}
std::cout << std::endl;
}
// 动态添加行
matrix.push_back(std::deque<int>{13, 14, 15, 16});
// 动态添加列
for (auto& row : matrix) {
row.push_back(99);
}
// 打印修改后的二维数组
std::cout << "\nModified matrix: " << std::endl;
for (const auto& row : matrix) {
for (int x : row) {
std::cout << x << " ";
}
std::cout << std::endl;
}
return 0;
}
2.使用 vector 创建二维数组
除了 deque,vector 也可以用于创建二维数组:
cpp
#include <iostream>
#include <vector>
int main() {
// 创建一个 3x4 的二维 vector
std::vector<std::vector<int>> matrix(3, std::vector<int>(4, 0));
// 初始化二维数组
int value = 1;
for (size_t i = 0; i < matrix.size(); ++i) {
for (size_t j = 0; j < matrix[i].size(); ++j) {
matrix[i][j] = value++;
}
}
// 打印二维数组
std::cout << "Matrix: " << std::endl;
for (const auto& row : matrix) {
for (int x : row) {
std::cout << x << " ";
}
std::cout << std::endl;
}
return 0;
}
3.使用指针创建二维数组
还可以使用指针动态创建二维数组:
cpp
#include <iostream>
int main() {
int rows = 3;
int cols = 4;
// 创建二维数组
int** matrix = new int*[rows];
for (int i = 0; i < rows; ++i) {
matrix[i] = new int[cols];
}
// 初始化二维数组
int value = 1;
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
matrix[i][j] = value++;
}
}
// 打印二维数组
std::cout << "Matrix: " << std::endl;
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
std::cout << matrix[i][j] << " ";
}
std::cout << std::endl;
}
// 释放内存
for (int i = 0; i < rows; ++i) {
delete[] matrix[i];
}
delete[] matrix;
return 0;
}
七、综合练习
练习 1:deque 基本操作
cpp
#include <iostream>
#include <deque>
int main() {
// 创建并初始化 deque
std::deque<int> dq = {3, 4, 5};
// 在前端添加元素
dq.push_front(2);
dq.push_front(1);
// 在后端添加元素
dq.push_back(6);
dq.push_back(7);
// 输出当前 deque
std::cout << "Current deque: ";
for (int x : dq) {
std::cout << x << " ";
}
std::cout << std::endl;
// 访问元素
std::cout << "Front: " << dq.front() << std::endl;
std::cout << "Back: " << dq.back() << std::endl;
std::cout << "Element at index 2: " << dq[2] << std::endl;
// 删除前端和后端元素
dq.pop_front();
dq.pop_back();
// 输出修改后的 deque
std::cout << "After pop_front and pop_back: ";
for (int x : dq) {
std::cout << x << " ";
}
std::cout << std::endl;
// 检查是否为空
std::cout << "Is empty: " << (dq.empty() ? "Yes" : "No") << std::endl;
return 0;
}
练习 2:使用 deque 实现队列
cpp
#include <iostream>
#include <deque>
class Queue {
private:
std::deque<int> dq;
public:
// 入队
void enqueue(int value) {
dq.push_back(value);
}
// 出队
void dequeue() {
if (!dq.empty()) {
dq.pop_front();
}
}
// 获取队首元素
int front() {
if (!dq.empty()) {
return dq.front();
}
return -1; // 表示队列为空
}
// 检查是否为空
bool empty() {
return dq.empty();
}
// 获取队列大小
size_t size() {
return dq.size();
}
// 打印队列
void print() {
std::cout << "Queue: ";
for (int x : dq) {
std::cout << x << " ";
}
std::cout << std::endl;
}
};
int main() {
Queue q;
// 入队
q.enqueue(1);
q.enqueue(2);
q.enqueue(3);
q.enqueue(4);
q.enqueue(5);
q.print(); // 输出: Queue: 1 2 3 4 5
// 出队
q.dequeue();
q.dequeue();
q.print(); // 输出: Queue: 3 4 5
// 获取队首元素
std::cout << "Front: " << q.front() << std::endl; // 输出: Front: 3
// 检查队列状态
std::cout << "Size: " << q.size() << std::endl; // 输出: Size: 3
std::cout << "Is empty: " << (q.empty() ? "Yes" : "No") << std::endl; // 输出: Is empty: No
return 0;
}
练习 3:使用 deque 处理二维数组
cpp
#include <iostream>
#include <deque>
int main() {
// 创建一个 4x4 的二维 deque,初始化为 0
std::deque<std::deque<int>> matrix(4, std::deque<int>(4, 0));
// 填充对角线元素为 1
for (size_t i = 0; i < matrix.size(); ++i) {
matrix[i][i] = 1;
}
// 打印矩阵
std::cout << "Identity matrix: " << std::endl;
for (const auto& row : matrix) {
for (int x : row) {
std::cout << x << " ";
}
std::cout << std::endl;
}
// 动态添加一行
matrix.push_back(std::deque<int>{1, 2, 3, 4});
// 动态添加一列
for (auto& row : matrix) {
row.push_back(9);
}
// 打印修改后的矩阵
std::cout << "\nModified matrix: " << std::endl;
for (const auto& row : matrix) {
for (int x : row) {
std::cout << x << " ";
}
std::cout << std::endl;
}
return 0;
}