C++——priority_queue模拟实现过程中的优化

news2024/11/6 18:02:19

前言的前言:

大佬写博客给别人看,菜鸟写博客给自己看,我是菜鸟。

前言:

1.priority_queue(优先队列)的底层原理和堆极其相似,因此在模拟实现的过程中,主要借助堆的思想取完成(但是本质还是队列)

2.本篇除了模拟实现优先队列外,还通过仿函数,类模板进行优化。 

3.对于类模板的调用问题,系统会根据最匹配原则进行调用,仿函数也是

☆☆☆4.无论是类,还是类模板,都只不过是空中楼阁,是写给你看的,当你不去实例化对象时,他是没有任何意义的,只有在实例化对象后,才可以使用内部公共的函数或成员!!!!!

5.针对4,一个命名空间里可以有很多类模板,就像list迭代器那篇一样(有三个类模板,假设为A、B、C),但是如果你不在C中实例化对象A,是无法使用A中的函数的。所以说,不要被很多行代码吓到,其实没什么,只不过是在C里面需要实例化对象A,和B,方可以对其中的函数进行调用。

5.对于不好好学习前面基础知识的博主(我)而言,简直是一场灾难。

一:优先队列的模拟实现(最初版本)

因为和堆极其类似,这里无需过多的赘述,代码如下:

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

		//向上调整函数
		void AdjustUp(int child)
		{
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				if (_con[parent] > _con[child])
				{
					swap(_con[parent], _con[child]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

		//入数据
		void push(const T& x)
		{
			_con.push_back(x);
			AdjustUp(_con.size()-1);
		}

		//向下调整函数
		void AdjustDown(int parent)
		{
			int child = 2 * parent + 1;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && _con[child] > _con[child + 1])
				{
					child++;
				}
				if (_con[parent] > _con[child])
				{
					swap(_con[parent], _con[child]);
					parent = child;
					child = 2 * parent + 1;
				}
				else
				{
					break;
				}
			}
		}

		//出数据
		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			AdjustDown(0);
		}
		//判空
		bool empty()
		{
			return _con.empty();
		}
		//取堆顶数据
		const T& top() const
		{
			return _con.front();
		}
		//容器数据个数
		size_t size() const
		{
			return _con.size();
		}

	private:
		Container _con;
	};
}

注1:

👉:出入数据是借助库函数(vector、list、deque)来实现的

👉:入数据时,要考虑建小堆,还是大堆,因此需要用到向上调整函数

👉:因为vector本身没有实现出堆顶数据,因此这里统一的想法是:交换堆顶和堆尾的数 据,再出数据,出完数据后,为了保持小堆/大堆,需要用到向下调整函数,这和建出数据是一致的。

👉:其他函数,例如top(),size(),empty(),.则能用库函数就用库函数实现

注2:这里还需要注意的一个地方是(其实也不是什么注意点,对于博主我而言比较重要)

👉:想必C++学到这个阶段时,大家对与函数中实参和形参之间的传递,以及类中默认构造的缺省值给如何给定都了然于心。但对于博主我而言,类模板是之前学习中,被我忽视的一个东西,其实类模板在很多地方与函数传参以及缺省值都极其类似,除此之外,这里还需要加深对类,以及类模板的认识,这个我们稍后再说。

先讨论上述代码中的这几段:

namespace jc
{
	template<class T,class Container = vector<T>,class Compare = less<T>>
	class priority_queue
	{
    public:
   
    /*
    ....
    */

    private:
    };
}

我们再来看看主函数中是如何实例化对象的:

int main()
{
    jc::priority_queue<int> pq;
    return 0;
}

结合先前知识,可以总结出以下几点:

