stack和queue及优先级队列和适配器(包括deque)的介绍

stack
stack的介绍
  1. stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。
  2. stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。
  3. stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下操作:
empty:判空操作
back:获取尾部元素操作
push_back:尾部插入元素操作
pop_back:尾部删除元素操作
  1. 标准容器vector、deque、list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器,默认情况下使用deque。

在这里插入图片描述
其实在数据结构中我们学习了栈和队列后我们在C++部分中学习起来stack和queue就很容易上手了!

stack的使用

在这里插入图片描述
stack:
stack构造一个空栈,用empty可以判断栈是否为空

int main()
{stack<int> st1;cout << st1.empty() << endl;
}

empty函数其实是一个布尔型的函数,返回1或者0
在这里插入图片描述
push和pop:
经过了数据结构中的学习我相信这些都是小菜一碟的
push就是从栈顶压入栈帧,pop就是从栈顶出栈,依旧是遵循着先进后厨的原则,我们打开监视窗口来观察即可

int main()
{stack<int> st1;st1.push(1);st1.push(2);st1.push(3);st1.push(4);st1.push(5);
}

在这里插入图片描述
在push五个元素后我们用pop删除两个栈顶元素,再次使用监视窗口就会发现栈内只有1,2,3前三个入栈的元素了

int main()
{stack<int> st1;st1.push(1);st1.push(2);st1.push(3);st1.push(4);st1.push(5);st1.pop();st1.pop();
}

在这里插入图片描述
top和size:
top就是返回栈顶的元素,size就是栈内元素的个数

int main()
{stack<int> st1;st1.push(1);st1.push(2);st1.push(3);st1.push(4);st1.push(5);cout << "st1pop前的size:";cout << st1.size() << endl;cout << "st1pop前的top:";cout << st1.top() << endl;st1.pop();st1.pop();cout << "st1pop后的的size:";cout << st1.size() << endl;cout << "st1pop后的top:";cout << st1.top() << endl;
}

在这里插入图片描述
打印栈内所有元素我们可以这样打印:
但是我们要打印一次栈顶元素就要pop一次

int main()
{stack<int> st1;st1.push(1);st1.push(2);st1.push(3);st1.push(4);st1.push(5);cout << "st1pop前的size:";cout << st1.size() << endl;cout << "st1pop前的top:";cout << st1.top() << endl;st1.pop();st1.pop();cout << "st1pop后的的size:";cout << st1.size() << endl;cout << "st1pop后的top:";cout << st1.top() << endl;int size = st1.size();for (int i = 1; i <= size; i++){cout << st1.top() << endl;st1.pop();}
}

在这里插入图片描述

queue
queue的介绍
  1. 队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端提取元素。
  2. 队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从队尾入队列,从队头出队列。
  3. 底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作:
empty:检测队列是否为空
size:返回队列中有效元素的个数
front:返回队头元素的引用
back:返回队尾元素的引用
push_back:在队列尾部入队列
pop_front:在队列头部出队列
  1. 标准容器类deque和list满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标准容器deque

在这里插入图片描述
其实stack和queue的区别就是queue是遵循着先进先出,而stack则是先进后出

queque的使用

在这里插入图片描述
queue:
同样的empty是一个布尔型的函数判断队列是否为空

using namespace std;
int main()
{queue<int> q;cout << q.empty() << endl;q.push(1);q.push(2);q.push(3);q.push(4);q.push(5);cout << q.empty() << endl;
}

在这里插入图片描述
front和back:
queue的对头和队尾都有函数作为返回值,很方便

int main()
{queue<int> q;q.push(1);q.push(2);q.push(3);q.push(4);q.push(5);cout << q.front() << endl;cout << q.back() << endl;
}

在这里插入图片描述
push和pop:
push是从队尾入队列,pop是从队头出队列

int main()
{queue<int> q;q.push(1);q.push(2);q.push(3);q.push(4);q.push(5);cout << "执行pop前:" << endl;cout << q.front() << endl;cout << q.back() << endl;q.push(6);q.pop();cout << "执行pop后:" << endl;cout << q.front() << endl;cout << q.back() << endl;
}

通过pop和push的操作我们的队头和队尾元素就发生了改变
在这里插入图片描述
size:
我们用size作为for循环的遍历次数来遍历整个queue

using namespace std;
int main()
{queue<int> q;q.push(1);q.push(2);q.push(3);q.push(4);q.push(5);cout << "执行pop前:" << endl;cout << q.front() << endl;cout << q.back() << endl;q.push(6);q.pop();cout << "执行pop后:" << endl;cout << q.front() << endl;cout << q.back() << endl;cout << endl;int size = q.size();for (int i = 0; i < size; i++){cout << q.front() << endl;q.pop();}
}

