C++:优先级队列模拟实现和仿函数的概念使用

news2024/11/18 13:57:06

文章目录

  • 使用方法
  • Compare仿函数
  • 一些场景
  • 模板参数和函数参数

本篇总结优先级队列

使用方法

首先在官网查看它的一些用法

template <class T, class Container = vector<T>,
  class Compare = less<typename Container::value_type> > class priority_queue;

从它的介绍可以看出,也是一个用到了容器适配器的容器,这里不同于stackqueue的适配器,这里使用的是vector作为它的适配器,也是用了模板来实例化,但是多了一个Compare的概念,关于Compare后面来引入

具体用法:

Priority queue
Priority queues are a type of container adaptors, specifically designed such that its first element is always the greatest of the elements it contains, according to some strict weak ordering criterion.

This context is similar to a heap, where elements can be inserted at any moment, and only the max heap element can be retrieved (the one at the top in the priority queue).

Priority queues are implemented as container adaptors, which are classes that use an encapsulated object of a specific container class as its underlying container, providing a specific set of member functions to access its elements. Elements are popped from the “back” of the specific container, which is known as the top of the priority queue.

The underlying container may be any of the standard container class templates or some other specifically designed container class. The container shall be accessible through random access iterators and support the following

从上面的英文中可以很明显的获取到信息,它的用法和堆非常类似,因此对应的接口设计也很像,下面用实验来简单使用一下
在这里插入图片描述

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

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

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

	return 0;
}

上面是对优先级队列的简单使用,从中可以看出,在队列中添加了3,2,1,4四个元素,但是最后输出的确实4,3,2,1,证明它确实是和堆是一样的,那么基于这个原理就可以对它进行模拟实现了,具体实现如下:

// Version1--初级版本
#include <iostream>
#include <vector>
using namespace std;

namespace my_priority_queue
{
	template<class T, class Container = vector<int>>
	class priority_queue
	{
	public:
		bool empty()
		{
			return _con.empty();
		}
		size_t size()
		{
			return _con.size();
		}
		const T& top()
		{
			return _con.front();
		}
		void adjust_up(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);
			adjust_up(_con.size() - 1);
		}
		void adjust_down(int parent)
		{
			int child = parent * 2 + 1;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && _con[child + 1] > _con[child])
				{
					child++;
				}
				if (_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);
		}
	private:
		Container _con;
	};
}

整体来说实现难度不大,只是需要对于向上和向下调整算法要熟悉,有了这两个算法解决问题并不难,但是这样的实现虽然可以适配前面的测试用例,但是并不是库内实现的方法,库内的实现还有一个Compare的概念

Compare仿函数

对于库内的函数,只能实现降序排序,很明显库内不可能只支持降序输出,那么如何控制降序输出?

库中定义的仿函数默认是less,与之对应的还有greater,如果使用greater就将是升序输出:

int main()
{
	priority_queue<int,vector<int>,less<int>> pq1;
	pq1.push(3);
	pq1.push(2);
	pq1.push(1);
	pq1.push(4);

	cout << "降序排列:" << endl;
	while (!pq1.empty())
	{
		cout << pq1.top() << " ";
		pq1.pop();
	}
	cout << endl;

	priority_queue<int, vector<int>, greater<int>> pq2;
	pq2.push(3);
	pq2.push(2);
	pq2.push(1);
	pq2.push(4);

	cout << "升序排列:" << endl;
	while (!pq2.empty())
	{
		cout << pq2.top() << " ";
		pq2.pop();
	}
	cout << endl;

	return 0;
}

// 输出结果
降序排列:
4 3 2 1
升序排列:
1 2 3 4

那么这个lessgreater到底是什么?由此引出下面仿函数的概念

在前面实现的Version1的实现中,并没有实现这个功能,这是由于在建堆的时候,建立的是大堆还是小堆已经限定了,但是在实际的应用中这里不应该被限定,应该根据实际情况创建对应的堆,但是代码逻辑只能根据大于和小于来判断,由此引出可以使用仿函数来实现这个功能

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;
	}
};

上面就是对于仿函数的定义,其实从中可以看出仿函数其实就是一个只有一个函数的类,里面定义了运算符重载,而当程序执行到需要进行比大小的时候,就可以借助这个运算符重载得出结论,进而执行,这样就实现了改变大小于关系的函数

根据这个原理,就可以实现下面的过程:

// Version2
// 头文件
#include <iostream>
#include <vector>
using namespace std;

namespace my_priority_queue
{
	template<class T, class Container = vector<int>, class Compare = Less<class T> >
	class priority_queue
	{
	public:
		bool empty()
		{
			return _con.empty();
		}
		size_t size()
		{
			return _con.size();
		}
		const T& top()
		{
			return _con.front();
		}
		void adjust_up(int child)
		{
			Compare com;
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				if (com(_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);
			adjust_up(_con.size() - 1);
		}
		void adjust_down(int parent)
		{
			Compare com;
			int child = parent * 2 + 1;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
				{
					child++;
				}
				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);
		}
	private:
		Container _con;
	};
}

