【C++】优先级队列(priority_queue)的用法与实现

目录

一、概念:

二、仿函数(Functor):

概念:

应用:

三、底层实现:

基本操作:

完整代码:

测试示例:


一、概念:

        优先级队列(priority_queue)在功能上类似于堆(heap)这个数据结构,在优先级队列中,元素的出队顺序(或检索顺序)基于它们的优先级,而不是它们进入队列的顺序。具有最高优先级的元素将最先出队,而具有最低优先级的元素将最后出队(或反之,取决于具体实现),就像是在大堆中堆顶元素是堆中最大的值,在删除堆顶元素时总是删除的最大值,而不是取决于它们入堆的顺序。

定义:

#include <queue> // 引头文件

// 类似于大堆; less表示按照递减(从大到小)的顺序插入元素
priority_queue<int, vector<int>, less<int>> s; 

// 类似于小堆; greater表示按照递增(从小到大)的顺序插入元素
priority_queue<int, vector<int>, greater<int>> s; 

二、仿函数(Functor)

概念:

        仿函数(Functor)也称为函数对象,是一种可调用的对象,一般通过重载函数调用操作符(operator())来实现。这使得仿函数类具有像函数一样的特性:可以接受参数并返回值。

        仿函数的目的为:使一个类的使用看上去像一个函数,从而方便地应用于算法、STL容器以及其他需要函数对象的场合。

仿函数的优点:

        1、可以拥有状态,这意味着仿函数可以在运行时动态地改变其行为。

        2、与纯函数相比,仿函数在某些情况下可能具有更快的执行速度。

注意:

        仿函数并不是真正的函数,而是具有函数行为的类对象。因此,它们可以像类一样存储和访问数据,同时又具有函数的调用特性。

应用:

例如在C++ algorithm 库 中的 sort 函数 的第三个参数:

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

comp:(可选):一个接受两个参数并返回一个布尔值的函数或函数对象

默认的排序是升序,此时我们不用库中提供的降序仿函数,自己实现一个简单的降序仿函数:

template<class T>
struct Greater // 函数对象(仿函数)功能实现
{
	bool operator()(const T& x, const T& y){
		return x > y;
	}
};

void Test()
{
	vector<int> v = { 5,3,7,8,3,0,6,3,1,2,9 };
	sort(v.begin(), v.end(), Greater<int>()); // 匿名调用

    // Greater<int> s1;
    // sort(v.begin(), v.end(), s1); // 显式调用

	for (auto e : v){
		cout << e << " ";
	}
}

三、底层实现:

基本操作:

1、push():将元素入队列。操作与堆类似,向尾部插入并不断向上调整至合适位置】 

2、pop():删除队列中优先级最高的元素。【操作与堆类似,首尾交换后删除尾部,队头元素不断向下调整至合适位置】

3、top():获取并返回队列中优先级最高的元素。

4、size():获取并返回队列大小。【返回值为 unsigned int(size_t)类型】

5、empty():判断队列是否为空。【返回值为bool类型。队列为空返回true,不空返回false】

完整代码:

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


template<class T, class Con = vector<T>, class Com = Less<T>>
class priority_queue
{
public:

	// 向上调整
	void adjust_up(int child)
	{
		Com com; // 模板实例化一个函数对象,默认Less,取小
		int parent = (child - 1) / 2;
		while (child > 0)
		{
			//if (_con[child] > _con[parent])
			// if(_con[parent] < _con[child])
			if (com(_con[parent], _con[child])) // 为真执行,为假跳出
			{
				swap(_con[parent], _con[child]);
				child = parent;
				parent = (child - 1) / 2;
			}
			else
			{
				break;
			}
		}
	}
	// 向下调整
	void adjust_down(size_t parent)
	{
		Com com;
		size_t child = parent * 2 + 1;
		while (child < _con.size())
		{
			// 找左右孩子中大的那个与父节点交换,换算关系如下
			//if (child + 1 < _con.size() && _con[child + 1] > _con[child])
			//if (child + 1 < _con.size() && _con[child] < _con[child + 1])
			if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
			{
				++child;
			}
			//if (_con[child] > _con[parent])
			//if (_con[parent] < _con[child])
			if (com(_con[parent], _con[child]))
			{
				swap(_con[parent], _con[child]);
				parent = child;
				child = parent * 2 + 1;
			}
			else
			{
				break;
			}
		}
	}

	void push(const T& val)
	{
		_con.push_back(val);
		adjust_up(_con.size() - 1); // 传入刚插入的子节点下标
	}
	void pop()
	{
		swap(_con[0], _con[_con.size() - 1]);
		_con.pop_back();
		adjust_down(0);
	}

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

	bool empty()
	{
		return _con.empty();
	}
private:
	Con _con;
};

测试示例:

给一个数组(升序)以此按序进入队列,此时优先级队列的功能类似大堆,应该按照降序返回:

int main()
{
	vector<int> v = { 1,2,3,4,5,6,7,8,9,10 };
	priority_queue<int> q1; // 默认大堆 Less
	for (auto i : v)
	{
		q1.push(i);
	}

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

    return 0;
}

测试结果:

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值