模拟实现priority_queue

文章目录

  • priority_queue简介
  • priority_queue的实现
    • Myless和Mygreater
    • push
    • pop
    • 常规接口
  • 全部代码
    • 测试代码
  • 总结

在这里插入图片描述

priority_queue简介

在这里插入图片描述

priority_queue是优先级队列。
什么是优先级队列?
优先级队列(Priority Queue)是一种数据结构,用于管理一组元素,使得每个元素都有一个关联的优先级,并且元素按照优先级进行排序和访问。优先级队列常用于调度算法、图算法(如Dijkstra算法)、操作系统任务管理等场景。它的主要特点是可以快速地插入元素和找到具有最高优先级的元素。
简单来说优先级队列就是一个堆,在STL底层默认是大堆,堆顶的元素是堆里最大的,搞懂了优先级队列,其实大概得几个接口我们也知道了,就是插入和删除还有几个常规的判空之类的。
为了更好的实现优先级队列,我们先来做做铺垫,我们看官方文档给出的模版参数
在这里插入图片描述
前两个我们都很熟悉了,第一个就是普通的模版参数,第二个是容器适配器,第三个是我们接下来要介绍的,仿函数
什么事仿函数?

int main()
{
	Myless<int> l;
	l(1, 2);
	return 0;
}

看上面代码,如果单看上面的代码的话,如果不看类的实例化,这很容易让人误会这是在调用一个名字是l的函数,但是如果我们把上面这个类给写出来呢?

template<class T>
class Myless
{
public:
	bool operator()(const T& x, const T& y)
	{
		return x < y;
	}
private:
};

其实这是一个重载了operator()的函数,这个函数的操作是由我们确定的,所以我们可以像调用函数一样,去调用类的成员函数。
仿函数概念:仿函数(Functor)也被称为函数对象(Function Object),是指一个可以像函数一样使用的对象。它是通过重载类的函数调用运算符operator()来实现的。仿函数在许多场景中都非常有用,例如在标准模板库(STL)中用于算法和容器。
我们通过控制仿函数的行为可以控制类中的比较的操作,也就是说,我们可以控制这个类到底是建小堆还是建大堆。

priority_queue的实现

Myless和Mygreater

由于我们要控制建大堆和建小堆,所以我们创建两个类,类的成员函数只有一个就是operator()用于控制优先级队列中的比较操作,当我们要建大堆的时候就调用Myless,这里在模版参数里,我们默认给的就是建大堆的Myless,如果大家还是不能理解的话接下来看了下面的代码,应该更能理解。
我们先将两个类定义出来:

template<class T>
class Myless
{
public:
	bool operator()(const T& x, const T& y)
	{
		return x < y;
	}
};
template<class T>
class Mygreater
{
public:
	bool operator()(const T& x, const T& y)
	{
		return x > y;
	}
};

模版参数:

template<class T, class Container = vector<int>, class Compare = Myless<T>>

push

由于优先级队列就类似于堆,所以和堆的操作类似,我们先将元素尾插,然后进行向上调整算法进行建堆操作

