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

news2024/12/22 15:23:06

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

目录

  ✨说在前面

🍋知识点一: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适配转换出stack
	template<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();
	//拷贝到vector
	vector<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.coloradmin.cn/o/2135061.html

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

相关文章

电脑之间如何快速传大文件?

这里&#xff0c;为大家介绍一款免费的远程桌面软件——远程看看&#xff0c;该软件不仅支持远程控制&#xff0c;还提供了文件传输功能&#xff0c; 用户可以传输单个文件大小不超过2TB&#xff0c;且传输速度可达每秒10MB。若您不知道电脑之间如何快速传大文件&#xff0c;远…

城市交通标线检测系统源码分享

城市交通标线检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer…

redis基本数据类型和常见命令

引言 Redis是典型的key-value&#xff08;键值型&#xff09;数据库&#xff0c;key一般是字符串&#xff0c;而value包含很多不同的数据类型&#xff1a; Redis为了方便我们学习&#xff0c;将操作不同数据类型的命令也做了分组&#xff0c;在官网&#xff08; Commands | Do…

Kafka 基于SASL/SCRAM动态认证部署,kafka加账号密码登录部署

文章目录 前言下载 kafka安装启动zookeeper添加账号密码 启动kafka修改kafka配置文件增加jaas授权文件修改启动文件&#xff0c;启动kafka检查是否部署成功 offset explore 连接 前言 其实挺简单的几个配置文件&#xff0c;问大模型一直没说到点上&#xff0c;绕晕了。SASL/SC…

Vue3.0组合式API:setup()函数

1、什么是组合式API Vue 3.0 中新增了组合式 API 的功能&#xff0c;它是一组附加的、基于函数的 API&#xff0c;可以更加灵活地组织组件代码。通过组合式 API 可以使用函数而不是声明选项的方式来编写 Vue 组件。因此&#xff0c;使用组合式 API 可以将组件代码编写为多个函…

浅谈EXT2文件系统----超级块

超级块概述 在 EXT2 文件系统中&#xff0c;超级块&#xff08;superblock&#xff09;是一个非常重要的数据结构&#xff0c;包含了文件系统的全局信息。每个文件系统都有一个超级块&#xff0c;位于文件系统的第一个块之后&#xff0c;通常在块组的起始处。 超级块包含以下关…

Autosar模式管理实战系列-COMM模块状态机及重要函数讲解

1.Channel状态管理 上一节提到ComM进行通信模式管理提供有两大状态机,另外一个就是Channel状态管理。这里的Channel指的是一个通信总线,目前项目主要是采用CAN总线。ComM 模块对每一个Channel都定义了一个状态机,用于描述通道的各种状态、状态转移关系和状态转移动作。该状…

NFT Insider #147:Sandbox 人物化身九月奖励上线;Catizen 付费用户突破百万

市场数据 加密艺术及收藏品新闻 Doodles 动画特别剧《Dullsville and The Doodleverse》在多伦多国际电影节首映 Doodles 最近在多伦多国际电影节&#xff08;TIFF&#xff09;首映了其动画特别剧《Dullsville and The Doodleverse》&#xff0c;这是该品牌的一个重要里程碑。…

享元模式详解:解锁高效资源管理的终极武器

&#x1f3af; 设计模式专栏&#xff0c;持续更新中 欢迎订阅&#xff1a;JAVA实现设计模式 &#x1f6e0;️ 希望小伙伴们一键三连&#xff0c;有问题私信都会回复&#xff0c;或者在评论区直接发言 享元模式 享元模式&#xff08;Flyweight Pattern&#xff09; 是一种结构型…

yaml配置文件(SpringBoot学习4)

SpringBoot使用一个全局的配置文件&#xff0c;配置文件名是固定的 application.properties 语法结构&#xff1a;keyvalue application.yaml 语法结构&#xff1a;key:空格value #是注释 #yaml 普通的key - value 例&#xff1a;name&#xff1a;…

力扣题解2390

