当前位置: 首页 > news >正文

STL c++ 详解——stack与queue模拟实现与deque的介绍

stack的介绍和模拟实现

stack的介绍

 

函数说明接口说明
           stack()构造空的栈
            empty()检测stack是否为空
            size()返回stack中元素的个数
             top()返回栈顶元素的引用
           push() 将元素val压入stack
             pop()stack中尾部的元素弹

 

stack的模拟实现 

从栈的接口中可以看出,栈实际是一种特殊的 vector ,因此使用 vector 完全可以模拟实现 stack
#include<vector>
namespace bite
{
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;
};
}

queue的介绍和模拟实现

queue的介绍

队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供

一组特定的成员函数来访问其元素。元素从队尾入队列,从队头出队列。

    函数声明接口说明
      queue()构造空的队列
        empty()检测队列是否为空,是返回true,否则返回false
       size()返回队列中有效元素的个数
       front()返回队头元素的引用
         back()返回队尾元素的引用
         push() 在队尾将元素val入队列
        pop()将队头元素出队列

模拟实现 

因为queue的接口中存在头删和尾插,因此使用vector来封装效率太低,故可以借助list来模拟实
现queue,具体如下: 

#include <list>
namespace bite
{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;};
}

容器适配器

适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设

计经验的总结)该种模式是将一个类的接口转换成客户希望的另外一个接口

上面的queue和stack都是容器适配器

 STL标准库中stackqueue的底层结构

虽然这两个也是可放元素的,但是没有划分到STL容器中,而是管他们叫容器适配器。他们可以接收其他容器的接口作为底层结构。

STLstackqueue默认 使用deque,比如:

 deque介绍

为什么stack和queue默认 使用deque,而不用其他容器。请看👇

deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端

进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与

list比较,空间利用率比较高。这种特性使得 deque 在很多场景下比普通队列更具优势,比如需要频繁在两端操作数据的情况。

deque 并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际 deque 类似于一个
动态的二维数组 ,其底层结构如下图所示:
  • 中控器(map):图中的 map 是 deque 的中央控制器,本质上是一个指针数组。它存储着指向各个缓冲区的指针,用于管理和定位 deque 中元素的存储位置。当 deque 需要扩展或访问元素时,通过 map 可以快速找到对应的缓冲区。

  • 缓冲区:多个固定大小的连续内存块,用于实际存储 deque 的元素。每个缓冲区能容纳一定数量的元素,当一个缓冲区存满后,deque 会利用 map 找到或分配新的缓冲区来继续存储元素。图中还提到当 map 使用率满载时,需要重新分配更大空间作为 map,这涉及到内存管理和数据迁移操作,以保证 deque 能持续正常工作。

deque 内容

deque 的迭代器是一个复杂的对象,它封装了对中控器和缓冲区的操作。迭代器包含以下几个重要成员:

  • cur:指向当前缓冲区中正在访问的元素。

  • first:指向当前缓冲区的起始位置。

  • last:指向当前缓冲区的结束位置。

  • node:指向中控器中当前缓冲区对应的指针。

通过这些成员,迭代器可以在不同的缓冲区之间移动,实现对 deque 中元素的遍历。当迭代器移动到当前缓冲区的边界时,它会通过 node 指针找到下一个或上一个缓冲区,并更新 curfirst 和 last 指针。

插入和删除操作

  • 两端插入和删除:在 deque 的两端插入或删除元素时,通常只需要在当前的缓冲区进行操作,或者在必要时分配或释放一个新的缓冲区。因此,在两端插入和删除元素的时间复杂度为 \(O(1)\)。

  • 中间插入和删除:在 deque 的中间插入或删除元素时,需要移动部分元素。具体来说,需要将插入或删除位置之后的元素在缓冲区之间进行移动,以腾出或填充空间。因此,在中间插入或删除元素的时间复杂度为 \(O(n)\),但由于 deque 的分段连续存储结构,移动的元素数量通常比 vector 要少。

为什么选择deque作为stackqueue的底层默认容器

stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()pop_back()操作的线性

结构,都可以作为stack的底层容器,比如vectorlist都可以;queue是先进先出的特殊线性数据

结构,只要具有push_backpop_front操作的线性结构,都可以作为queue的底层容器,比如

list。但是STL中对stackqueue默认选择deque作为其底层容器,主要是因为:

  1. 1. stackqueue不需要遍历(因此stackqueue没有迭代器),只需要在固定的一端或者两端进 行操作。
  2. 2. stack中元素增长时, dequevector的效率高(扩容时不需要搬移大量数据)queue中的 元素增长时, deque不仅效率高,而且内存使用率高。

结合了deque的优点,而完美的避开了其缺陷。

 

相关文章:

  • 【Sequelize】
  • 地理人工智能中位置编码的综述:方法与应用
  • VMware下Ubuntu空间扩容
  • 开展东南亚货运专线业务,有哪家系统提高管理效率?
  • flutter json解析增强
  • Android 9.0系统源码定制:实现开机启动特定App的全面指南
  • 《分布式软总线:不同频段Wi-Fi环境下设备发现兼容性难题》
  • leetcode面试经典算法题——2
  • 微店商品详情API接口:功能解析与数据应用实践
  • LLM-as-Judge真的更偏好AI输出?
  • 鸿蒙应用元服务开发-Account Kit配置登录权限
  • Prometheus架构组件
  • 国内开源医疗模型研究报告
  • 自动化测试工具playwright中文文档-------14.Chrome 插件
  • 如何在NS3中搭建窄带干扰和扫频干扰场景?
  • 844. 比较含退格的字符串
  • 安装SQLServer管理工具
  • 日语学习-日语知识点小记-构建基础-JLPT-N4阶段(4): 可能形(かのうけい)
  • Coze平台技术解析:零代码AI开发与智能体应用实践
  • 跑得快的标准详细规则·棒球1号位
  • 上海畅通“外转内”,外贸优品成“香饽饽”
  • 中国首位、亚洲首位!赵心童夺得斯诺克世锦赛冠军
  • 媒体:不能让追求升学率,成为高中不双休的借口
  • 五一上海楼市热闹开局:售楼处全员到岗,热门楼盘连续触发积分
  • 黎巴嫩9年来首次举行地方选举
  • 习近平将对俄罗斯进行国事访问并出席纪念苏联伟大卫国战争胜利80周年庆典