①:类模板在实例化对象时,必须显式实例化(这个是先前学的,顺便提一句,函数模板是不需要显式实例化的

②:类模板声明的固定格式为:template<class T1,class T2,....> (注:T1,T2名字不固定,根据参数的实际意义命名即可,用多了就熟悉了)

③:类模板在实例化对象时,可以缺省参数(这里和默认构造函数中的缺省参数一样的用法)

④:针对③中,缺省值不是随随便便给的!一定要根据参数的实际意义来给定值

注:对④的补充说明(以上述代码为例):

类模板声明中

class T :是为了说明,容器中存储的是什么类型的数据。

class Container:是为了说明,这些数据存储在什么容器当中,这里使用了缺省参数,说明默认情况下是vector<int>,(还需补充一点是,类名不是类型!类名<显式实例化> 才是类型)

class Compare = less<T>:这一点是为了代码后续方便维护,新加的参数是用来方便判断是建立小堆还是大堆,这个后续优化的时候会说。

二:优化

优化方向:只需通过传参来实现大小堆之间的切换,而不需要重新写大堆代码或者是小堆代码。

结合上述优化方向以及前言中提到的第4点,做出以下优化:

namespace jc
{
    //定义两个个类模板来控制向上和向下调整函数中是建立大堆还是小堆
	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;
		}
	};

    //less是库函数自带的函数,因此我们需要调用自己命名空间内的less函数
	template<class T,class Container = vector<T>,class Compare = jc::less<T>>
	class priority_queue
	{
	public:

		priority_queue() = default;//在我们不写默认构造函数时,系统会自动调用自定义类类型成员       
                                   //的默认构造函数,当但我们写了默认构造函数时,系统就不会调
                                   //用,因此这里强制他调用默认构造,是为了其自定义类成员能够        
                                   //调用其默认的构造函数。
		

        Compare com;               //此处就是前言4所说的,不实例化对象,是没法调用其他类模板中            
                                   //的函数的       

		template<class InputIterator>//函数模板,需要显式实例化,系统会根据最匹配的来
		priority_queue(const InputIterator left,const InputIterator right)
			:_con(left,right)
		{
			cout << typeid(InputIterator).name() << endl;//为了观察InputIterator本质是什么
			for (int i = _con.size()-1; i >=0; i--)
			{
				AdjustDown(i);
			}
		}

	private:
		Container _con;
	};
}

上面这串代码涉及的问题很多,我们来一一讨论:

1:类模板参数的缺省参数不是随便命名的

先前就已经提过这一点,我们来看看这里的命名是有多么的巧妙:这串代码的实际意义是,将一串数组按大堆建堆,数据类型是int,容器是vector,为了后续代码维护,所以类目中加了第三个参数 Compare,这个参数是为了控制建立小堆还是大堆,默认情况下是大堆。

less<int> 是系统自带的类,因此这里我们在命名空间中自己定义了一个less类来做比较,

复习:命名空间可以与外界库函数隔绝,避免重命名,通过 命名空间名:: 的方式调用。

我们需要的比较函数,放在这个自定义类当中,因此priority_queue类中,需要对该模板类进行实例化对象后,方可使用,而用来对比的函数是通过运算符重载的方式实现的,具体为 operator(),原本的写法应该为 com.operator()(参数A,参数B),省略后可以写成 com(参数A,参数B)

2:系统会调用最匹配的函数:

我们在主函数中写下了这么一串代码:该代码中,通过地址来进行默认构造

int main()
{
	int arr[] = { 6,1,2,3,7,5 };
	jc::priority_queue<int> pq(arr,arr+sizeof(arr)/sizeof(int));

	return 0;
}

在头文件中,对应这一部分代码:

		template<class InputIterator>
		priority_queue(const InputIterator left,const InputIterator right)
			:_con(left,right)
		{
			cout << "InputIterator is :" << typeid(InputIterator).name() << endl;
			for (int i = _con.size()-1; i >=0; i--)
			{
				AdjustDown(i);
			}
		}

运行后的结果为:

可以发现模板参数会根据函数中实际参数类型,变为int*型。

3:模板的特化

有时我们实现的对比函数没法实现,就比如当类型是Date*类型时,这时我们需要用到模板特化,具体代码如下:

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

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

