有关优先级队列的4道题 priority_queue


数组第k大的元素

题目链接
在这里插入图片描述

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int, vector<int>, greater<int>> que;
        for(int i = 0; i < nums.size(); i++){
            que.push(nums[i]);
            if(que.size() > k){
                que.pop();
            }
        }
        return que.top();
    }
};

快排法:

class Solution {
    int adjustBase(vector<int>& nums,  int l, int r){
        int temp = nums[l];
        while(l < r){
            while(l < r && nums[r] <= temp) r--;
            nums[l] = nums[r];
            while(l < r && nums[l] >= temp) l++;
            nums[r] = nums[l];
        }
        nums[r] = temp;
        return l;
    }
public:
    int findKthLargest(vector<int>& nums, int k) {
        int l = 0;
        int r = nums.size() - 1;
        while(1){
            int t = adjustBase(nums, l, r);
            if(t > k - 1) r = t - 1;
            else if(t < k - 1) l = t + 1;
            else return nums[t];
        }
        return 0;
    }
};

最小k个数

题目链接
在这里插入图片描述
维护一个k大小的大根堆,将所有元素插入,堆中元素个数超过k时就将堆顶元素弹出,这样最后堆中所有元素即为最小k个数(因为大数会被优先弹出堆)

class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        vector<int> vec(k, 0);
        if (k == 0) { // 排除 0 的情况
            return vec;
        }
        priority_queue<int> Q;
        for (int i = 0; i < k; ++i) {
            Q.push(arr[i]);
        }
        for (int i = k; i < (int)arr.size(); ++i) {
            if (Q.top() > arr[i]) {
                Q.pop();
                Q.push(arr[i]);
            }
        }
        for (int i = 0; i < k; ++i) {
            vec[i] = Q.top();
            Q.pop();
        }
        return vec;
    }
};

另一种基于快排的方法:

class Solution {
    int adjustBase(vector<int>& nums,  int l, int r){
        int temp = nums[l];
        while(l < r){
            while(l < r && nums[r] >= temp) r--;
            nums[l] = nums[r];
            while(l < r && nums[l] <= temp) l++;
            nums[r] = nums[l];
        }
        nums[r] = temp;
        return l;
    }
public:
    vector<int> smallestK(vector<int>& arr, int k) {
        if(arr.size() == 0) return {};
        if(arr.size() <= k) return arr;
        int i = 0;
        int j = arr.size() - 1; 
        vector<int> res;
        while(1){
            int t = adjustBase(arr, i, j);
            if(t > k ) j = t - 1;
            else if(t < k) i = t + 1;
            else{
                res.assign(arr.begin(), arr.begin() + t);
                break;
            }
        }
        return res;
    }
};

前k个高频单词

题目链接
在这里插入图片描述
找元素频率的问题,一般都用哈希来统计元素的频率
然后维护一个k大小的小根堆,将哈希中所有元素插入(哈希可以自动去重)
最后堆中元素即为前k高频的单词
pair作为一个类更推荐用emplace_back,有一部分性能提升

注意的是:C++的优先级队列自定义比较函数时,与sort等的自定义比较函数正好相反,比如,实现大根堆就需要用<小于号(理解上是数组递增为小根堆才对,其实不然)

class Solution {
public:
    vector<string> topKFrequent(vector<string>& words, int k) {
        unordered_map<string, int> cnt;
        for (auto& word : words) {
            cnt[word]++;
        }
        auto cmp = [](const pair<string, int>& a, const pair<string, int>& b) {
            return a.second == b.second ? a.first < b.first : a.second > b.second;
        };
        priority_queue<pair<string, int>, vector<pair<string, int>>, decltype(cmp)> que(cmp);
        for (auto& it : cnt) {
            que.emplace(it);
            if (que.size() > k) {
                que.pop();
            }
        }
        vector<string> ret(k);
        for (int i = k - 1; i >= 0; i--) {
            ret[i] = que.top().first;
            que.pop();
        }
        return ret;
    }
};

数据流的中位数

题目链接
在这里插入图片描述
维护两个堆,大根堆用来存储较小半部分元素,小根堆存储较大半部分元素
并且为了保证当总元素个数为奇数时,中位数始终在大根堆里面,因此,我们需要定义小根堆元素个数一旦超过大根堆元素个数就立刻调整,把小根堆元素堆顶元素加入大根堆中。同时,为了保持平衡,大根堆元素个数也要保证最多只比小根堆元素个数最多多一个。

class MedianFinder {
public:
    /** initialize your data structure here. */
    MedianFinder() {

    }
    priority_queue<int, vector<int>, greater<int>> up; //小顶堆
    priority_queue<int> down; //大顶堆
    void addNum(int num) {
        if (down.empty() || num <= down.top()) down.push(num); //插入元素小于大顶堆堆顶,插入大顶堆中。
        else up.push(num); //否则插入小顶堆中。
        //调整大顶堆和小顶堆元素
        if (down.size() == up.size() + 2) {//大顶堆元素个数大于小顶堆元素个数超过一个时进行调整。
            up.push(down.top());
            down.pop();
        } else if (up.size() == down.size() + 1) {//小顶堆元素个数大于小顶堆元素个数一个时,就要进行调整,为了使中点保持在大顶堆上。
        //始终保持左边大顶堆的元素个数比右边小顶堆的元素个数等于,或最多大于一个
            down.push(up.top());
            up.pop();
        }
    }
    double findMedian() {
        if ((down.size() + up.size()) % 2) return down.top(); //元素总数为奇数时。
        return (down.top() + up.top()) / 2.0; //元素总数为偶数时。
    }
};

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder* obj = new MedianFinder();
 * obj->addNum(num);
 * double param_2 = obj->findMedian();
 */
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

VioletEvergarden丶

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

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

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

打赏作者

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

抵扣说明:

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

余额充值