堆和优先队列
参考链接:https://www.cnblogs.com/xzxl/p/7266404.html
优先队列
能够完成下列操作的数据结构叫做优先队列。
- 插入一个数值
- 取出最小的数值(获得数值,并且删除)
能够使用二叉树高效地解决上述问题的,是一种叫做“堆” 的数据结构。
堆的性质,主要是通过堆排序来体现。
Java和c++中都有相应的数据结构,下面介绍c++ STL中的优先队列。
#include<queue>
一,相关定义
优先队列容器与队列一样,只能从队尾插入元素,从队首删除元素。但是它有一个特性,就是队列中最大的元素总是位于队首,所以出队时,并非按照先进先出的原则进行,而是将当前队列中最大的元素出队。这点类似于给队列里的元素进行了由大到小的顺序排序。元素的比较规则默认按元素值由大到小排序,可以重载“<”操作符来重新定义比较规则。
优先级队列可以用向量(vector)或双向队列(deque)来实现(注意list container不能用来实现queue,因为list的迭代器不是任意存取iterator,而pop中用到堆排序时是要求randomaccess iterator 的!):
priority_queue<vector<int>, less<int> > pq1; 等价于priority_queue<int> q;// 大根堆,默认也是大根堆
priority_queue<int,vector<int>,greater<int> > q2;// 小根堆
其成员函数有“判空(empty)” 、“尺寸(Size)” 、“栈顶元素(top)” 、“压栈(push)” 、“弹栈(pop)”等。
二、priority_queue
基本操作:
empty() 如果队列为空,则返回真
pop() 删除对顶元素,删除第一个元素
push() 加入一个元素
size() 返回优先队列中拥有的元素个数
top() 返回优先队列对顶元素,返回优先队列中有最高优先级的元素
在默认的优先队列中,优先级高的先出队。在默认的int型中先出队的为较大的数。
头文件:
声明方式:
1、普通方法:
2、自定义优先级:
#include<iostream>
#include<queue>
using namespace std;
struct Node{
int x;
int y;
}node[5];
struct cmp
{
bool operator()(const Node &a,const Node &b) { return a.x > b.x; }
};
int main()
{
priority_queue<Node,vector<Node>,cmp> q1;
node[1].x=1;
node[1].y=2;
node[2].x=-1;
node[2].y=2;
node[3].x=9;
node[3].y=0;
node[4].x=3;
node[4].y=2;
q1.push(node[1]);
q1.push(node[2]);
q1.push(node[3]);
q1.push(node[4]);
while(!q1.empty()){
printf("%3d",q1.top().x);
q1.pop();
}
return 0;
}
3、结构体声明方式:
POJ 2431
题目大意:一辆车从起点出发(装有一定量的油)驶向终点,路上不同位置有加油站,每个加油站都有能加油的上限,问汽车到达终点需要加油次数最少为多少?(若不能到达终点则输出-1),目前距离终点L个单位,当前拥有P单位油
Sample Input
4 4 4 5 2 11 5 15 10 25 10
Sample Output
2
分析:加油站的数量比较多,逐个比较是不可能的。我们稍微变换一下思考方式。在卡车开往终点的途中,只有在加油站才可以加油。但是,如果认为“在到达加油站时,就获得了一次在之后的任何时候都可以加Bi单位汽油的权利”,在解决问题上应该也是一样的。而在之后需要加油时,就认为是在之前经过的加油站加的油就可以了。那么,因为希望到达终点时加油次数尽可能少,所以当燃料为0了之后再进行加油看上去是-一个不错的方法。在燃料为0时,应该使用哪个加油站来加油呢?显然,应该选能加油量Bi最大的加油站。
利用优先队列,经过每个加油站都先把能加的油加入到优先队列当中。等到车没油了,就把优先队列中的最大值弹出(此时相当于加了一次油)。如果优先队列为空,则表示无法到达终点。
注意点:1、本题输入的距离是到终点的距离,应该把距离转化为离起点的距离(这样数据比较好处理);
2、输入的距离并不是按顺序的,所以应该排序(用内置的sort)(冒泡会超时);
3、将终点也看成一个加油站,处理起来比较方便;
c++版本解法
#include<cstdio>
#include<algorithm>
#include<queue>
#define M 10005
using namespace std;
struct gas
{
int dis;//注意这里的距离是距终点的距离
int fule;
};
bool cmp(gas A, gas B)
{
return A.dis < B.dis;
}
gas a[M];
priority_queue<int> que;
int N, L, P;
int main()
{
scanf("%d", &N);
for(int i = 0; i < N; i++)
scanf("%d %d", &a[i].dis, &a[i].fule);
scanf("%d %d", &L, &P);
for(int i = 0; i < N; i++)
a[i].dis = L - a[i].dis;
//把加油站按照距离起点距离从小到大排序
sort(a, a + N, cmp);
//把终点也看成一个加油站
a[N].dis = L;
a[N].fule = 0;
N++;//绝对不能少,加一个油站
//count:加油次数,position:现在所在位置,tank:油箱中的油量
int count = 0, position = 0, tank = P;
for(int i = 0; i < N; i++)
{
//经过每个加油站要前进的距离
int d = a[i].dis - position;
//不断加油直到可以行驶到下一个加油站
while(tank - d < 0)
{
if(que.empty())
{
puts("-1");
return 0;
}
tank = tank + que.top();
que.pop();
count++;
}
//行驶到了该油站,把该油站的油放入优先队列中
tank = tank - d;
position = a[i].dis;
que.push(a[i].fule);
}
printf("%d\n", count);
return 0;
}
Java版本解法:
package 优先队列;
import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Scanner;
public class Main {
static class Gas implements Comparable<Gas>{
int dis;
int fule;
public Gas(int dis, int fule) {
this.dis = dis;
this.fule = fule;
}
@Override
public int compareTo(Gas o) {
return this.dis>o.dis?1:-1;
}
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int N=sc.nextInt();
Gas arr[]=new Gas[N+1];
for (int i = 0; i < N; i++) {
int dis=sc.nextInt();
int fule=sc.nextInt();
Gas gas=new Gas(dis, fule);
arr[i]=gas;
}
int L=sc.nextInt();
int P=sc.nextInt();
for(int i = 0; i < N; i++)
arr[i].dis = L - arr[i].dis;
arr[N]=new Gas(L, 0);
Arrays.sort(arr);
//count:加油次数,position:现在所在位置,tank:油箱中的油量
int count = 0, position = 0, tank = P;
//java中默认创建的是小根堆,所以要改成大根堆
PriorityQueue<Integer> que= new PriorityQueue<>(N+1, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
});
for(int i = 0; i < N+1; i++)
{
//经过每个加油站要前进的距离
int d = arr[i].dis - position;
//不断加油直到可以行驶到下一个加油站
while(tank - d < 0)
{
if(que.isEmpty())
{
throw new RuntimeException("-1");
}
tank = tank + que.poll();
count++;
}
//行驶到了该油站,把该油站的油放入优先队列中
tank = tank - d;
position = arr[i].dis;
que.add(arr[i].fule);
}
System.out.println(count);
}
}
Fence Repair ( POJ 3253)
农夫约翰为了修理栅栏,要将一块很长的木板切割成N块。准备切成的木板的长度为L、L2、... LN,未切割前木板的长度恰好为切割后木板长度的总和。每次切断木板时,需要的开销为这块木板的长度。例如长度为21的木板要切成长度为5、8、8的三块木板。长21的木板切成长为13和8的板时,开销为21。再将长度为13的板切成长度为5和8的板时,开销是13。于是合计开销是34。请求出按照目标要求将木板切割完最小的开销是多少。
Input
Lines 2..N+1: Each line contains a single integer describing the length of a needed plank
Output
Sample Input
3 8 5 8
Sample Output
34
#include <iostream>
#include <algorithm>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
#define MAX_N 10000
int N,L[MAX_N];
void solve() {
ll ans=0;
//声明一个从小到大取出数值的优先队列
priority_queue<int,vector<int>,greater<int>> que;
for(int i=0;i<N;i++){
que. push(L[i]);
}
//循环到只剩一块木板为止
while (que.size() > 1) {
//取出最短的木板和次短的木板int 11,12 ;
int l1 = que.top();
que.pop() ;
int l2 = que.top();
que.pop() ;
//把两块木板合并
ans+=l1+l2;
que. push(l1 + l2) ;
}
printf ( "%lld\n", ans) ;
}
int main() {
cin >> N;
for (int i = 0; i < N; i++) {
cin >> L[i];
}
solve();
return 0;
}
JAVA解法
package 优先队列;
import java.util.PriorityQueue;
import java.util.Scanner;
public class Main1 {
static int N;
static int[] L;
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
N=sc.nextInt();
L=new int[N];
for (int i = 0; i < N; i++) {
L[i]=sc.nextInt();
}
solve();
}
private static void solve() {
long ans=0;
PriorityQueue<Integer> que=new PriorityQueue<>();//默认小根堆
for (int i = 0; i < N; i++) {
que.add(L[i]);
}
while (que.size() > 1) {
//取出最短的木板和次短的木板int 11,12 ;
int l1 = que.poll();
int l2 = que.poll();
//把两块木板合并
ans+=l1+l2;
que.add(l1 + l2) ;
}
System.out.printf( "%d\n", ans) ;
}
}

浙公网安备 33010602011771号