C++入门 容器适配器 / stack queue模拟实现

 目录

容器适配器

deque的原理介绍

stack模拟实现

queue模拟实现

priority_queue模拟实现

仿函数


容器适配器

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

可以这样理解:通过适配器模式,可以将不兼容的接口正常运作。


 STL标准库中stack和queue的底层结构

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


那么这里的deque是什么东西呢?

deque的原理介绍

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

 deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组。

 我们假设一个buff是10个int大小的空间数组,map中存放了每个buff数组的地址,buff数组用来存放数据,可以理解他从中间开始利用空间,向左右扩容

 我们可以理解为他在map中间进行插入删除操作:

  1. 与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。
  2. 与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。

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

为什么选择deque作为stack和queue的底层默认容器?我们给出了以下两点原因:

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

stack模拟实现

目前为止,我们已经了解了两种模式:

  1. 适配器模式 -- 封装转换
  2. 迭代器模式 -- 封装统一访问方式(数据结构访问都可以用)

接下来试着通过运用适配器来模拟实现stack和queue:

//Stack.h
#pragma oncenamespace bit
{template<class T, class Container = deque<T>>class stack{public:void push(const T& x){_con.push_back(x);}void pop(){_con.pop_back();}const T& top(){return _con.back();}bool empty(){return _con.empty();}size_t size(){_con.size();}private:Container _con;};
}

 我们只需要定义一个适配器类型的成员变量_con,他默认可以使用push_back,pop_back等常规接口,直接利用接口实现操作即可。stack用vector和list都可以实现,测试代码如下:

void test_stack()
{//bit::stack<int, vector<int>> s;//bit::stack<int, list<int>> s; bit::stack<int, deque<int>> s;  //bit::stack<int> s; //不传第二个参数默认使用deques.push(1);s.push(2);s.push(3);s.push(4);while (!s.empty()){cout << s.top() << " ";s.pop();}cout << endl;
}

queue模拟实现

//Queue.h
#pragma oncenamespace bit
{// deque list// 不支持vectortemplate<class T, class Container = deque<T>>class queue{public:void push(const T& x){_con.push_back(x);}void pop(){_con.pop_front();// 这样就可以支持vector了,但是效率就很低了//_con.erase(_con.begin());}T& back(){return _con.back();}T& front(){return _con.front();}bool empty(){return _con.empty();}size_t size(){_con.size();}private:Container _con;};
}

注意:queue不支持vector适配器,原因是vector没有pop_front的接口,如果要实现要用erase接口,但是头删完后面所有数据要往前挪,消耗时间太大,不建议这样做。测试代码如下:

void test_queue()
{bit::queue<int> q;//bit::queue<int,list<int>> q;// 不支持//bit::queue<int, vector<int>> q;q.push(1);q.push(2);q.push(3);q.push(4);while (!q.empty()){cout << q.front() << " ";q.pop();}cout << endl;
}

priority_queue模拟实现

仿函数

在实现之前先了解仿函数的概念:重载了operator()的类

特点:参数个数和返回值根据需求确定,不固定,很灵活

 通过这个概念可以实现STL库里的greater和less同等效果的仿函数。

