priority_queue模拟

news2024/9/24 17:12:09

一、什么是priority_queue?

        priority_queue是C++标准库中的一个容器适配器,用于实现优先队列(priority queue)的数据结构。优先队列是一种特殊的队列,其中的元素按照一定的优先级进行排序,每次取出的元素都是优先级最高的。它的优先级是可以通过传入参数自己调整的,它的底层实现通常使用堆(heap)数据结构。以下是堆结构的学习链接:堆(建堆算法,堆排序)-CSDN博客
主要特点
        元素有序:队列中的元素会根据其优先级进行排序,优先级最高的元素总是位于队列的头部(或称为队首)。
        操作:主要操作包括插入新元素(push)、删除优先级最高的元素(pop)以及访问优先级最高的元素(top,但不删除)。
        默认行为:默认情况下,priority_queue使用最大堆实现,即优先级最高的元素(值最大的元素)存储在根节点。但可以通过指定比较函数来改变元素的排序方式,例如使用std::greater可以实现最小堆,即优先级最低的元素(值最小的元素)存储在根节点。

二、priority_queue如何用?

模板参数

priority_queue的模板定义通常包含三个参数:

  • typename T:元素类型。
  • typename Container = std::vector<T>:底层容器类型,默认为std::vector<T>。虽然std::deque也满足条件,但std::vector因其高效的随机访问性能而更常被用作底层容器。
  • typename Compare = std::less<T>:比较函数类型,用于确定元素的优先级。默认为std::less<T>,表示元素按从大到小的顺序排列;若需按从小到大的顺序排列,可指定为std::greater<T>。

以上是priority_queue的接口函数,该篇文章只来学习和模拟c++11以前的接口。

以下是这些接口的使用:

//使用示例
#include<iostream>
#include<queue>
using namespace std;
//这是自定义优先级的一种格式
template<typename T>
class gt
{
public:
	//如果返回true则b优先,返回false则a优先,所以这里使用gt会生成小堆
	bool operator()(T a, T b)
	{
		return a > b;
	}
private:
};
int main()
{
	priority_queue<int> heap1;//int表示储存的类型
	priority_queue<int, vector<int>> heap2;//这里vector表示使用的底层容器,这里也可以换成deque<int>。
	
	//greater为编译器提供的类模板,默认的优先级是大堆,而使用它可以生成小堆。
	priority_queue<int, vector<int>, greater<int>> heap3;

	//当然也可以自己设计一个优先级方式传入,该方法常常用于储存自定义类型,而内置类型编译器提供的就够用。
	priority_queue<int, vector<int>, gt<int>> heap4;

	//push接口用于存入元素
	heap4.push(2);
	heap4.push(7);
	heap4.push(1);
	heap4.push(5);
	cout << heap4.size() << endl;//size用于计算队列中元素的个数
	while (!heap4.empty())//empty用于判断队列是否为空
	{
		cout << heap4.top() << ' ';//top获取队头元素
		heap4.pop();//pop删除队头元素
	}
	return 0;
}

以上输出为:

三、priority_queue模拟实现

1.模板参数

       首先为了区别于库里面的优先级队列,我们可以用命名空间限制它的作用域。通过观察库里面的priority_queue模板参数一共有三个,第一个为储存的元素类型,第二个参数为需要用的底层容器(默认为vector),第三个参数为用来调整优先级的类模板(需要我们写一个默认模板),那么我们可以做以下设计:

#include<iostream>
#include<vector>
using namespace std;
namespace byte//用命名空间限制它的作用域,来区别于库里面的优先级队列
{
	template<typename T, class Container = vector<T>, class Compare = less<T>>
	class priority_queue
	{
	public:
		//...
	private:
		//...
	};
}

 注意:这里vector模板我们可以使用库里面的,但优先级队列模板(less<T>)需要我们自己写。

