stack、queue、priority_queue、deque的使用和模拟实现

目录

1.容器适配器

2.stack

stack的常用接口及使用示例

stack的模拟实现

3.queue

queue的常用接口及使用示例

queue的模拟实现

4.priority_queue

priority_queue的常用接口及使用示例

priority_queue的模拟实现

5.deque

认识deque

deque底层的数据结构

deque和vector、list的对比


1.容器适配器

stack、queue和priority_queue在STL中并不属于容器,而是属于容器适配器,那什么是容器适配器呢?所谓容器适配器就是对其他容器的接口进行了封装,封装出自己想要的功能

比如我们学习数据结构的时候:

  • 当我们想要实现一个栈,由于栈只在一端进行进行元素的插入和删除,因此我们选择顺序表作为其底层存储数据的结构,对顺序表进行封装,从而实现栈。
  • 当我们想要实现一个队列,由于队列需要进行头删和尾插,因此我们选择链表作为其底层存储数据的结构,对链表进行封装,从而实现队列。

举一个生活中的例子:比如我们的电脑没有typec接口,只有USB接口,可我们偏偏要使用typec接口,这个时候我们都会买一个适配器,适配出我们想要的接口以便达到我们想要的功能。

在STL中,对于stack和queue而言,其实就是默认对deque(双端队列)的接口进行封装。(详细过程后面讲解)

priority_queue默认对vector的接口进行封装


2.stack

stack其实就是这种数据结构,栈这种数据结构的特点是只能在一端进行数据的插入和删除,所以,栈的底层容器应该支持以下操作:

  • push_back():尾插
  • pop_back():尾删
  • back():获取最后一个元素
  • empty():判断是否为空

STL标准容器中的vector、list、deque均符合要求,但是默认情况下,STL采用deque作为其底层容器。


stack的常用接口及使用示例

常用接口:

  • 判断是否为空:bool empty() const。
  • 获取有效元素个数:size_type size() const。
  • 取栈顶元素:
             value_type& top();
    const value_type& top() const;
  • 在栈顶插入数据:void push (const value_type& val)。
  • 删除栈顶元素:void pop()。

使用示例:

#include <iostream>
#include <stack>using namespace std;void test_use_stack()
{stack<int> st; // 构造一个空的stackst.push(1);    // 插入数据st.push(2);st.push(3);st.push(4);st.push(5);cout << "st.size(): " << st.size() << endl;while (!st.empty())    // 当st不为空时{auto e = st.top(); // 取栈顶元素cout << e << ' ';st.pop();          // 删除栈顶元素}
}int main()
{test_use_stack();return 0;
}运行结果:
st.size() : 5
5 4 3 2 1

stack的模拟实现

我们默认采用vector为stack的底层容器,对vector的接口进行封装即可实现特定功能的stack。

