C++之TopK求解

TopK问题,就是找到一堆数据中最大/最小、出现频率最高等问题。现在想一下其实解决TopK问题的话,有一下几个想法:
1、直接各种排序,前提是内存足够的话,如果数据量极大,那就不太抗得住了。
2、最大最小堆。保留K个值,然后其他值与之比较,对最大/最小堆进行增加删除操作。
3、分治思想。把所有数据分成无数的k段,然后再合并不同的段称为新的k段,直到最后剩下一个段,即为所求。
4、hash法

下面就用实例来上手一下:

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

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

class Solution {
public:
    //此时要定义为静态函数,否则在类中无法直接调用
    static bool cmp(pair<int,int> m1,pair<int,int> m2)
        {
            return m1.second>m2.second;
        }

    vector<int> topKFrequent(vector<int>& nums, int k) {
        //用map来统计每个元素的个数
        map<int,int> m;
        for(int i=0;i<nums.size();i++)
        {
            m[nums[i]]++;
        } 
        //将每个map放入vector中(用迭代器),便于后续的按数量多少进行排序
        vector<pair<int,int>> v;
        map<int,int>::iterator it;
        for(it=m.begin();it!=m.end();it++)
        {
            v.push_back(*it);
        }
        //依据自定义的静态函数,进行排序,数量多的在前面
        sort(v.begin(),v.end(),cmp);
        //将数量topk元素放入结果向量中
        vector<int> res;
        for(int i=0;i<k;i++)
        {
            res.push_back(v[i].first);
        }
        return res;
    }
};

Leetcode692. 前K个高频单词
给一非空的单词列表,返回前 k 个出现次数最多的单词。
**注意:**返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率,按字母顺序排序。

输入: [“i”, “love”, “leetcode”, “i”, “love”, “coding”], k = 2
输出: [“i”, “love”]
解析: “i” 和 “love” 为出现次数最多的两个单词,均为2次。
注意,按字母顺序 “i” 在 “love” 之前。

class Solution {
public:
    static bool cmp(pair<string,int> m1,pair<string,int> m2)
    {
        if(m1.second==m2.second) return m1.first<m2.first;
        return m1.second>m2.second;
    }
    
    vector<string> topKFrequent(vector<string>& words, int k) {
        
        map<string,int> m;
        for(int i=0;i<words.size();i++)
        {
            m[words[i]]++;
        }

        vector<pair<string,int>> v;
        map<string,int>::iterator it;
        for(it=m.begin();it!=m.end();it++)
        {
            v.push_back(*it);
        }

        sort(v.begin(),v.end(),cmp);

        vector<string> v1;
        for(int i=0;i<k;i++)
        {
            v1.push_back(v[i].first);
        }

        return v1;
    }
};

可以看出,这和第一题的答案几乎相同,只是自定义的比较函数考虑的数量相同情况下的顺序问题。所以这里也不需要再注释了。

下面利用最大最小堆来解决TopK问题:

构造大顶堆:

//构造一个空的优先队列(此优先队列默认为大顶堆)
priority_queue<int> big_heap;   
//另一种构建大顶堆的方法
priority_queue<int,vector<int>,less<int> > big_heap2;   

构造小顶堆:

//构造一个空的优先队列,此优先队列是一个小顶堆
priority_queue<int,vector<int>,greater<int> > small_heap;   

需要注意的是,如果使用less和greater,需要头文件:

#include 

剑指 Offer 40. 最小的k个数
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        vector<int> v(k,0);
        if (k == 0) return v; // 排除 0 的情况
        //priority_queue q;构造大顶堆的两种方式
        priority_queue<int,vector<int>,less<int>> q;
        for(int i=0;i<k;i++)
        {
            q.push(arr[i]);
        }
        for(int i=k;i<arr.size();i++)
        {
            if(arr[i]<q.top())
            {
                q.pop();
                q.push(arr[i]);
            }
        }
        for (int i = 0; i < k; ++i) {
            v[i] = q.top();
            q.pop();
        }
        return v;
    }
};

你可能感兴趣的:(C++之TopK求解)