当前位置: 首页 > article >正文

【priority_queue的使用及模拟实现】—— 我与C++的不解之缘(十六)

前言

​ priority_queue,翻译过来就是优先级队列,但是它其实是我们的堆结构(如果堆一些遗忘的可以看一下前面的文章复习一下【数据结构】二叉树——顺序结构——堆及其实现_二叉树顺序结构-CSDN博客),本篇文章就来使用并且模拟实现一下priority_queue。

priority_queue的使用

在这里插入图片描述

​ 这个容器接口就这些,使用起来比较简单:这里就简单使用一下。

int main()
{
	priority_queue<int> pq1;//无参构造
	int arr[] = { 1,5,8,9,2,10,6 };
	//使用一段迭代器区间构造(这里可以使用数组,因为原始指针可以像迭代器那样使用)
	priority_queue<int> pq2(arr, arr + sizeof(arr) / sizeof(arr[0]));
	//依次输出pq2
	while (!pq2.empty())
	{
		cout << pq2.top() << " ";
		pq2.pop();
	}
	cout << endl;
	
	//往pq1插入数据
	pq1.push(1);
	pq1.push(5);
	pq1.push(7);
	pq1.push(8);
	pq1.push(9);
	//依次输出pq1
	while (!pq1.empty())
	{
		cout << pq1.top() << " ";
		pq1.pop();
	}
	cout << endl;
	return 0;
}

输出结果如下:

10 9 8 6 5 2 1
9 8 7 5 1

​ 经过观察,我们会发现,默认构建的都是大堆,先看一下容器priority_queue 的定义

在这里插入图片描述

​ 这里有三个模版参数,第一个是数据类型,第二个是容器类型(默认是vector),第三个是compare 默认是 less(这个是一个仿函数,再模拟实现后面再讲解)。

我们不难发现,只有第三个模版参数才也可能控制是大堆还是小堆,(这里,我们如果要建小堆传**greater**即可)。

//建小堆
int main()
{
	priority_queue<int, vector<int>, greater<int>> pq1;
	pq1.push(9);
	pq1.push(7);
	pq1.push(5);
	pq1.push(3);
	pq1.push(2);
	while (!pq1.empty())
	{
		cout << pq1.top() << " ";
		pq1.pop();
	}
	cout << endl;
	return 0;
}
2 3 5 7 9

​ 这样就建了小堆;对于仿函数,在模拟实现之后,我相信就会有了深刻的理解。

priority_queue模拟实现

​ 通过看priority_queue的定义,我们不难发现其也是一个容器适配器,默认容器是vector;所以它的成员变量就是一个容器,其的每一个操作就是在容器中进行一系列操作。

先来看一下priotity_queue的大致内容:

namespace HL
{
	//默认——大堆
	template<class T, class Contianer = vector<T>, class Compare = less<T>>
	class priority_queue
	{
		//向下调整算法
		void AdjustDown(int parent)
		{}
		//向上调整算法
		void AjustUp(int child)
		{}
	public:
		//默认构造
		priority_queue() {}
		//迭代器区间构造
		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
		{}
		void push(const T& x) {}
		void pop() {}
		T& top() {}
		const T& top()const {}
		size_t size()const {}
		bool empty()const {}
	private:
		Contianer _con;
	};
}

1、向上调整算法

​ 所谓向上调整算法,就是向上调整当前节点,使其满足堆结构。

主要应用:插入节点时,最后一个节点(插入的节点)向上调整。

		//向上调整算法
		void AjustUp(int child)
		{
			int parent = (child - 1) / 2;
			while (parent >= 0)
			{
				if (_con[parent] < _con[child])
				{
					std::swap(_con[parent], _con[child]);
				}
				else
				{
					break;
				}
				child = parent;
				parent = (child - 1) / 2;
			}
		}

2、向下调整算法

​ 所谓向上调整算法,就是向下调整当前节点,使其满足堆结构。

主要应用:

  • 构造堆结构时,从最后一个节点的父节点开始依次向下调整,创建堆结构。
  • 删除节点时,第一个(堆顶)与最后一个(堆底)交换,然后让第一个(堆顶)节点向下调整。
    在这里插入图片描述
		void AdjustDown(int parent)
		{
			int child = 2 * parent + 1;
			while (child < _con.size())
			{
				if (child < _con.size() - 1 && _con[child] < _con[child + 1])
				{
					child++;
				}
				if (_con[parent] < _con[child])
				{
					std::swap(_con[parent], _con[child]);
				}
				else
				{
					break;
				}
				parent = child;
				child = 2 * parent + 1;
			}
		}

3、构造函数

​ 构造函数有两种,一是默认构造函数,另一个就是使用迭代器区间构造。

1.默认构造

//默认构造
priority_queue() {}

2.迭代器区间构造

​ 迭代器区间,这里就先将数据插入到容器(vector)中,再从最后一个节点的父节点开始,依次往下调整建堆即可。

//迭代器区间构造
template<class InputIterator>
priority_queue(InputIterator first, InputIterator last)
{
	while (first != last)
	{
		_con.push_back(*first);
		++first;
	}
	for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(i);
	}
}	

4、push、pop、top

  • push: 插入数据,然后向上调整,保持堆结构。
void push(const T& x) 
{
	_con.push_back(x);
	AdjustUp(_con.size() - 1);
}
  • pop: 删除数据(删除堆顶数据),堆顶数据与堆低数据交换,然后从堆顶位置向下调整即可。
void pop() 
{
	std::swap(_con[0], _con[_con.size() - 1]);
	AdjustDown(0);
}
  • top: 返回堆顶数据
T& top() 
{
	return _con[0];
}
const T& top()const 
{
	return _con[0];
}