注:一定要先有基础模板,才可以使用特化的模板。

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

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

相关文章

蓝桥杯真题——三角回文数(C语言)

问题描述 对于正整数 n, 如果存在正整数 k 使得 n123⋯kk(k1)2n123⋯kk(k1)/2​, 则 n 称为三角数。例如, 66066 是一个三角数, 因为 66066123⋯36366066123⋯363 。 如果一个整数从左到右读出所有数位上的数字, 与从右到左读出所有数位 上的数字是一样的, 则称这个数为回文数…

密码学知识点整理一:密码学概论

密码学是什么&#xff1f; 密码学是一门研究编制密码和破译密码的技术科学。 密码学&#xff0c;作为信息安全的核心技术之一&#xff0c;其重要性在于能够为信息传输提供安全保障&#xff0c;确保数据在存储或传输过程中的机密性、完整性与真实性不被破坏。从古至今&#x…

51单片机教程(五)- LED灯闪烁

1 项目分析 让输入/输出口的P1.0或P1.0~P1.7连接的LED灯闪烁。 2 技术准备 1、C语言知识点 1 运算符 1 算术运算符 #include <stdio.h>int main(){// 算术运算符int a 13;int b 6;printf("%d\n", ab); printf("%d\n", a-b); printf("%…

Unity中实现伤害飘字或者提示飘字效果(DoTween实现版本)

&#xff01;&#xff01;&#xff01;在实现以下效果之前&#xff0c;一定要往项目中导入DoTween插件。 一、搭建测试场景 1、在场景中新建一个带有Text组件的游戏物体A&#xff0c;并把这个游戏物体A中Text组件的Color属性中alpha值为0&#xff0c;让文字在场景中隐藏。 …

其他节点使用kubectl访问集群,kubeconfig配置文件 详解

上述两种方式&#xff1a;可使用kubectl连接k8s集群。 $HOME/.kube/config 是config文件默认路径&#xff0c;要么直接定义环境变量&#xff0c;要么就直接把文件拷过去 config文件里面&#xff0c;定义了context&#xff0c;里面指定了用户和对应的集群信息&#xff1a; ku…

【vim文本编辑器gcc编译器gdb调试器】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、vimvim安装vim常用快捷键vim使用vimtutor zh文档 二、gcc编译器安装gcc工具编译源代码 三、gdb调试器gdb安装gdb常用指令gdb简单上手使用gdb的单步调试功能 总结…

陀螺仪BMI323驱动开发测试(基于HAL库SPI通信)