大家好&#xff0c;欢迎来到无限大的频道。 今日继续给大家带来力扣题解。 题目描述​&#xff08;中等&#xff09;&#xff1a; 从字符串中移除星号 给你一个包含若干星号 * 的字符串 s 。 在一步操作中&#xff0c;你可以&#xff1a; 选中 s 中的一个星号。 移除星号…

23. Revit API: 几何对象(四)- BrepBuilder

一、前言 上一篇写了Solid的创建、展示、变换、布尔操作&#xff0c;这一篇写另一种Solid的创建方法。 需要再次强调的是&#xff0c;BrepBuilder不适合用来创建Solid。 二、边界表示 【Wiki】&#xff1a;边界表示&#xff08;Boundary representation&#xff0c;简称B-Re…

顶刊算法 | 鹈鹕算法POA-Transformer-LSTM多变量回归预测

顶刊算法 | 鹈鹕算法POA-Transformer-LSTM多变量回归预测 目录 顶刊算法 | 鹈鹕算法POA-Transformer-LSTM多变量回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现顶刊算法 | 鹈鹕算法POA-Transformer-LSTM多变量回归预测&#xff08;程序可以作为JCR…

外贸展会全流程、如何高效转化,获取更多名片

马上又到展会季了&#xff0c;九十月份是各行各业的展会旺季&#xff0c;很多伙伴们比较关注的是&#xff0c;展会上如何拿到更多名片&#xff0c;如何高效转化客户 一、展会的好处 1、扩展客户资源&#xff1a;接触潜在客户、扩大市场覆盖 2、提升品牌知名度&#xff1a;展示…

学生学籍管理系统可行性分析报告

引言 一、编写目的 随着科学技术的不断提高,计算机科学日渐成熟,其强大的功能已为人们深刻认识,它已进入人类社会的各个领域并发挥着越来越重要的作用。而学籍管理系统软件&#xff0c;可广泛应用于全日制大、中小学及其他各类学校&#xff0c;系统涵盖了小学、初中、高中学籍…

122.rk3399 uboot(2017.09) 源码分析2-initf_dm(2024-09-09)

这里接着上一篇来吧&#xff1a; https://blog.csdn.net/zhaozhi0810/article/details/141927053 本文主要是dm_init_and_scan函数的分析&#xff0c;这个内容比较复杂&#xff0c;我也是第一次阅读&#xff0c;错误之处在所难免&#xff0c;请多指教。 uboot的dm框架需要了解…

去除单细胞数据中环境游离的RNA污染-decontX工具学习

DecontX 是一种用于单细胞 RNA 测序数据的去除环境污染物&#xff08;decontamination&#xff09;的工具&#xff0c;主要用于减少由细胞外RNA造成的污染效应。 开发者在20年的文章中已经把这个工具适用的情况说的非常清楚了&#xff1a;简单来说就是基于微流控的单细胞技术会…

解决PowerAutomate日期处理报错

问题来源 今天尝试做一个简单到到不能再简单的PowerAutomate流&#xff0c;就是读取一个Sharepoint上的Excle表格里的每一行&#xff0c;然后更新到一个list。然鹅确收到了一个意想不到的报错&#xff0c; 报错信息如下&#xff1a; The runtime value "" to be c…

Android 签名、空包签名 、jarsigner、apksigner

jarsigner是JDK提供的针对jar包签名的通用工具, 位于JDK/bin/jarsigner.exe apksigner是Google官方提供的针对Android apk签名及验证的专用工具, 位于Android SDK/build-tools/SDK版本/apksigner.bat jarsigner&#xff1a; jarsigner签名空包执行的命令&#xff1a; jar…

关于Spring Cloud Gateway中 Filters的理解

Spring Cloud Gateway中 Filters的理解 Filters Filters拦截器的作用是,对请求进行处理 可以进行流量染色 ⭐增加请求头 例子 spring:cloud:gateway:routes:- id: add_request_header_routeuri: http://localhost:8123predicates:- Path=/api/**filters:- AddRequestHea…