c++:优先级队列(priority queue)使用及底层详解,附带仿函数初步使用

news2024/11/18 7:52:05

文章目录

  • 优先级队列的使用
    • 大堆
    • 小堆
      • **注意**
  • 优先级队列的模拟实现
    • push
    • pop
    • size
    • empty
    • top
  • 仿函数
    • 仿函数是什么
    • push
    • pop
  • 仿函数结合优先级队列的优势


优先级队列的使用

优先级队列本质是就是完全二叉树,是个堆.我们可以用优先级队列来取出一段序列中的前N个最大值.

在这里插入图片描述

	priority_queue<int> pq;

第一个模板参数是数据类型,第二个是容器适配器,通俗点来讲就是这个堆用什么容器来实现,第三个就是排序方式(用仿函数来实现),这个能决定这个堆是大堆还是小堆.

大堆

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
int main()
{
	priority_queue<int> pq;
	pq.push(1);
	pq.push(100);
	pq.push(66);
	pq.push(9);
	pq.push(20);
	pq.push(2);
	pq.push(8);
	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop();
	}
	cout << endl;
	return 0;
}

在这里插入图片描述

将一些无序的数据存到优先级队列里面,它会给数据排成一个堆,我们可以依次取出数据,然后删除.
默认是大堆

小堆

	priority_queue<int, vector<int>, greater<int>> pq;	

只需要在定义时声明就行.
在这里插入图片描述

注意

greater是小堆,less是大堆

优先级队列的模拟实现

这里我们先实现一个大堆

	template<class T, class Container = vector<T>>
	class priority_queue
	{
	private:
		Container _con;
	}

因为二叉树push数据的时候经常要交换位置,我们经常要访问它的父亲节点和子节点,所以默认的容器适配器是vector,你也可以改成deque(双向队列)

push

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

