【C++】_stack和_queue容器适配器、_deque

当别人都在关注你飞的有多高的时候,只有父母在关心你飞的累不累。💓💓💓

 

目录

  ✨说在前面

🍋知识点一:stack

•🌰1.stack介绍

•🌰2.stack的基本操作

🍋知识点二:queue

•🌰2.queue介绍

•🌰2.queue的基本操作

🍋知识点三:priority_queue

•🌰1.priority_queue介绍

•🌰2.priority_queue的基本使用

•🌰3.仿函数

🔥仿函数控制序列单调性

🔥仿函数在优先级队列中的应用

🍋知识点四:容器适配器

•🌰1.什么是适配器

•🌰2.stack和queue的底层结构

•🌰3.deque介绍

🔥deque的实现原理

🔥deque的缺陷

🔥选择deque的原因

 • ✨SumUp结语


  ✨说在前面

亲爱的读者们大家好!💖💖💖,我们又见面了,上一篇文章我给大家介绍了一下list的定义、常用接口以及模拟实现。如果大家没有掌握好相关的知识,上一篇篇文章讲解地很详细,可以再回去看看,复习一下,再进入今天的内容。

我们今天简单给大家讲解一下STL中的两大适配器——stack和queue。stack和queue分别对应C语言中的栈和队列,如果大家准备好了,那就接着往下看吧~

   👇👇👇
💘💘💘知识连线时刻(直接点击即可)

【C++】_string类字符串万字详细解析

【C++】_vector定义、_vector常用方法解析

【C++】_list常用方法解析及模拟实现

  🎉🎉🎉复习回顾🎉🎉🎉

         

 博主主页传送门:愿天垂怜的博客

 ​​​​​​

 

🍋知识点一:stack

•🌰1.stack介绍

stack是一个容器适配器,它提供了一种后进先出(LIFO, Last In First Out)的数据结构。stack只允许在容器的顶部进行元素的添加(push)和移除(pop)操作,以及访问顶部元素(top)的功能,但不提供遍历容器内部元素的功能

我们来查看一下文档中对stack的介绍:

注意:stack不属于容器,而是一种容器适配器。它看起来像是容器,但实际上它们是通过封装其他容器来工作的。

•🌰2.stack的基本操作

stack的使用接口如下:

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

它的接口非常简单,实现部分可以参考C语言的写法:C语言实现栈和队列

如果大家觉得不好理解,可以看我之前写的栈和队列的博客:【数据结构】栈和队列超详细讲解

接口文档如下:

stack::stack - C++ Reference (cplusplus.com)

stack::empty - C++ Reference (cplusplus.com)

stack::size - C++ Reference (cplusplus.com)

stack::top - C++ Reference (cplusplus.com)

stack::push - C++ Reference (cplusplus.com)

stack::emplace - C++ Reference (cplusplus.com)

stack::pop - C++ Reference (cplusplus.com)

stack::swap - C++ Reference (cplusplus.com)

 ​​​​​​

🍋知识点二:queue

•🌰2.queue介绍

queue是一种先进先出(FIFO, First In First Out)的数据结构。它允许在队尾添加元素(enqueue/push),在队首移除元素(dequeue/pop),以及查看队首元素(front)的值。与stack类似,queue也不提供遍历功能

我们来查看一下文档中对queue的介绍:

 注意:queue不属于容器,而是一种容器适配器。它看起来像是容器,但实际上它们是通过封装其他容器来工作的。

 

•🌰2.queue的基本操作

stack的使用接口如下:

函数说明接口说明
queue()构造空的队列
empty()检测队列是否为空,是返回true,否则返回false
size()返回队列中元素的个数
front()返回队头元素的引用
back()返回队尾元素的引用

push()

在队尾将元素val入队列
pop()

将队头元素出队列

它的接口非常简单,实现部分可以参考C语言的写法:C语言实现栈和队列

如果大家觉得不好理解,可以看我之前写的栈和队列的博客:【数据结构】栈和队列超详细讲解

接口文档如下:

queue::queue - C++ Reference (cplusplus.com)

queue::empty - C++ Reference (cplusplus.com)

queue::size - C++ Reference (cplusplus.com)

queue::front - C++ Reference (cplusplus.com)

queue::back - C++ Reference (cplusplus.com)

queue::push - C++ Reference (cplusplus.com)

queue::emplace - C++ Reference (cplusplus.com)

queue::pop - C++ Reference (cplusplus.com)

queue::swap - C++ Reference (cplusplus.com)

 ​​​​​​

🍋知识点三:priority_queue

•🌰1.priority_queue介绍

学习完了队列,我们再来了解一下优先级队列:

C++中的优先级队列(Priority Queue)是一种特殊的队列,它的元素被赋予优先级,元素的出队顺序基于它们的优先级,而不是它们被加入队列的顺序。默认情况下,priority_queue使用最大堆(Max Heap)来实现,所以队列中最大的元素总是位于队列的顶部,因此这个元素会首先被移除(即出队)。

 

•🌰2.priority_queue的基本使用

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

相同地,priority_queue也有如下接口:

函数声明接口说明
priority_queue()/priority_queue(first, last)
构造一个空的优先级队列
empty( )检测优先级队列是否为空,是返回true,否则返回false
top( )返回优先级队列中最大(最小元素),即堆顶元
push(x)在优先级队列中插入元素x
pop()
删除优先级队列中最大 ( 最小 ) 元素,即堆顶元

接口文档如下:

priority_queue::priority_queue - C++ Reference (cplusplus.com)

priority_queue::empty - C++ Reference (cplusplus.com)

priority_queue::size - C++ Reference (cplusplus.com)

priority_queue::top - C++ Reference (cplusplus.com)

priority_queue::push - C++ Reference (cplusplus.com)

priority_queue::emplace - C++ Reference (cplusplus.com)

priority_queue::pop - C++ Reference (cplusplus.com)

priority_queue::swap - C++ Reference (cplusplus.com)

 

•🌰3.仿函数

仿函数(Functors)是那些重载了()操作符的对象,它们的行为类似于函数。通过使用仿函数,你可以将函数的行为封装在对象内部,这样做的好处包括能够传递额外的状态信息、支持泛型编程(例如,与标准库算法一起使用时),以及更好的封装性。

🔥仿函数控制序列单调性

下面是一个仿函数示例,用于控制冒泡排序将原先序列按递增还是递减顺序排列:

//递增
template<class T>
class Less
{
public:bool operator()(const T& x, const T& y){return x < y;}
};
//递减
template<class T>
class Greater
{
public:bool operator()(const T& x, const T& y){return x > y;}
};
//冒泡排序
template<class Compare>
void BubbleSort(int* arr, int length, Compare com)
{assert(arr);int flag = 1;while (flag && length--){flag = 0;for (int i = 0; i < length; i++){//if (arr[i] > arr[i + 1])if (com(arr[i], arr[i + 1])){swap(arr[i], arr[i + 1]);flag = 1;}}}
}
//打印数组元素
void printArr(int* arr, int length)
{for (size_t i = 0; i < length; i++){cout << arr[i] << " ";}cout << endl;
}int main()
{Less<int> LessFunc;Greater<int> GreaterFunc;int arr[] = { 5,23,789,12,8,1,86,12,7 };//升序BubbleSort(arr, 9, GreaterFunc);printArr(arr, 9);//降序BubbleSort(arr, 9, LessFunc);printArr(arr, 9);return 0;

在这个例子中,Less类和Greater类是仿函数,它重载了()操作符以比较两个整数。我们创建了Less或Greater的实例并将其作为第三个参数传递给BubbleSort函数,以指定排序的准则

结果如下:

1 5 7 8 12 12 23 86 789
789 86 23 12 12 8 7 5 1

 

🔥仿函数在优先级队列中的应用

利用仿函数,我们可以控制优先级队列的底层是大堆还是小堆,进而控制优先级队列的优先级。我们默认优先级队列的底层是大堆

它的实现用到了仿函数,可以参考我这里的实现:优先级队列模拟实现

优先级队列举例:

#include <iostream>
using namespace std;
#include <queue>int main()
{priority_queue<int> pq;pq.push(3);pq.push(7);pq.push(1);pq.push(9);pq.push(10);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}return 0;
}

结果如下:

10 9 7 3 1

 ​​​​​​

🍋知识点四:容器适配器

•🌰1.什么是适配器

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

•🌰2.stackqueue的底层结构

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

我们来看看stack的模拟实现:

namespace stl_stack
{//Container适配转换出stacktemplate<class T, class Container>class stack{public:void push(const T& x){_con.push_back(x);}void pop(){_con.pop_back();}const T& top(){return _con.back();}size_t size() const{return _con.size();}bool empty(){return _con.empty();}private:Container _con;};
}

再来看看stack的模拟实现:

很显然我们发现,这里容器的缺省值给的是deque<T>,这是个什么东西呢?继续往下看。

•🌰3.deque介绍

🔥deque的实现原理

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

deque并不是真正连续的空间, 而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,其底层结果如下图所示:

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

 

🔥deque的缺陷

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

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

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

我们可以用下面的代码来测试vector和deque的[]利率:

//deque与vector[]的效率对比
void test_op1()
{srand(time(0));const int N = 1000000;deque<int> dq;vector<int> v;for (int i = 0; i < N; ++i){auto e = rand() + i;dq.push_back(e);v.push_back(e);}int begin1 = clock();sort(dq.begin(), dq.end());int end1 = clock();int begin2 = clock();sort(v.begin(), v.end());int end2 = clock();printf("deque:%d\n", end1 - begin1);printf("vector:%d\n", end2 - begin2);
}void test_op2()
{srand(time(0));const int N = 1000000;deque<int> dq1;deque<int> dq2;for (int i = 0; i < N; ++i){auto e = rand() + i;dq1.push_back(e);dq2.push_back(e);}int begin1 = clock();sort(dq1.begin(), dq1.end());int end1 = clock();int begin2 = clock();//拷贝到vectorvector<int> v(dq2.begin(), dq2.end());sort(v.begin(), v.end());dq2.assign(v.begin(), v.end());int end2 = clock();printf("deque sort:%d\n", end1 - begin1);printf("deque copy vector sort, copy back deque:%d\n", end2 - begin2);
}int main()
{test_op1();test_op2();return 0;
}

realse且x86结果如下:

deque:117
vector:66
deque sort:104
deque copy vector sort, copy back deque:59

 基本上相差两倍左右。

 

🔥选择deque的原因

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

 ​

 • ✨SumUp结语

到这里本篇文章的内容就结束了,本节介绍了C++中_stack_queue的相关知识。这里的内容虽然很熟悉了,而且也很简单。但是也希望大家能够认真学习,打好基础,迎接接下来的挑战,期待大家继续捧场~💖💖💖

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

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

相关文章

怎么利用XML发送物流快递通知短信

现如今短信平台越来越普遍了&#xff0c;而短信通知也分很多种&#xff0c;例如服务通知、订单通知、交易短信通知、会议通知等。而短信平台在物流行业通知这一块作用也很大。在家时:我们平时快递到了&#xff0c;如果电话联系不到本人&#xff0c;就会放到代收点&#xff0c;然…

力扣之1777.每家商店的产品价格

文章目录 1. 1777.每家商店的产品价格1.1 题干1.2 建表1.3 题解1.4 结果截图 1. 1777.每家商店的产品价格 1.1 题干 表&#xff1a;Products -------------------- | Column Name | Type | -------------------- | product_id | int | | store | enum | | price | int | ---…

分布式调度方案:Elastic-Job

文章目录 一、什么是分布式调度二、Elastic-Job 介绍三、Elastic-Job 实战3.1 环境搭建3.1.1 本地部署3.1.2 服务器部署3.1.3 Zookeeper 管控台界面 3.2 入门案例3.3 SpringBoot 集成 Elastic-Job3.4 任务分片&#xff08;★&#xff09;3.5 Dataflow 类型调度任务 一、什么是分…

提升LLM能力表现的四种AI代理策略

欢迎来到AI产品经理从0到1研习之旅。 “AI Agent”系列的第二篇&#xff0c;它来了&#xff01;我说过&#xff0c;AI Agent才是我最看好的&#xff08;大模型落地应用&#xff09;未来&#xff0c;嘿嘿&#xff0c;目测有很多大佬与我有相似的看法。 本文从吴恩达&#xff0…

Python | Leetcode Python题解之第401题二进制手表

题目&#xff1a; 题解&#xff1a; class Solution:def readBinaryWatch(self, turnedOn: int) -> List[str]:ans list()for i in range(1024):h, m i >> 6, i & 0x3f # 用位运算取出高 4 位和低 6 位if h < 12 and m < 60 and bin(i).count("1&…

浙大数据结构:03-树2 List Leaves

这道题我借用了一点上一题的代码思路&#xff0c;这题考察的主要是层序遍历&#xff0c;即用队列来实现&#xff0c;当然此处我依然采用数组模拟队列来实现。 机翻 1、条件准备 map的键存下标&#xff0c;后面值分别存左右子树的下标&#xff0c;没有子树就存-1. head数组只…

产业园服务体系建设,是否已全面覆盖企业成长的每一个阶段?

在当今竞争激烈的商业环境中&#xff0c;产业园作为企业发展的重要载体&#xff0c;其服务体系的完善程度至关重要。那么&#xff0c;产业园服务体系建设&#xff0c;是否已全面覆盖企业成长的每一个阶段呢&#xff1f; 从企业的初创期来看&#xff0c;产业园可以提供办公场地的…

数据结构与算法学习day21-二叉树

一、从中序与后序遍历序列构造二叉树 1.题目 106. 从中序与后序遍历序列构造二叉树 - 力扣&#xff08;LeetCode&#xff09; 2.思路 以后序&#xff08;左右中&#xff09;数组的最后一个元素为切割点&#xff08;根节点&#xff09;&#xff0c;切割的节点切中序&#xf…

初学者指南:MyBatis 入门教程

主要介绍了Mybatis的基本使用、JDBC、数据库连接池、lombok注解&#xff01; 文章目录 前言 什么是Mybatis? 快速入门 使用Mybatis查询所有的用户信息 配置SQL提示 JDBC介绍 Mybatis 数据库连接池 lombok 总结 前言 主要介绍了Mybatis的基本使用、JDBC、数据库连接…

Tableau学习日记

Day1&#xff1a;Tableau简介、条形图与直方图 1.Tableau绘制条形图 1.1 条形图1&#xff1a;各地区酒店数量 1.2 条形图2&#xff1a;各地区酒店均价 1.3 堆积图&#xff1a;价格等级堆积图 2.Tableau绘制直方图 2.1创建评分直方图 Day2&#xff1a;Tableau简介、条形图与直…

Flutter类

Dart中的对象都继承自 Object 类&#xff0c;单继承&#xff08;extend关键字&#xff09;。Dart与Java、kotlin不同的是其无public、private、protected修饰符&#xff0c;默认public &#xff0c;通过在属性名、方法名前加 _下划线 来定义是否私有。 实现一个简单的类 class…

pod被驱逐追溯根因

背景 收到pod报警&#xff0c;看到其状态是error&#xff0c;新的pod又在另外一台机器上起来了。 原因分析 describe pod看到pod被驱逐 Status: Failed Reason: Evicted Message: The node was low on resource: ephemeral-storage. Containe…

数据结构之红黑树的 “奥秘“

目录&#xff1a; 一.红黑树概念 二. 红黑树的性质 三.红黑树的实现 四.红黑树验证 五.AVL树和红黑树的比较 一.红黑树概念 1.红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或Black。 通过对任何 一条从根…

YOLO配合 PYQT做自定义虚拟电子围-自定义绘制多边形虚拟电子围栏

电子围栏标注以及显示 1、目标检测&#xff1a; YOLO可以识别检测物体&#xff0c;这是众所周知的。使用YOLO来做目标检测&#xff0c;并获取坐标信息。 2、电子围栏 比如在监控中&#xff0c;指定一块区域&#xff0c;如果有目标进入&#xff0c;则发出警报&#xff0c;并提…

《基于深度半监督学习的目标检测综述》泛读

基于深度半监督学习的目标检测方法分为 1、生成式方法 2、一致性正则化方法 3、基于图的方法 4、伪标记方法和混合方法 然后基于常用数据集 对典型方法进行了性能对比&#xff0c;最后分析了其挑战和发展趋势&#xff0c;旨在为相关研究提供参考 收获就是&#xff1a; 1…

JMeter 入门之远程启动,服务模式,多机联测,负载均衡测试

本文主要介绍 JMeter 远程启动及使用多节点完成大并发测试&#xff08;负载均衡测试&#xff09;&#xff0c;主打一个压力山大&#xff0c;借用 黑神话&#xff1a;悟空 的技能来描述就是远程开大&#xff0c;释放猴子猴孙技能。 搜了一些 jmeter 的案例或教程&#xff0c;讲的…

MyBatis-Plus分页查询、分组查询

目录 准备工作1. 实体类2. Mapper类3. 分页插件4. 数据 分页查询1. 使用条件构造器2. 使用自定义sql 分组查询1. 分组结果类2. 自定义sql3. 测试类 准备工作 1. 实体类 对地址字段address使用字段类型转换器&#xff0c;将List转为字符串数组保存在数据库中 package com.exa…

Docker 部署 Redis (图文并茂超详细)

部署 Redis ( Docker ) [Step 1] : 拉取 Redis 镜像, 推荐使用 7 的 Redis 版本 docker pull redis:7.0.12[Step 2] : 创建 Redis 相关目录 ➡️ 启动 Redis 容器 ➡️ 拷贝文件 ➡️ 授权文件夹 ➡️ 删除容器 # 创建 Redis 相关目录 mkdir -p /data/redis/{conf,data,log…

探索Python中文拼音转换的奥秘:xpinyin库

文章目录 **探索Python中文拼音转换的奥秘&#xff1a;xpinyin库**背景介绍库简介安装指南函数使用示例应用场景常见问题与解决方案总结 探索Python中文拼音转换的奥秘&#xff1a;xpinyin库 背景介绍 在处理中文文本数据时&#xff0c;我们经常需要将中文字符转换为拼音。无…

【LabVIEW学习篇 - 23】:简单状态机

文章目录 简单状态机状态机的创建和了解状态机实现红绿灯 简单状态机 一个优秀的应用程序离不开好的程序框架&#xff0c;不仅要很好满足用户的功能需求&#xff0c;还要考虑到系统的稳定性、实时性、可扩展性、可维护性&#xff0c;执行效率等方面。借用一些成熟的设计框架&a…