#include <vector>namespace xy                   // 防止命名冲突
{/**  T:元素的数据类型*  Container:底层容器,我们默认其为vector*/template<class T, class Container = std::vector<T>>class stack{public:void push(const T& x)  // 栈顶插入元素,复用vector的push_back实现{_con.push_back(x);}void pop()             // 删除栈顶元素,复用vector的pop_back实现{_con.pop_back();}const T& top()         // 取栈顶元素,复用vector的back实现{return _con.back();}size_t size()          // 获取元素个数,复用vector的size实现{return _con.size();}bool empty()           // 判断是否为空,复用vector的empty实现{return _con.empty();}private:Container _con;        // 对指定容器进行封装};
}

3.queue

queue其实就是队列这种数据结构,队列这种数据结构的特点是元素在队尾入队列,在队头出队列,所以,queue的底层容器应该支持以下操作:

  • empty:判断是否为空
  • size:返回队列中有效元素的个数
  • front:返回队头元素的引用
  • back:返回队尾元素的引用
  • push_back:在队列尾部插入数据
  • pop_front:在队列头部删除数据

STL标准容器中的list、deque均符合要求,但是默认情况下,STL采用deque作为其底层容器。


queue的常用接口及使用示例

常用接口:

  • empty():检测队列是否为空,是返回true,否则返回false。
  • size():返回队列中有效元素的个数。
  • front():返回队头元素的引用。
  • back():返回队尾元素的引用。
  • push():在队尾将元素val入队列。
  • pop():将队头元素出队列。

使用示例:

#include <iostream>
#include <queue>using namespace std;void test_use_queue()
{queue<int> q; // 构造空的队列q.push(1);    // 入队列q.push(2);q.push(3);q.push(4);q.push(5);// 获取有效元素的个数cout << "q.size(): " << q.size() << endl;// 获取队头元素cout << "q.front(): " << q.front() << endl;	// 获取队尾元素cout << "q.back(): " << q.back() << endl;while (!q.empty())            // 判断队列是否为空{cout << q.front() << ' '; q.pop();                  // 出队列}
}int main()
{test_use_queue();return 0;
}运行结果:
q.size() : 5
q.front() : 1
q.back() : 5
1 2 3 4 5

queue的模拟实现

我们实现的queue底层采用std中的list,对list的接口进行封装即可得到特定功能的queue。

#include <list>namespace xy
{/**  T:存储的数据类型*  Container:queue的底层容器,我们采用std中的list*/template<class T, class Container = std::list<T>>class queue{public:void push(const T& x) // 入队列,复用list的push_back实现{_con.push_back(x);}void pop()            // 出队列,复用list的pop_front实现{_con.pop_front();}const T& front()      // 获取队头元素,复用list的front实现{return _con.front();}const T& back()       // 获取队尾元素,复用list的back实现{return _con.back();}size_t size()         // 获取有效元素的个数,复用list的size实现{return _con.size();}bool empty()          // 判断queue是否为空,复用list的empty实现{return _con.empty();}private:Container _con;       // queue的底层容器};
}

4.priority_queue

priority_queue(优先级队列)其实就是这种数据结构,堆这种数据结构的特点是元素在容器的尾部插入和弹出,所以,priority_queue的底层容器应该支持以下操作:

  • empty():检测容器是否为空。
  • size():返回容器中有效元素个数
  • front():返回容器中第一个元素的引用
  • push_back():在容器尾部插入元素
  • pop_back():删除容器尾部元素

STL标准容器中的vector和deque均符合要求,但是默认情况下,STL采用vector作为其底层容器。

注意:虽然list也满足这些接口的要求,但是不能将list作为priority_queue的底层数据结构。因为priority_queue是符合堆的特点的,堆的底层其实是连续存储的物理空间,堆的相关操作涉及下标的随机访问,list并不具备这一点,所以不能将list作为priority_queue的底层数据结构。


priority_queue的常用接口及使用示例

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,priority_queue的使用就是堆的使用。

注意:默认情况下priority_queue是大堆。

如果读者不清楚堆相关知识,推荐阅读:

数据结构 —— 堆icon-default.png?t=O83Ahttps://blog.csdn.net/D5486789_/article/details/142999937?spm=1001.2014.3001.5501常用接口:

  • empty:检测优先级队列是否为空,是返回true,否则返回false。
  • size:获取有效元素的个数。
  • top:获取堆顶元素
  • push:在优先级队列中插入元素。
  • pop:删除堆顶元素。

使用示例:

#include <iostream>
#include <vector>using namespace std;void test_use_priority_queue()
{// 默认建立大堆// priority_queue<int> heap;// 构建小堆:指定底层容器为vector,比较方式为greater,建立小堆priority_queue<int,vector<int>,greater<int>> heap;heap.push(1); // 向堆中插入数据heap.push(5);heap.push(3);heap.push(2);heap.push(4);// 获取有效元素的个数cout << "heap.size(): " << heap.size() << endl; while (!heap.empty()) // 判断是否为空{cout << heap.top() << ' '; // 获取堆顶元素heap.pop();                // 删除堆顶元素}
}int main()
{test_use_priority_queue();return 0;
}运行结果:
heap.size() : 5
1 2 3 4 5

priority_queue的模拟实现

我们实现priority_queue的时候将vector作为底层容器,对vector的接口进行封装,并配合堆的向上和向下调整算法,让底层容器中的元素保持堆的特点,从而实现priority_queue。

#include <iostream>
#include <vector>using namespace std;namespace xy
{// 当堆是使用该比较方式的时候,建立大堆template<class T> struct less{bool operator()(const T& left, const T& right){return left < right;}};// 当堆是使用该比较方式的时候,建立小堆template<class T>struct greater{bool operator()(const T& left, const T& right){return left > right;}};/**  T:存储的数据类型*  Container:priority_queue的底层容器,默认使用std中的vector*  Compare:堆中元素的比较方式,控制建立大堆还是小堆,默认建立大堆*/template<class T, class Container = std::vector<T>, class Compare = less<T>>class priority_queue{private:Container _con;private:// 向上调整void AdjustUP(int child){int parent = ((child - 1) >> 1);while (child){if (Compare()(_con[parent], _con[child])){swap(_con[child], _con[parent]);child = parent;parent = ((child - 1) >> 1);}else{return;}}}// 向下调整void AdjustDown(int parent){size_t child = parent * 2 + 1;while (child < _con.size()){// 找以parent为根的较大的孩子if (child + 1 < c.size() && Compare()(_con[child], _con[child + 1]))child += 1;// 检测双亲是否满足情况if (Compare()(_con[parent], _con[child])){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}elsereturn;}}public:// 创造空的优先级队列priority_queue():_con(){}// 向堆中插入元素,复用尾插之后,进行向上调整保持堆的特点。void push(const T& data){_con.push_back(data);AdjustUP(_con.size() - 1);}// 删除堆顶元素void pop(){if (empty())return;swap(_con.front(), _con.back()); // 替换法删除_con.pop_back();AdjustDown(0);      // 删除后进行向下调整保持堆的结构}size_t size()const{return _con.size();}bool empty()const{return _con.empty();}// 堆顶元素不允许修改,因为:堆顶元素修改可以会破坏堆的特性const T& top()const{return _con.front();}};
}

注意:对于自定义类型,我们需要自己在自定义类型中提供 > 和 < 运算符重载。

5.deque

认识deque

经过前面的学习,我们已经知道了stack和queue都采用deque作为底层容器,那deque到底是个什么东西呢?下面我们来看看deque这种数据结构。

deque叫做双端队列,是一种双开口的,具有 "连续" 空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1)。

  • deque与vector比较,头插效率高,不需要搬移元素。
  • deque与list比较,空间利用率比较高。

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

deque底层的数据结构

底层结构如下图所示:每一段连续的小空间由一个指针数组map管理,当存储空间不够时,只需要开辟一段空间之后,将其添加到map中即可,当map不够时,需要扩大map的空空间。

为了维护其 "整体连续" 以及随机访问的假象,这个艰巨的任务就落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂了。


deque迭代器的设计如下:迭代器有四个成员。

  • first:指向一个buffer的开始。
  • last:指向一个buffer的结束。
  • cur:指向一个buffer中的一个元素。
  • node:指向存储当前buffer指针的中控位置。


 deque借助迭代器维护其假象的连续结构如下图所示:

  • start迭代器标识第一个buffer,其中的cur成员指向这个buffer中的第一个数据。
  • finish迭代器标识最后一个buffer,其中的cur成员指向这个buffer中的最后一个数据的下一个位置。

deque和vector、list的对比

vector、list、deque的优缺点:

优点缺点
vector

1.支持下标的随机访问

1.前面和中间进行插入删除效率低
2.CPU缓存命中率高(物理空间连续的优势)2.扩容有消耗

list

1.任意位置插入删除效率高1.不支持下标的随机访问
2.空间按需申请和释放,无扩容消耗2.CPU缓存命中率低
deque头插头删,尾插尾删效率高中间插入删除效率低
下标随机访问效率不错,但和vector相比还有一定差距。

deque的缺陷:

  • 与vector比较,deque的优势是:前面和中间位置插入、删除元素时,不需要搬移元素,效率特别高;而且在扩容时,也不需要搬移大量的元素,因此,其效率是比vector高的。
  • 与list比较,其底层是连续空间,支持随机访问,并且空间利用率比较高,因为不需要存储额外字段。

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

  • stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性结构,都可以作为stack的底层容器,比如vector和list都可以。
  • queue是先进先出的特殊线性数据结构,只要具有push_back()和pop_front()操作的线性结构,都可以作为queue的底层容器,比如list。

那为什么选择deque作为stack和queue的底层容器呢?

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

选择deque作为stack和queue的底层容器,其结合了deque的优点,而完美的避开了deque的缺点。

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

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

相关文章

Linux的cuDNN(cudnn)安装教程(CUDA(cuda\cuda toolkit))

CUDA(cuda\cuda toolkit&#xff09;安装教程 https://blog.csdn.net/huiyayaya/article/details/143863835?spm1001.2014.3001.5502官网下载cudnn https://developer.nvidia.com/rdp/cudnn-archive这个下载到自己的电脑 下载到本地就好 复制到服务器 切换到cudnn文件所在目…

Kafka中ACKS LSO LEO LW HW AR ISR OSR解析

名称解释 ACKS&#xff08;Acknowledgments&#xff09;确认、回执 LW&#xff08;Low watermark&#xff09;低水位、LSO&#xff08;Log start offset&#xff09;起始偏移量 HW&#xff08;High watermark&#xff09;高水位 LEO&#xff08;Log end offset&#xff09;…

C++设计模式行为模式———迭代器模式

文章目录 一、引言二、迭代器模式三、总结 一、引言 迭代器模式是一种行为设计模式&#xff0c; 让你能在不暴露集合底层表现形式 &#xff08;列表、 栈和树等&#xff09; 的情况下遍历集合中所有的元素。C标准库中内置了很多容器并提供了合适的迭代器&#xff0c;尽管我们不…

智能体Agent调研

单个智能体建模与优化现状 人类长期以来追求类似于或超越人类水平的人工智能 (AI)&#xff0c;而 基于AI的代理&#xff08;Agent&#xff09;被认为是一个有前途的研究方向。传统计算机领域的Agent有多种&#xff0c;如自动化脚本、网络爬虫、推荐系统、软件机器人能够独立自…

SAP PI/PO Proxy2JDBC SQL_QUERY动态接口示例

目录 背景&#xff1a; 完整demo步骤&#xff1a; IR: ID: SPROXY: 测试代码&#xff1a; 注意点&#xff1a; 背景&#xff1a; 中途临时帮客户项目做其他功能&#xff0c;项目上有部分开发项需要通过PO去第三方数据库取数&#xff0c;项目上的开发对PO不太熟&#xf…

【汇编语言】数据处理的两个基本问题(三) —— 汇编语言的艺术:从div,dd,dup到结构化数据的访问

文章目录 前言1. div指令1.1 使用div时的注意事项1.2 使用格式1.3 多种内存单元表示方法进行举例1.4 问题一1.5 问题一的分析与求解1.5.1 分析1.5.2 程序实现 1.6 问题二1.7 问题二的分析与求解1.7.1 分析1.7.2 程序实现 2. 伪指令 dd2.1 什么是dd&#xff1f;2.2 问题三2.3 问…

模型的评估指标——IoU、混淆矩阵、Precision、Recall、P-R曲线、F1-score、mAP、AP、AUC-ROC

文章目录 预测框的预测指标——IoU&#xff08;交并比&#xff09;分类预测指标混淆矩阵&#xff08;Confusion Matrix&#xff0c;TP、FP、FN、TN)Precision&#xff08;精度&#xff09;Recall&#xff08;召回率&#xff09;P-R曲线F1-scoreTPR、TNR、FPR、FNRROC曲线下面积…

轻量云服务器:入门级云计算的最佳选择

在云计算领域&#xff0c;轻量云服务器逐渐成为小型企业、个人开发者和初创公司关注的热点。它凭借高性价比、简单易用和稳定性能&#xff0c;正在改变传统的服务器部署方式。本文将详细解析轻量云服务器的定义、优势、适用场景及如何选择最佳方案&#xff0c;帮助您全面了解这…

【初阶数据结构篇】归并排序、计数排序

文章目录 须知 &#x1f4ac; 欢迎讨论&#xff1a;如果你在学习过程中有任何问题或想法&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流学习。你的支持是我继续创作的动力&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;觉得这篇文章对你有帮助吗&#xff1…

移远通信5G RedCap模组RG255C-CN通过中国电信5G Inside终端生态认证

近日&#xff0c;移远通信5G RedCap模组RG255C-CN荣获中国电信颁发的5G Inside终端生态认证证书。这表明&#xff0c;该产品在5G基本性能、网络兼容性、安全特性等方面已经过严格评测且表现优异&#xff0c;将进一步加速推动5G行业终端规模化应用。 中国电信5G Inside终端生态认…

Towards Reasoning in Large Language Models: A Survey

文章目录 题目摘要引言什么是推理?走向大型语言模型中的推理测量大型语言模型中的推理发现与启示反思、讨论和未来方向 为什么要推理?结论题目 大型语言模型中的推理:一项调查 论文地址:https://arxiv.org/abs/2212.10403 项目地址: https://github.com/jeffhj/LM-reason…

Android Studio | 修改镜像地址为阿里云镜像地址,启动App

在项目文件的目录下的 settings.gradle.kts 中修改配置&#xff0c;配置中包含插件和依赖项 pluginManagement {repositories {maven { urluri ("https://www.jitpack.io")}maven { urluri ("https://maven.aliyun.com/repository/releases")}maven { urlu…

Python | Leetcode Python题解之第564题数组嵌套

题目&#xff1a; 题解&#xff1a; class Solution:def arrayNesting(self, nums: List[int]) -> int:ans, n 0, len(nums)for i in range(n):cnt 0while nums[i] < n:num nums[i]nums[i] ni numcnt 1ans max(ans, cnt)return ans

2024-11-17 -MATLAB三维绘图简单实例

1. x -1:0.05:1; y x; [X, Y] meshgrid(x, y); f (X, Y) (sin(pi * X) .* sin(pi * Y)) .^ 2.*sin(2.*X2.*Y); mesh(X, Y, f(X, Y)); % 调用函数f并传递X和Y xlabel(X-axis); ylabel(Y-axis); zlabel(Z-axis); title(Surface Plot of (sin(pi * X) .* sin(pi * Y)) .^ 2.*…

网络层9——虚拟专用网VPN和网络地址转换NAT

目录 一、为什么有虚拟专用网&#xff1f; 二、如何理解“虚拟专用网”&#xff1f; 三、IP隧道技术实现虚拟专用网 四、网络地址变换 一、为什么有虚拟专用网&#xff1f; 第一&#xff0c;IPv4只有32位&#xff0c;最多有40亿个全球唯一的IP地址数量不够&#xff0c;无法…

小程序如何完成订阅

小程序如何完成订阅 参考相关文档实践问题处理授权弹窗不再触发引导用户重新授权 参考相关文档 微信小程序实现订阅消息推送的实现步骤 发送订阅消息 小程序订阅消息&#xff08;用户通过弹窗订阅&#xff09;开发指南 实践 我们需要先选这一个模板&#xff0c;具体流程参考…

Oracle OCP认证考试考点详解082系列19

题记&#xff1a; 本系列主要讲解Oracle OCP认证考试考点&#xff08;题目&#xff09;&#xff0c;适用于19C/21C,跟着学OCP考试必过。 91. 第91题&#xff1a; 题目 解析及答案&#xff1a; 关于 Oracle 数据库中的索引及其管理&#xff0c;以下哪三个陈述是正确的&#x…

HuggingFace:基于YOLOv8的人脸检测模型

个人操作经验总结 1、YOLO的环境配置 github 不论base环境版本如何&#xff0c;建议在conda的虚拟环境中安装 1.1、创建虚拟环境 conda create -n yolov8-face python3.9conda create &#xff1a;创建conda虚拟环境&#xff0c; -n &#xff1a;给虚拟环境命名的…

2024-11-15 Element-ui的tab切换中table自适应宽度无法立即100%的问题

前言 今天在写一个统计图表的时候&#xff0c;将所有的table表格和echarts图表放到一个页面中&#xff0c;这样会在纵向上出现滚动条&#xff0c;上下滑动对用户体验不好&#xff0c;于是改成tab切换的形式 遇到的问题 正如标题所述&#xff0c;elementui在tab中使用table时&…

【汇编】c++游戏开发

由一起学编程创作的‘C/C项目实战&#xff1a;2D射击游戏开发&#xff08;简易版&#xff09;&#xff0c; 440 行源码分享来啦~’&#xff1a; C/C项目实战&#xff1a;2D射击游戏开发&#xff08;简易版&#xff09;&#xff0c; 440 行源码分享来啦~_射击c-CSDN博客文章浏览…