优先队列的相关知识与例题

介绍

优先队列是在正常队列的基础上加了优先级,保证每次的队首元素都是优先级最大的。

可以实现每次从优先队列中取出的元素都是队列中优先级最大的一个。

它的底层是通过来实现的

//头文件
#include<queue>
//初始化定义
priority_queue<int> q;

函数方法

代码含义
q.top()访问队首元素 n(1)O(1)
q.push()入队 O(logN)
q.pop()堆顶(队首)元素出队O(logN)
q.size()队列元素个数O(1)
q.empty()是否为空O(1)
注意没有clear()不提供该方法
优先队列只能通过top()访问队首元素(优先级最高的元素)

设置优先级

1 基本数据类型的优先级

priority_queue<int> pq; // 默认大根堆, 即每次取出的元素是队列中的最大值
priority_queue<int, vector<int>, greater<int>> q; // 小根堆, 每次取出的元素是队列中的最小值

Cpp

参数解释:

  • 第一个参数:就是优先队列中存储的数据类型

  • 第二个参数:

    vector<int> 是用来承载底层数据结构堆的容器,若优先队列中存放的是double型数据,就要填vector< double >
    总之存的是什么类型的数据,就相应的填写对应类型。同时也要改动第三个参数里面的对应类型。

  • 第三个参数:
    less<int> 表示数字大的优先级大,堆顶为最大的数字
    greater<int>表示数字小的优先级大,堆顶为最小的数字
    int代表的是数据类型,也要填优先队列中存储的数据类型


下面介绍基础数据类型优先级设置的写法:

  1. 基础写法(非常常用):
priority_queue<int> q1; // 默认大根堆, 即每次取出的元素是队列中的最大值
priority_queue<int, vector<int>, less<int> > q2; // 大根堆, 每次取出的元素是队列中的最大值,同第一行

priority_queue<int, vector<int>, greater<int> > q3; // 小根堆, 每次取出的元素是队列中的最小值

Cpp

  1. 自定义排序(不常见,主要是写着麻烦):

下面的代码比较长,基础类型优先级写着太麻烦,用第一种即可。

struct cmp1 {
	bool operator()(int x, int y) {
		return x > y;
	}
};
struct cmp2 {
	bool operator()(const int x, const int y) {
		return x < y;
	}
};
priority_queue<int, vector<int>, cmp1> q1; // 小根堆
priority_queue<int, vector<int>, cmp2> q2; // 大根堆

Cpp

2 高级数据类型(结构体)优先级

即优先队列中存储结构体类型,必须要设置优先级,即结构体的比较运算(因为优先队列的堆中要比较大小,才能将对应最大或者最小元素移到堆顶)。

优先级设置可以定义在结构体内进行小于号重载,也可以定义在结构体外

//要排序的结构体(存储在优先队列里面的)
struct Point {
	int x, y;
};

Cpp

  • 版本一:自定义全局比较规则
//定义的比较结构体
//注意:cmp是个结构体 
struct cmp {//自定义堆的排序规则 
	bool operator()(const Point& a,const Point& b) {
		return a.x < b.x;
	}
};

//初始化定义, 
priority_queue<Point, vector<Point>, cmp> q; // x大的在堆顶

Cpp

  • 版本二:直接在结构体里面写

因为是在结构体内部自定义的规则,一旦需要比较结构体,自动调用结构体内部重载运算符规则。

结构体内部有两种方式:

方式一 :

struct node {
	int x, y;
	friend bool operator < (Point a, Point b) {//为两个结构体参数,结构体调用一定要写上friend
		return a.x < b.x;//按x从小到大排,x大的在堆顶
	}
};

Cpp

方式二 :(推荐此种)

struct node {
    int x, y;
    bool operator < (const Point &a) const {//直接传入一个参数,不必要写friend
        return x < a.x;//按x升序排列,x大的在堆顶
    }
};

Cpp

优先队列的定义

priority_queue<Point> q;

Cpp

注意: 优先队列自定义排序规则和sort()函数定义cmp函数很相似,但是最后返回的情况是相反的。即相同的符号,最后定义的排列顺序是完全相反的。
所以只需要记住sort的排序规则和优先队列的排序规则是相反的就可以了。

当理解了堆的原理就会发现,堆调整时比较顺序是孩子和父亲节点进行比较,如果是 > ,那么孩子节点要大于父亲节点,堆顶自然是最小值。

 存储特殊类型的优先级

存储pair类型

  • 排序规则:
    默认先对pairfirst进行降序排序,然后再对second降序排序
    first先排序,大的排在前面,如果first元素相同,再对second元素排序,保持大的在前面。

pair请参考下文

#include<bits/stdc++.h>
using namespace std;
int main() {
    priority_queue<pair<int, int> >q;
	q.push({7, 8});
	q.push({7, 9});
	q.push(make_pair(8, 7));
    while(!q.empty()) {
        cout << q.top().first << " " << q.top().second << "\n";
        q.pop();
    }
    return 0;
}

Cpp

结果:
8 7
7 9
7 8

例题

P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

Description

在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。

每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n−1 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。

因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 11 ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。

例如有 33 种果子,数目依次为 11 , 22 , 99 。可以先将 11 、 22 堆合并,新堆数目为 33 ,耗费体力为 33 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 1212 ,耗费体力为 1212 。所以多多总共耗费体力 =3+12=15=3+12=15 。可以证明 1515 为最小的体力耗费值。

Input

共两行。
第一行是一个整数n(1≤n≤10000) ,表示果子的种类数。

第二行包含 n 个整数,用空格分隔,第 i 个整数 ai​(1≤ai​≤20000) 是第 i 种果子的数目。

Output

一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 231231 。

Sample 1

InputcopyOutputcopy
3 
1 2 9 
15

Hint

对于 30%30% 的数据,保证有 n≤1000:

对于 50%50% 的数据,保证有 n≤5000;

对于全部的数据,保证有n≤10000。

Sponsor

我的解决代码

#include<iostream>
#include<queue> 
using namespace std;
int main (){
     priority_queue<int,vector<int> ,greater<int> > pq;
     int n;
     cin>>n;
     for(int i=0,x;i<n;i++) {
         cin>>x;
         pq.push(x);
     }
     int ans=0;
    for(int i=0;i<n-1;i++){
        int num1=pq.top();
        pq.pop();
        int num2=pq.top();
        pq.pop();
        ans=ans+num1+num2;
        pq.push(num1+num2);
    }
    cout<<ans;
    
    
    return 0;
} 


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Zzcat.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值