优先队列实际上是堆
根节点的值比所有节点值都大,称为最大堆;
根节点的值比所有节点值都小,称为最小堆;
基本操作:
- top 访问队头元素
- empty 队列是否为空
- size 返回队列内元素个数
- push 插入元素到队尾 (并排序)
- emplace 原地构造一个元素并插入队列
- pop 弹出队头元素
- swap 交换内容
第一种用法:
priority_queue<int> q1;//默认从大到小排序,整数中元素大的优先级高
第二种用法:
//升序队列 小顶堆
priority_queue <int,vector<int>,greater<int> > q;
//降序队列 大顶堆
priority_queue <int,vector<int>,less<int> >q;
//greater和less是std实现的两个仿函数(就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了)
第三种用法自定义:
struct cmp{
bool operator()(pair<int,int> m , pair<int,int>n){
return m.second > n.second;
}
};
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
priority_queue<pair<int,int>,vector<pair<int,int>>,cmp> q;
}
};
简单例题:347. 前 K 个高频元素 - 力扣(LeetCode) (leetcode-cn.com)
基本思想:
维护一个小顶堆,如果size() < k 则进入,如果size() == k 时,当堆顶小于当前次数大小,则弹出队头,该次数入队
struct cmp{
bool operator()(pair<int,int> m , pair<int,int>n){
return m.second > n.second;
}
};
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
int n = nums.size() , x = 0;
if(n == 1) return nums;
unordered_map<int,int>map;
for(auto x : nums)map[x]++;
priority_queue<pair<int,int>,vector<pair<int,int>>,cmp> q;
for(auto &[num,count] : map){
if(q.size() == k){
if(q.top().second < count){
q.pop();
q.push({num , count});
}
} else{
q.push({num , count});
}
}
vector<int>res;
while (!q.empty()){
res.emplace_back(q.top().first);
q.pop();
}
return res;
}
};
中等题目:1705. 吃苹果的最大数目 - 力扣(LeetCode) (leetcode-cn.com)
首先这道题是让你吃掉最多的苹果树,那么每天找最近过期的吃是最优解,别问我怎么知道
其次,既然是吃最近过期的那么,用map记录临期的最后一天和苹果个数,由于map的底层是红黑树,那么它本身就是默认升序的。分为两大块:1.在days天内,2.过期日期在days天外
class Solution {
public:
int eatenApples(vector<int>& apples, vector<int>& days) {
int ans = 0 , d = 0 ;
map<int,int> map;
while (d < days.size() || !map.empty()){
if(d < days.size()) map[days[d] + d -1] += apples[d];
//尝试从map中取出一个最接近过期但是没有过期的苹果
while (!map.empty()){
if(map.begin()->first < d || map.begin()->second == 0) map.erase(map.begin()->first);
else{
//如果找到了 我们就吃掉它
ans++;
//苹果数要减1
map.begin()->second--;
break;
}
}
d++;
}
return ans;
}
};
第二种方法:用优先队列
class Solution {
public int eatenApples(int[] apples, int[] days) {
//首先应该吃临近的过期食品,才能保证我吃的最多
int ans = 0 , d = 0;
//其次用小根堆记录过期时间和苹果个数
PriorityQueue<int[]> q = new PriorityQueue<>((a,b)->a[0]-b[0]); //升序为小根堆,默认以第一个元素升序
while (d < days.length || !q.isEmpty()){
//如果没到最后一天堆中已空,那么等待下一天长出苹果
if(d < days.length) q.add(new int[]{days[d] + d -1 , apples[d]});
//如果有过期苹果则删除
while (!q.isEmpty()){
if(q.peek()[0] < d || q.peek()[1] == 0) q.poll();
else {
q.peek()[1]--;
ans++;
break;
}
}
d++;
}
return ans;
}
}
264. 丑数 II - 力扣(LeetCode) (leetcode-cn.com)
解法一:动态规划(三指针)由于每个丑数都有2,3,5组成,1除外,故利用三个指针p1 = 1,p2 = 1,p3 = 1,p1*2,p2*3,p3*5 , 将每次的最小值记录下来,如果等于p1*2则p1++,如果等于p2*2则p2++,如果等于p3*2则p3++,话不多说上代码
class Solution {
public int nthUglyNumber(int n) {
int[] dp = new int[n+10];
dp[1] = 1;
int p1 = 1 , p2 = 1 , p3 = 1;
for(int i = 2 ; i <= n; i++){
int num1 = dp[p1]*2 , num2 = dp[p2]*3 , num3 = dp[p3]*5;
dp[i] = Math.min(Math.min(num1,num2) , num3);
if(dp[i] == num1)p1++;
if(dp[i] == num2)p2++;
if(dp[i] == num3)p3++;
}
return dp[n];
}
}
解法二:优先队列(小顶堆),由于后面的丑数能有前面的丑数乘2,3,5得到,1除外,由于会出现重复排序则利用map判断map集合中是否存在来决定是否入堆,利用堆的自然排序,每次取堆顶元素为第几个丑数,由于Java中的HashSet插入时可判断集合中是否存在比较方便,故Java判重
class Solution {
public int nthUglyNumber(int n) {
int[] arr = {2,3,5};
Set<Long> set = new HashSet<>();
PriorityQueue<Long> q = new PriorityQueue<>();
set.add(1L);
q.offer(1L);
int ugly = 0;
for(int i = 0 ; i < n; i++){
long curr = q.poll();
ugly = (int)curr;
for(int x : arr){
long next = curr*x;
if(set.add(next)) q.offer(next);
}
}
return ugly;
}
}
class Solution {
public:
int nthUglyNumber(int n) {
vector<int>arr = {2,3,5};
unordered_map<long,long>map;
priority_queue<long,vector<long>,greater<long>>q;
map[1]++;
q.push(1);
long ugly = 0;
for(int i = 1 ; i <= n; i++){
ugly = q.top();
q.pop();
for(int x : arr){
long y = x *ugly;
if(map[y] == 0){
map[y]++;
q.push(y);
}
}
}
return ugly;
}
};