1 基础概念
功能:
双端数组,可以对头端进行插入删除操作。
deque与vector区别:
-
vector对于头部的插入删除效率低,数据量越大,效率越低。
-
deque相对而言,对头部的插入删除速度回比vector快。
-
vector访问元素时的速度会比deque快,这和两者内部实现有关。
deque内部工作原理:
-
deque内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据。
-
中控器维护的是每个缓冲区的地址,使得使用deque时像一片连续的内存空间。
-
deque容器的迭代器也是支持随机访问的。
2 代码解释
Talk is cheap, show me the code.
#include<iostream>
using namespace std;
#include<deque>
#include<algorithm>
void printDeque(const deque<int>& dd)
{
//注意这里的只读迭代器的操作
for (deque<int>::const_iterator it = dd.begin(); it != dd.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
/*
构造函数原型:
deque<T> deqT; //默认构造形式
deque(beg, end); //构造函数将[beg, end)区间中的元素拷贝给本身。
deque(n, elem); //构造函数将n个elem拷贝给本身。
deque(const deque &deq); //拷贝构造函数
*/
void test01()
{
deque<int> d1;
for (int i = 0; i < 10; i++)
{
d1.push_back(i);
}
printDeque(d1);
deque<int> d2(d1.begin(), d1.end());
printDeque(d2);
deque<int> d3(10, 88);
printDeque(d3);
deque<int> d4(d3);
printDeque(d4);
}
/*
赋值函数原型:
deque& operator=(const deque &deq); //重载等号操作符
assign(beg, end); //将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem); //将n个elem拷贝赋值给本身。
*/
void test02()
{
deque<int> d1;
for (int i = 0; i < 10; i++)
{
d1.push_back(i);
}
deque<int> d2;
d2 = d1;
printDeque(d2);
deque<int> d3;
d3.assign(d2.begin(), d2.end());
printDeque(d3);
deque<int> d4;
d4.assign(10, 88);
printDeque(d4);
}
/*
大小操作函数原型:
deque.empty(); //判断容器是否为空
deque.size(); //返回容器中元素的个数
deque.resize(num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除。
deque.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除。
*/
// 我们发现跟vector相比没有了capacity容量相关的操作,因为可以无限地扩展,只需要有一段地址来维护空间
void test03()
{
deque<int> d1;
for (int i = 0; i < 10; i++)
{
d1.push_back(i);
}
printDeque(d1);
if (d1.empty())
{
cout << "EMPTY" << endl;
}
else
{
cout << d1.size() << endl;
}
d1.resize(20, 999);
printDeque(d1);
}
/*
插入和删除函数原型:
两端插入操作:
push_back(elem); //在容器尾部添加一个数据
push_front(elem); //在容器头部插入一个数据
pop_back(); //删除容器最后一个数据
pop_front(); //删除容器第一个数据
指定位置操作:
insert(pos,elem); //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值。
insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值。
clear(); //清空容器的所有数据
erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置。
erase(pos); //删除pos位置的数据,返回下一个数据的位置。
*/
void test04()
{
deque<int> d1;
d1.push_back(100);
d1.push_back(100);
d1.push_back(100);
d1.push_back(100);
d1.push_back(100);
d1.push_back(100);
printDeque(d1);
d1.push_front(200);
printDeque(d1);
d1.pop_back();
printDeque(d1);
d1.pop_front();
printDeque(d1);
d1.insert(d1.begin() + 2, 5, 1000);
printDeque(d1);
d1.insert(d1.end() - 1, d1.begin(), d1.end());
printDeque(d1);
d1.erase(d1.begin()+1);
printDeque(d1);
d1.clear();
printDeque(d1);
}
/*
数据存取函数原型:
at(int idx); //返回索引idx所指的数据
operator[]; //返回索引idx所指的数据
front(); //返回容器中第一个数据元素
back(); //返回容器中最后一个数据元素
*/
void test05()
{
deque<int> d1;
for (int i = 0; i < 10; i++)
{
d1.push_back(i);
}
printDeque(d1);
cout << d1.at(2) << endl;
cout << d1[2] << endl;
cout << d1.front() << endl;
cout << d1.back() << endl;
}
/*
排序算法:
sort(iterator beg, iterator end) //对beg和end区间内元素进行排序
*/
void test06()
{
deque<int> d1;
d1.push_back(10);
d1.push_back(20);
d1.push_back(15);
d1.push_front(89);
d1.push_front(78);
d1.push_front(99);
printDeque(d1);
sort(d1.begin(), d1.end());
printDeque(d1);
}
int main()
{
test01();
test02();
test03();
test04();
test05();
test06();
system("pause");
return 0;
}
3 应用场景
C++标准模板库(STL)中的deque
(双端队列)容器是一种支持在两端高效插入和删除操作的数据结构。它在实际项目中有许多应用场景,其中一些例子包括:
-
任务调度队列: 在多线程或并发编程中,
deque
可以用作任务调度队列。新任务可以在队列的前端或后端插入,而工作线程可以从队列的另一端获取任务执行。这种方式允许高效的任务调度和分发。#include <deque> #include <mutex> #include <thread> std::deque<Task> taskQueue; std::mutex queueMutex; void AddTask(const Task& task) { std::lock_guard<std::mutex> lock(queueMutex); taskQueue.push_back(task); } Task GetTask() { std::lock_guard<std::mutex> lock(queueMutex); Task task = taskQueue.front(); taskQueue.pop_front(); return task; }
-
实时数据流处理: 在需要高性能数据流处理的应用中,
deque
可以用于缓存数据,支持快速的数据插入和删除。这对于实时数据处理系统(如金融数据处理或传感器数据处理)非常有用。#include <deque> #include <iostream> void ProcessData(std::deque<Data>& dataQueue) { // 实时数据处理逻辑 while (!dataQueue.empty()) { Data currentData = dataQueue.front(); dataQueue.pop_front(); // 处理数据... } }
-
存储历史记录: 在需要保留最近N个元素的场景中,
deque
可以用于实现一个固定大小的历史记录缓存。新元素可以从一端插入,而旧元素可以从另一端删除,以保持缓存的大小。#include <deque> #include <iostream> const int MaxHistorySize = 10; std::deque<Record> historyDeque; void AddToHistory(const Record& record) { historyDeque.push_back(record); if (historyDeque.size() > MaxHistorySize) { historyDeque.pop_front(); } }
-
实现高效的双端队列: 当需要在两端执行频繁插入和删除操作时,使用
deque
而不是vector
可能更为高效,因为deque
的设计允许在两端进行快速操作,而vector
在插入或删除头部元素时的性能较差。
总的来说,deque
是一个灵活的数据结构,适用于许多不同的应用场景,其中需要高效支持两端操作的情况。
4 实际用例
下面是一个简单的示例,演示了使用deque
相对于vector
在频繁插入和删除操作时的优势。这个示例模拟一个实时日志系统,其中日志消息被不断添加到队列,并定期删除旧的日志消息,以保持队列的大小。
#include <iostream>
#include <deque>
#include <ctime>
#include <cstdlib>
struct LogMessage {
std::string message;
time_t timestamp;
};
class RealTimeLogger {
public:
void AddLog(const std::string& message) {
LogMessage log;
log.message = message;
log.timestamp = std::time(nullptr);
logQueue.push_back(log);
}
void PruneOldLogs() {
const int MaxLogCount = 5;
while (logQueue.size() > MaxLogCount) {
logQueue.pop_front();
}
}
void PrintLogs() {
for (const auto& log : logQueue) {
std::cout << "[" << log.timestamp << "] " << log.message << std::endl;
}
std::cout << std::endl;
}
private:
std::deque<LogMessage> logQueue;
};
int main() {
RealTimeLogger logger;
// 模拟日志生成
for (int i = 0; i < 10; ++i) {
logger.AddLog("Log message #" + std::to_string(i));
logger.PruneOldLogs();
logger.PrintLogs();
}
return 0;
}
在这个示例中,RealTimeLogger
类使用deque
作为存储日志消息的容器。在每次添加日志消息时,它会执行PruneOldLogs
方法来删除旧的消息,以保持队列的大小。由于deque
支持在两端高效地进行插入和删除操作,这个实现在频繁添加和删除日志消息时的性能表现更好。
当使用vector
时,由于在头部删除元素的代价相对较高,性能可能不如deque
。在实际项目中,这种优势可能在需要处理大量实时数据的场景下更为明显,例如日志记录、事件处理等。