参考资料 编写代码 读取芯片ID void BMI160_Init(void) {uint16_t chipID BMI323_read(BMI160_REG_CHIP_ID);debug("BMI323芯片ID为0x%x;", chipID);if (chipID ! 0x43){debug("未检测到BMI323;");}elsedebug("检测到陀螺仪BMI323;");u8 buf_…

【MySQL初阶】--- MySQL在Ubuntu环境下安装

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; MySQL 本篇博客博主采用的是ubuntu 22.04的系统按照MySQL&#xff0c;且在root用户下安装。 &#x1f3e0; MySQL安装 1. 更新系统的软件包列表 sudo a…

Charles简单压力测试

1.接口请求次数&#xff0c;并发量&#xff0c;请求延迟时间均可配置 1.1选中需要进行测试的接口&#xff0c;鼠标右键选中【repeat advance】 2.设置并发参数 下面的图中&#xff0c;选择了1个接口&#xff0c;每次迭代中1个接口同时请求&#xff0c;迭代1000次&#xff08;…

【大模型LLM面试合集】大语言模型架构_chatglm系列模型

chatglm系列模型 1.ChatGLM 1.1 背景 主流的预训练框架主要有三种&#xff1a; autoregressive自回归模型&#xff08;AR模型&#xff09;&#xff1a;代表作GPT。本质上是一个left-to-right的语言模型。通常用于生成式任务&#xff0c;在长文本生成方面取得了巨大的成功&a…

从 vue 源码看问题 — 你知道 Hook Event 吗?

前言 在之前的几篇文章中&#xff0c;都有提到 vue 中调用生命周期钩子时是通过 callHook() 方法进行调用的&#xff0c;比如在初始化篇章中调用 beforeCreate 和 created 生命周期钩子方式如下: 那么接下来一起来了解下到底什么是 Hook Event &#xff1f; Hook Event 是什…

html练习2

实现下列图片的效果 代码&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style>* {margin: 0;padding: 0;}#menu {background-color: #0c0048;width: 100%;height: 50px;margin: auto;…

计算机视觉常用数据集Cityscapes的介绍、下载、转为YOLO格式进行训练

我在寻找Cityscapes数据集的时候花了一番功夫&#xff0c;因为官网下载需要用公司或学校邮箱邮箱注册账号&#xff0c;等待审核通过后才能进行下载数据集。并且一开始我也并不了解Cityscapes的格式和内容是什么样的&#xff0c;现在我弄明白后写下这篇文章&#xff0c;用于记录…

Java | Leetcode Java题解之第523题连续的子数组和

题目&#xff1a; 题解&#xff1a; class Solution {public boolean checkSubarraySum(int[] nums, int k) {int m nums.length;if (m < 2) {return false;}Map<Integer, Integer> map new HashMap<Integer, Integer>();map.put(0, -1);int remainder 0;fo…

MATLAB计算朗格朗日函数

1. 朗格朗日函数介绍 朗格朗日函数&#xff08;Lagrange function&#xff09;通常用于优化问题&#xff0c;尤其是带有约束的优化问题。其一般形式为&#xff1a; 其中&#xff1a; f(x) 是目标函数。 是约束条件。 是拉格朗日乘子。 为了编写一个MATLAB代码来计算和绘制…

手机的ip地址是固定的吗?多角度深入探讨

手机的IP地址是否固定&#xff0c;这一问题涉及到网络连接、技术配置以及运营商策略等多个方面。为了全面解答这一问题&#xff0c;我们需要从多个角度进行深入探讨。 首先&#xff0c;明确IP地址&#xff08;Internet Protocol Address&#xff09;的基本概念。IP地址是互联网…

宠物空气净化器推荐,哪款除毛好、噪音小?希喂、352性能对比

大家都有选购宠物空气净化器时在各大品牌里挑挑拣拣、费时费力的体验吧...本以为只要多看点推荐&#xff0c;确定了品牌&#xff0c;就能买到好用的产品&#xff0c;不过实际情况却并非如此。 身为宠物博主&#xff0c;之前用过不少宠物空气净化器&#xff0c;20年还写过几篇测…

`掌握Python-PPTX,让PPt制作变得轻而易举!`

文章目录 掌握Python-PPTX&#xff0c;让PPT制作变得轻而易举&#xff01;背景介绍python-pptx 是什么&#xff1f;如何安装 python-pptx&#xff1f;简单库函数使用方法应用场景常见Bug及解决方案总结 掌握Python-PPTX&#xff0c;让PPT制作变得轻而易举&#xff01; 背景介绍…

【python】OpenCV—Connected Components

文章目录 1、任务描述2、代码实现3、完整代码4、结果展示5、涉及到的库函数6、参考 1、任务描述 基于 python opencv 的连通分量标记和分析函数&#xff0c;分割车牌中的数字、号码、分隔符 cv2.connectedComponentscv2.connectedComponentsWithStatscv2.connectedComponents…

ENSP (虚拟路由冗余协议)VRRP配置

VRRP&#xff08;Virtual Router Redundancy Protocol&#xff0c;虚拟路由冗余协议&#xff09;是一种用于提高网络可用性和可靠性的协议。它通过在多个路由器之间共享一个虚拟IP地址&#xff0c;确保即使一台路由器发生故障&#xff0c;网络依然能够正常运行&#xff0c;防止…