目录
01.容器适配器
02.栈(stack)
1.stack的介绍
2.stack的使用
3.stack的模拟实现
03.队列(queue)
1.queue的介绍:
2.queue的使用
3.queue的模拟实现
04.双端队列(deque)
1.介绍
2.优缺点
3.为什么选择deque
01.容器适配器
适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口。
就好比由于国内外电器插头标准不同,出国时就需要携带插头转换器,使得国内的电器插头可以和外国的插座兼容。插头转换器就是一种适配器。
C++中的容器适配器是一种特殊的容器,它们提供了特定的接口和功能,使其在特定的使用场景下更加方便和高效。C++标准库提供了三种主要的容器适配器:栈(stack)、队列(queue)和优先队列(priority_queue)。这些适配器都是基于其他基本的容器实现的,栈和队列通常基于deque(双端队列)实现,而优先队列通常基于vector实现。
02.栈(stack)
1.stack的介绍
这是cplusplus网站上对stack的文档介绍:stack的文档介绍
-
stack 是一种容器适配器:它专门用于后进先出(LIFO)操作,例如压入和弹出元素。stack 实现了一种简化的接口,只允许在容器的一端进行元素的插入和提取操作。
-
底层容器的封装:stack 封装了一个底层容器,该容器负责实际存储元素。stack 提供了一组特定的成员函数,用于访问其底层容器的元素,并将特定类作为其底层容器,元素在容器的尾部(栈顶)进行压入和弹出操作。
-
底层容器的要求:stack 的底层容器可以是任何标准的容器类模板或者其他特定的容器类。底层容器需要支持以下操作:判空操作(empty)、获取尾部元素操作(back)、尾部插入元素操作(push_back)和尾部删除元素操作(pop_back)。
-
支持的容器:标准容器 vector、deque、list 都符合 stack 底层容器的要求。默认情况下,如果没有为 stack 指定特定的底层容器,stack 会使用 deque 作为默认的底层容器。
2.stack的使用
构造函数:
stack()
: 创建一个空栈。stack(const stack& other)
: 复制构造函数,根据另一个栈创建一个新的栈。
赋值和交换:
operator=
: 赋值操作符,用另一个栈的内容替换当前栈的内容。swap(stack& other) noexcept
: 交换两个栈的内容。
容量查询:
empty() const noexcept
: 判断栈是否为空。size() const noexcept
: 返回栈中元素的数量。
访问元素:
top() const
: 返回栈顶元素的引用。
修改容器:
push(const T& value)
: 将元素压入栈顶。emplace(Args&&... args)
: 在栈顶构造一个新元素。pop()
: 弹出栈顶元素。
以下为代码示例:
#include <iostream>
#include <stack>
int main() {
// 定义一个存储整数的栈
std::stack<int> myStack;
// 向栈中压入一些元素
myStack.push(10);
myStack.push(20);
myStack.push(30);
// 访问栈顶元素
std::cout << "栈顶元素: " << myStack.top() << std::endl;
// 弹出栈顶元素
myStack.pop();
// 再次访问栈顶元素
std::cout << "弹出后的栈顶元素: " << myStack.top() << std::endl;
// 判断栈是否为空
if (myStack.empty()) {
std::cout << "栈为空" << std::endl;
} else {
std::cout << "栈不为空,大小为: " << myStack.size() << std::endl;
}
return 0;
}
运行结果如下:
栈顶元素: 30
弹出后的栈顶元素: 20
栈不为空,大小为: 2
3.stack的模拟实现
从栈的接口可以看出,栈实际是一种特殊的vector,因此使用vector完全可以模拟实现stack。
#include<vector>
namespace my
{
template<class T>
class stack
{
public:
stack() {}
void push(const T& x) {
_c.push_back(x);
}
void pop() {
_c.pop_back();
}
T& top() {
return _c.back();
}
const T& top()const {
return _c.back();
}
size_t size()const {
return _c.size();
}
bool empty()const {
return _c.empty();
}
private:
std::vector<T> _c;
};
}
03.队列(queue)
1.queue的介绍:
queue的文档介绍
-
队列是一种容器适配器:队列容器适配器专门用于先进先出(FIFO)的上下文环境中操作。元素从队尾入队列,从队头出队列。
-
作为容器适配器的实现:队列作为容器适配器,封装了一个特定的底层容器类作为其内部实现。它提供了一组特定的成员函数,用于访问其元素。
-
底层容器的要求:队列的底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。底层容器至少需要支持以下操作:
empty()
: 检测队列是否为空。size()
: 返回队列中有效元素的个数。front()
: 返回队头元素的引用。back()
: 返回队尾元素的引用。push_back()
: 在队尾入队列。pop_front()
: 在队头出队列。
-
支持的标准容器类:标准容器类 deque 和 list 都满足了这些要求。默认情况下,如果没有为队列指定底层容器类,则会使用标准容器 deque。
2.queue的使用
构造函数:
queue()
: 创建一个空队列。queue(const queue& other)
: 复制构造函数,根据另一个队列创建一个新的队列。
赋值和交换:
operator=
: 赋值操作符,用另一个队列的内容替换当前队列的内容。swap(queue& other) noexcept
: 交换两个队列的内容。
容量查询:
empty() const noexcept
: 判断队列是否为空。size() const noexcept
: 返回队列中元素的数量。
访问元素:
front() const
: 返回队头元素的引用。back() const
: 返回队尾元素的引用。
修改容器:
push(const T& value)
: 将元素入队列。emplace(Args&&... args)
: 在队尾构造一个新元素。pop()
: 出队列。
代码示例:
#include <iostream>
#include <queue>
int main() {
// 定义一个存储整数的队列
std::queue<int> myQueue;
// 入队列
myQueue.push(10);
myQueue.push(20);
myQueue.push(30);
// 访问队头元素
std::cout << "队头元素: " << myQueue.front() << std::endl;
// 出队列
myQueue.pop();
// 再次访问队头元素
std::cout << "出队后的队头元素: " << myQueue.front() << std::endl;
// 判断队列是否为空
if (myQueue.empty()) {
std::cout << "队列为空" << std::endl;
} else {
std::cout << "队列不为空,大小为: " << myQueue.size() << std::endl;
}
return 0;
}
运行结果:
队头元素: 10
出队后的队头元素: 20
队列不为空,大小为: 2
3.queue的模拟实现
因为queue的接口中存在头删和尾删,因此用vector来封装效率太低,因此可以借助list来实现:
#include <list>
namespace my
{
template<class T>
class queue
{
public:
queue() {}
void push(const T& x) {
_c.push_back(x);
}
void pop() {
_c.pop_front();
}
T& back() {
return _c.back();
}
const T& back()const {
return _c.back();
}
T& front() {
return _c.front();
}
const T& front()const {
return _c.front();
}
size_t size()const {
return _c.size();
}
bool empty()const {
return _c.empty();
}
private:
std::list<T> _c;
};
}
04.双端队列(deque)
1.介绍
deque(双端队列):是一种双开口的“连续”空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率较高。
deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组。
2.优缺点
与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不 需要搬移大量的元素,因此其效率是必vector高的。
与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。
但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到 某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构 时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作 为stack和queue的底层数据结构。
3.为什么选择deque
stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性结构,都可以作为stack的底层容器,vector和list都可以;queue是先进先出的特殊线性数据结构,只要具有push_back()和pop_front()操作的线性结构,都可以作为queue的底层容器,比如list。但是STL中对stack和queue默认选择deque作为其底层容器,主要是因为:
- stack和queue不需要遍历(因此他们没有迭代器),只需要在固定的一端或者两端进行操作。
- 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的元素增长时,dequeue不仅效率高,而且内存使用率高。
完美结合了deque的优点,并且规避了其缺点。
以上就是stack & queue的介绍说明了,欢迎在评论区留言,觉得这篇博客对你有帮助的可以点赞关注收藏支持一波喔~😉