STL-queue和priority_queue的模拟实现

回顾

对于STL,我们已经知道了vector和list,而它们是STL中被称为六大组件之一的容器,我们还学习了模拟实现stack,而stack在STL中被称为六大组件之一的适配器,今天,我们来学习queue的模拟实现和priority_queue的模拟实现,并且,它们也是适配器。

对于适配器而言,我们参照stack的模拟实现的经验,适配器的接口不过是调用容器的接口实现的,所以要实现一个适配器,只要理解了它的数据结构,实现是很简单的。

queue(队列)

什么是队列?在日常生活中,我们如果要去超市买东西,收银的时候如果人比较多,我们就需要排队,从队尾排,直到我们前面的人都收完钱,这就是一个队列,先排的人先走,后排的人后走。队列遵循的是先进先出的原则。

再回过头来看看stack,stack是遵循先进后出的原则,它与我们今天要学的queue相反。

所以,queue的整体还是很好理解的,那么我们如果要实现它,应该采用怎样的数据结构呢? 

stack我们可以使用vector作为容器完成,那么queue呢?是否也能使用vector作为容器完成?

这里就需要考虑到我们的时间复杂度来进行衡量了,因为queue的先进后出的原则,如果我们要进行删除,就只能从队头的数据开始删除,但是如果从队头开始删除数据,vector作为连续的数组空间,那么势必每次删除都需要挪动数据,每次删除数据都将会是一个O(N)的时间复杂度,所以并不高效,也就不能使用vector作为queue的容器。那么大家是否已经想到了比较合适容器比较高效的实现我们的queue呢? -----list和deque就很好,它们对于头删的成本并不高,时间也很高效!

而STL是采用deque作为queue的默认容器。

模拟实现queue

template<typename T , class Container = deque<T>>
class queue
{
public:
	void push(const T& data)
	{
		_data.push_back(data);
	}

	size_t size() const
	{
		return _data.size();
	}

	void pop()
	{
		_data.pop_front();
	}

	bool empty() const
	{
		return _data.empty();
	}
		
	T& front()
	{
		return _data.front();
	}
	const T& front() const 
	{
		return _data.front();
	}

	T& back()
	{
		return _data.back();
	}

	const T& back() const
	{
		return _data.back();
	}
private:
	Container _data;
	};

模版的实现很简单,实现它的接口也只是调用容器的接口,这里也有一个原因说明了了为什么不能使用vector来作为容器,因为vector根本没有pop_front这个接口,也就不可能用来作为我们的容器。

priority_queue(优先级队列)

priority_queue与queue的区别还是挺大的,priority_queue的数据结构其实本质就是堆,对于堆的数据结构而言,它的容器可以为vector,而queue是不可以的。

 对于堆的内容,如果不太理解,可以去查找堆先关的内容,堆是一种基于二叉树的数据结构。

queue的增删是这样的(以大堆举例)

对于删除数据,它会先使堆顶元素(最大元素)与最后的元素交换,然后删除最大元素(尾删),然后再进行堆排序,堆顶元素(本来是尾部的元素)进行向下调整,这一系列操作完成删除操作

对于插入数据,它先在尾部插入数据,然后再进行堆排序

因为它要采用堆的储存方式,所以,我们就必须要实现堆排的两个函数---向上调整和向下调整

向上调整和向下调整都是堆 用于保持堆结构的排序算法

向上调整

用于插入数据时,进行堆排序,保持堆的特性

