【C++】priority_queuepriority_queue模拟实现

news2024/10/5 15:29:46

个人主页 : zxctscl
如有转载请先通知

文章目录

  • 1. priority_queue的介绍
  • 2. priority_queue的使用
  • 3. 函数模板与类模板
  • 4. 仿函数
  • 5. priority_queue模拟实现
    • 5.1 push
    • 5.2 pop
    • 5.3 empty
    • 5.4 size
    • 5.5 top
    • 5.6 仿函数实现大小堆
    • 5.7 实现自定义类型的优先级队列
  • 6. 附模拟实现代码

1. priority_queue的介绍

在这里插入图片描述

  1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
  2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。
  3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。
  4. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭
    代器访问,并支持以下操作:
    empty():检测容器是否为空
    size():返回容器中有效元素个数
    front():返回容器中第一个元素的引用
    push_back():在容器尾部插入元素
    pop_back():删除容器尾部元素
  5. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指定容器类,则使用vector。
  6. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作。

2. priority_queue的使用

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。注意:默认情况下priority_queue是大堆。
在这里插入图片描述
来代码测试看看大堆:

void test_priority_queue()
{
	priority_queue<int> pq;
	pq.push(2);
	pq.push(1);
	pq.push(4);
	pq.push(3);

	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop();
	}
	cout << endl;

}
int main()
{
	test_priority_queue();
	return 0;
}

默认情况下,priority_queue是大堆
在这里插入图片描述

在这里插入图片描述
priority_queue默认情况下是less大堆,
在这里插入图片描述
想要priority_queue默认改为小堆,就得传三个参数。

用代码来测试一下:

#include<iostream>
using namespace std;
#include<vector>
#include<queue>

void test_priority_queue()
{
	priority_queue<int,vector<int>,greater<int>> pq;
	pq.push(2);
	pq.push(1);
	pq.push(4);
	pq.push(3);

	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop();
	}
	cout << endl;

}
int main()
{
	test_priority_queue();
	return 0;
}

在这里插入图片描述

3. 函数模板与类模板

sort默认排序也是升序
在这里插入图片描述

  vector<int> v = { 3,1,7,4,6,3 };
	// 升序
	sort(v.begin(), v.end());
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

在这里插入图片描述

想要将sort排序默认改为降序,就得加仿函数:

    vector<int> v = { 3,1,7,4,6,3 };
	sort(v.begin(), v.end(), greater<int>());
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

在这里插入图片描述
sort这里是函数模板,函数参数传的是对象。
在这里插入图片描述
而priority_queue传的是类型,需要显示实例化
在这里插入图片描述

4. 仿函数

仿函数就是一个函数对象,就是一个类型。
先来实现一个类:

struct Less
{
	bool operator()(const int& x, const int& y)
	{
		return x < y;
	}
};
int main()
{
	Less lessfunc;
	cout << lessfunc(1, 2) << endl;
	cout << lessfunc.operator()(1, 2) << endl;
	cout << Less()(1, 2) << endl;
	return 0;
}

如果单独看红色框这一行,就会以为lessfunc是一个函数名,但它并不是,它是一个像函数的对象。
在这里插入图片描述

仿函数它的对象可以像函数一样去使用,本质上就是调用operator():
这两个是等价的:
在这里插入图片描述

还有能用匿名对象:
在这里插入图片描述

它本质上就是:
在这里插入图片描述

在这里插入图片描述
这里重载函数调用参数的(),其实就像[]一样,v[0]也就是v.operator
在这里插入图片描述

本质上就是参数留下,变成运算符的操作数。
运算符重载中operator加运算符才是函数名,省略掉的就是.operator加括号。
一个类只要重载了operator()就可以调仿函数。
在这里插入图片描述
加模板就直接可以重载为泛型,什么类型就可以支持。

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

	return 0;
}

这里定义类用struct和class有什么区别?
一般所有数据都公有就用struct,
有些公有,有些私有就用class。

5. priority_queue模拟实现

直接复用容器

5.1 push

在这里插入图片描述
插入一个数据后,向上调整,具体实现过程有需要可以看【数据结构】堆的实现

push代码:

		void adjust_up(size_t child)
		{
			size_t parent = (child - 1) / 2;
			while (child>0)
			{
				if (_con[child] > _con[parent])
				{
					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);
		}

在这里插入图片描述

5.2 pop

在这里插入图片描述
就这样删除效率太低了,通常首位交换一下。向下调整,具体实现过程有需要可以看【数据结构】堆的实现

pop代码实现:

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

		}

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

5.3 empty

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

5.4 size

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

5.5 top

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

5.6 仿函数实现大小堆

想要大堆改小堆可以直接在代码上面修改,但是要实现大堆,代码又得重新修改。
这里就得用到上面提到的仿函数了。
写两个仿函数,一个实现大堆,一个实现小堆。

大堆实现:

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

	