2.成员变量

        STL的任何模拟,如果在考虑成员变量如何设计而发愁,我们可以去想如何储存对象的数据进而来设计我们的成员变量。这里我们用的是容器Container(即vector<int>)来储存数据的,所以我们的成员变量之一类型一定是Container,其次因为想到不同对象传入的Compare可能不同,具有特殊性,所以我们也可以把Compare(优先级调整的类模板)作为成员变量。如下:

private:
	Container arr;
	Compare comp;

3.成员函数

        因为在priority_queue模拟中并不涉及到动态内存开辟和一些特殊理,所以这里的构造函数用编译器默认提供的构造函数就够用了,并不需要我们自己编写。

        在优先级队列中因为涉及到堆的调整所以在push和pop接口的设计中有些复杂,其他接口的设计都非常简单就不在讲解,后面大家直接看源码。

3.1.push

        首先需要把arr看做是一个堆结构,无论arr里面有没有元素,然后直接把元素push_back到arr中,此时该元素所在位置为堆底,而且并不一定是正确的位置,接下来要做的就是对该元素进行向上调整。

父子节点的定义:子节点记为child,父节点记为father。

child=arr.size()-1

father=(child-1)/2(理解原理后可以当做公式记忆,可通过一下链接参考学习)

向下调整的方法我在以下文章中有具体讲解:

堆(建堆算法,堆排序)_初始建堆算法-CSDN博客

		void AdjustUP()
		{
			int child = arr.size() - 1, father = (child - 1) / 2;
			while (child > 0)
			{
				if (comp(arr[father], arr[child]))
				{
					std::swap(arr[child], arr[father]);
					child = father;
					father = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

3.2.pop

        该函数主要功能是pop堆顶元素,但是如果直接pop堆顶元素(下标为0)的话,那么下标为1的会成为堆顶元素,会使原来的父子关系和兄弟关系混乱,要重新调整起来极其复杂,所一需要换一种方案,我们可以把堆顶元素与堆底元素交换然后pop堆底元素,然后对堆进行向下调整。

父子节点的定义:子节点记为child,父节点记为father。

father=0

child=father*2+1(这里father和child的计算公式是可以互推的)

向下调整的方法我在以下文章中有具体讲解:

堆(建堆算法,堆排序)_初始建堆算法-CSDN博客

四、源码

#include<iostream>
#include<vector>
using namespace std;
namespace byte
{
	template<typename T>
	class less
	{
	public:
		//如果返回true则b优先,返回false则a优先,所以这里使用gt会生成小堆
		bool operator()(T a, T b)
		{
			return a < b;
		}
	private:
	};
	template<typename T>
	class greater
	{
	public:
		//如果返回true则b优先,返回false则a优先,所以这里使用gt会生成小堆
		bool operator()(T a, T b)
		{
			return a > b;
		}
	private:
	};

	template<typename T, class Container = vector<T>, class Compare = less<T>>
	class priority_queue
	{
	public:

		void AdjustUP()
		{
			int child = arr.size() - 1, father = (child - 1) / 2;
			while (child > 0)
			{
				if (comp(arr[father], arr[child]))
				{
					std::swap(arr[child], arr[father]);
					child = father;
					father = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}
		void AdjustDOWN()
		{
			int father = 0, child = father * 2 + 1;
			while (child < arr.size())
			{
				if (child + 1 < arr.size() && comp(arr[child], arr[child + 1]))
				{
					child++;
				}
				if (comp(arr[father], arr[child]))
				{
					std::swap(arr[child], arr[father]);
					father = child;
					child = father * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}
		void push(T x)
		{
			arr.push_back(x);
			AdjustUP();
		}
		void pop()
		{
			std::swap(arr[0], arr[arr.size() - 1]);
			arr.pop_back();
			AdjustDOWN();
		}
		T top()
		{
			return arr[0];
		}
		size_t size()
		{
			return arr.size();
		}
		bool empty()
		{
			return arr.size() == 0;
		}
	private:
		Container arr;
		Compare comp;
	};
}

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

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

相关文章

OpenAI融资谈判 估值或超1000亿美元

&#x1f989; AI新闻 &#x1f680; OpenAI融资谈判 估值或超1000亿美元 摘要&#xff1a;OpenAI正在进行一轮融资谈判&#xff0c;预计估值将超过1000亿美元&#xff0c;主导投资方为Thrive Capital&#xff0c;将投资10亿美元。今年早些时候&#xff0c;OpenAI估值已超过8…

vue按钮弹框

在Vue中实现按钮点击后弹出对话框&#xff08;弹框&#xff09;的功能&#xff0c;通常可以使用一些Vue的UI组件库&#xff0c;如Element UI、Vuetify、BootstrapVue等&#xff0c;这些库提供了丰富的组件&#xff0c;包括对话框&#xff08;Dialog&#xff09;、模态框&#x…

一般中小型企业网站用哪种类型的SSL证书?

对于一般中小型企业网站&#xff0c;常用的SSL证书类型主要包括域名验证型SSL证书&#xff08;DV SSL证书&#xff09;和组织验证型SSL证书&#xff08;OV SSL证书&#xff09;。 域名验证型SSL证书&#xff08;DV SSL证书&#xff09; 特点&#xff1a; 验证简单&#xff1…

android 将新建的底部导航的demo,修改首页默认显示的字符串为helloworld。

1、先上个图&#xff0c;demo建好了以后&#xff0c;默认显示一个字符串&#xff1a; 2、这个demo的结构&#xff1a; activity_main.xml中用navGraph与其关联。 3、增加方法&#xff0c;给text赋值&#xff1a; package com.example.helloworld.ui.homeimport androidx.lifec…

三级_网络技术_53_应用题

一、 请根据下图所示网络结构回答下列问题。 1.设备1应选用__________网络设备。 2.若对整个网络实施保护&#xff0c;防火墙应加在图中位置1~3的__________位置上。 3.如果采用了入侵检测设备对进出网络的流量进行检测&#xff0c;并且探测器是在交换机1上通过端口镜像方式…

Launcher3 长按Hotseat图标,显示删除角标(红底白杠杠用于删除图标或者显示应用未读消息数量)

基于Android 13,Launcher3实现需求&#xff1a; 1. 长按Hotseat的图标显示红色删除角标 2. 点击角标&#xff0c;删除图标并保存到Database 3.点击其他地方&#xff0c;取消编辑hotseat图标模式 实现效果&#xff1a; 实现原理&#xff1a; 图标是由BubbleTextView来是实现…

剑侠情缘c#版(游戏源码+资源+工具+程序),百度云盘下载,大小1.68G

剑侠情缘c#版&#xff08;游戏源码资源工具程序&#xff09;&#xff0c;c#开发的&#xff0c;喜欢研究游戏的可以下载看看。亲测可进游戏。 剑侠情缘c#版&#xff08;游戏源码资源工具程序&#xff09;下载地址&#xff1a; 通过网盘分享的文件&#xff1a;【游戏】剑侠情缘c#…

jmeter如何把一个请求的响应中部分字段提取出来便于下个请求用

jmeter如何把一个请求的响应中部分字段提取出来便于下个请求用&#xff0c;可以通过json提取器提取&#xff0c;如果提取多个&#xff0c;就设置多个json提取。 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/dd5afb1fca3f4e31b636e17e11e8dfc3.png

2.10鼠标事件

目录 实验原理 实验代码 运行结果 文章参考 实验原理 在 OpenCV 中存在鼠标的操作&#xff0c;比如左键单击、双击等。对于 OpenCV 来讲&#xff0c;用户的鼠标操作被认为发生了一个鼠标事件&#xff0c;需要对这个鼠标事件进行处理&#xff0c;这就是事件的响应。下面我们…

手机ip频繁跳动的原因是什么?手机ip地址老是变怎么解决

在当今数字化时代&#xff0c;‌手机已成为我们生活中不可或缺的一部分。‌然而&#xff0c;‌有些用户可能会遇到手机IP地址频繁变动的问题&#xff0c;‌这不仅可能影响网络连接的稳定性&#xff0c;‌还可能对特定的在线活动造成困扰。‌本文将深入探讨手机IP频繁跳动的原因…

传输大咖36 | 镭速轻松解决医疗卫生行业跨网文件传输难题

在医疗领域&#xff0c;医疗数据的关键性显而易见。病历详尽记载与医学影像数据等&#xff0c;均为确保精确诊断与治疗成效的基石。但是&#xff0c;医疗数据量的迅猛增长使得传统文件传输方法的不足之处日益凸显&#xff0c;难以跟上现代医学的步伐。特别是在跨网文件交换这一…

SSM框架之Mybatis

前言 什么是框架&#xff1f; 框架就是对技术的封装&#xff0c;将基础的技术进行封装&#xff0c;便于程序员使用&#xff0c;提高开发效率 ssm框架是什么&#xff1f; ssm包括spring、springMvc、Mybatis&#xff0c;是后端企业级开发时会使用到的框架组合&#xff0c;在…

无人机搭载高压喷水清洗绝缘子技术详解

随着电力行业的快速发展&#xff0c;高压输电线路作为电力传输的“大动脉”&#xff0c;其安全稳定运行至关重要。绝缘子作为输电线路中的重要组件&#xff0c;长期暴露于自然环境中&#xff0c;易受尘埃、鸟粪、盐雾等污染物附着&#xff0c;导致绝缘性能下降&#xff0c;甚至…

Visual Basic 6.0教程/Visual Basic从入门到实践/Visual Basic学习视频教程

Visual Basic 6.0教程/Visual Basic从入门到实践/Visual Basic学习视频教程 李天生VB从入门到精通 第一章 VisualBasic6基本介绍 第二章 VisualBasic6的数据类型与运算符表达式 第三章 VisualBasic6的内部函数 第四章 VisualBasic6的基本语句 第五章 VisualBasic6的数组 第六章…

AMC8美国数学竞赛备考:吃透625道真题和知识点(持续)

距离接下来最近的2025年AMC8美国数学竞赛还有几个月的时间&#xff0c;实践证明&#xff0c;做真题&#xff0c;吃透真题和背后的知识点是备考AMC8有效的方法之一。 2000-2024年AMC8真题和解析&#xff1a;2023年第13题 这道题的考点是分数。 题意的重点是均匀分布&#xff0c…

OpenCV绘图函数(5)绘制标记函数drawMarker()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::drawMarker 函数在 OpenCV 中用于在一个给定的位置上绘制标记。目前支持几种不同的标记类型&#xff0c;具体信息可以参考 MarkerTypes 函数…

这样图解Transformer应该没人看不懂了吧——多头注意力机制详解

这是关于Transformer系列文章的第三篇部分&#xff0c;我们将用自上而下的方式深入探讨Transformer的功能。 在前两篇文章中&#xff0c;我们已经了解了Transformer是什么、它的架构以及工作原理。 没看过的同学可以点击图片进行查看 Transformer图解1—基础与架构 Transform…

ITopologicalOperator.Intersect 直接崩软件,也不报错的解决方案

1、问题描述&#xff1a; 说多崩溃就有多崩溃&#xff0c;执行到下面这个相交分析的时候&#xff0c;就一直崩软件&#xff0c;用catch也catch不到东西&#xff0c;相同的数据放到另外一个工程中的时候&#xff0c;又是正常的&#xff0c;一直找不到原因。 2、终级方案 最终…

LLM - 自定义图像数据集 使用 LoRA 微调 图像生成 Flux 模型

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/141638928 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 在 Dif…

假设以带头结点的循环链表表示队列,并且只设一个指针指向队尾元素结点(注意不设头指针),试编写相应的置空队列、判断队列是否为空、入队和出队等算法。

typedef int Datatype; typedef struct queue {Datatype data;struct queue* next; }queue; //队列 typedef struct {queue* rear; }LinkQueue; //初始化 void queueinit(LinkQueue* ps) {ps->rear (queue*)malloc(sizeof(queue));if (ps->rear NULL){perror("err…