5、size、empty、swap

  • size: 返回堆中的数据个数
size_t size()const 
{
	return _con.size();
}
  • empty: 判断堆是否为空
bool empty()const 
{
	return _con.empty();
}
  • swap: 交换函数
void swap(priority_queue& pq)
{
	std::swap(_con, pq._con);
}

​ 到这里,priority_queue 大致就实现成功了一半,因为这里我们只实现了大堆。

接下来,我们来看一下仿函数,如何实现大小堆。

仿函数

​ 仿函数,也是STL中比较中要的内容;

那为什么叫做仿函数呢?因为,他实际上是一个类,这个类重载了operator() 这样,我们就可以像函数应用实使用这个类。

先来看一下仿函数**less**:

	//这里可以使用struct(默认成员是共有)
	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;
		}
	};

​ 这里实现了仿函数**Less** 和**Greater**,我们可以像函数那样去使用这个类。

int main()
{
	HL::Less<int> l;
	cout << (l(11, 22)) << endl;
	return 0;
}

greater 与less类似,greater 是判断大的(就是,返回的是 x>y)。

​ 知道了仿函数,再回头看一下**priority_queue** 第三个模板参数,这个模板参数就是来控制大小堆的(默认是less,就是大堆;如果我们传greater 就是小堆。)

这样我们再修改一下,向上调整算法和向下调整算法,让我们可以通过这个模板参数来控制大小堆。

		//向下调整算法
		void AdjustDown(int parent)
		{
			Compare com;
			int child = 2 * parent + 1;
			while (child < _con.size())
			{
				//if (child < _con.size() - 1 && _con[child] < _con[child + 1])
				if (child < _con.size() - 1 && com(_con[child], _con[child + 1]))
				{
					child++;
				}
				//if (_con[parent] < _con[child])
				if (com(_con[parent] , _con[child]))
				{
					std::swap(_con[parent], _con[child]);
				}
				else
				{
					break;
				}
				parent = child;
				child = 2 * parent + 1;
			}
		}
		//向上调整算法
		void AdjustUp(int child)
		{
			Compare com;
			int parent = (child - 1) / 2;
			while (parent >= 0)
			{
				//if (_con[parent] < _con[child])
				if (com(_con[parent] , _con[child]))
				{
					std::swap(_con[parent], _con[child]);
				}
				else
				{
					break;
				}
				child = parent;
				parent = (child - 1) / 2;
			}
		}

​ 这里测试一下,使用模拟实现的堆进行排序,pq1建大堆,排降序;pq2建小堆,排升序。

void test_priority_queue()
{
	HL::priority_queue<int,vecotr<int>, HL::Less<int>> pq1;
	pq1.push(1);
	pq1.push(7);
	pq1.push(3);
	pq1.push(5);
	pq1.push(9);

	while (!pq1.empty())
	{
		cout << pq1.top() << " ";
		pq1.pop();
	}
	cout << endl;
	int arr[] = { 1,3,5,7,9,8,6,4,2 };
	HL::priority_queue<int,vector<int>,HL::Greater<int>> pq2(arr, arr + sizeof(arr) / sizeof(arr[0]));
	while (!pq2.empty())
	{
		cout << pq2.top() << " ";
		pq2.pop();
	}
	cout << endl;
}
int main()
{
	test_priority_queue();
	return 0;
}
9 7 5 3 1
1 2 3 4 5 6 7 8 9

​ 到这里,priority_queue 使用以及模拟实现就完成了,感谢各位的支持。

继续加油!!!

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2oul0hvapjsws

Greater<int>> pq2(arr, arr + sizeof(arr) / sizeof(arr[0]));
	while (!pq2.empty())
	{
		cout << pq2.top() << " ";
		pq2.pop();
	}
	cout << endl;
}
int main()
{
	test_priority_queue();
	return 0;
}
9 7 5 3 1
1 2 3 4 5 6 7 8 9

到这里,priority_queue 使用以及模拟实现就完成了,感谢各位的支持。

继续加油!!!

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2oul0hvapjsws


http://www.kler.cn/a/398474.html

相关文章:

  • 【C++派生类新增对象的初始化顺序】单继承下派生类新增成员对象的初始化顺序
  • 现代密码学|公钥密码体制 | RSA加密算法及其数学基础
  • 31DNS设置
  • mqtt学习笔记(一)
  • rust高级特征
  • 琐碎笔记——pytest实现前置、后置、参数化、跳过用例执行以及重试
  • 题目讲解17 判断链表中是否有环
  • BigQuery中jobUser和dataViewer的角色有什么不同
  • C++ 内联函数
  • 006.精读《Apache Paimon Docs - Concepts》
  • ArkTs简单入门案例:简单的图片切换应用界面
  • AWTK-WIDGET-WEB-VIEW 发布
  • C++11实现线程库
  • 21.3D surface
  • Python 子进程输出重定向以后,部分内容会出现在父进程的控制台屏幕上
  • .NET 一款SYSTEM权限隐藏的计划任务工具
  • vxe-grid table 校验指定行单元格的字段,只校验某个列的字段
  • Leetcode 3356. Zero Array Transformation II
  • uni-app快速入门(六)--rpx尺寸单位与Flex布局
  • 【网络安全面经】OSI七层模型每层都有什么协议
  • 【网络安全】SSL(一):为什么需要 Keyless SSL?
  • 023、ELK 从入门到实践
  • 【AI日记】24.11.17 看 GraphRAG 论文,了解月之暗面
  • 深度学习中常见的学习率调整策略
  • 蓝桥杯c++算法学习【4】之简单数论(阶乘约数、求值、循环小数、等差数列、最大比例:::非常典型的必刷例题!!!)
  • PyCharm2024.2.4安装