【C++/STL】:stack/queue的使用及底层剖析&&双端队列&&容器适配器

简介: 【C++/STL】:stack/queue的使用及底层剖析&&双端队列&&容器适配器

🚀前言

点击跳转到文章【list容器的基本使用】

点击跳转到文章【list容器的深度剖析及底层实现】

前面我们已经学习了list容器的相关知识,本文主要介绍STL中另外两种重要的结构,stack和queue。但是在STL中这两者并没有划分在容器范围内,而是将其称为容器适配器

一,容器适配器

1,什么是容器适配器

适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口

2,STL标准库中stack和queue的底层适配

虽然stack和queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为容器适配器,这是因为stack和queue只是对其他容器的接口进行了包装,STL中stack和queue默认使用deque,比如:

二,双端队列deque的介绍

1,deque的原理介绍

deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高。

deque在功能上是vector和list的结合体,如图:

deque并不是真正连续的空间,而是由一小段一小段连续的buff小数组和中控数组(指针数组)构成,实际deque类似于一个动态的二维数组,其底层结构如下图所示:

小数组满了之后不扩容,而是再开辟一小段空间做buff,并且开辟buff数组时并不是从中控数组的开头开始申请的,而是在中间,头插尾插时才向两边申请

双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂,如下图所示:

2,deque的缺陷

与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。

与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。

但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下并且deque的中间位置的insert 和erase 也要挪动数据,效率并不高。而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构

3,为什么选择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的优点,而完美的避开了其缺陷。

三,对于stack和queue的使用和模拟实现

1,stack和queue的使用

首先,使用stack和queue需要包含头文件< satck > 和 < queue >

stack和queue的主要接口十分简单:

代码如下:

#include<stack>
#include<queue>
int main()
{ 
  //stack的使用
  stack<int> st;
  st.push(1);
  st.push(2);
  st.push(3);
  st.push(4);
  while (!st.empty())
  {
    cout << st.top() << " ";
    st.pop();
  }
  cout << endl;
  
  //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;
  return 0;
}

2,stack的模拟实现

//template <class T,class Con = list<T>>
//template <class T,class Con = vector<T>>
template <class T,class Con = deque<T>>
class stack
{
public:
  void push(const T& x)
  {
    _con.push_back(x);
  }
  void pop()
  {
    _con.pop_back();
  }
  T& top()
  {
    return _con.back();
  }
  const T& top()const
  {
    return _con.back();
  }
  size_t size()const
  {
    return _con.size();
  }
  bool empty()const
  {
    return _con.empty();
  }
private:
  Con _con;
};

3,queue的模拟实现

//template<class T, class Con = list<T>>
template<class T, class Con = deque<T>>
class queue
{
public:
  void push(const T& x)
  {
    _con.push_back(x);
  }
  void pop()
  {
    _con.pop_front();
  }
  T& back()
  {
    return _con.back();
  }
  const T& back()const
  {
    return _con.back();
  }
  T& front()
  {
    return _con.front();
  }
  const T& front()const
  {
    return _con.front();
  }
  size_t size()const
  {
    return _con.size();
  }
  bool empty()const
  {
    return _con.empty();
  }
private:
  Con _con;
};
目录
相关文章
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
1266 77
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
635 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
存储 算法 C++
【c++丨STL】priority_queue(优先级队列)的使用与模拟实现
本文介绍了STL中的容器适配器`priority_queue`(优先级队列)。`priority_queue`根据严格的弱排序标准设计,确保其第一个元素始终是最大元素。它底层使用堆结构实现,支持大堆和小堆,默认为大堆。常用操作包括构造函数、`empty`、`size`、`top`、`push`、`pop`和`swap`等。我们还模拟实现了`priority_queue`,通过仿函数控制堆的类型,并调用封装容器的接口实现功能。最后,感谢大家的支持与关注。
1076 1
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
441 9
|
C++
【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
657 7
|
缓存 安全 C++
C++无锁队列:解锁多线程编程新境界
【10月更文挑战第27天】
1246 7
|
消息中间件 存储 安全
|
存储 设计模式 C++
【C++】优先级队列(容器适配器)
本文介绍了C++ STL中的线性容器及其适配器,包括栈、队列和优先队列的设计与实现。详细解析了`deque`的特点和存储结构,以及如何利用`deque`实现栈、队列和优先队列。通过自定义命名空间和类模板,展示了如何模拟实现这些容器适配器,重点讲解了优先队列的内部机制,如堆的构建与维护方法。
324 0
|
设计模式 存储 C++
【C++】C++ STL探索:容器适配器 Stack 与 Queue 的使用及模拟实现(二)
【C++】C++ STL探索:容器适配器 Stack 与 Queue 的使用及模拟实现
254 0
|
Kubernetes Docker Python
Docker 与 Kubernetes 容器化部署核心技术及企业级应用实践全方案解析
本文详解Docker与Kubernetes容器化技术,涵盖概念原理、环境搭建、镜像构建、应用部署及监控扩展,助你掌握企业级容器化方案,提升应用开发与运维效率。
1469 108