【C++】priority_queue的使用及其模拟实现

一、priority_queue的使用

1. 基本使用

优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆。

函数声明说明
priority_queue()/priority_queue(first, last)构造一个空的优先级队列
empty ()检测优先级队列是否为空,是返回true,否则返回false
top ()返回优先级队列中最大(最小元素),即堆顶元素
push(x)在优先级队列中插入元素x
pop ()删除优先级队列中最大(最小)元素,即堆顶元素

例:

#include <iostream>
#include <vector>
#include <queue>
#include <functional>   // greater算法的头文件
using namespace std;
void TestPriorityQueue()
{
	// 默认情况下,创建的是大堆,其底层按照小于号比较
	vector<int> v{ 3,2,7,6,0,4,1,9,8,5 };
	priority_queue<int> q1;
	for (auto& e : v)
		q1.push(e);
	cout << q1.top() << endl;//输出9

	// 如果要创建小堆,将第三个模板参数换成greater比较方式
	priority_queue<int, vector<int>, greater<int>> q2(v.begin(), v.end());
	cout << q2.top() << endl;//输出0
}
int main()
{
	TestPriorityQueue();
	return 0;
}

2. 自定义排序

如果在priority_queue中放自定义类型的数据,需要在自定义类型中提供> 或者< 的重载

#include <iostream>
#include <vector>
#include <queue>
#include <functional>   // greater算法的头文件
using namespace std;
class Date
{
public:
	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);
	}
	friend ostream& operator<< (ostream& _cout, const Date& d)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}
private:
	int _year;
	int _month;
	int _day;
};
void TestPriorityQueue()
{
	// 大堆,需要用户在自定义类型中提供<的重载
	priority_queue<Date> q1;
	q1.push(Date(2023, 1, 25));
	q1.push(Date(2023, 1, 26));
	q1.push(Date(2023, 1, 30));
	cout << q1.top() << endl;//输出:2023-1-30

	// 如果要创建小堆,需要用户提供>的重载
	priority_queue<Date, vector<Date>, greater<Date>> q2;
	q2.push(Date(2023, 1, 25));
	q2.push(Date(2023, 1, 26));
	q2.push(Date(2023, 1, 30));
	cout << q2.top() << endl;//输出:2023-1-25
}
int main()
{
	TestPriorityQueue();
	return 0;
}

小心使用指针类型作为优先级队列的元素:

priority_queue<Date*> q1;
q1.push(new Date(2023, 1, 25));
q1.push(new Date(2023, 1, 26));
q1.push(new Date(2023, 1, 30));
cout << *q1.top() << endl;//输出结果随机

如果像上面这样写,<比较的是地址


二、priority_queue的模拟实现

1. 仿函数

简单概述:

在使用priority_queue时,用到的greater和less都是仿函数(functors),又称为函数对象(function objects)。

仿函数是一种具有函数特质的对象,或者说行为类似函数的对象(通过重载函数调用运算符实现,operator()),它能够像函数一样的被调用:

#include <iostream>
#include <functional>   // greater算法的头文件
using namespace std;
void TestFunctors()
{
	greater<int> g;
	//boolalpha意思是从此以后对bool值的输出,改为字符串"true" 或 "false"
	cout << boolalpha << g(10, 20) << endl;//false
	cout << greater<int>()(2, 1) << endl;//true
}
int main()
{
	TestFunctors();
	return 0;
}

仿函数的作用:

在C语言阶段实现堆时,想要指定大小堆,可行的办法是函数指针。但函数指针不能满足STL对泛型和抽象的要求——函数指针无法和STL其他组件(如配接器)搭配。

一般而言,不会有人在简单的场景下使用仿函数实现一些并不复杂的功能。
仿函数的主要用途是为了搭配STL算法。如sort:

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>   // greater算法的头文件
using namespace std;
void TestFunctors()
{
	vector<int> v{ 1,3,8,4,5,6,7,0,2,9 };
	sort(v.begin(), v.end(), greater<int>());//降序
	for (int& e : v) cout << e << " ";
}
int main()
{
	TestFunctors();
	return 0;
}

2. 调整算法

  • 向上调整
void adjust_up(size_t child)
{
	size_t parent = (child - 1) / 2;
	while (child > 0)
	{
		//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 adjust_down(size_t parent)
{
	size_t child = parent * 2 + 1;
	while (child < _con.size())
	{
		//if (child+1 < _con.size() && _con[child] < _con[child+1])
		if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
		{
			child++;
		}

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

3. 整体代码

priority_queue的底层结构就是堆,因此只需对堆进行通用的封装即可。

#pragma once
#include <vector>
#include <algorithm>
namespace nb
{
	template<class T>
	class less
	{
	public:
		bool operator()(const T& x, const T& y) const
		{
			return x < y;
		}
	};

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

	template<class T, class Container = std::vector<T>, class Compare = less<T>>
	class priority_queue
	{
	private:
		Container _con;
		Compare com;
	public:
		priority_queue()
		{}

		template <class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
			:_con(first, last)
		{
			for (int i = (_con.size() - 1 - 1) / 2; i >= 0; --i)
			{
				adjust_down(i);
			}
		}

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

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

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

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

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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二木 同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值