同样的我们也需要打印一个元素pop一个元素
在这里插入图片描述

priority_queue
priority_queue的介绍
  1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
  2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。
  3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
  4. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:
empty():检测容器是否为空
size():返回容器中有效元素个数
front():返回容器中第一个元素的引用
push_back():在容器尾部插入元素
pop_back():删除容器尾部元素
  1. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector。
  2. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作。

其实优先级队列它的底层实现就类似于一个堆,支持随机访问的迭代器,同时也支持随机的插入操作

priority_queue的使用

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue
注意:默认情况下priority_queue是大堆。

在这里插入图片描述
构造一个空的优先级队列:
用类似的代码来熟悉一下就可了

int main()
{priority_queue<int> pq;cout << pq.empty() << endl;pq.push(1);pq.push(2);pq.push(3);pq.push(4);pq.push(5);cout << pq.empty() << endl;
}

在这里插入图片描述
pop和push:
pop就是删除最大(或者最小)即堆顶的元素,这里默认就是大堆,所以删除的是最大的元素

int main()
{priority_queue<int> pq;pq.push(1);pq.push(2);pq.push(3);pq.push(4);pq.push(5);cout << "堆顶元素为:";cout << pq.top() << endl;cout << "队列size为:";cout << pq.size() << endl;pq.pop();cout << "删除后堆顶元素为:";cout << pq.top() << endl;cout << "删除后队列size为:";cout << pq.size() << endl;
}

在这里插入图片描述
插入后依旧回进行堆排序:

int main()
{priority_queue<int> pq;pq.push(1);pq.push(2);pq.push(3);pq.push(7);pq.push(5);cout << "堆顶元素为:";cout << pq.top() << endl;cout << "队列size为:";cout << pq.size() << endl;pq.pop();cout << "删除后堆顶元素为:";cout << pq.top() << endl;cout << "删除后队列size为:";cout << pq.size() << endl;pq.push(4);int size = pq.size();for (int i = 0; i < size; i++){cout << pq.top() << endl;pq.pop();}
}

在上面的这段代码中我们可以看到,在删除栈顶元素7后我们插入元素4,并不是插入到5的后面,而是在堆中适合他大小的地方,说明它进行了排序
在这里插入图片描述
此外这里有几个需要注意的点:

  1. 默认情况下,priority_queue是大堆。
#include <vector>
#include <queue>
#include <functional> // greater算法的头文件
void TestPriorityQueue()
{// 默认情况下,创建的是大堆,其底层按照小于号比较vector<int> v{ 3,2,7,6,0,4,1,9,8,5 };priority_queue<int> q1;for (auto& e : v)q1.push(e);cout << q1.top() << endl;// 如果要创建小堆,将第三个模板参数换成greater比较方式priority_queue<int, vector<int>, greater<int>> q2(v.begin(), v.end());cout << q2.top() << endl;
}
  1. 如果在priority_queue中放自定义类型的数据,用户需要在自定义类型中提供> 或者< 的重载。
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}bool operator<(const Date& d)const{return (_year < d._year) ||(_year == d._year && _month < d._month) ||(_year == d._year && _month == d._month && _day < d._day);}bool operator>(const Date& d)const{return (_year > d._year) ||(_year == d._year && _month > d._month) ||(_year == d._year && _month == d._month && _day > d._day);}friend ostream& operator<<(ostream& _cout, const Date& d){_cout << d._year << "-" << d._month << "-" << d._day;return _cout;}
private:int _year;int _month;int _day;
};
void TestPriorityQueue()
{// 大堆,需要用户在自定义类型中提供<的重载priority_queue<Date> q1;q1.push(Date(2018, 10, 29));q1.push(Date(2018, 10, 28));q1.push(Date(2018, 10, 30));cout << q1.top() << endl;// 如果要创建小堆,需要用户提供>的重载priority_queue<Date, vector<Date>, greater<Date>> q2;q2.push(Date(2018, 10, 29));q2.push(Date(2018, 10, 28));q2.push(Date(2018, 10, 30));cout << q2.top() << endl;
}
容器适配器
什么是适配器

适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口。
就比如说我们自己的充电头用到欧洲的插座我们就需要用到有一个适配的插头来当作接口,这就是适配器
在这里插入图片描述

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