void adjust_up(size_t pos)    //向上调整
{
	Compare com;
	size_t child = pos;
	size_t parent = (child - 1) / 2;
	while (child > 0)
	{
		//if (_data[parent] < _data[child])
		if (com(_data[parent],_data[child])) //仿函数  等会讲
		{
			std::swap(_data[parent], _data[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

向下调整

void adjust_down(size_t pos)    //向下调整
{
	Compare com;
	size_t parent = pos;
	size_t child = parent * 2 + 1;
	while (child < _data.size())
	{
		if (child + 1 < size() && com(_data[child], _data[child + 1]))
		{
			child = child + 1;
		}
		if (com(_data[parent], _data[child]))  //仿函数
		{
			std::swap(_data[parent], _data[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

这里判断是否向上(下)调整的if判断语句中,用了仿函数的知识,这里暂时不讲,后面再讲,只需要知道这是用来判断是否需要调整。

push

void push(const T& data)
{
	_data.push_back(data);
	adjust_up(size() - 1);
}

当插入数据之后,对新插入的元素进行向上调整,使得保持堆结构。

pop

void pop()     //头部删除
{
	assert(!empty());
	std::swap(_data.front(), _data.back());   
	_data.pop_back();
	adjust_down(0);
}

为什么是采用先交换再尾删的方法呢?因为如果要进行堆排序,使用向上调整的时间复杂度为O(N*logN),而如果使用向下调整它的复杂度为O(N)。

其他函数

bool empty() const
{
	return _data.empty();
}


size_t size() const
{
	return _data.size();
}


const T& top() const 
{
	assert(!empty());
	return _data.front();
}

仿函数

template<typename T , class Container = vector<T>, class Compare = less<T> >
class priority_queue
{
private:
	Container _data;
};

template<typename T>
struct greater
{
public:
	bool operator()(const T& t1, const T& t2) const
	{
		return t1 > t2;
	}
};

在实现priority_queue中,它的模版函数是这样的

template<typename T , class Container = vector<T>, class Compare = less<T> >

前两个不难理解,作为适配器,需要传入容器,那么第三个是什么呢?

在实现刚刚的priority_queue中,既然是堆的方式实现,那么有时候我们就可能需要大堆或者是小堆,这时候,就可以使用仿函数来实现,可为什么叫做仿函数?

仿函数也是一个类,但是你看我们使用它的时候,是不是就像一个函数的调用?

void adjust_up(size_t pos)    //向上调整
{
	Compare com;
	size_t child = pos;
	size_t parent = (child - 1) / 2;
	while (child > 0)
	{
		//if (_data[parent] < _data[child])
		if (com(_data[parent],_data[child])) //仿函数
		{
			std::swap(_data[parent], _data[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

而它在类中是如何实现的?-> 是通过重载()达到这种效果。

template<typename T>
struct greater
{
public:
	bool operator()(const T& t1, const T& t2) const
	{
		return t1 > t2;
	}
};

这就是仿函数类 greater

如果你想实现 仿函数类 less,就是这样写

template<typename T>
struct less
{
public:
	bool operator()(const T& t1, const T& t2) const
	{
		return t1 < t2;
	}
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

java 课程信息管理系统Myeclipse开发mysql数据库struts2结构java编程计算机网页项目

一、源码特点 java 课程信息管理系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql&#xff0c;使…

javascript基础五:深拷贝浅拷贝的区别?如何实现一个深拷贝?

一、数据类型存储 JavaScript中存在两大数据类型&#xff1a; 基本类型引用类型 基本类型数据保存在在栈内存中 引用类型数据保存在堆内存中&#xff0c;引用数据类型的变量是一个指向堆内存中实际对象的引用&#xff0c;存在栈中 二、浅拷贝 浅拷贝&#xff0c;指的是创建新…

( 数组) 209. 长度最小的子数组——【Leetcode每日一题】

❓209. 长度最小的子数组 难度&#xff1a;中等 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl1, ..., numsr-1, numsr] &#xff0c;并返回其长度。如果不存在符合条件的子数组&#xff0c;…

在线Excel绝配:SpreadJS 16.1.1+GcExcel 6.1.1 Crack

前端&#xff1a;SpreadJS 16.1.1 后端&#xff1a; GcExcel 6.1.1 全能 SpreadJS 16.1.1此版本的产品中包含以下功能和增强功能。 添加了各种输入掩码样式选项。 添加了在保护工作表时设置密码以及在取消保护时验证密码的支持。 增强了组合图以将其显示为仪表图。 添加了…

Tugraph的设计和源码初步解析

1. Tugraph Tugraph是一款开源的性能优秀的图数据库&#xff0c;该图数据库使用多版本的BTree作为数据的存储引擎&#xff0c;同时设置了点边数据在这个存储引擎上的布局&#xff08;主要考虑数据的局部性&#xff09;&#xff0c;从而达到高性能查询的目的。本文主要从Tugrap…

研发工程师玩转Kubernetes——自动扩缩容

在《研发工程师玩转Kubernetes——使用Deployment进行多副本维护》一文中&#xff0c;我们通过Deployment实现了多副本维护——即维持在一个确定数量的副本个数。而在现实场景中&#xff0c;我们往往需要根据服务的压力&#xff0c;采用水平&#xff08;横向&#xff09;扩容的…

Springboot +spring security,前后端分离时的security处理方案(二)

一.简介 在前后端分离这样的开发模式下&#xff0c;前后端的交互都是通过 JSON 来进行数据传递的&#xff0c;无论登录成功还是失败&#xff0c;都不会有服务端跳转或者客户端跳转之类的操作。 也就是说无论登录成功还是失败&#xff0c;服务端都会返回一段登录成功或失败的 …

使用【Python+Appium】实现自动化测试

一、环境准备 1.脚本语言&#xff1a;Python3.x IDE&#xff1a;安装Pycharm 2.安装Java JDK 、Android SDK 3.adb环境&#xff0c;path添加E:\Software\Android_SDK\platform-tools 4.安装Appium for windows&#xff0c;官网地址 Redirecting 点击下载按钮会到GitHub的…

解决dpdk reserve的内存返回的虚拟地址和iova地址一样的问题

1. 背景: 在ubuntu20.04上用dpdk API: rte_memzone_reserve_aligned("L1L2_PCIE_MEMORY", 1.5*1024*1024*1024, rte_socket_id(), RTE_MEMZONE_1GB|RTE_MEMZONE_IOVA_CONTIG, RTE_CACHE_LINE_SIZE); 分配1.5…

如何在 GNU Linux 上通过 Nvm 安装 Node 和 Npm?

Node.js 是一个流行的 JavaScript 运行时环境&#xff0c;用于开发服务器端和网络应用程序。它带有一个强大的软件包管理器 npm&#xff0c;可以方便地安装和管理 JavaScript 包和依赖项。在 GNU/Linux 系统上&#xff0c;使用 Nvm&#xff08;Node Version Manager&#xff09…

Framework开发环境搭建

Framework开发环境搭建 开启Android Framework之旅&#xff0c;一步步记录自己学习过程。 硬件配置 RAM&#xff1a;最低16GB&#xff0c;建议32GB&#xff0c;有条件64GB&#xff0c;内存越高&#xff0c;编译时间越短ROM&#xff1a;最低400GB&#xff0c;代码250GB构建15…

Svn安装

目录 一. 软件环境 二. SVN服务端 1. yum安装svn 2. 查看安装的文件列表 3. 建立版本库 3.1 修改数据存储默认位置 3.2 使用svnadmin建立版本库 4. 配制 4.1 添加用户 4.2 配制读写权限 4.3 配制服务 5. 启动服务 5.1 停止服务 5.2 启动服务 5.3 拉取项目 三.…

Zookeeper集群 + Kafka集群

Zookeeper 概述 Zookeeper 定义 Zookeeper是一个开源的分布式的&#xff0c;为分布式框架提供协调服务的Apache项目。 Zookeeper 工作机制 Zookeeper从设计模式角度来理解&#xff1a;是一个基于观察者模式设计的分布式服务管理框架&#xff0c;它负责存储和管理大家都关心的…

CodeForces.1786A2.发牌.[中等][flg标识][数学规律][双色牌]

题目描述&#xff1a; 题目解读&#xff1a; 发牌问题&#xff0c;给两人发双色牌&#xff0c;同样还是 给a发1张&#xff0c;然后给b发2&#xff0c;3张&#xff1b; 给a发4&#xff0c;5张&#xff0c;给b发6&#xff0c;7张&#xff1b; 给a发8&#xff0c;9张&#xff…

《最新出炉》Python+Playwright自动化测试-2-playwright的API及其他知识

一.简介 上一篇我已经将PythonPlaywright的环境搭建好了&#xff0c;而且也简单的演示了一下三款浏览器的启动和关闭&#xff0c;是不是很简单啊。今天主要是把一篇的中的代码进行一次详细的注释&#xff0c;然后说一下playwright的API和其他相关知识点。那么首先将上一篇中的…

Spring Cloud面试题

组件 Spring Cloud Eureka&#xff1a;服务注册与发现 Spring Cloud Zuul&#xff1a;服务网关 Spring Cloud Ribbon&#xff1a;客户端负载均衡 Spring Cloud Feign&#xff1a;声明性的Web服务客户端 Spring Cloud Hystrix&#xff1a;断路器 Spring Cloud Config&#xff1…

[C++]octomap安装后测试

测试环境&#xff1a; vs2019 octomap1.9.6 release x64 代码&#xff1a; #include <octomap/octomap.h> #include <octomap/OcTree.h> using namespace std; using namespace octomap; void print_query_info(point3d query, OcTreeNode* node) { if (…

矿井水除氟,污水除氟的工艺分析

高矿化度的废水是指含有高浓度溶解性矿物质的废水&#xff0c;通常指的是含有高浓度钠、钙、镁、铁、铝、钾等离子的废水。这些离子通常来自于废水所处的环境、工业或生产过程中使用的原材料和化学品。高矿化度的废水通常具有高盐度、高电导率、高硬度等特征&#xff0c;对环境…

Vivado综合属性系列之十二 BLACK_BOX

目录 一、前言 二、BLACK_BOX ​2.1 属性说明 ​2.2 工程代码 ​2.3 结果 一、前言 ​在调试中&#xff0c;有时不需要知道一个模块或实例的具体实现&#xff0c;或者需要使其对外属于不可见&#xff0c;只知道它的输入输出&#xff0c;即像一个黑盒&#xff0c;此时可以对模…

港联证券|“面值退”增多凸显A股市场化进程良性态势

近日&#xff0c;多家陷入“1元退市”危机的公司纷纷发布风险提示公告称&#xff0c;公司股票存在可能因股价低于面值被终止上市的风险。据《经济参考报》记者不完全统计&#xff0c;今年以来&#xff0c;沪深两市已有10余只个股锁定“面值退”&#xff0c;其中多以披星戴帽公司…