在这里插入图片描述

小堆实现:

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

在这里插入图片描述

在写类模板的时候就把仿函数也一起传过去

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

5.7 实现自定义类型的优先级队列

定义一个日期类:

class Date
{
public:
	friend ostream& operator<<(ostream& _cout, const Date& d);

	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}

	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}

	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}
private:
	int _year;
	int _month;
	int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
	_cout << d._year << "-" << d._month << "-" << d._day;
	return _cout;
}

用priority_queue来实现日期排序:

在这里插入图片描述
在这里插入图片描述
但是如果给的是指针:
在这里插入图片描述
在这里插入图片描述
传地址每次给的结果不一样。
new不能保证每次new的地址都比前面的大,这里就得重新写一个仿函数。

class GreaterPDate
{
public:
	bool operator()(const Date* p1, const Date* p2)
	{
		return *p1 > *p2;
	}
};

这个时候传地址就不会在变了:
在这里插入图片描述
所以说仿函数控制比较逻辑,可以控制用什么去比较。

6. 附模拟实现代码

#pragma once
#include<vector>

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

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

	
	template<class T, class Container = vector<T>, class Compare = less<T>>
	class priority_queue
	{
	public:
		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]))
				{
					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() && _con[child] < _con[child + 1])
				if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
				{
					++child;
				}

				//if (_con[child] > _con[parent])
				//if (_con[parent] < _con[child])
				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);
		}

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

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

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

	private:
		Container _con;
	};


}

有问题请指出,大家一起进步!!!

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

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

相关文章

Linux的启动过程,了解一下?

Linux 系统启动过程 linux启动时我们会看到许多启动信息。 Linux系统的启动过程并不是大家想象中的那么复杂&#xff0c;其过程可以分为5个阶段&#xff1a; 内核的引导。运行 init。系统初始化。建立终端 。用户登录系统。 init程序的类型&#xff1a; SysV: init, CentO…

Linux系统编程开发环境搭建

开发环境搭建 桥接网络&#xff08;Bridged Network&#xff09;、网络地址转换&#xff08;NAT, Network Address Translation&#xff09;和主机模式网络&#xff08;Host-only Networking&#xff09; 在虚拟化环境中&#xff0c;常见的三种网络模式是桥接网络&#xff08…

好用的Python开发工具合集

​ Python是一种功能强大且易于学习的编程语言&#xff0c;被广泛应用于数据科学、机器学习、Web开发等领域。随着Python在各个领域的应用越来越广泛&#xff0c;越来越多的Python开发工具也涌现出来。但是&#xff0c;对于新手来说&#xff0c;选择一款合适的Python开发工具可…

OpenHarmony开发实例:【鸿蒙.bin文件烧录】

使用HiBurn烧录鸿蒙.bin文件到Hi3861开发板 鸿蒙官方文档的“Hi3861开发板第一个示例程序”中描述了——如何使用DevEco Device Tool工具烧录二进制文件到Hi3861开发板&#xff1b; 本文将介绍如何使用HiBurn工具烧录鸿蒙的.bin文件到Hi3861开发板。 获取HiBurn工具 通过鸿蒙…

云仓酒庄品酒师培训破大世界基尼斯纪录,市场专业化趋势势如破竹

近日&#xff0c;云仓酒庄举办的品酒师培训活动成功创下大世界基尼斯纪录&#xff0c;这一荣誉不仅彰显了云仓酒庄在酒类培训领域的专业实力&#xff0c;更折射出酒类市场专业化趋势的势如破竹。随着酒类市场的日益成熟和消费者品鉴需求的提升&#xff0c;酒类市场专业化趋势对…

通义千问:官方开放API开发基础

目录 一、模型介绍 1.1主要模型 1.2 计费单价 二、前置条件 2.1 开通DashScope并创建API-KEY 2.2 设置API-KEY 三、基于DashScope SDK开发 3.1 Maven引入SDK 3.2 代码实现 3.3 运行代码 一、模型介绍 通义千问是由阿里云自主研发的大语言模型&#xff0c;用于理解和分…

Vitis HLS 学习笔记--BLAS库之WideType

目录 1. WideType 数据类型 2. WideType 类模板参数 2.1 SFINAE技术 3. WideType 类中的函数 3.1 operator[](unsigned int p_Idx) 3.2 operator(const WideType& p_w) const 3.3 getValAddr() 3.4 operator const t_TypeInt() 4. 总结 1. WideType 数据类型 在 …

Java+saas模式 智慧校园系统源码Java Android +MySQL+ IDEA 多校运营数字化校园云平台源码

Javasaas模式 智慧校园系统源码Java Android MySQL IDEA 多校运营数字化校园云平台源码 智慧校园即智慧化的校园&#xff0c;也指按智慧化标准进行的校园建设&#xff0c;按标准《智慧校园总体框架》中对智慧校园的标准定义是&#xff1a;物理空间和信息空间的有机衔接&#…