堆的插入是尾插,然后再把这个数据向上调整

	void adjust_up(size_t child)
		{
			size_t parent = (child - 1) / 2;
			while (child > 0)
			{
				if(_con[child] > _con[parent])
				{
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

比较逻辑:找到父亲节点,将父亲节点和子节点比较,如果子节点>父亲节点,交换,然后更新节点.
注意:父亲节点=(子节点-1)/2

pop

		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			adjust_down(0);
		}

在删除节点的时候,为了效率.我们把第一个节点和最后一个节点交换,删除最后一个节点(此时堆的最大值).再把第一个节点(原来的最后一个节点)向下调整.

	void adjust_down(size_t parent)
	{
		size_t child = parent * 2 + 1;
		if (child + 1 < _con.size() && _con[child] < _con[child + 1])
			child++;
		while (child < _con.size())
		{
			if (_con[parent]<_con[child])
			{
				swap(_con[parent], _con[child]);
				parent = child;
				child = parent * 2 + 1;
			}
			else
			{
				break;
			}
		}
	}

向下调整的过程中,如果满足父亲节点<子节点,二者交换.直到父亲节点>子节点或者子节点越界.
注意在向下调整的过程中始终选择大的孩子进行比较,这是为了满足大堆的性质,父亲节点一定>=子节点.
左孩子=父亲节点2+1,右孩子=父亲节点2+2

size

		size_t size()
		{
			return _con.size();
		}

size和empty都可以复用vector里面的函数,但是我们要在前面包上vector的头文件

empty

		bool empty()
		{
			return _con.empty();
		}

top

		const T& top()
		{
			return _con[0];
		}

堆的头就是数组里面的第一个数据

仿函数

我们如果要改变大小堆的话,其实只需要把小于改成大于,大于改成小于就行.但如果每次都这么改,会大大增加我们的工作量,在c语言中,我们可以使用函数指针来解决.在c++中,祖师爷设计了一种更方便的做法,也就是仿函数.

仿函数是什么

仿函数本质上是一个类,这个类能模拟函数的行为.实现内置类型或者自定义类型的比较

template<class T>
struct Less
{
	bool operator()(const T& x, const T& y)
	{
		return x < y;
	}
};
int main()
{
	Less<int> func;
	cout << func(1, 2) << endl;
	cout << func.operator()(1, 2) << endl;

	return 0;
}

在这里插入图片描述

func(1,2)看起来是不是跟我们平时调用函数一样,传两个参数.实际上,Less仿函数里对()进行操作符重载,让我们可以比较两个数的大小

这个做法衍生到堆上面,我们可以传两个仿函数,一个大于,一个小于.然后在堆里面实例化出对象.

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

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

push

void adjust_up(size_t child)
{
	Compare com;
	size_t parent = (child - 1) / 2;
	while (child > 0)
	{
		//if(_con[child] > _con[parent])
		//if(_con[parent] < _con[child])
		if (com(_con[parent], _con[child]))
		{
			swap(_con[child], _con[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
void push(const T& val)
{
	_con.push_back(val);
	adjust_up(_con.size() - 1);
}

pop

		void adjust_down(size_t parent)
		{
			Compare com;
			size_t child = parent * 2 + 1;
			if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
				child++;
			while (child < _con.size())
			{
				//if (_con[parent] < _con[sun])
				if (com(_con[parent], _con[child]))
				{
					swap(_con[parent], _con[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}
		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			adjust_down(0);

		}

仿函数结合优先级队列的优势

priority_queue<int> pq;
priority_queue<int,vector<int>,greater<int>> pq;

首先我们可以自己控制大堆/小堆.
其次在面对商品等复杂类型中,需要我们根据价格,评价,销量等等进行排序时.单靠商品类里面的一个操作符重载小于或者大于是不够的,我们可以写多个仿函数,争对价格,评价等进行比较.

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

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

相关文章

【k8s多集群管理平台开发实践】十二、开发总结及注意事项【完结】

文章目录 简介总结前面11章节所实现的功能&#xff1a; 一.完善集群更多功能1.1.可以扩展更多的功能 二.该系列课程代码地址三.技术栈及开发经验3.1.开发过程中所使用到的一些技术栈 四.开发过程中需要注意的事项五.反馈与交流 简介 该系列文章主要是介绍了多k8s集群平台开发的…

SF 不消费buffer

1、请求合成请求vsync MessageQueue.cpp 返回nextWakeupTime struct ArmingInfo { nsecs_t mActualWakeupTime; nsecs_t mActualVsyncTime; nsecs_t mActualReadyTime; }; 在schedule 请求vsync 时会根据算法计算出nextVsyncTime时间&#…

【matlab基础知识代码】(十二)逆矩阵与广义逆矩阵

>> Hhilb(4);H1inv(H),norm(H*H1-eye(4))H1 1.0e03 *0.0160 -0.1200 0.2400 -0.1400-0.1200 1.2000 -2.7000 1.68000.2400 -2.7000 6.4800 -4.2000-0.1400 1.6800 -4.2000 2.8000ans 2.8455e-13 矩阵维数较大&#xff0c;警告: 矩阵接近奇…

豆芽机置入语音芯片WTN6040-8S:开启智能生活新篇章,让豆芽制作更便捷有趣

豆芽机的开发背景&#xff1a; 豆芽作为一种营养丰富、味道鲜美的食品&#xff0c;深受广大消费者的喜爱。然而&#xff0c;传统的豆芽生产过程繁琐&#xff0c;需要耗费大量的时间和人力&#xff0c;且存在生产效率低、质量不稳定等问题。随着人们生活节奏的加快和对健康饮食的…

开源即时通讯IM框架 MobileIMSDK v6.5 发布

一、更新内容简介 本次更新为次要版本更新&#xff0c;进行了bug修复和优化升级&#xff08;更新历史详见&#xff1a;码云 Release Notes、Github Release Notes&#xff09;。 MobileIMSDK 可能是市面上唯一同时支持 UDPTCPWebSocket 三种协议的同类开源IM框架。轻量级、高…

django和vue开发的前后端分离网站怎么部署到服务器上,django和vue前后端分离网站怎么通过宝塔部署

提示&#xff1a;如果看完全部教程后仍然部署不成功&#xff0c;可以联系作者 一、提前准备 想要把django vue 前后端分离网站部署到服务器上&#xff0c;有一些提前准备的东西 1、备案域名&#xff08;域名必须备案&#xff09; 这里需要解析两个域名&#xff0c;一个前端&…

������森林消防泵:特点及使用场景 /恒峰智慧科技������

在大自然的怀抱中&#xff0c;森林是地球上最美丽的绿色家园。然而&#xff0c;森林火灾却时常威胁着这片美丽的家园。为了保护森林&#xff0c;我们有幸拥有了一支强大的森林消防泵队伍。今天&#xff0c;就让我们一起来了解一下这些守护者们的故事吧&#xff01; &#xfffd…

从Flutter范儿的单例来看Dart的构造函数

点击上方蓝字关注我&#xff0c;知识会给你力量 单例模式 单例模式应该是设计模式中使用的最广泛的一种设计模式了&#xff0c;在Kotlin中&#xff0c;甚至为它单独创建了一个语法糖——object类&#xff0c;来快速实现单例模式&#xff0c;而在Dart中&#xff0c;并没有像Kotl…

人大金仓数据库报com.kingbase8.util.KSQLException: 致命错误: 用户 “SYSTEM“ Password 认证失败

com.kingbase8.util.KSQLException: 致命错误: 用户 “SYSTEM” Password 认证失败 解决办法&#xff1a; 问题在于用户权限只不足&#xff0c;相关配置文件在一般在 /data/sys hba.conf,修改IPV4 local connections选项中的改为trust。

第1章 信息系统综合知识 1.1 信息的定义和属性

第1章 信息系统综合知识 1.1 信息的定义和属性 1.1.1 信息的基本概念 控制论创始人维纳认为&#xff1a;信息就是信息&#xff0c;既不是物质也不是能量。 信息论的奠基者香农认为&#xff1a;信息就是能够用来消除不确定性的东西。 其他流行说法&#xff1a;信息是事先不…

回归的无分布预测推理

摘要 我们利用保形推理&#xff0c;开发了回归中无分布预测推理的一般框架。所提出的方法允许使用回归函数的任何估计量构建响应变量的预测带。所得的预测带在标准假设下保留了原始估计量的一致性&#xff0c;同时保证了有限样本边际覆盖&#xff0c;即使这些假设不成立。我们…

代码随想录算法训练营第二十一天:树树树

代码随想录算法训练营第二十一天&#xff1a;树树树 ‍ 513.找树左下角的值 力扣题目链接​**&#xff08;打开新窗口&#xff09;** 给定一个二叉树&#xff0c;在树的最后一行找到最左边的值。 示例 1: ​​ 示例 2: ​​ #算法公开课 《代码随想录》算法视频公开课…

Spring底层入门(七)

1、异常处理 在DispatcherServlet中&#xff0c;doDispatch(HttpServletRequest request, HttpServletResponse response) 方法用于进行任务处理&#xff1a; 在捕获到异常后没有立刻进行处理&#xff0c;而是先用一个局部变量dispatchException进行记录&#xff0c;然后统一由…

【GaussTech速递】数据库技术解读之细粒度资源管控

背景 对数据库集群内资源管控与资源隔离一直是企业客户长久以来的诉求。华为云GaussDB作为一款企业级分布式数据库&#xff0c;一直致力于满足企业对大型数据库集群的管理需要。 数据库可以管理的资源有计算资源与存储资源&#xff0c;计算资源包括CPU、内存、IO与网络&#…

FastAPI vs Flask: 选择最适合您的 Python Web 框架

文章目录 1. 简介2. 安装和设置3. 路由和视图4. 自动文档生成5. 数据验证和序列化6. 性能和异步支持结论 在 Python Web 开发领域&#xff0c;FastAPI 和 Flask 是两个备受欢迎的选择。它们都提供了强大的工具和功能&#xff0c;但是在某些方面有所不同。本文将比较 FastAPI 和…

error LNK2001: 无法解析的外部符号 “__declspec(dllimport) public: __cdecl ......

运行程序时&#xff0c;报如上图所示错误&#xff0c;其中一条是&#xff1a; ReflectionProbe.obj : error LNK2001: 无法解析的外部符号 "__declspec(dllimport) public: __cdecl osg::Object::Object(bool)" (__imp_??0ObjectosgQEAA_NZ) 报这个错误一般是因为…

前端Web如何实现将一个 ECharts 动效保存为一张 GIF 动图?

前端Web如何实现将一个 ECharts 动效保存为一张 GIF 动图&#xff1f; 博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;…

[Cpp]类和对象 | 实现日期类

标题&#xff1a;[Cpp]类和对象 | 实现日期类 水墨不写bug 正文开始&#xff1a; 类和对象是Cpp面向对象编程区别于C的面向过程编程的重要的一部分&#xff0c;因此打好坚实的类和对象的基础对于深入学习Cpp语言是比较明智的。 本文通过实现简单的日期类来加深对类和对象的理解…

【算法】-- 二分查找详注

引入 二分查找&#xff0c;也称为折半查找&#xff1b;首先&#xff0c;二分查找是一种基于有序数组中查找特定元素的算法&#xff0c;所以它会因为数组的一些特性而受限。它的工作原理是不断将要查找的区间分成两部分&#xff0c;然后确定目标值可能存在的区间&#xff0c;直…

VUE 或 Js封装通用闭包循环滚动函数

1、vue3 闭包滚动函数的使用 js 调用也基本雷同 // 滚动Tab组件const scoreTabRef ref()// 滚动的选项const scrollOption ref({// 滚动的Dom元素scrollDom: null,// 滚动的时间间隔scrollInterval: 1500,// 滚动的距离scrollSep: 100,// 滚动历时时间scrollDuration: 10…