虽然stack和queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为容器适配器,这是因为stack和队列只是对其他容器的接口进行了包装STL中stack和queue默认使用deque,在官方的网站中可以查询到标准库中的默认适配器:
在这里插入图片描述
在这里插入图片描述
可以看到他们的默认适配器均是deque,那么deque又有什么样的魔力呢,为何底层的适配器不用vector和list呢?下面我们就来介绍deque:

deque的简单介绍

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

他的结构如图所示:
他的头尾两端都可以进行插入和删除操作
在这里插入图片描述
但是要注意:
deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组

他的底层结构如图所示:
在这里插入图片描述
双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其“整体连续”以及随机访问的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂,如下图所示:
在这里插入图片描述
那deque是如何借助其迭代器维护其假想连续的结构呢?
如图所示:
它的一个节点里卖弄有四个指针
在这里插入图片描述
但是deque的缺点也很多,所以用的少:

deque的优点和缺点

优点:
与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。
与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。
缺点:
但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器
要频繁的去检测其是否移动到某段小空间的边界,导致效率低下
,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构。

选择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都不需要遍历这是主要的原因!

好了,今天的分享到这里就结束了,感谢大家的支持!

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

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

相关文章

Android使用ScrollView导致鼠标点击事件无效

平台 测试平台: RK3288 Android8.1RK3588 Android 12 问题 首先, 这个问题的前提是, 使用的输入设备是**鼠标**, 普通的触摸屏并不会出现这个问题. 大致的流程是APP的UI布局中采用ScrollView作为根容器, 之后添加各类子控件, 在一起准备就绪后, 使用鼠标进行功能测试, 出现…

国产隔离芯片的质量控制与发展趋势

随着电子技术的飞速发展&#xff0c;国产隔离芯片在电力电子、通信设备等领域中扮演着重要角色。然而&#xff0c;随之而来的是对于其质量控制的迫切需求。本文将从结构、制造工艺、测试手段等方面对国产隔离芯片的质量控制进行分析&#xff0c;并展望其未来的发展趋势。 一、国…

element-ui link 组件源码分享

link 组件的 api 涉及的内容不是很多&#xff0c;源码部分的内容也相对较简单&#xff0c;下面从以下这三个方面来讲解&#xff1a; 一、组件结构 1.1 组件结构如下图&#xff1a; 二、组件属性 2.1 组件主要有 type、underline、disabled、href、icon 这些属性&#xff0c;…

KVM-安装-使用-迁移

一. KVM安装 1. 基础安装 # 下载源 curl -o /etc/yum.repos.d/Centos-7.repo http://mirrors.aliyun.com/repo/Centos-7.repo# 安装基础软件 yum -y install tree vim wget bash-completion bash-completion-extras lrzsz net-tools sysstat iotop iftop htop unzip nc nmap …

批量修改文件后缀名

需要将/opt/module/test/路径下的txt文件后缀修改为cpp&#xff0c;并且以年份结尾 代码如下&#xff1a; #!/bin/bashyear2020 directory"/opt/module/test/"cd "$directory" || exit 1for name in *.txt; donew_name"${name%.txt}_${year}.cpp&qu…

SpringBoot security 安全认证(一)——登录验证

本节内容&#xff1a;使用springboot自动security模块实现用户登录验证功能&#xff1b; 登录过程如下图&#xff1a; AuthenticationManager内容实现用户账号密码验证&#xff0c;还可以对用户状态&#xff08;启用/禁用&#xff09;&#xff0c;逻辑删除&#xff0c;账号是否…

LeetCode.189. 轮转数组

题目 题目链接 分析 首先能想到的就是可以用一个新数组&#xff0c;先保存原数组的后 k 个元素&#xff0c;再保存原数组的前 n−k 个元素。但题目要求不使用额外的数组空间&#xff0c;那么就需要在原数组上做操作。 我们可以先把整个数组翻转一下&#xff0c;这样后半段元…

虚幻UE5Matehuman定制自己的虚拟人,从相机拍照到UE5制作全流程

开启自己的元宇宙,照片扫描真实的人类,生成虚拟形象,保姆级教程,欢迎大家指正。 需要的软件: 制作流程: 一.拍照。 围绕自己拍照,大概20多张图就差不多了,把脑门漏出来,无需拍后脑勺。 拍照方式 例如,拍照时尽量不要在脸上体现出明显的光源方向。

07. 【Linux教程】远程登录

Linux 远程登录 前面介绍了如何安装 Linux 终端工具&#xff0c;本小节介绍本地电脑如何使用 ssh 命令远程登录、Linux 终端工具远程登录的方式&#xff0c;这两种登录方式都是基于 ssh 网络安全协议的&#xff0c;学会使用远程登录 Linux 服务器&#xff0c;会让你对 Linux 系…

