【C++】STL使用仿函数控制优先级队列priority_queue


文章目录

  • 前言
  • 一、priority_queue的底层实现
  • 二、使用仿函数控制priority_queue的底层
  • 总结


前言

本文章讲解C++STL的容器适配器:priority_queue的实现,并实现仿函数控制priority_queue底层。


一、priority_queue的底层实现

priority_queue叫做优先级队列,它的底层结构是堆,在库中,默认生成的是大堆
在这里插入图片描述
在库的实现中,使用vector作为该优先级队列的适配容器。

由于priority_queue也是一个适配器,所以它的接口函数也可以对其他容器的函数进行封装使用。

在这里插入图片描述
下面来对priority_queue进行模拟实现。


#pragma once

//优先级队列底层是堆,heap
namespace bit
{
	//仿函数
	template<class T>
	class Less
	{
	public:
		bool operator()(const T& t1, const T& t2)
		{
			return t1 < t2;
		}

	};

	template<class T>
	class Greater
	{
	public:
		bool operator()(const T& t1, const T& t2)
		{
			return t1 > t2;
		}

	};

	//类名不是类型
	template<class T, class Container = vector<T>, class Compare = Less<T> >
	//默认大堆
	class PriorityQueue
	{
		//com是一个仿函数
		Compare com;
		void AdjustDown(int parent)
		{
			int child = parent * 2 + 1;
			while (child > 0)
			{
				//可能右孩子存在且大于左孩子
				if (child + 1 < _con.size() && com(_con[child],_con[child + 1]))
				{
					++child;
				}
				//如果孩子存在且孩子大于父亲,交换
				if (child < _con.size() && com(_con[parent],_con[child]))
				{
					swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}

		void AdjustUp(int child)
		{
			Compare com;
			//类名实例化类对象,该类型是一个仿函数,实例化的com可以调用仿函数的比较方法
			//记录父亲的下标
			int parent = (child - 1) / 2;

			while (child > 0)
			{	
				if (com(_con[parent], _con[child]))
				{
					swap(_con[child], _con[parent]);
				}
				child = parent;
				parent = (child - 1) / 2;
			}
		}

	public:

		PriorityQueue()
		{}

		//默认建大堆
		template<class InputIterator>
		PriorityQueue(InputIterator first, InputIterator end)
		{
			//插入数据
			while (first != end)
			{
				_con.push_back(*first);
				++first;
			}
			//向下调整建堆
			for (int i = (_con.size() - 1 - 1) / 2; i >= 0; --i)
			{
				AdjustDown(i);
			}
		}

		void push(const T& val)
		{
			//插入元素
			_con.push_back(val);
			//然后向上调整
			AdjustUp(_con.size() - 1);
		}

		void pop()
		{
			//1.堆顶和堆尾交换
			swap(_con[0], _con[_con.size() - 1]);

			//2.删除堆尾
			_con.pop_back();

			//3.向下调整,恢复堆
			AdjustDown(0);
		}

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

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

	private:
		Container _con;

注解:

  • 1.在进行构造时,我们使用迭代区间进行构造
    • (1)向空间中插入数据
    • (2)向下调整建堆,建N个数据,从第一个非叶子节点开始进行建堆,每次都向下调整,时间复杂度O(N)。

push函数的实现:
向堆尾插入一个元素,插入的元素可能会改变堆的结构,所以我们需要将该元素向上调整,以维护堆的特性。

pop函数的实现:
删除堆顶元素,首先将堆顶元素和堆的最后一个元素进行交换,然后进行尾删,删除后原来的堆尾的元素现在在堆顶,我们需要将其进行向下调整以维持堆的状态。

empty函数:
判断堆是否为空即可。

size函数:
计算堆的元素个数。

top函数:
取堆顶元素,也就是取第一个元素。

priority_queue容器的作用:有需要使用堆的地方就可以使用该容器。

二、使用仿函数控制priority_queue的底层

什么是仿函数?
仿函数:仿造的函数,它并不是一个真正意义上的函数。能够重载operator(),内部实现自己想要的方法,然后实例化出一个方法对象,调用该对象作为参数,此时就可以调用该方法类对象内部实现的operator()方法了。

由于库中的优先级队列的底层结构是堆,且默认是大堆,我们在模拟实现和使用时,如果遇到需要使用小堆的场景,需要改变的东西很多,比如向上调整算法和向下调整算法的比较方法。
现在我们需要指定一个方法,这个方法可以由我们控制,从而实现大小堆。

仿函数:

//仿函数
template<class T>
class Less
{
public:
	bool operator()(const T& t1, const T& t2)
	{
		return t1 < t2;
	}

};

template<class T>
class Greater
{
public:
	bool operator()(const T& t1, const T& t2)
	{
		return t1 > t2;
	}

};

这里实现了两个类,并重载了一个比较方法。
在priority_queue模板的实现中,我们可以多传递一个模板参数:class Compare,也就是多传递一个比较方法。然后我们自己写一个比较方法传递过去,然后在priority_queue的实现中实例化一个方法对象来控制堆的生成。


总结

本文章讲解了仿函数控制priority_queue容器的底层实现的过程以及priority_queue的底层实现。

只要有堆的地方就可以使用priority_queue容器。

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

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

相关文章

C进阶:文件操作

C语言文件操作 什么是文件 磁盘上的数据是文件。 但是在程序设计中&#xff0c;我们一般谈的文件有两种&#xff1a;程序文件&#xff08;例如.c,.h这一类编译&#xff0c;链接过程中的文件&#xff09;&#xff0c;数据文件。 程序文件 包括源程序文件&#xff08;后缀为.c&…

【PostgreSQL内核学习(十)—— 查询执行(可优化语句执行)】

可优化语句执行 概述物理代数与处理模型物理操作符的数据结构执行器的运行 声明&#xff1a;本文的部分内容参考了他人的文章。在编写过程中&#xff0c;我们尊重他人的知识产权和学术成果&#xff0c;力求遵循合理使用原则&#xff0c;并在适用的情况下注明引用来源。 本文主要…

macOS系统下编译linux-adk源码

1.下载 linux-adk源码 https://github.com/gibsson/linux-adk.git 2.安装libusb库 brew install libusb 3.修改Makefile CFLAGS += -Isrc -I/usr/local/Cellar/libusb/1.0.26/include/libusb-1.0 4.编译 make ./linux-adk -h 查看用法 查看系统已连接USB设备 system_p…

C#使用Linq和Loop计算集合的平均值、方差【标准差】

方差【标准差】 标准差公式是一种数学公式。标准差也被称为标准偏差&#xff0c;或者实验标准差&#xff0c;公式如下所示&#xff1a; 样本标准差方差的算术平方根ssqrt(((x1-x)^2 (x2-x)^2 ......(xn-x)^2)/n) 总体标准差σsqrt(((x1-x)^2 (x2-x)^2 ......(xn-x)^2)/n ) …

win11我们无法创建新的分区也找不到现有的分区

U盘重装系统的时候 提示&#xff1a;win11我们无法创建新的分区也找不到现有的分区 ShiftF10 &#xff0c;调出 命令提示符&#xff1b; diskpart list disk select disk 盘编号 clean convert gpt 参考&#xff1a;怎么解决我们无法创建新的分区也找不到现有的分区问题&#x…

OpenCV实现照片换底色处理

目录 1.导言 2.引言 3.代码分析 4.优化改进 5.总结 1.导言 在图像处理领域&#xff0c;OpenCV是一款强大而广泛应用的开源库&#xff0c;能够提供丰富的图像处理和计算机视觉功能。本篇博客将介绍如何利用Qt 编辑器调用OpenCV库对照片进行换底色处理&#xff0c;实现更加…

HTTP 缓存机制 强制缓存/协商缓存

为什么被缓存&#xff0c;如何命中缓存以及缓存什么时候生效的&#xff0c;我们却很少在实际开发中去了解。借助动画形式来从根上理解 HTTP 缓存机制及原理。 HTTP 缓存&#xff0c;对于前端的性能优化方面来讲&#xff0c;是非常关键的&#xff0c;从缓存中读取数据和直接向服…

REST和RPC的区别

1 REST REST 不是一种协议&#xff0c;它是一种架构。大部分REST的实现中使用了RPC的机制&#xff0c;大致由三部分组成&#xff1a; method&#xff1a;动词&#xff08;GET、POST、PUT、DELETE之类的&#xff09;Host&#xff1a;URI&#xff08;统一资源标识&#xff09;&…

前端Vue uni-app App/小程序/H5 通用tree树形结构图

随着技术的发展&#xff0c;开发的复杂度也越来越高&#xff0c;传统开发方式将一个系统做成了整块应用&#xff0c;经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改&#xff0c;造成牵一发而动全身。 通过组件化开发&#xff0c;可以有效实现…

Apple M1 Pro macOS 切换中文输入法卡住

(macOS 在切换中文输入法时出现卡住的情况 1&#xff0c;切换为中文输入法后再次卡住2&#xff0c;杀死 简体中文输入方式的进程参考 将光标移到菜单栏的输入法切换为英文输入法 多次切换为英文输入法&#xff0c;可以切换为英文输入法 切换为英文输入法后电脑不卡顿了&#xf…

云原生基础设施实践:NebulaGraph 的 KubeBlocks 集成故事

像是 NebulaGraph 这类基础设施上云&#xff0c;通用的方法一般是将线下物理机替换成云端的虚拟资源&#xff0c;依托各大云服务厂商实现“服务上云”。但还有一种选择&#xff0c;就是依托云数据基础设施&#xff0c;将数据库产品变成为云生态的一环&#xff0c;不只是提供自身…

《面试1v1》如何提高远程用户的吞吐量

&#x1f345; 作者简介&#xff1a;王哥&#xff0c;CSDN2022博客总榜Top100&#x1f3c6;、博客专家&#x1f4aa; &#x1f345; 技术交流&#xff1a;定期更新Java硬核干货&#xff0c;不定期送书活动 &#x1f345; 王哥多年工作总结&#xff1a;Java学习路线总结&#xf…

【单调栈 +前缀和】AcWing 4738. 快乐子数组

原题链接 原题链接 相关算法概念介绍 前缀和&#xff08;Prefix Sum&#xff09; 前缀和是指将数组中从开头位置到当前位置的所有元素累加得到的新数组。通常&#xff0c;我们使用一个额外的数组来保存这些累加和&#xff0c;这个数组被称为前缀和数组。对于原始数组A&…

性能测试Ⅵ(总结)

locust&#xff1a;是基于Python语言的性能测试工具&#xff0c;它是基于协程的思想来进行设计的。Python语言是没有办法利用多核的优势&#xff0c;所以了Python为了解决这个问题&#xff0c;设计了协程&#xff0c;作为协程的任务&#xff0c;遇到IO堵塞就立刻切换。 生命是协…

项目初始化--uniapp--vscode--vue3--ts

HBuilderX 创建 uni-app 项目 注意开启服务端口 目录结构 ├─pages 业务页面文件存放的目录 │ └─index │ └─index.vue index页面 ├─static 存放应用引用的本地静态资源的目录(注意&#xff1a;静态资源只能存放于此) ├─unpackage …

Ceph 应用

Ceph 应用 一、创建 CephFS 文件系统 MDS 接口 1.服务端操作 1&#xff09;在管理节点创建 mds 服务 cd /etc/ceph ceph-deploy mds create node01 node02 node032&#xff09;查看各个节点的 mds 服务 ssh rootnode01 systemctl status ceph-mdsnode01 ssh rootnode02 syst…

代码随想录| 图论02●695岛屿最大面积 ●1020飞地的数量 ●130被围绕的区域 ●417太平洋大西洋水流问题

#695岛屿最大面积 模板题&#xff0c;很快.以下两种dfs&#xff0c;区别是看第一个点放不放到dfs函数中处理&#xff0c;那么初始化的area一个是1一个是0 int dir[4][2]{0,1,0,-1,1,0,-1,0};void dfs(int x, int y,int n, int m, int &area,vector<vector<bool>…

K8S初级入门系列之十一-安全

一、前言 安全是K8S重要的特性&#xff0c;在K8S初级入门系列之四-Namespace/ConfigMap/Secret章节&#xff0c;我们已经已经了解了Namespace&#xff0c;Secret与安全相关的知识。本篇将梳理K8S在安全方面的策略。主要包括两个方面&#xff0c;API安全访问策略以及Pod安全策略…

YOLOv5基础知识

定位和检测: 定位是找到检测图像中带有一个给定标签的单个目标 检测是找到图像中带有给定标签的所有目标 图像分割和目标检测的区别&#xff1a; 图像分割即为检测出物体的完整轮廓&#xff0c;而目标检测则是检测出对应的目标。&#xff08;使用框框把物体框出来&#xff…

Linux:多进程和多线程回环socket服务器和客户端

多进程socket服务器代码&#xff1a; #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <string.h> #include <ctype.h> #include <sys/wait.h> #i…