C++中 priority_queue 使用详细说明

一、priority_queue 的介绍

priority_queue 又称为优先队列,其底层是用来进行实现的。

在优先队列中,队首元素一定是当前队列中优先级最高的那一个。

在任何时候往优先队列里面加入(push)元素,优先队列底层的数据结构堆(heap)会随时调整结构,使得每次的队首元素都是优先级最大的。

(这里的优先级是规定出来的)

二、priority_queue 的定义

单独定义一个 priority_queue:

priority_queue<typename> name; 
//这里的typename可以是任何基本类型,例如 int、double、char、结构体等,也可以是STL标准容器,例如vector、set、queue等。


 

三、priority_queue 容器内元素的访问

和队列不一样的是,优先队列没有 front()函数与 back()函数,而只能通过 top()函数来访问队首元素(也可以称为堆顶元素),也就是优先级最高的元素。

#include <stdio.h>
#include <queue>
using namespace std;

int  main()
{
	priority_queue<int> q;
	for (int i = 0; i < 6; i++)
	{
		q.push(i); //push(i) 用来将 i 压入队列,因此依次入队 0 1 2 3 4 5
	}
	printf("%d\n", q.top());
	return 0;
}

 

四、priority_queue 常用函数实例解析

(1)push()

push(x) 将令 x 入队,时间复杂度为 O(logN),其中 N 为当前优先队列中的元素个数。

(2)top()

top() 可以获得队首元素(即堆顶元素),时间复杂度为 O(1)。

(3)pop()

pop()令队首元素(即堆顶元素)出队,时间复杂度为 O(logN),其中 N 为当前优先队列中的元素个数。

#include <stdio.h>
#include <queue>
using namespace std;

int  main()
{
	priority_queue<int> q;
	q.push(5);
	q.push(2);
	q.push(6);
	printf("%d\n", q.top());
	q.pop();
	printf("%d\n", q.top());
	return 0;
}

 

(4)empty()

empty() 检测 优先队列 是否为空,返回 true 则空,返回 false 则非空 。时间复杂度为 O(1)。

(5)size()

size() 返回 优先队列 中元素的个数,时间复杂度为 O(1)。

#include <stdio.h>
#include <queue>
using namespace std;

int  main()
{
	priority_queue<int> q;
	if (q.empty() == true)
	{
		printf("EMPTY! \n");
	}
	else
	{
		printf("NOT EMPTY! \n");
	}
	q.push(5);
	q.push(2);
	q.push(6);
	if (q.empty() == true)
	{
		printf("EMPTY! \n");
	}
	else
	{
		printf("NOT EMPTY! \n");
	}

	printf("%d\n", q.size());
	return 0;
}

 

五、priority_queue 内元素优先级的设置

如何定义优先队列内的优先级是运用好优先队列的关键,下面分别介绍基本数据类型(例如 int,double,char)与结构体类型的优先级设置方法。

(1)基本数据类型的优先级设置

此处指的基本数据类型就是 int 型,double 型,char 型等可以直接使用的数据类型,优先队列对它们的优先级设置一般是数字大的优先级越高,因此队首元素就是优先队列内元素最大的那个(如果 char 型,则是字典序最大的)。

对于基本数据类型来说,下面两种优先队列的定义是等价的(以 int 为例,注意最后两个 > 之间有一个空格:

priority_queue<int> q;
priority_queue<int,vector<int>,less<int> >q;

可以发现,第二种定义方式的尖括号内多出了两个参数:一个是 vector<int> , 另一个是 less<int>。

其中 vector<int>(也就是第二个参数)填写的是来承载底层数据结构堆(heap)的容器,如果第一个参数是 double 型或 char 型,则此处只需要填写 vector<double> 或 vector<char>;

而第三个参数 less<int> 则是对第一个参数的比较类,less<int> 表示数字大的优先级越大,而 greater<int> 表示数字小的优先级越大。

#include <stdio.h>
#include <queue>
using namespace std;

int  main()
{
	priority_queue<int,vector<int>,greater<int> > q;
	q.push(5);
	q.push(2);
	q.push(6);
	printf("%d\n", q.top());
	return 0;
}

(2)结构体的优先级设置

事实上,即便是基本数据类型,也可以使用下面讲解的结构体的优先级设置方法,只不过第三个参数的写法不太一样了。下面来看结构体的优先级设置方法。

现在希望按水果的价格高的为优先级高,就需要重载(overload)小于号 "<"。重载是指对已有的运算符进行重新定义,也就是说,可以改变小于号的功能。

可以看到,fruit 结构体中增加了一个函数,其中 “friend” 为友元。后面的 bool operator < (fruit f1,fruit f2) 对 fruit 类型的操作符 “<" 进行了重载(重载大于号会编译错误,因为从数学上来说只需要重载小于号,即 f1>f2 等价于判断 f2<f1,而 f1==f2 则等价于判断 !(f1<f2)&&!(f2<f1)。)

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

struct fruit   //对水果的名称和价格建立一个结构体。
{
	string name;
	int price;
	friend bool operator < (fruit f1,fruit f2)
	{
		return f1.price > f2.price;
	}
}f1,f2,f3;

int  main()
{
	priority_queue<fruit> q;
	f1.name = "桃子";
	f1.price = 3;
	f2.name = "梨子";
	f2.price = 4;
	f3.name = "苹果";
	f3.price = 1;
	q.push(f1);
	q.push(f2);
	q.push(f3);
	cout << q.top().name << " " << q.top().price << endl;
	return 0;
}

把重载的函数写在结构体外面,同时用 struct 包装起来。

如下这样,即便是基本数据类型或者其他STL容器(例如set),也可以通过同样的方式来定义优先级。

注意:在这种情况下,需要用之前的第二种定义方式来定义优先队列:priority_queue<int,vector<int>,less<int> >q;

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

struct fruit
{
	string name;
	int price;
}f1,f2,f3;

struct cmp
{
	bool operator () (fruit f1, fruit f2)
	{
		return f1.price > f2.price;
	}
};

int  main()
{
	priority_queue<fruit,vector<fruit>,cmp> q;
	f1.name = "桃子";
	f1.price = 3;
	f2.name = "梨子";
	f2.price = 4;
	f3.name = "苹果";
	f3.price = 1;
	q.push(f1);
	q.push(f2);
	q.push(f3);
	cout << q.top().name << " " << q.top().price << endl;
	return 0;
}

最后指出,如果结构体内的数据较为庞大(例如出现了字符串或者数组),建议使用引用来提高效率,此时比较类的参数中需要加上 "const" 和 "&"。如下:

friend bool operator < (const fruit &f1, const fruit &f2)
{
	return f1.price > f2.price;
}

bool operator () (const fruit &f1, const fruit &f2)
{
	return f1.price > f2.price;
}

 

六、priority_queue 的常见用途

priority_queue 可以解决一些贪心问题,也可以对 Dijkstra 算法进行优化(因为优先队列的本质是堆)。

注意:使用 top()函数前,必须用 empty()判断优先队列是否为空,否则可能因为队空而出现错误。

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值