C++ 初阶 :stackqueue

news2024/11/26 15:53:50

文章目录

  • 1 stack的介绍
  • 2 stack的模拟实现
  • 3 queue的介绍
  • 4 queue的模拟实现
  • 5 priority_queue(优先级队列)介绍
  • 6 priority_queue 模拟实现
  • 7 仿函数
  • 8 deque的简单介绍
    • 8.1 deque与vector list的比较
    • 8.2 为什么选择deque作为stack和queue的底层默认容器

1 stack的介绍

①stack是一种容器适配器,用于后进先出的操作中,我们把允许插入和删除的一端称为栈顶,另一端称为栈底。
② stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素。
③ stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下操作:
a empty:判空操作
b push_back:尾部插入元素操作
c pop_back:尾部删除元素操作
d back:获取尾部元素操作
④标准容器vector、deque、list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器,默认情况下使用deque。

2 stack的模拟实现

先来了解下容器适配器

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

在这里插入图片描述

简单来说就是,stack封装好后是专门用在具有后进先出操作的环境中,但底层实现这种后进先出的操作可以是vector(顺序栈),list(链栈)以及deque。(deque后面会有讲解)

#include<deque>
namespace zbt
{
	template<class T, class Container = deque<T>>
	class stack
	{
	public:
		void push(const T& x)//入栈
		{
			con.push_back(x);//假如con实例化为vector,那么这里调用的就是vector的尾插函数
		}
		void pop()//出栈
		{
			con.pop_back();
		}
		T& top()//返回栈顶元素
		{
			return con.back();
		}
		const T& top()const
		{
			return con.back();
		}
		bool empty()const//判空
		{
			return con.empty();
		}
		size_t size()const//返回元素个数
		{
			return con.size();
		}


	private:
		Container con;//con模板实例化的时候可以为vector,list,deque等


	};
}


3 queue的介绍

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

4 queue的模拟实现

namespace zbt
{
	template <class T, class Container = deque<T>>
	class Queue
	{
	public:
		void push(const T& x)//入队列
		{
			con.push_back(x);//尾插
		}
		void pop()//出队列
		{
			con.pop_front();//头删
		}
		T& front()//返回队头元素
		{
			return con.front();
		}
		const T& front()const//第一个const是函数的返回值不能修改,第二个const在该成员函数中不能对类的任何成员进行修改
		{
			return con.front();
		}
		T& back()//返回队尾元素
		{
			return con.back();
		}
		const T& back()const
		{
			
			return con.back();
		}
		bool empty()const//判空
		{
			return con.empty();
		}
		 size_t size()const//返回元素个数
		{
			return con.size();
		}

	private:
		Container con;
	};

}

5 priority_queue(优先级队列)介绍

