leetcode#347. 前 K 个高频元素

题目链接

题目描述:

给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

小根堆

  1. 遍历数组,哈希表录入频率
  2. 遍历哈希表,维护一个出现频率前k多的小根堆
  3. 优先队列已满,需要判断当前元素的频率是否大于优先队列的最小频率元素的频率,如果大于,则替换。
  4. 优先队列未满,进队即可

  注意:c++ 优先队列默认大根堆,设置时需要priority_queue< pair<int,int> , vector< pair<int,int> >, greater< pair<int,int> > >实现小根堆。

代码

class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int,int> record;  //(元素,频率)
        //遍历数组,录入频率
        for(int i = 0; i < nums.size(); i++){
            record[nums[i]]++;
        }
        int n = record.size();

        //扫描record。维护当前出现频率最多的k个元素
        //最小堆。如果当前元素的频率大于优先队列中最小频率元素的频率,则替换
        //优先队列中,按频率排序,所以数据对是(频率,元素)形式
        priority_queue< pair<int,int> , vector< pair<int,int> >, greater< pair<int,int> > > pq;
        for(auto iter = record.begin(); iter != record.end(); iter++){
            if(k == pq.size()){ //队列已满
                if(iter->second > pq.top().first){
                    pq.pop();
                    pq.push(make_pair(iter->second,iter->first));
                }
            }
            else{
                pq.push(make_pair(iter->second,iter->first));
            }
        }

        vector<int> result;
        while(!pq.empty()){
            result.push_back(pq.top().second);
            pq.pop();
        }
        return result;
    }
};

大根堆

  通常情况下求前k大用小根堆,求前k小用大根堆。我们知道,小根堆的方法的时间复杂度为O(nlogk),如果kn很接近呢?如果我非要用大根堆呢?其实也是可以的,只是需要转换一下思路即可。既然让我们求前k大,那么我们是不是可以转换一下思路求前n-k小呢?当然是可以的,而且c++的堆默认是大根堆,这样会稍微省一下声明的麻烦。但最后两者的执行用时一样,哭了~~
  注意:一定要考虑n == k的情况,不然你就会像我一样卡在测试用例nums = [1], k = 1上,错误信息为Char 17: runtime error: reference binding to null pointer of type 'const struct pair' (stl_iterator.h)。其实很好理解:对于该测试用例,n == k会使得一开始if( (n - k) == (int)pq.size() )就通过,那么在初始条件下,就会进行取堆顶出堆的操作,显然队列为空,不能访问。

代码

class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
         
        unordered_map<int,int> record;  //(元素,频率)
        //遍历数组,录入频率
        for(int i = 0; i < nums.size(); i++){
            record[nums[i]]++;
        }
        int n = record.size();
         
        vector<int> result;
        if(n != k){ // 如果 n == k,根据题意,直接统计 record 中的元素就好了
             
            //扫描record。维护当前出现频率最少的n-k个元素
            //最大堆。如果当前元素的频率小于优先队列中最大频率元素的频率,则替换
            //优先队列中,按频率排序,所以数据对是(频率,元素)形式
            priority_queue< pair<int,int> > pq;
            for(auto iter = record.begin(); iter != record.end(); iter++){
                if((n - k) == (int)pq.size()){ //队列已满
                    if(iter->second < pq.top().first){
                        pq.pop();
                        pq.push(make_pair(iter->second,iter->first));
                    }
                }
                else{
                    pq.push(make_pair(iter->second,iter->first));
                }
            }
         
            while(!pq.empty()){
                record.erase(pq.top().second);
                pq.pop();
            }
        }
         
        for(auto iter : record){
            result.push_back(iter.first);
        }
        return result;
    }
};

如果有错误或者不严谨的地方,请务必给予指正,十分感谢。
本人blog:http://breadhunter.gitee.io

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值