void push(const T& x)
{
	_con.push_back(x);
	Adjust_Up(_con.size() - 1);
}
void Adjust_Up(int child)
{
	Compare confunc;
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (confunc(_con[parent], _con[child]))
		{
			std::swap(_con[parent], _con[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

pop

由于pop最后一个数据是没什么意义的,所以这里我们pop的是堆顶的数据,直接pop堆顶的数据,我们需要重新建堆付出的代价太大了,所以这里我们将收尾两个元素交换,然后直接pop队尾的数据,这样只有堆顶的数据是不符合堆的定义的,所以我们只需要向下调整即可。

const T& top()
{
	return _con.front();
}
void Adjust_Down(int parent)
{
	Compare confunc;
	int child = parent * 2 + 1;
	while (child < _con.size())
	{
		if (child + 1 < _con.size() && confunc(_con[child], _con[child + 1]))
		{
			child++;
		}
		//默认建大堆
		if (confunc(_con[parent], _con[child]))
		{
			std::swap(_con[child], _con[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

在这里插入图片描述

在这里插入图片描述
可以看到在向上调整算法和向下调整算法中我们都用到了我们的仿函数,先创建一个对象,然后调用这个类的operator(),这个函数的行为就是去控制大于和小于,从而达到控制建小堆还是建大堆,如果是缺省参数,就直接建大堆,如果是调用Mygreater就建小堆。

常规接口

size_t size()
{
	return _con.size();
}
const T& top()
{
	return _con.front();
}
bool empty()
{
	return _con.empty();
}

全部代码

namespace lyrics
{
	template<class T>
	class Myless
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};
	template<class T>
	class Mygreater
	{
	public:
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};
	template<class T, class Container = vector<int>, class Compare = Myless<T>>
	class priority_queue
	{
	public:
		priority_queue() = default;
		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 push(const T& x)
		{
			_con.push_back(x);
			Adjust_Up(_con.size() - 1);
		}
		void pop()
		{
			std::swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			Adjust_Down(0);
		}
		void Adjust_Down(int parent)
		{
			Compare confunc;
			int child = parent * 2 + 1;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && confunc(_con[child], _con[child + 1]))
				{
					child++;
				}
				//默认建大堆
				if (confunc(_con[parent], _con[child]))
				{
					std::swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}
		void Adjust_Up(int child)
		{
			Compare confunc;
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				if (confunc(_con[parent], _con[child]))
				{
					std::swap(_con[parent], _con[child]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}
		size_t size()
		{
			return _con.size();
		}
		const T& top()
		{
			return _con.front();
		}
		bool empty()
		{
			return _con.empty();
		}
	private:
		Container _con;
	};
}

测试代码

void test()
{
	vector<int> v{ 1,2,3,4,5,6,7 };
	lyrics::priority_queue<int> pq(v.begin(), v.end());
	while (!pq.empty())
	{
		cout << pq.top() << ' ';
		pq.pop();
	}
	lyrics::priority_queue<int> pq1(v.begin(), v.end());
	pq1.push(8);
	cout << endl;
	cout << endl << pq1.size() << endl;
	while (!pq1.empty())
	{
		cout << pq1.top() << ' ';
		pq1.pop();
	}
	cout << endl << pq1.size() << endl;
}

在这里插入图片描述

总结

在本篇博客中,我们深入探讨了优先级队列(priority_queue)的实现及其在各种应用中的重要性。通过详细的代码示例,我们演示了如何利用堆数据结构高效地管理具有优先级的元素,展示了优先级队列在插入、查找和删除操作中的性能优势。无论是在调度算法、图算法还是操作系统任务管理中,优先级队列都展现出了不可或缺的作用。

此外,我们还介绍了仿函数(Functor)这一强大的编程概念。通过重载函数调用运算符,仿函数可以像普通函数一样使用,但同时具备存储状态、实现灵活逻辑和代码复用的优点。通过具体的C++和Python代码示例,我们展示了如何定义和使用仿函数,并讨论了其在标准模板库(STL)和实际编程中的应用场景。

总的来说,理解和掌握优先级队列和仿函数这两个概念,对于提升编程能力和编写高效、灵活的代码具有重要意义。希望通过本篇博客的讲解,读者能够更好地理解这两个重要的编程技术,并在实际项目中加以应用。如果你对优先级队列或仿函数有任何疑问或建议,欢迎在评论区留言,我们共同探讨、学习进步。

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

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

相关文章

【动手学深度学习】使用块的网络(VGG)的研究详情

目录 &#x1f30a;1. 研究目的 &#x1f30a;2. 研究准备 &#x1f30a;3. 研究内容 &#x1f30d;3.1 多层感知机模型选择、欠拟合和过拟合 &#x1f30d;3.2 练习 &#x1f30a;4. 研究体会 &#x1f30a;1. 研究目的 理解块的网络结构&#xff1b;比较块的网络与传统…

Linux基础 (十四):socket网络编程

我们用户是处在应用层的&#xff0c;根据不同的场景和业务需求&#xff0c;传输层就要为我们应用层提供不同的传输协议&#xff0c;常见的就是TCP协议和UDP协议&#xff0c;二者各自有不同的特点&#xff0c;网络中的数据的传输其实就是两个进程间的通信&#xff0c;两个进程在…

activiti用法随记

案例&#xff1a; 摘抄于官网&#xff0c;假设我们有如下流程&#xff1a; 流程对应的bpmn文件如下&#xff1a; <definitions xmlns:activiti"http://activiti.org/bpmn" xmlns:bpmndi"http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc&quo…

configuration_auto.py in getitem raise KeyError(key) KeyError: ‘llama‘解决方案

运行LLaMA-7b模型有时候会报错“configuration_auto.py in getitem raise KeyError(key) KeyError:llama”如下所示&#xff1a; 解决办法升级安装transformer库即可&#xff0c;如下所示&#xff1a; pip install transformers4.30.0

vue3设置全局变量并获取 全局响应式变量 窗口大小

设置 js文件统一管理全局变量 方法1 app provide() 全局提供变量 通过inject()使用 方法2 app实例配置全局变量 获取 通过 getCurrentInstance.appContext.config.globalProperties.$innerWidth访问到 code import { ref } from vue export const useGlobalState () > {c…

新型AI编程语言Mojo来了!比Python快68000倍! 坚持每天写代码,真的能提高编程水平吗?

2024 年 3 月 29 日&#xff0c;Modular Inc. 宣布开源 Mojo 的核心组件。 Mojo语言是一种新的编程语言&#xff0c;由 Chris Lattner&#xff08;LLVM 和 Swift 语言的创始人&#xff09;创建的 Modular AI 公司开发。 它结合了Python的易用性和C的性能&#xff0c;旨在为人…

二说springboot3的自动配置机制

大家好&#xff0c;这里是教授.F 目录 SpringBootApplication&#xff1a; EableAutoConfiguration&#xff1a; 上一篇文章粗略的讲了自动配置机制&#xff0c;二说系列将从源码的角度进行讲解。 SpringBootApplication&#xff1a; 首先我们还是得从SpringBootApplication…

电商APP用户体验提升技巧:一个实战案例

随着网络和移动技术的快速发展&#xff0c;加上全球疫情的影响&#xff0c;电子商务应用程序改变了人们的购物方式&#xff0c;积累了大量的用户群体。如今&#xff0c;一个成功的电子商务应用程序&#xff0c;除了网站用户界面的美&#xff0c;电子商务用户体验的设计&#xf…

图片格式怎么转成pdf,简单的方法

在现代数字化时代&#xff0c;图片格式转换成PDF已经成为许多人的日常需求。无论是为了存档、分享还是打印&#xff0c;将图片转换为PDF都是一项非常实用的技能。本文将详细介绍如何将图片格式转换成PDF的方法。 用浏览器打开 "轻云处理pdf官网&#xff0c;上传图片。 图…

SpringBoot 请求响应

SpringBoot 请求响应 来源于黑马程序员JavaWeb课程&#xff0c;总结笔记 1.ApiFox Apifox快速入门教程 2.基本参数 简单参数&#xff1a;在向服务器发起请求时&#xff0c;向服务器传递的是一些普通的请求数据。 //RequestController.java import jakarta.servlet.http.Htt…

一线教师教学工具汇总

亲爱的教师们&#xff01;我们的教学工具箱里也该更新换代啦&#xff01;今天&#xff0c;就让我来给大家安利一波超实用的教学神器&#xff1a; 百度文库小程序 —— 在线图书馆 百度文库&#xff0c;一个宝藏级的在线文档分享平台&#xff01;在这里&#xff0c;你可以找到海…

康谋技术 | 自动驾驶:揭秘高精度时间同步技术(二)

在自动驾驶中&#xff0c;对车辆外界环境进行感知需要用到很多传感器的数据&#xff08;Lidar&#xff0c;Camera&#xff0c;GPS/IMU&#xff09;&#xff0c;如果计算中心接收到的各传感器消息时间不统一&#xff0c;则会造成例如障碍物识别不准等问题。 为了对各类传感器进…

VSCode插件EditorConfig for VS Code

1. 安装 2. 配置 # http://editorconfig.orgroot true[*] # 表示所有文件适用 charset utf-8 # 设置文件字符集为 utf-8 indent_style space # 缩进风格&#xff08;tab | space&#xff09; indent_size 4 # 缩进大小 end_of_line lf # 控制换行类型(lf | cr | crlf) tr…

鸿蒙低代码开发一个高频问题

在版本是DevEco Studio 3.1.1 Release&#xff0c;SDK是3.1.0(API9)。 创建和设计的visual文件经常会遇到无法渲染的情况&#xff0c;或者自定义组件在Custom列表中突然不见了的情况。 有以下报错信息的&#xff1a; JSON schema validation error: data/visualModel/value/…

Face Forgery Detection by 3D Decomposition

文章目录 Face Forgery Detection by 3D Decomposition研究背景研究目标创新点方法提出问题研究过程技术贡献实验结果未来工作Face Forgery Detection by 3D Decomposition 会议:CVPR2021 作者: 研究背景 面部伪造引发关注传统面部伪造检测主要关注原始RGB图像

React权限管理系统实现

目录 一、需求 二、逻辑 三、实现 &#xff08;一&#xff09;代码 &#xff08;二&#xff09;解释 1. 获取权限对照数组 (queryReferencePermissionsInfo) 2. 获取处理对照数组 (queryDisposePermissionsInfo) 3. 获取权限映射表信息并处理 (queryPermissionsInfo) 4…

【分享】两种方法设置PDF“打开密码”

想要保护PDF文件的私密性&#xff0c;只允许特定人查看&#xff0c;我们可以给PDF设置“打开密码”&#xff0c;这样只有知道密码的人才可以打开文件。如果小伙伴们不知道如何设置&#xff0c;就一起看看以下两种方法吧&#xff01; 方法1&#xff1a;使用PDF编辑器 大部分PD…

【异常检测】MVTec LOCO AD 数据集

every blog every motto: There’s only one corner of the universe you can be sure of improving, and that’s your own self. https://blog.csdn.net/weixin_39190382?spm1010.2135.3001.5343 0. 前言 MVTec LOCO AD 数据集介绍 相比MVTec AD 增加了逻辑异常 1. 正文…

【grafana】创建多变量table

这个普罗米修斯的指标啊&#xff0c;大多数都是键值对&#xff0c;而且笔者如果没记错&#xff0c;他这个值还必须是浮点。少数可以设成离散值&#xff08;Enum&#xff09;&#xff0c;但本质还是一个带翻译功能的键值对 这样的好处是&#xff0c;做起来非常简单&#xff0c;…

Java环境配置(超详细)

Java环境配置&#xff08;超详细&#xff09; 引言1、安装 JDK1.1、下载安装JDK1.2、配置环境变量&#xff1a;JAVA_HOME1.3、将JAVA_HOME添加到Path中 2、安装 Maven2.1、下载安装Maven2.2、配置maven的环境变量: M2_HOME2.3、将Maven变量添加到Path中 引言 Java开发环境的配…