// main.c文件
#include <iostream>
#include <queue>
#include "priority_queue.h"
using namespace std;

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;
	}
};

void test1()
{
	my_priority_queue::priority_queue<int, vector<int>, Less<int>> pq1;
	pq1.push(3);
	pq1.push(2);
	pq1.push(1);
	pq1.push(4);

	cout << "降序排列:" << endl;
	while (!pq1.empty())
	{
		cout << pq1.top() << " ";
		pq1.pop();
	}
	cout << endl;

	my_priority_queue::priority_queue<int, vector<int>, Greater<int>> pq2;
	pq2.push(3);
	pq2.push(2);
	pq2.push(1);
	pq2.push(4);

	cout << "升序排列:" << endl;
	while (!pq2.empty())
	{
		cout << pq2.top() << " ";
		pq2.pop();
	}
	cout << endl;
}


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

具体的指向演示如下:

在这里插入图片描述

一些场景

其实仿函数的应用场景很多,这里说比较基础的场景

例如算法库中存在的sort排序函数,其实也是有仿函数的存在的

template <class RandomAccessIterator>
  void sort (RandomAccessIterator first, RandomAccessIterator last);
 
template <class RandomAccessIterator, class Compare>
  void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);

于是可以利用这些函数实现效果

void test2()
{
	Less<int> com;
	int arr[] = { 7,6,5,4,3,9,8,6 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	sort(arr, arr + sz, com);
	for (auto ch : arr)
	{
		cout << ch << " ";
	}
	cout << endl;

	Greater<int> comp;
	sort(arr, arr + sz, comp);
	for (auto ch : arr)
	{
		cout << ch << " ";
	}
	cout << endl;
}

模板参数和函数参数

对比模板参数和函数参数:

// 模板参数
template<class T, class Container = vector<int>, class Compare = Less<class T> >
my_priority_queue::priority_queue<int, vector<int>, Greater<int>> pq;

// 函数参数
sort(arr, arr + sz, Less<int>());

对于这里需要注意的是,模板参数内传的是类的名字,而函数传参传的是对象,因此上面的写法其实是匿名对象的写法

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

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

相关文章

软件测试之接口测试

1、什么是接口测试 顾名思义&#xff0c;接口测试是对系统或组件之间的接口进行测试&#xff0c;主要是校验数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及相互逻辑依赖关系。其中接口协议分为HTTP,WebService,Dubbo,Thrift,Socket等类型&#xff0c;测试类型又主…

Crypto:MD5

题目 下载了题目给的压缩包解压后&#xff0c;打开文件 使用md5解码器解码后得到&#xff0c;即为flag

DS18B20温度传感器

DS18B20简介 DS18B20 是由 DALLAS 半导体公司推出的一种的“一线总线&#xff08;单总线&#xff09;”接口的温度传感器 这种一线总线就是 三线制 SPI DS18B20的 配置寄存器&#xff1a; TM 是测试位&#xff0c;出厂设置就被设置为0&#xff0c;不需要改动&#xff0c; R1、R…

linux————ceph分布式部署

目录 一、概述 特点 1、统一存储 2、高扩展性 3、可靠性强 4、高性能 组件 1、Monitor 2、OSD 3、MOD 4、Objet 5、PG 6、RADOS 7. Libradio 8. CRUSH 9. RBD 10. RGW 11. CephFS 架构图 二、准备工作 三、ceph安装 创建集群目录 修改配置文件 安装 初…

数据结构上机1

1、题目&#xff1a; 将1~10存入数组a[10]&#xff0c;并将其逆序输出 #define _CRT_SECURE_NO_WARNINGS 1 //(1) 将1~10存入数组a[10]&#xff0c;并将其逆序输出#include <stdio.h>int main() {int a[10];// 将1到10存入数组a[10]for (int i 0; i < 10; i){a[i] i…

[硬件基础]-快速了解I2C串行通信协议

快速了解I2C串行通信协议 文章目录 快速了解I2C串行通信协议1、硬件接口2、数据帧3、数据操作4、时钟拉伸&#xff08;Clock Stretching&#xff09;5、总线仲裁6、权衡&#xff1a;功率与速度7、总结 内部集成电路协议&#xff08;Inter-Integrated Circuit Protocol&#xff…

TS编译选项——TS代码错误不生成编译文件

一、TS不生成编译文件 在tsconfig.js文件中配置noEmit属性 {"compilerOptions": {// outDir 用于指定编译后文件所在目录"outDir": "./dist", // 将编译后文件放在dis目录下// 不生成编译后的文件"noEmit": true,} } 二、TS代码错…

看到一个外贸经典案例, 分享一下

最近看到一个经典案例&#xff0c;案例可能没有多少新奇&#xff0c;但是大家的评论以及给出的解决方案却能给我们很多启发&#xff0c;一个事情要从多方面去进行假设然后一一排除去找到最合适的解决方法&#xff0c; 下面&#xff0c;让我们一起来看看这个外贸小伙伴遇到的问…

《你好,C语言》:从另一个视角学习并重新审视C语言的意义

《你好&#xff0c;C语言》&#xff1a;从另一个视角学习并重新审视C语言的意义 尽管C语言诞生了这么多年&#xff0c;但是它依然活跃在开发者一线&#xff0c;不可否认的是C语言的确有它独特的魅力。本文将从一个全新的视角&#xff0c;重新带领大家学习领悟C语言的奥秘&#…

[XR-FRAME] 1.O3 文档导览 || XR-FRAME / 有点寡淡,加上图像

开始 | 微信开放文档 文档导览 - XR-FRAME / 有点寡淡&#xff0c;加上图像 。 文档导览&#xff0c;知识点整理。 加入纹理 &#xff1a; 新学习标签&#xff1a; <xr-assets bind:progress"handleAssetsProgress" bind:loaded"handleAssetsLoaded…

TS编译选项——编译TS文件同时对JS文件进行编译

一、允许对JS文件进行编译 我们在默认情况下编译TS项目时是不能编译js文件的&#xff0c;如下图中的hello.js文件并未编译到dist目录下&#xff08;这里配置了编译文件放到dist目录下&#xff09; 如果我们想要实现编译TS文件同时对JS文件进行编译&#xff0c;就需要在tsconfi…

GIS基础教程之坐标系

本教程从以下几个方面入手&#xff1a; 坐标系的基本概念 地理坐标系 投影坐标系 如何选择坐标系 根据研究区域大小 根据研究目的&#xff08;等角&#xff1f;等面积&#xff1f;等距离&#xff1f;其他&#xff09; 推荐一个在线坐标系选择网站 GIS坐标系几种情况 数…

【dbeaver】win环境的kerberos认证和Clouders集群中Kerberos认证使用Dbeaver连接Hive和Phoenix

一、下载驱动 cloudera官网 1.1 官网页面下载 下载页面 的Database Drivers 挑选比较新的版本即可。 1.2 集群下载 Hive可能集群没有驱动包。驱动包名称&#xff1a;HiveJDBC42.jar。41结尾的包也可以使用的。注意Jar包的大小一定是十几MB的。几百KB的是thin包不可用。 …

nginx(七十九)nginx与tls/ssl续

一 nginx与tls/ssl续 说明&#xff1a; 本文是基于tls/ssl学习之后,对之前所写tls/ssl博客的补充,重复的不再啰嗦curve: [椭圆] 曲线补充&#xff1a; 会不定期的补充教育&#xff1a; 解决问题为出发点,不要停留在理论上​遗留&#xff1a; wiresahrk分析TLS 1.2 1.3 握手各…

win11系统固定到快速访问的文件夹无法调整顺序的问题

最近在使用win11系统时&#xff0c;固定到快速访问的文件夹无法调整顺序。网上搜了一大圈没有对应的解决方法&#xff0c;柳暗花明&#xff0c;在博主yin0hao的一篇文章中找到了类似的&#xff0c;跟着做了一下&#xff0c;结果问题也解决了。在此记录。 在文件资源管理器地址…

关于10万并发压测时弹性扩容失效问题回答

之前压测过2万用户在30秒内每个用户每秒5次请求&#xff0c;OPS大概在3千左右&#xff0c;但是弹性伸缩虽然在扩容预期值的时候很给力&#xff0c;也能将新加的服务器添加到负载均衡中&#xff0c;但是当cpu使用率超过80%的时候&#xff0c;我其实是有配置报警任务的&#xff0…

MySQL:基础操作(1)

下载配置mysql MySQL&#xff1a;码包安装mysql&#xff08;5.6.51&#xff09;_鲍海超-GNUBHCkalitarro的博客-CSDN博客https://blog.csdn.net/w14768855/article/details/133186897?spm1001.2014.3001.5501 初始登录MySQL mysql -u 用户名 [-p] 一开始什么都没设置 mysql…

MIPI协议介绍-CPHY

MIPI协议概述 MIPI(Mobile Industry Processor Interface): 是MIPI联盟发起为移动应用处理器制定的开放标准.MIPI接口协议层主要包括CSI和DSI两种,其中CSI主要用于图像输出&#xff0c;如图像传感器等&#xff1b; DSI主要用于图像输入&#xff0c;如屏幕显示器等.对于camera而…

AUTOSAR扫盲贴--不是黑神话【基本概念和方法论】

猴子纵有72搬变化,也跳不出如来的手掌 目录 1. 引言 2. AUTOSAR的基本概念 2.1. AUTOSAR的架构和组成部分 2.2. AUTOSAR的规范和

验证和观察Activity生命周期【Intent跳转】

创建测试项目&#xff0c;验证和观察Activity生命周期&#xff0c;使用Intent实现Activity之间的跳转&#xff1a; 1、创建TestActivity项目&#xff1b; 2、创建SecondActivity及相应布局&#xff1b; 3、在MainActivity和SecondActivity中重写onStart()、onResume()等生命…