返回顶部

priority_queue优先队列

定义于头文件queue

可能需要vector

priority_queue 是容器适配器,它提供常数时间的(默认)最大元素查找,对数代价的插入与释出。

C++中,以int类型为例,定义priority_queue<int>heap;表示的是大根堆,也即顶元素是优先队列中的最大值,但平时使用中需要使用小根堆,即顶元素是优先队列中的最小值。故需要进行比较函数的重载。C++提供了以下方式:

重载小于运算符

  • 对于基本数据类型,可以使用如下方式实现大/小根堆

    #include<queue>
    #include<vector>
    std::priority_queue<int>heap;//定义一个大根堆
    std::priority_queue<int, std::vector<int>, std::greater<int>>heap;//定义一个小根堆
    
  • 对于自定义基本数据类型

    或者你想对基本数据类型的比较运算符进行更改,我们以二维平面上的点为例,声明相应的结构体。

    #include<queue>
    #include<vector>
    struct Point {
    	int x, y;
    	Point(int _x = 0 , int _y = 0):x(_x) , y(_y){}
    	bool operator < (const Point& point)const {
    		return x < point.x;//以横坐标大小为优先级
    	}
    };
    std::priority_queue<Point>heap;//定义一个大根堆
    
    #include<queue>
    #include<vector>
    struct Point {
    	int x, y;
    	Point(int _x = 0 , int _y = 0):x(_x) , y(_y){}
    	bool operator < (const Point& point)const {
    		return x > point.x;//横坐标为优先级
    	}
    };
    std::priority_queue<Point>heap;//定义一个小根堆
    

    当然在结构体外部重载小于运算符也可以。对于STL库的几个常用的有序容器,一般只需要重载小于运算符就可以正常使用。

仿函数

仿函数(functor)又称为函数对象(function object)是一个能行使函数功能的类。仿函数的语法几乎和我们普通的函数调用一样,不过作为仿函数的类,都必须重载operator()运算符。

struct Point {
	int x, y;
	Point(int _x = 0 , int _y = 0):x(_x) , y(_y){}
};
struct Compare {
	bool operator () (const Point& a, const Point& b) {
		return a.x < b.x;
	}
};
std::priority_queue<Point, std::vector<Point>, Compare>heap;//定义一个大根堆
struct Point {
	int x, y;
	Point(int _x = 0 , int _y = 0):x(_x) , y(_y){}
};
struct Compare {
	bool operator () (const Point& a, const Point& b) {
		return a.x > b.x;
	}
};
std::priority_queue<Point, std::vector<Point>, Compare>heap;//定义一个小根堆

lambda表达式

在实际使用中,我们可能需要对于自定义类型即定义小根堆,又定义大根堆,但小于运算符只能重载一个。所以使用lambda表示式可以很好的解决上述问题,且更直观。

#include<queue>
#include<vector>
#include<iostream>
struct Point {
	int x, y;
	Point(int _x = 0 , int _y = 0):x(_x) , y(_y){}
};
auto Compare1 = [&](const Point& a, const Point& b)->bool {
	return a.x < b.x;
};
auto Compare2 = [&](const Point& a, const Point& b)->bool {
	return a.x > b.x;
};
//大根堆
std::priority_queue<Point, std::vector<Point>, decltype(Compare1)> heap1(Compare1);
//小根堆
std::priority_queue<Point, std::vector<Point>, decltype(Compare2)> heap2(Compare2);
int main() {
	//大根堆测试
	heap1.push({ 1 , 2 });
	heap1.push({ 4 , 3 });
	heap1.push({ 0 , 1 });
	//小根堆测试
	heap2.push({ 1 , 2 });
	heap2.push({ 4 , 3 });
	heap2.push({ 0 , 1 });
	//大根堆
	std::cout << "大根堆" << std::endl;
	while (!heap1.empty()) {
		auto [x, y] = heap1.top();
		heap1.pop();
		std::cout << x << " " << y << std::endl;
	}
	//小根堆
	std::cout << "小根堆" << std::endl;
	while (!heap2.empty()) {
		auto [x, y] = heap2.top();
		heap2.pop();
		std::cout << x << " " << y << std::endl;
	}
	return 0;
}
/*
//输出
大根堆
4 3
1 2
0 1
小根堆
0 1
1 2
4 3
*/

Java 中的优先队列

Java中的优先队列类为PriorityQueue。默认是小根堆,即顶元素为优先队列中的最小的元素。对于基本数据类型,使用如下方式定义小根堆:

 PriorityQueue<Integer> heap = new PriorityQueue<>();//定义小根堆

由于Java中没有提供运算符重载。但他提供了比较方便的lambda表达式更改比较方式的方法。

还是以Integer为例,定义大根堆:

PriorityQueue<Integer> heap = new PriorityQueue<>((a , b)->{
            return b - a;
        });//定义大根堆

注意,Java中的比较需要返回三个值,即小于-1,等于0,大于1。所以lambda表达式中使用减法可以减少代码的书写。

对于没有减法的基本类,如String,可以使用其自带的compareTo函数,可以按照如下实现:

PriorityQueue<String> heap = new PriorityQueue<>();//小根堆
PriorityQueue<String> heap = new PriorityQueue<>((a , b)->{
            return b.compareTo(a);
        });//大根堆

但实际还有问题,因为如果我们使用的类是Long,直接使用减法会导致报错。

 PriorityQueue<Long> heap = new PriorityQueue<Long>((a , b)->{
           b - a;
        });//大根堆

即按照上述方式声明大根堆会出现问题,有时候在使用Integer时,若使用减法可能会存在溢出。故在比较明确数据范围不会产生溢出(在Integer范围内)可以使用减法,否则需要调用类自带的compareTo方法规避上述风险。即声明方式如下:

PriorityQueue<Long> heap = new PriorityQueue<>((a , b)->{
           return b.compareTo(a);
        });//大根堆

出现上述问题的原因是两个Long类型相减还是Long类型(long类型),而compareTo的结果是Integer类型也即int类型,但Java不支持从longint的隐式类型转化。即:

long a = 1;
int b = a;//error
int c = (int)a;//correct
posted @ 2022-01-18 10:33  cherish-lgb  阅读(160)  评论(0编辑  收藏  举报