目录
1.queue的介绍
2.queue的使用:
3.queue的模拟实现:
4.deque的介绍:
5.deque的函数接口和底层原理:
6.deque的优缺点:
1.queue的介绍
queue的文档内容
- 1. 队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端提取元素。
- 2. 队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从队尾入队列,从队头出队列。
- 3. 底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作:
- empty:检测队列是否为空
size:返回队列中有效元素的个数
front:返回队头元素的引用
back:返回队尾元素的引用
push_back:在队列尾部入队列
pop_front:在队列头部出队列- 4. 标准容器类deque和list满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标准容器deque。
2.queue的使用:
函数声明 | 接口说明 |
queue<> | 构造空的队列 |
empty() | 检测队列是否为空,是返回true,否则返回false |
size() | 返回队列中有效元素的个数 |
front() | 返回队头元素的引用 |
back() | 返回队尾元素的引用 |
push() | 在队尾将元素val入队列 |
pop() | 将队头元素出队列 |
#include <iostream>
#include <queue>
using namespace std;
//queue的使用:
int main()
{
// 构建队列qu
queue<int> qu;
//给队列尾插数据
qu.push(1);
qu.push(2);
qu.push(3);
qu.push(4);
cout << "size: " << qu.size() << endl; //读取队列内元素个数
cout << "队头: " << qu.front() << endl; //查看队头元素
cout << "队尾: " << qu.back() << endl; //查看队尾元素
//循环打印栈
while (!qu.empty())//判空
{
cout << qu.front() << " "; //读取队头顶元素
qu.pop();//删除队头元素
}
return 0;
}
3.queue的模拟实现:
#include <iostream>
#include <vector>
#include <list>
using namespace std;
//queue的模拟实现
namespace moyu
{
// 适配器模式/配接器
template<class T, class Container = list<T>>
class queue
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_front();
}
const T& front()
{
return _con.front();
}
const T& back()
{
return _con.back();
}
size_t size()
{
return _con.size();
}
bool empty()
{
return _con.empty();
}
private:
Container _con;
};
void test_queue()
{
queue<int> q;
q.push(1);
q.push(2);
q.push(3);
q.push(4);
while (!q.empty())
{
cout << q.front() << " ";
q.pop();
}
cout << endl;
}
}
int main()
{
moyu::test_queue();
return 0;
}
4.deque的介绍:
点击查看depue的使用文档
- 1.deque是双端队列的不规则首字母缩写
- deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端进行插入 和 删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高。
- 2.双端队列是具有动态大小的序列容器,可以在两端(前端或后端)进行扩展或收缩。特定的库可能以不同的方式实现deques,通常是某种形式的动态数组。
- 3.在任何情况下,它们都允许通过随机访问迭代器直接访问单个元素,并根据需要通过扩展和收缩容器来自动处理存储。因此,它们提供了类似于vector的功能,但在序列的开头,而不仅仅是结尾,还可以有效地插入和删除元素。
- 4.但是,与vector不同的是,deque不能保证将其所有元素存储在连续的存储位置:通过将指针偏移到另一个元素来访问deque中的元素会导致未定义的行为。
- 5.vector和deque都提供了非常相似的接口,可以用于类似的目的,但在内部,两者的工作方式截然不同:虽然vector使用的是一个偶尔需要重新分配才能增长的阵列,但deque的元素可以分散在不同的存储块中,容器在内部保留必要的信息,以在恒定时间内提供对其任何元素的直接访问,并使用统一的顺序接口(通过迭代器)。
- 6.因此,deques的内部比向量稍微复杂一点,但这使它们在某些情况下能够更有效地生长,尤其是在非常长的序列中,重新定位变得更加昂贵。对于涉及在起始或结束以外的位置频繁插入或删除元素的操作,与列表和前向列表相比,deques的性能更差,迭代器和引用的一致性也更低。
5.deque的函数接口和底层原理:
可以看到deque的接口不仅包含了list的还包含了vector
可以说,deque是list和vector两者的结合
deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,
我们来看deque的具体存储形式:
双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落 在了deque的迭代器身上,因此deque的迭代器设计就比较复杂,如下图所示:
6.deque的优缺点:
就可以理解为是vector和list的结合,但是结合之后,弥补了两者的缺陷,但同时弱化了数组与链表的优点。
优缺点:
- 与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。
- 与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。
- 但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到 某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构 时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作 为stack和queue的底层数据结构
deque一般是作为stack和queue的底层适配容器来用的
- stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性结构,都可以作为stack的底层容器,比如vector和list都可以;queue是先进先出的特殊线性数据结构,只要具有push_back和pop_front操作的线性结构,都可以作为queue的底层容器,比如list。但是STL中对stack和queue默认选择deque作为其底层容器,主要是因为:
- 1. stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进行操作。
- 2. 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的元素增长时,deque不仅效率高,而且内存使用率高。
- 结合了deque的优点,而完美的避开了其缺陷。