//priority_queue.h
#pragma oncenamespace bit
{template<class T>class myless{public:bool operator()(const T& x, const T& y){return x < y;}};template<class T>class mygreater{public:bool operator()(const T& x, const T& y){return x > y;}};template<class T, class Container = vector<T>, class Comapre = myless<T>>class priority_queue{public:priority_queue() = default;template <class InputIterator>priority_queue(InputIterator first, InputIterator last){while (first != last){_con.push_back(*first);++first;}for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--){adjust_down(i);}}void adjust_up(int child){Comapre comfunc;int parent = (child - 1) / 2;while (child > 0){//if (_con[parent] < _con[child])if (comfunc(_con[parent], _con[child]))//if (comfunc.operator()(_con[parent], _con[child])){swap(_con[parent], _con[child]);child = parent;parent = (child - 1) / 2;}else{break;}}}void push(const T& x){_con.push_back(x);adjust_up(_con.size() - 1);}void adjust_down(int parent){Comapre comfunc;size_t child = parent * 2 + 1;while (child < _con.size()){//if (child+1 < _con.size() && _con[child] < _con[child+1] )if (child + 1 < _con.size() && comfunc(_con[child], _con[child + 1])){++child;}//if (_con[parent] < _con[child])if (comfunc(_con[parent], _con[child])){swap(_con[parent], _con[child]);parent = child;child = parent * 2 + 1;}else{break;}}}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjust_down(0);}const T& top(){return _con[0];}size_t size(){return _con.size();}bool empty(){return _con.empty();}private:Container _con;};
}

我们可以通过显式调用greater仿函数来实现小堆,测试代码如下:

void test_priority_queue()
{//vector<int> v = { 3,2,7,6,0,4,1,9,8,5 };//priority_queue<int> q1(v.begin(), v.end());int a[] = { 3,2,7,6,0,4,1,9,8,5 };// 默认是大堆//bit::priority_queue<int> q1(a, a+sizeof(a)/sizeof(int));// 小堆bit::priority_queue<int, vector<int>, bit::mygreater<int>> q1(a, a + sizeof(a) / sizeof(int));while (!q1.empty()){cout << q1.top() << " ";q1.pop();}cout << endl;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/41293.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度学习Week19——学习残差网络和ResNet50V2算法

文章目录 深度学习Week18——学习残差网络和ResNet50V2算法 一、前言 二、我的环境 三、论文解读 3.1 预激活设计 3.2 残差单元结构 四、模型复现 4.1 Residual Block 4.2 堆叠Residual Block 4.3. ResNet50V2架构复现 一、前言 &#x1f368; 本文为&#x1f517;365天深度学…

Kubernetes k8s 命名空间 namespace 介绍以及应用 资源限额配置

目录 命名空间 什么是命名空间&#xff1f; namespace应用场景 namespacs使用案例分享 namespace资源限额 文档中的YAML文件配置直接复制粘贴可能存在格式错误&#xff0c;故实验中所需要的YAML文件以及本地包均打包至网盘 链接&#xff1a;https://pan.baidu.com/s/1qv8Tc…

Python中异步事件触发

1、问题背景 在Python中&#xff0c;我想创建一个由事件生成控制流程的类结构。为此&#xff0c;我做了以下工作&#xff1a; class MyEvent: EventName_FunctionName {}classmethoddef setup(cls, notificationname, functionname):if notificationname in MyEvent.EventN…

ONLYOFFICE 8.1版本震撼来袭,让办公更高效、更智能

官网链接&#xff1a; 在线PDF查看器和转换器 | ONLYOFFICE 在线办公套件 | ONLYOFFICE 随着科技的不断发展&#xff0c;办公软件已经成为现代企业提高工作效率、实现信息共享的重要工具。在我国&#xff0c;一款名为ONLYOFFICE的在线办公套件受到了越来越多企业的青睐。今天…

golang中的类型转换那些事

由于golang是一门强类型的语言&#xff0c; 所以我们在golang的开发中不可避免的会对一些数据类型进行手动转换&#xff0c;以适应我们的业务需求。 golang中类型转换的途径大致有4种&#xff0c;强制转换&#xff0c;类型断言&#xff0c;类型匹配 还有使用strconv包中提供的…

[TensorFlow-Lite][深度学习]【快速简介-1】

前言&#xff1a; 很多场景下面我们需要需要把我们的深度学习模型部署到Android,IOS 手机上面. Google 通过TensorFlow Lite 提供了对应的解决方案. 目录&#xff1a; 端侧部署优点 硬件支持 性能 应用案例 一 端侧部署优点 1; 很多场景下面&#xff1a; 无网络,数据无法…

Hadoop 远程 debug

Hadoop 命令行 在执行 hadoop fs 命令行之前&#xff0c;先执行以下命令&#xff1a; export HADOOP_CLIENT_OPTS"-Xdebug -Xrunjdwp:transportdt_socket,servery,suspendy,address8000"

昇思25天学习打卡营第10天|基于MindSpore实现BERT对话情绪识别

基于MindSpore实现BERT对话情绪识别 模型简介数据集模型构建模型验证模型推理自定义推理数据集 模型简介 BERT全称是来自变换器的双向编码器表征量&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;&#xff0c;它是Google于2018年末开发并发…

HTML超链接和锚链接

HTML超链接和锚链接 一、定义 HTML的超链接&#xff08;Hyperlink&#xff09;用于在网页之间创建链接&#xff0c;使用户可以点击这些链接来导航到其他页面或资源。 二、基本语法 1、语法 HTML中的超链接使用a标签来定义 <a href"URL">链接文本</a&g…

yolov8实战——yolov8TensorRT部署(python推理)(保姆教学)

yolov8实战——yolov8TensorRT部署&#xff08;python推理&#xff09;&#xff08;保姆教学&#xff09; 一 、准备好代码和环境安装TensorRt下载代码和安装环境 部署和推理构建ONNX构建engine无torch推理torch推理 最近用到yolov8&#xff0c;但是寻找了一圈才找到了yolov8最…

[SAP ABAP] 版本管理

版本管理是指软件开发过程中各种程序代码、配置文件以及说明文档等文件变更的管理 生成版本 版本管理 对比版本 点击上述版本管理即可进行版本对比操作 补充扩展 我们可以使用事务码SE10对传输请求进行创建、修改、删除、合并以及更改所有者等操作 使用事务码SCC1进行不同cl…

3D生成模型TripoSR完美搭建流程,包含所有问题解决方案!

最近需要使用3D生成模型,无意中看到了TripoSR,觉得效果还行,于是打算在Linux系统上部署一下,结果遇到很多坑,在这里写一下详细的部署流程和部署过程中遇到的问题。 下面是TripoSR的源码地址。 GitHub - VAST-AI-Research/TripoSRContribute to VAST-AI-Research/TripoSR…

java-Linkedlist源码分析

## 深入分析 Java 中的 LinkedList 源码 LinkedList 是 Java 集合框架中的一个重要类&#xff0c;它基于双向链表实现&#xff0c;提供了高效的插入和删除操作。与 ArrayList 不同&#xff0c;LinkedList 的结构使其在特定操作上有更优的性能表现。本文将详细分析 LinkedList …

android 进程,线程调度的区别

一 分析&#xff1a; 进程和线程在调度上有什么不同呢&#xff1f;当有一个task去占用指定的资源时候叫进程&#xff0c;当有多个task去共享使用这些资源时候&#xff0c;这个task和之后的task都叫线程&#xff08;最初这个task叫主线程&#xff09;而linux调度主要调的就是cp…

【Portswigger 学院】文件上传

教程和靶场来源于 Burpsuite 的官网 Portswigger&#xff1a;File upload vulnerabilities - PortSwigger 原理与危害 很多网站都有文件上传的功能&#xff0c;比如在个人信息页面允许用户上传图片作为头像。如果网站应用程序对用户上传的文件没有针对文件名、文件类型、文件内…

前端基础:JavaScript(篇一)

目录 JavaScript概述 JavaScript历史&#xff1a; 须知&#xff1a; 基本语法 变量 代码 运行 数据类型 1、数值型(number)&#xff1a; 代码 运行 2、布尔型(boolean)&#xff1a; 代码 运行 3、字符串型&#xff1a; 代码 运行 4、 undefined类型 代码…

TCP的pop网络模式

TCP的pop网络模式 1、tcp连接的状态有以下11种 CLOSED&#xff1a;关闭状态LISTEN&#xff1a;服务端状态&#xff0c;等待客户端发起连接请求SYN_SENT&#xff1a;客户端已发送同步连接请求&#xff0c;等待服务端相应SYN_RECEIVED&#xff1a;服务器收到客户端的SYN请请求&…

MySQL 基本语法讲解及示例(下)

第六节&#xff1a;如何检索资料 在本节中&#xff0c;我们将介绍如何使用SQL语句检索数据库中的资料&#xff0c;具体包括选择特定列、排序、条件过滤以及组合排序等操作。我们以一个名为student的表格为例&#xff0c;演示不同的检索方法。 初始表格 student student_idname…

修复harbor的/account/sign-in\?globalSearch=b不登录可以查询镜像的问题

Nginx的location指令不能直接匹配查询参数&#xff0c;所以需要通过其他方式来处理。这里是一个使用if指令结合查询参数来实现的方法。该方法会在请求路径中带有特定查询参数时返回404。 使用if指令匹配查询参数 打开Nginx配置文件&#xff1a; sudo vim /etc/nginx/sites-ava…

Python中frozenset,秒变不可变集合,再也不用担心多线程了!

目录 1、Frozenset基础介绍 🌐 1.1 Frozenset定义与创建 1.2 不可变集合特性 1.3 与Set的区别对比 2、Frozenset操作实践 🧩 2.1 初始化与添加元素尝试 2.2 成员测试: in & not in 2.3 集合运算: 并集、交集、差集 2.4 使用场景示例: 字典键、函数参数默认值 …