Postman(接口测试工具),什么是Postman接口

目录 一.基本介绍 Postman 是什么Postman 快速入门快速入门需求说明 二.Postman 完成 Controller 层测试 需要的代码&#xff1a; Java类request.jspsuccess.jsp1. 完成请求2. 完成请求3. 完成请求4. 完成请求5. 完成请求 三.发送join 目录 一.基本介绍 Postman 是什么 …

使用 Dockerfile 定制镜像详解

使用 Dockerfile 定制镜像详解 1.DockerfileFROM 指定基础镜像RUN 执行命令构建镜像 2.COPY 复制文件3.ADD 更高级的复制文件4.CMD 容器启动命令5.ENTRYPOINT 入口点6.ENV 设置环境变量7.ARG 构建参数8.VOLUME 定义匿名卷9.EXPOSE 暴露端口10.WORKDIR 指定工作目录11.USER 指定…

通过Netbackup恢复Oracle备份实操手册

1、系统环境描述 1 2、恢复前数据备份 2 2.1 在NBU上执行一次完整的备份 2 2.2 查看ORACLE的备份集 3 2.2.1在备份客户端上查看备份集 3 2.2.2在备份服务器netbackup上查看客户端备份集 4 3、本机恢复方法 5 3.1丢失SPFILE文件恢复方法 5 3.2丢失CONTROLFILE文件恢复方…

51单片机编程应用(C语言):模块化编程

下面我们模块化几个函数&#xff1a; Delay.c //延时子函数 void Delay(unsigned int xms) {unsigned char i, j;while(xms--){i 2;j 239;do{while (--j);} while (--i);} } Delay.h #ifndef __DELAY_H__ #define __DELAY_H__void Delay(unsigned int xms);#endifNixie.h …

js 设置、获取、删除标签属性以及H5自定义属性

1. 设置标签属性 使用setAttribute()(‘属性名’, ‘属性值’)方法可以添加、修改、删除属性。   下面的demo是为input添加、修改、删除value属性&#xff1a; 1.1. HTML <input type"text" class"input"> <input type"text" class…

有没有合适写毕业论文的AI工具?

最近挺多同学在忙着写毕业论文&#xff0c;不断在“提交-打回-修改-提交”过程里循环着&#xff0c;好不容易写完了&#xff0c;还得考虑论文查重的问题&#xff01;基哥作为一名曾经的毕业生&#xff0c;当然也体验过这种痛苦了。 但是&#xff0c;大人&#xff0c;时代变了&…

无法在 word 中登录 Grammarly

目录 1. 情况描述 2. 解决方法 3. 原因分析 1. 情况描述 在浏览器中可以登录 Grammarly&#xff0c;但是在 word 中登录失败&#xff0c;大致如下图所示&#xff1a; 我自己没有截图&#xff0c;这是网上别人的图&#xff0c;但差不多都长这个样子。 2. 解决方法 我点击了…

推荐一个好用的人脑解剖结构及功能注释3D图谱——3D Brain

大脑是一个非凡的结构&#xff0c;它定义了我们是谁&#xff0c;以及我们如何体验世界。神经成像技术的最新进展使我们能够看到大脑内部&#xff0c;让我们能够看到构成大脑的组成部分并了解它们对应的功能。大脑的大体结构对大多数人来说都很熟悉。 前脑的外层构成了我们熟悉…

shell - 正则表达式和grep命令和sed命令

一.正则表达式概述 1.正则表达式定义 1.1 定义 使用字符串描述、匹配一系列符合某个规则的字符串 1.2 了解 普通字符&#xff1a; 大小写字母、数字、标点符号及一些其它符号元字符&#xff1a; 在正则表达式中具有特殊意义的专用字符 1.3 层次分类 基础正则表达式扩展正…

MySQL 备份恢复

1.1 MySQL日志管理 在数据库保存数据时&#xff0c;有时候不可避免会出现数据丢失或者被破坏&#xff0c;这样情况下&#xff0c;我们必须保证数据的安全性和完整性&#xff0c;就需要使用日志来查看或者恢复数据了。 数据库中数据丢失或被破坏可能原因&#xff1a; 误删除数…

机器学习2-简单的二分类问题

需求&#xff1a; 假设现在需要对数据进行二分类&#xff0c;小于0.5的&#xff0c;打上0的标记&#xff0c;大于0.5的&#xff0c;打上1的标记&#xff0c;怎么做&#xff1f; 分析&#xff1a; 这是一个简单的二分类问题&#xff0c;使用逻辑回归模型。 代码&#xff1a; …