前端学习<四>JavaScript基础——26-闭包

闭包的引入 我们知道&#xff0c;变量根据作用域的不同分为两种&#xff1a;全局变量和局部变量。 函数内部可以访问全局变量和局部变量。 函数外部只能访问全局变量&#xff0c;不能访问局部变量。 当函数执行完毕&#xff0c;本作用域内的局部变量会销毁。 比如下面这样…

Ubuntu20从0开始选择合适版本手动安装cuda,torch-geometric,jax

一个全新的ubuntu20台式机&#xff0c;在Additional Drivers安装nvidia-470-server&#xff08;一开始安装450&#xff0c;cunda版本只能到11.0&#xff0c;torch有些库用不了&#xff0c;可以直接切换点击Apply Changes重启就行&#xff09; nvidia-smi查看CUDA Version可到…

Redis 配置与使用 (Linux 虚拟机Windows客户端)

Centos7 安装Redis详细教程 - JcongJason - 博客园 (cnblogs.com) 安装 下载redis安装包并解压 # 下载&#xff0c;我是在root下执行的下载&#xff0c;所以我的下载目录为&#xff1a;/root/redis-5.0.5&#xff0c;这里按照自己的实际情况调整 wget https://download.redi…

JVM、maven、Nexus

一、jvm简介 1.应用程序申请内存时出现的三种情况&#xff1a; ①OOM:内存溢出&#xff0c;是指应用系统中存在无法回收的内存或使用的内存过多&#xff0c;最终使得程序运行要用到的内存大于能提供的最大内存。此时程序就运行不了&#xff0c;系统会提示内存溢出&#xff0c…

css3实现微信扫码登陆动画

在做微信扫码登陆时&#xff0c;出现一个背景光图上下扫码动画&#xff0c;用css3图片实现。 实现原理&#xff1a; 1.准备一个渐变的背景.png图 2.css动画帧实现动画 看效果&#xff1a; css代码&#xff1a; #wx-scan{position: absolute;top:0px;left: 50%;z-index: 3;ma…

错误分析 (Machine Learning研习十九)

错误分析 您将探索数据准备选项&#xff0c;尝试多个模型&#xff0c;筛选出最佳模型&#xff0c;使用 Grid SearchCV微调其超参数&#xff0c;并尽可能实现自动化。在此&#xff0c;我们假设您已经找到了一个有前途的模型&#xff0c;并希望找到改进它的方法。其中一种方法就…

数据密集型应用系统设计 PDF 电子书(Martin Kleppmann 著)

简介 《数据密集型应用系统设计》全书分为三大部分&#xff1a; 第一部分&#xff0c;主要讨论有关增强数据密集型应用系统所需的若干基本原则。首先开篇第 1 章即瞄准目标&#xff1a;可靠性、可扩展性与可维护性&#xff0c;如何认识这些问题以及如何达成目标。第 2 章我们比…

JQuery(四)---【使用JQuery实现动画效果】

目录 前言 一.隐藏和显示 1.1使用方法 1.2案例演示(1) 1.3隐藏/显示效果一键切换 二.淡入淡出效果 2.1使用方法 2.2案例演示(fadeIn) 2.3案例演示(fadeOut) 2.4案例演示(fadeToggle) 2.5案例演示(fadeTo) 三.滑动 3.1使用方法 3.2案例演示(slideDown) 3.3案例演示…

三道模拟题

P1003 [NOIP2011 提高组] 铺地毯 题目描述 原题点这里-->P1003 [NOIP2011 提高组] 铺地毯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 为了准备一个独特的颁奖典礼&#xff0c;组织者在会场的一片矩形区域&#xff08;可看做是平面直角坐标系的第一象限&#xff09;铺…

黑马头条项目结构

微服务架构具有许多优点&#xff0c;其中一些主要优点包括&#xff1a; 松耦合性&#xff1a;每个微服务都是独立的&#xff0c;可以独立部署、独立扩展和独立更新&#xff0c;这种松耦合性使得系统更加灵活&#xff0c;易于维护和演化。 技术多样性&#xff1a;由于每个微服务…

鸿蒙开发 @ohos/hypium找不到问题

用的是最新的 开发工具 DevEco Studio 3.1.1 新建的空项目 报错 ohpm ERROR: Install failed ENOENT: no such file or directory, stat ‘E:\win\Project\MyApplication1\oh_modulesohos\hypium’ 解决方式 当前项目中 \oh_modules.ohpmohoshypium1.0.6\oh_modules 这里面有o…

算法练习第19天|222.完全二叉树的节点个数

222.完全二叉树的节点个数 222. 完全二叉树的节点个数 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/count-complete-tree-nodes/description/ 题目描述&#xff1a; 给你一棵 完全二叉树 的根节点 root &#xff0c;求出该树的节点个数。题目数据保…