① 优先级队列是一种容器适配器,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
② 优先级队列的结构是堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)
③ 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:
a empty():检测容器是否为空
b size():返回容器中有效元素个数
c front():返回容器中第一个元素的引用
d push_back():在容器尾部插入元素(插入元素后仍要保持堆的结构)
e pop_back():删除容器尾部元素(删除元素后仍要保持堆的结构
④ 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector
⑤需要支持随机访问迭代器,以便始终在内部保持堆结构。

6 priority_queue 模拟实现

在这里插入图片描述

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

在这里插入图片描述

namespace zbt
{
	template <class T,class Container=vector<T>,class Compare=std::less<T>>//默认仿函数参数为less<T>,排大堆
	class priority_queue
	{
	public:
		priority_queue()
		{

		}
		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(size_t child)//向上调整算法
		{
			Compare com;
			size_t  parent = (child - 1) / 2;
			while (child>0)
			{
				//if (con[child] > con[parent]) 孩子大于父亲就调整(大堆)
				if(com(con[parent],con[child]))
				{
					std::swap(con[child], con[parent]);
					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(size_t parent)//向下调整算法
		{
			Compare com;
			size_t child = parent * 2 + 1;//左孩子
			while (child < con.size())
			{
				//if (child + 1 < con.size() && con[child + 1] > con[child])选出左右孩子中较大的结点
				if(child+1<con.size()&&com(con[child],con[child+1]))
				{
					child = child + 1;
				}
				//if (con[child] > con[parent])孩子大于父亲就调整(大堆)
				if(com(con[parent],con[child]))
				{
					std::swap(con[child], con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}

		}
		void pop()//删除堆顶元素
		{
			//为尽量完整保持堆的结构,先将堆顶和最后一个元素交换位置,删除最后一个位置的元素,此时除了堆顶元素,它的左右子树还保持
			//堆的结构,只需调用向下调整算法调整堆
			std::swap(con[0], con[con.size() - 1]);
			con.pop_back();
			adjust_down(0);

		}
		const T& top()const//返回堆顶元素
		{
			return con[0];
		}
		bool empty()const//判空
		{
			return con.empty();
		}
		size_t size()const//返回元素个数
		{
			return con.size();
		}
	private:
		Container con;
	};

7 仿函数

实现大堆和小堆的代码区别就是在向下和向上调整算法的比较代码
调整大堆是 if(con[child] > con [parent])
调整小堆是 if(con[parent]>con[child])
如何在一份代码里实现两种比较,此时就需要用到仿函数

仿函数 就是模仿函数的调用方式,通过在类里面重载操作符() ,然后实例化出对象并且调用该重载函数来实现具体的功能

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;
		}
	};

使用

zbt::less<int> ls;
	cout << ls.operator()(5, 3) << endl;;
	cout << ls(5, 3) << endl;
	zbt::greater<int> gr;
	cout << gr.operator()(5, 3) << endl;
	cout << gr(5, 3) << endl;

在这里插入图片描述

8 deque的简单介绍

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

deque的底层实现并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,其底层结构如下图所示
在这里插入图片描述

8.1 deque与vector list的比较

① 与vector比较
deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不
需要搬移大量的元素
② 与list比较
deque的优势是:其底层是连续空间,空间利用率比较高,不需要存储额外字段。
③ deque的缺点
不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下。

8.2 为什么选择deque作为stack和queue的底层默认容器

1.stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进行插入删除操作。
2 在扩容时,相比vector效率会高,不需要搬移大量数据。

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

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

相关文章

Springboot校园食堂外卖点餐系统357

目 录 1 概述 1 1.1课题背景及意义 1 1.2 国内外研究现状 1 1.3 本课题主要工作 2 2 系统开发环境 3 2.1 java简介 3 2.2 Mysql数据库 3 2.3 B/S结构 4 2.4 JSP技术介绍 4 3 系统分析 5 3.1 可行性分析 5 3.1.1 技术可行性 5 3.1.2操作…

javaEE 初阶 — Socket 套接字与 UDP 数据报套接字编程

文章目录1. Socket 套接字1.1 有连接与无连接1.2 可靠与不可靠传输1.3 面向字节流与面向数据报1.4 全双工与半双工2. UDP数据报套接字编程2.1 DatagramSocket API2.2 DatagramPacket API2.3 InetSocketAddress API3. UDP 版本的客户端服务器程序3.1 服务器实现3.2 客户端实现3.…

【JUC并发编程】Java内存模型——JMM

【JUC并发编程】Java内存模型——JMM详解 文章目录【JUC并发编程】Java内存模型——JMM详解一&#xff1a;并发编程模型的两个关键问题二&#xff1a;Java内存模型的抽象结构1&#xff1a;从 CPU 缓存模型说起2&#xff1a;JMM3&#xff1a;JMM与Java内存区域划分的区别与联系4…

(考研湖科大教书匠计算机网络)第三章数据链路层-第二节:封装成帧

专栏目录首页&#xff1a;【专栏必读】考研湖科大教书匠计算机网络笔记导航 文章目录一&#xff1a;封装成帧概述二&#xff1a;封装成帧作用&#xff08;1&#xff09;帧定界A&#xff1a;概述B&#xff1a;注意&#xff08;2&#xff09;透明传输A&#xff1a;字符填充B&…

设置Linux的信任列表

前言 我们在使用普通用户的时候&#xff0c;我们可以对指令提权&#xff0c;需要用到sudo指令,但是我们在使用的时候会报错。 原因就是&#xff0c;我们linux系统不信任你&#xff0c;必须将你放到我们的信任列表中&#xff0c;我们就可以使用我们的指令提权。 下图是我们在不是…

数据库系统概论②——关系数据库基础

本篇文章主要讲解关系数据库基础中的基本概念&#xff0c;包括关系模型概述、关系的完整性约束等等内容。 同时想要了解更多数据库系统概论知识的朋友可以看下我的上一篇文章数据库系统概论①——数据库系统基本概念 文章目录1、关系数据库的基本概念1.1 关系模型概述1.2 关系数…

【26】C语言_数据存储

目录 数据类型的意义 大小端介绍 例题1&#xff1a;设计一个小程序输出存储方式&#xff1a; 例题2&#xff1a;下列程序输出什么&#xff0c;为什么 例题3&#xff1a;下列程序输出什么&#xff0c;为什么 例题4&#xff1a;下列程序输出什么&#xff0c;为什么 例题6&a…

函数知识点总结

函数知识点总结 函数知识点总结 一、平面直角坐标系中点的坐标 1. 各象限内2. 坐标轴上3. 各象限角平分线上4. 与坐标轴平行的直线上的点5. 点到坐标轴及原点的距离6. 平面上两点距离 一、平面直角坐标系中点的坐标 1. 各象限内 象限x,yx,\,yx,y 的关系第一象限x>0,y&…

powerDesigner如何将数据库中已有表逆向生成pdm文件

问题背景 系统升级&#xff0c;要在原有数据库表结构基础之上重构表系统&#xff0c;为了节省时间&#xff0c;原来能使用的表结构保留&#xff0c;制作升级变动&#xff0c;所以用到了powerDesigner的逆向生成工具。 解决方案 第一种 创建新的PDM工程 点击左上角File&…

一起Talk Android吧(第四百七十六回:缩放类视图动画)

文章目录使用方法属性介绍示例代码各位看官们大家好&#xff0c;上一回中咱们说的例子是"渐变类视图动画",这一回中咱们说的例子是" 缩放类视图动画"。闲话休提&#xff0c;言归正转&#xff0c;让我们一起Talk Android吧&#xff01;使用方法 缩放类动画…

Servlet进阶2:JSP≈Servlet、MVC=JSP+Servlet

Servlet进阶2一、JSP的运行1. 启动tomcat2. 准备JSP文件3. 将JSP文件放在Tomcat的webapps文件夹下4. 利用Tomcat运行JSP文件二、JSP和Servlet的异同三、MVC JSP Servlet1. Servlet与JSP的优缺点2. MVC的出现一、JSP的运行 1. 启动tomcat 2. 准备JSP文件 <span style&quo…

【Ⅰ绪论】1.数据结构起源

一、起源 1、早期理解 人们都把计算机理解为数值计算工具 数值计算的特点&#xff1a;有数学方程&#xff0c;可以用计算机去做传统的数值计算 比如&#xff1a;一个线性回归的模型【机器学习】 ①根据历史数据&#xff08;黑点&#xff09;&#xff0c;去拟合这条线&#x…

【算法基础】快速排序(分治思想)

一、快速排序原理 1. 算法介绍 快速排序算法通过多次比较和交换来实现排序,其排序流程如下: (1)首先设定一个分界值,通过该分界值将数组分成左右两部分。(记左端为L,最右端为R) 分界点的选取有如下四种方法:(1)q[L];(2)q[(L+R)/2];(3)q[R];(4)随机选取 (2)…

node封装一个控制台进度条插件

说在前面 控制台的进度条大家都见得不少了吧&#xff1f;大家都知道控制台的进度条是怎么实现的吗&#xff1f;最近自己在写几个node脚本工具&#xff0c;期间有需要进度展示的一个需求&#xff0c;所以就顺手写了一个可以自定义的进度条插件&#xff0c;可以直接引入并配置使用…

【自然语言处理】情感分析(三):基于 Word2Vec 的 LSTM 实现

情感分析&#xff08;三&#xff09;&#xff1a;基于 Word2Vec 的 LSTM 实现本文是 情感分析 系列的第 333 篇&#xff0c;前两篇分别是&#xff1a; 【自然语言处理】情感分析&#xff08;一&#xff09;&#xff1a;基于 NLTK 的 Naive Bayes 实现【自然语言处理】情感分析…

web字体和图标 web字体 字体图标

目录web字体和图标web字体字体图标网站图标使用方法&#xff08;font class 版本&#xff08;推荐&#xff09;&#xff09;图标离线使用方法图标使用方法&#xff08;Unicode 版本&#xff09;web字体和图标 web字体 用户电脑上没有安装相应字体&#xff0c;强制让用户下载该…

C++动态内存管理:new 和 delete

目录 一.前言 二.new和delete的基本使用 1.new/delete操作内置类型 三.定位new表达式(placement-new) 四.new操作数出现内存申请错误时的处理方式&#xff1a;抛异常 五.new和malloc的区别 一.前言 C沿用了C语言的底层内存管理机制&#xff1a; 然而在动态内存管理方面&am…

Java——三角形最小路径和

题目链接 leetcode在线oj题——三角形最小路径和 题目描述 给定一个三角形 triangle &#xff0c;找出自顶向下的最小路径和。 每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 1 的两个结点。也就是…

AcWing 1057. 股票买卖 IV(状态机DP)

AcWing 1057. 股票买卖 IV &#xff08;1&#xff09;问题 &#xff08;2&#xff09;分析 这道题我们首先得明确一点&#xff0c;我们只有一支股票&#xff0c;只是这支股票在不同天有着不同的价格&#xff0c;因此我们可以把天作为单位划分不同的状态。同时这道题中还有一个…

极限存在准则 两个重要极限——“高等数学”

各位uu们你们好呀&#xff0c;今天小雅兰要学习的内容仍然是高等数学&#xff0c;是为&#xff1a;极限存在准则 两个重要极限。那现在就让我们一起进入高等数学的世界吧 引例 夹逼准则 准则Ⅰ 数列的夹逼准则 准则Ⅰ’ 函数的夹逼准则 重要极限Ⅰ 准则Ⅱ 单调有界数列必有极…