C++优先队列、Java堆

优先队列实际上是堆

根节点的值比所有节点值都大,称为最大堆;

根节点的值比所有节点值都小,称为最小堆;

基本操作:

  • 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;
    }
};

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值