堆和优先队列
参考链接: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) ; } }